Merge pull request #18 from zoominsoftware/customize

Allow customized whitelists.
This commit is contained in:
Michael Snoyman 2018-07-02 08:51:35 +03:00 committed by GitHub
commit be213a84a4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 59 additions and 15 deletions

View File

@ -1,5 +1,5 @@
name: xss-sanitize name: xss-sanitize
version: 0.3.5.7 version: 0.3.6
synopsis: sanitize untrusted HTML to prevent XSS attacks synopsis: sanitize untrusted HTML to prevent XSS attacks
description: run untrusted HTML through Text.HTML.SanitizeXSS.sanitizeXSS to prevent description: run untrusted HTML through Text.HTML.SanitizeXSS.sanitizeXSS to prevent
XSS attacks. see README.md <http://github.com/yesodweb/haskell-xss-sanitize> for XSS attacks. see README.md <http://github.com/yesodweb/haskell-xss-sanitize> for

View File

@ -12,6 +12,7 @@ module Text.HTML.SanitizeXSS
-- * Custom filtering -- * Custom filtering
, filterTags , filterTags
, safeTags , safeTags
, safeTagsCustom
, balanceTags , balanceTags
-- * Utilities -- * Utilities
@ -33,7 +34,7 @@ import Network.URI ( parseURIReference, URI (..),
isAllowedInURI, escapeURIString, uriScheme ) isAllowedInURI, escapeURIString, uriScheme )
import Codec.Binary.UTF8.String ( encodeString ) import Codec.Binary.UTF8.String ( encodeString )
import Data.Maybe (catMaybes) import Data.Maybe (mapMaybe)
-- | Sanitize HTML to prevent XSS attacks. This is equivalent to @filterTags safeTags@. -- | Sanitize HTML to prevent XSS attacks. This is equivalent to @filterTags safeTags@.
@ -53,8 +54,10 @@ sanitizeBalance = filterTags (balanceTags . safeTags)
balanceTags :: [Tag Text] -> [Tag Text] balanceTags :: [Tag Text] -> [Tag Text]
balanceTags = balance [] balanceTags = balance []
-- | Parse the given text to a list of tags, apply the given filtering function, and render back to HTML. -- | Parse the given text to a list of tags, apply the given filtering
-- You can insert your own custom filtering but make sure you compose your filtering function with 'safeTags'! -- function, and render back to HTML. You can insert your own custom
-- filtering, but make sure you compose your filtering function with
-- 'safeTags' or 'safeTagsCustom'.
filterTags :: ([Tag Text] -> [Tag Text]) -> Text -> Text filterTags :: ([Tag Text] -> [Tag Text]) -> Text -> Text
filterTags f = renderTagsOptions renderOptions { filterTags f = renderTagsOptions renderOptions {
optMinimize = \x -> x `member` voidElems -- <img><img> converts to <img />, <a/> converts to <a></a> optMinimize = \x -> x `member` voidElems -- <img><img> converts to <img />, <a/> converts to <a></a>
@ -74,17 +77,36 @@ balance unclosed (TagOpen name as : tags) =
TagOpen name as : balance (name : unclosed) tags TagOpen name as : balance (name : unclosed) tags
balance unclosed (t:ts) = t : balance unclosed ts balance unclosed (t:ts) = t : balance unclosed ts
-- | Filters out any usafe tags and attributes. Use with filterTags to create a custom filter. -- | Filters out unsafe tags and sanitizes attributes. Use with
-- filterTags to create a custom filter.
safeTags :: [Tag Text] -> [Tag Text] safeTags :: [Tag Text] -> [Tag Text]
safeTags [] = [] safeTags = safeTagsCustom safeTagName sanitizeAttribute
safeTags (t@(TagClose name):tags)
| safeTagName name = t : safeTags tags -- | Filters out unsafe tags and sanitizes attributes, like
| otherwise = safeTags tags -- 'safeTags', but uses custom functions for determining which tags
safeTags (TagOpen name attributes:tags) -- are safe and for sanitizing attributes. This allows you to add or
| safeTagName name = TagOpen name -- remove specific tags or attributes on the white list, or to use
(catMaybes $ map sanitizeAttribute attributes) : safeTags tags -- your own white list.
| otherwise = safeTags tags --
safeTags (t:tags) = t:safeTags tags -- @safeTagsCustom safeTagName sanitizeAttribute@ is equivalent to
-- 'safeTags'.
--
-- @since 0.3.6
safeTagsCustom ::
(Text -> Bool) -- ^ Select safe tags, like
-- 'safeTagName'
-> ((Text, Text) -> Maybe (Text, Text)) -- ^ Sanitize attributes,
-- like 'sanitizeAttribute'
-> [Tag Text] -> [Tag Text]
safeTagsCustom _ _ [] = []
safeTagsCustom safeName sanitizeAttr (t@(TagClose name):tags)
| safeName name = t : safeTagsCustom safeName sanitizeAttr tags
| otherwise = safeTagsCustom safeName sanitizeAttr tags
safeTagsCustom safeName sanitizeAttr (TagOpen name attributes:tags)
| safeName name = TagOpen name (mapMaybe sanitizeAttr attributes) :
safeTagsCustom safeName sanitizeAttr tags
| otherwise = safeTagsCustom safeName sanitizeAttr tags
safeTagsCustom n a (t:tags) = t : safeTagsCustom n a tags
safeTagName :: Text -> Bool safeTagName :: Text -> Bool
safeTagName tagname = tagname `member` sanitaryTags safeTagName tagname = tagname `member` sanitaryTags

View File

@ -11,9 +11,19 @@ test f actual expected = do
let result = f actual let result = f actual
result @?= expected result @?= expected
sanitized :: Text -> Text -> Expectation sanitized, sanitizedB, sanitizedC :: Text -> Text -> Expectation
sanitized = test sanitize sanitized = test sanitize
sanitizedB = test sanitizeBalance sanitizedB = test sanitizeBalance
sanitizedC = test sanitizeCustom
sanitizeCustom :: Text -> Text
sanitizeCustom = filterTags $ safeTagsCustom mySafeName mySanitizeAttr
where
mySafeName t = t `elem` myTags || safeTagName t
mySanitizeAttr (key, val) | key `elem` myAttrs = Just (key, val)
mySanitizeAttr x = sanitizeAttribute x
myTags = ["custtag"]
myAttrs = ["custattr"]
main :: IO () main :: IO ()
main = hspec $ do main = hspec $ do
@ -87,3 +97,15 @@ main = hspec $ do
sanitizedB "<img></img>" "<img />" sanitizedB "<img></img>" "<img />"
it "interleaved" $ it "interleaved" $
sanitizedB "<i>hello<b>world</i>" "<i>hello<b>world<i></i></b></i>" sanitizedB "<i>hello<b>world</i>" "<i>hello<b>world<i></i></b></i>"
describe "customized white list" $ do
it "does not filter custom tags" $ do
let custtag = "<p><custtag></custtag></p>"
sanitizedC custtag custtag
it "filters non-custom tags" $ do
sanitizedC "<p><weird></weird></p>" "<p></p>"
it "does not filter custom attributes" $ do
let custattr = "<p custattr=\"foo\"></p>"
sanitizedC custattr custattr
it "filters non-custom attributes" $ do
sanitizedC "<p weird=\"bar\"></p>" "<p></p>"