diff --git a/lib/NodeUtils.js b/lib/NodeUtils.js index 53e5927..60c84bf 100644 --- a/lib/NodeUtils.js +++ b/lib/NodeUtils.js @@ -110,17 +110,17 @@ function escapeAttr(s) { function attrname(a) { var ns = a.namespaceURI; if (!ns) - return a.localName; + return escape(a.localName); if (ns === NAMESPACE.XML) - return 'xml:' + a.localName; + return 'xml:' + escape(a.localName); if (ns === NAMESPACE.XLINK) - return 'xlink:' + a.localName; + return 'xlink:' + escape(a.localName); if (ns === NAMESPACE.XMLNS) { if (a.localName === 'xmlns') return 'xmlns'; - else return 'xmlns:' + a.localName; + else return 'xmlns:' + escape(a.localName); } - return a.name; + return escape(a.name); } /** @@ -176,7 +176,8 @@ function serializeOne(kid, parent) { case 1: //ELEMENT_NODE var ns = kid.namespaceURI; var html = ns === NAMESPACE.HTML; - var tagname = (html || ns === NAMESPACE.SVG || ns === NAMESPACE.MATHML) ? kid.localName : kid.tagName; + var unescapedTagname = (html || ns === NAMESPACE.SVG || ns === NAMESPACE.MATHML) ? kid.localName : kid.tagName; + var tagname = escape(unescapedTagname); s += '<' + tagname; @@ -221,10 +222,10 @@ function serializeOne(kid, parent) { break; case 7: //PROCESSING_INSTRUCTION_NODE const content = escapeProcessingInstructionContent(kid.data); - s += ''; + s += ''; break; case 10: //DOCUMENT_TYPE_NODE - s += '", - "noQuirksBodyHtml": "" + "html": "", + "noQuirksBodyHtml": "" } }, { @@ -10990,7 +10990,7 @@ ] } ], - "html": "
", + "html": "
", "noQuirksBodyHtml": "
" } }, @@ -58367,7 +58367,7 @@ ] } ], - "html": ">-->", + "html": ">-->", "noQuirksBodyHtml": ">-->" } }, @@ -65403,7 +65403,7 @@ ] } ], - "html": "

\n", + "html": "

\n", "noQuirksBodyHtml": "

\n" } }, @@ -79080,8 +79080,8 @@ ] } ], - "html": "", - "noQuirksBodyHtml": "" + "html": "", + "noQuirksBodyHtml": "" } }, { diff --git a/test/xss.js b/test/xss.js index 1e60da5..9b12ea6 100644 --- a/test/xss.js +++ b/test/xss.js @@ -352,4 +352,66 @@ exports.verifyEscapeProcessingInstructionContent = function () { for (const [rawContent, expected] of cases) { NodeUtils.ɵescapeProcessingInstructionContent(rawContent).should.equal(expected); } +} + +exports.testXssInAttributeName = function () { + var document = domino.createDocument(); + var attr = document.createAttribute('safe'); + attr.localName = '>'; + document.appendChild(doctype); + + var serialized = document.serialize(); + serialized.should.not.containEql(''; + document.appendChild(pi); + + var serialized = document.serialize(); + serialized.should.not.containEql('