Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 11 additions & 3 deletions lib/NodeUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -133,12 +133,20 @@ function attrname(a) {
return a.name;
}

function serializedTagName(node) {
var ns = node.namespaceURI;
return (ns === NAMESPACE.HTML || ns === NAMESPACE.SVG || ns === NAMESPACE.MATHML)
? node.localName
: node.tagName;
}

function fallbackRawContentTags(node) {
const tags = [];
while (node) {
if (node.nodeType === 1 /*ELEMENT_NODE*/) {
if (node.namespaceURI === NAMESPACE.HTML && hasRawContentFallback[node.tagName]) {
tags.push(node.localName);
const tagname = serializedTagName(node);
if (tagname && hasRawContentFallback[tagname.toUpperCase()]) {
tags.push(tagname);
}
node = node.parentNode;
} else if (node.nodeType === 11 /*DOCUMENT_FRAGMENT_NODE*/ && node._host) {
Expand Down Expand Up @@ -281,7 +289,7 @@ 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 tagname = serializedTagName(kid);

s += '<' + tagname;

Expand Down
64 changes: 64 additions & 0 deletions test/xss.js
Original file line number Diff line number Diff line change
Expand Up @@ -1001,6 +1001,70 @@ exports.fallbackRawTextProcessingInstructionEscapesAncestorClosingTag = async fu
}
};

exports.fallbackRawTextProcessingInstructionUsesSerializedAncestorName = async function () {
const cases = [
{
label: 'SVG noembed',
namespace: 'http://www.w3.org/2000/svg',
qualifiedName: 'noembed',
serializedName: 'noembed',
},
{
label: 'MathML noembed',
namespace: 'http://www.w3.org/1998/Math/MathML',
qualifiedName: 'noembed',
serializedName: 'noembed',
},
{
label: 'SVG iframe',
namespace: 'http://www.w3.org/2000/svg',
qualifiedName: 'iframe',
serializedName: 'iframe',
},
{
label: 'qualified HTML iframe',
namespace: 'http://www.w3.org/1999/xhtml',
qualifiedName: 'x:iframe',
serializedName: 'iframe',
},
];

for (const testCase of cases) {
const document = domino.createDocument('');
const fallbackEl = document.createElementNS(
testCase.namespace,
testCase.qualifiedName,
);

fallbackEl.appendChild(
document.createProcessingInstruction(
'x',
`</${testCase.serializedName} `,
),
);

const img = document.createElement('img');
img.setAttribute('src', 'x');
img.setAttribute('onerror', 'alert(1)');
fallbackEl.appendChild(img);

document.body.appendChild(fallbackEl);

const serialized = document.body.serialize();

serialized.should.containEql(
`<?x &lt;/${testCase.serializedName} ?>`,
`${testCase.label}: matching fallback closing tag was not escaped: ${serialized}`,
);

const alerted = await alertFired(serialized);
alerted.should.equal(
false,
`alert fired for PI under ${testCase.label}: ${serialized}`,
);
}
};

exports.commentNodeEscapesAbruptClosingComment = async function () {
// A comment content that starts with `>` or `->` is closed by the parser
// right away (the "abrupt-closing-of-empty-comment" parse error), so the rest
Expand Down