@@ -54,7 +54,7 @@ import Text.Pandoc.Walk
5454import qualified Data.Map as M
5555import Data.Maybe ( fromMaybe, isJust)
5656import Data.List ( intercalate, isInfixOf, isPrefixOf )
57−import Data.Char ( isDigit )
57+import Data.Char ( isDigit, isLetter, isAlphaNum )
5858import Control.Monad ( guard, mzero, void, unless )
5959import Control.Arrow ((***))
6060import Control.Applicative ( (<|>) )
@@ -1032,13 +1032,22 @@ htmlTag f = try $ do
10321032 let (next : _) = canonicalizeTags $ parseTagsOptions
10331033 parseOptions{ optTagWarning = False } inp
10341034 guard $ f next
1035+
1036+ -- <www.boe.es/buscar/act.php?id=BOE-A-1996-8930#a66>
1037+ -- should NOT be parsed as an HTML tag, see #2277,
1038+ -- so we exclude . even though it's a valid character
1039+ -- in XML elemnet names
1040+ let isNameChar c = isAlphaNum c || c == ':' || c == '-' || c == '_'
1041+ let isName s = case s of
1042+ [] -> False
1043+ (c:cs) -> isLetter c && all isNameChar cs
1044+
10351045 let handleTag tagname = do
1036− -- <www.boe.es/buscar/act.php?id=BOE-A-1996-8930#a66>
1037− -- should NOT be parsed as an HTML tag, see #2277
1038− guard $ not ('.' `elem` tagname)
1046+ -- basic sanity check, since the parser is very forgiving
1047+ -- and finds tags in stuff like x<y)
1048+ guard $ isName tagname
10391049 -- <https://example.org> should NOT be a tag either.
10401050 -- tagsoup will parse it as TagOpen "https:" [("example.org","")]
1041− guard $ not (null tagname)
10421051 guard $ last tagname /= ':'
10431052 rendered <- manyTill anyChar (char '>')
10441053 return (next, rendered ++ ">")
@@ -1050,7 +1059,9 @@ htmlTag f = try $ do
10501059 char '>'
10511060 return (next, "<!--" ++ s ++ "-->")
10521061 | otherwise -> fail "bogus comment mode, HTML5 parse error"
1053− TagOpen tagname _attr -> handleTag tagname
1062+ TagOpen tagname attr -> do
1063+ guard $ all (isName . fst) attr
1064+ handleTag tagname
10541065 TagClose tagname -> handleTag tagname
10551066 _ -> mzero
10561067