Ensure balanced tags

This commit is contained in:
Michael Snoyman 2010-10-31 23:03:40 +02:00
parent 405710bbae
commit 99a0388dc2
2 changed files with 29 additions and 11 deletions

View File

@ -9,20 +9,38 @@ import Network.URI ( parseURIReference, URI (..),
isAllowedInURI, escapeURIString, uriScheme ) isAllowedInURI, escapeURIString, uriScheme )
import Codec.Binary.UTF8.String ( encodeString ) import Codec.Binary.UTF8.String ( encodeString )
import qualified Data.Map as Map
-- | santize the html to prevent XSS attacks. See README.md <http://github.com/gregwebs/haskell-xss-sanitize> for more details -- | santize the html to prevent XSS attacks. See README.md <http://github.com/gregwebs/haskell-xss-sanitize> for more details
sanitizeXSS :: String -> String sanitizeXSS :: String -> String
sanitizeXSS = renderTagsOptions renderOptions { sanitizeXSS = renderTagsOptions renderOptions {
optMinimize = \x -> x `elem` ["br","img"] -- <img><img> converts to <img />, <a/> converts to <a></a> optMinimize = \x -> x `elem` ["br","img"] -- <img><img> converts to <img />, <a/> converts to <a></a>
} . safeTags . parseTags } . safeTags Map.empty . parseTags
where where
safeTags :: [Tag String] -> [Tag String] safeTags :: Map.Map String Int -> [Tag String] -> [Tag String]
safeTags [] = [] safeTags m [] =
safeTags (t@(TagClose name):tags) | safeTagName name = t:(safeTags tags) concatMap go $ Map.toList m
| otherwise = safeTags tags where
safeTags (TagOpen name attributes:tags) go (name, i) = replicate i $ TagClose name
| safeTagName name = TagOpen name (filter safeAttribute attributes) : safeTags tags safeTags m (t@(TagClose name):tags)
| otherwise = safeTags tags | safeTagName name =
safeTags (t:tags) = t:safeTags tags case Map.lookup name m of
Nothing -> safeTags m tags
Just i ->
let m' = if i == 1
then Map.delete name m
else Map.insert name (i - 1) m
in t : safeTags m' tags
| otherwise = safeTags m tags
safeTags m (TagOpen name attributes:tags)
| safeTagName name =
let m' =
case Map.lookup name m of
Nothing -> Map.insert name 1 m
Just i -> Map.insert name (i + 1) m
in TagOpen name (filter safeAttribute attributes) : safeTags m' tags
| otherwise = safeTags m tags
safeTags m (t:tags) = t:safeTags m tags
safeTagName :: String -> Bool safeTagName :: String -> Bool
safeTagName tagname = tagname `member` sanitaryTags safeTagName tagname = tagname `member` sanitaryTags

View File

@ -1,8 +1,8 @@
import Text.HTML.SanitizeXSS import Text.HTML.SanitizeXSS
main = do main = do
let test = " <a href='http://safe.com'>safe</a><a href='unsafe://hack.com'>anchor</a> <img src='evil://evil.com' /> <unsafe></foo> <bar /> <br></br> " let test = " <a href='http://safe.com'>safe</a><a href='unsafe://hack.com'>anchor</a> <img src='evil://evil.com' /> <unsafe></foo> <bar /> <br></br> <b>Unbalanced</div>"
let actual = (sanitizeXSS test) let actual = (sanitizeXSS test)
let expected = " <a href=\"http://safe.com\">safe</a><a>anchor</a> <img /> <br /> " let expected = " <a href=\"http://safe.com\">safe</a><a>anchor</a> <img /> <br /> <b>Unbalanced</b>"
putStrLn $ "testing: " ++ test putStrLn $ "testing: " ++ test
putStrLn $ if actual == expected then "pass" else "failure\n" ++ "\nexpected:" ++ (show expected) ++ "\nactual: " ++ (show actual) putStrLn $ if actual == expected then "pass" else "failure\n" ++ "\nexpected:" ++ (show expected) ++ "\nactual: " ++ (show actual)