DuyTa's picture
Upload folder using huggingface_hub
bc20498 verified
'use strict';
var domino = require('../lib');
var puppeteer = require("puppeteer");
var NodeUtils = require('../lib/NodeUtils');
exports = exports.xss = {};
// Tests for HTML serialization concentrating on possible "Mutation based
// XSS vectors"; see https://cure53.de/fp170.pdf
// If we change HTML serialization such that any of these tests fail, please
// review the change very carefully for potential XSS vectors!
async function alertFired(html) {
let alerted = false;
const page = await incognito.newPage();
page.on("dialog", async dialog => {
alerted = true;
await dialog.accept();
});
await page.goto("data:text/html," + html, {waitUntil: 'load'});
return alerted;
}
/** @type {puppeteer.Browser} */
let browser;
/** @type {puppeteer.BrowserContext} */
let incognito;
exports.before = async function() {
browser = await puppeteer.launch({headless:"new"});
incognito = await browser.createIncognitoBrowserContext();
}
exports.after = async function() {
await incognito.close();
await browser.close();
}
exports.fp170_31 = function() {
var document = domino.createDocument(
'<img src="test.jpg" alt="``onload=xss()" />'
);
// In particular, ensure alt attribute is quoted, not: ...alt=``onload=xss()
document.body.innerHTML.should.equal(
'<img src="test.jpg" alt="``onload=xss()">'
);
};
exports.fp170_32 = function() {
var document = domino.createDocument(
'<article xmlns="urn:img src=x onerror=xss()//">123'
);
// XXX check XML serialization as well, once that's implemented
// In particular, ensure that the xmlns string isn't used as an XML prefix
// when serializing (and, of course, that attribute value is quoted)
document.body.innerHTML.should.equal(
'<article xmlns="urn:img src=x onerror=xss()//">123</article>'
);
};
exports.fp170_33 = function() {
var document = domino.createDocument(
'<p style="font -family:\'ar\\27\\3bx\\3aexpression\\28xss\\28\\29\\29\\3bial\'"></p>'
);
// Be sure domino doesn't decode the backslash escapes
// (especially in the future if we parse the CSS values more fully)
document.body.innerHTML.should.equal(
'<p style="font -family:\'ar\\27\\3bx\\3aexpression\\28xss\\28\\29\\29\\3bial\'"></p>'
);
};
exports.fp170_34 = function() {
var document = domino.createDocument(
'<p style="font -family:\'ar&quot;;x=expression(xss())/*ial\'"></p>'
);
// Be sure domino re-encodes the entities correctly
// (especially in the future if we parse the CSS values more fully)
document.body.innerHTML.should.equal(
'<p style="font -family:\'ar&quot;;x=expression(xss())/*ial\'"></p>'
);
};
exports.fp170_35 = function() {
var document = domino.createDocument(
'<img style="font-fa\\22onload\\3dxss\\28\\29\\20mily:\'arial\'" src="test.jpg" />'
);
// Again, ensure domino doesn't decode the backslash escapes
// (especially in the future if we parse the CSS values more fully)
document.body.innerHTML.should.equal(
'<img style="font-fa\\22onload\\3dxss\\28\\29\\20mily:\'arial\'" src="test.jpg">'
);
};
exports.fp170_36 = function() {
var document = domino.createDocument(
'<style>*{font-family:\'ar&lt;img src=&quot;test.jpg&quot; onload=&quot;xss()&quot;/&gt;ial\'}</style>'
);
// Ensure that HTML entities are properly encoded inside <style>
document.head.innerHTML.should.equal(
'<style>*{font-family:\'ar&lt;img src=&quot;test.jpg&quot; onload=&quot;xss()&quot;/&gt;ial\'}</style>'
);
};
exports.fp170_37 = function() {
var document = domino.createDocument(
'<p><svg><style>*{font-family:\'&lt;&sol;style&gt;&lt;img/src=x&Tab;onerror=xss()&sol;&sol;\'}</style></svg></p>'
);
// Ensure that HTML entities are properly encoded inside <style>
document.body.innerHTML.should.equal(
'<p><svg><style>*{font-family:\'&lt;/style&gt;&lt;img/src=x\tonerror=xss()//\'}</style></svg></p>'
);
};
exports.escapeAngleBracketsInDivAttr = function() {
var document = domino.createDocument(
`<div>You don't have JS! Click<a href="#" title="Search for </div><script>alert(1)</script> without JS">here</a> to go to the no-js website.</div>`
);
document.body.innerHTML.should.equal(
`<div>You don't have JS! Click<a href="#" title="Search for &lt;/div&gt;&lt;script&gt;alert(1)&lt;/script&gt; without JS">here</a> to go to the no-js website.</div>`
);
};
exports.escapeAngleBracketsInNoScriptAttr = function() {
var document = domino.createDocument(
`<div><noscript>You don't have JS! Click<a href="#" title="Search for </noscript><script>alert(1)</script> without JS">here</a> to go to the no-js website.</noscript></div>`
);
document.body.innerHTML.should.equal(
`<div><noscript>You don't have JS! Click<a href="#" title="Search for &lt;/noscript&gt;&lt;script&gt;alert(1)&lt;/script&gt; without JS">here</a> to go to the no-js website.</noscript></div>`
);
};
exports.styleMatchingClosingTagInRawText = function() {
const document = domino.createDocument('');
const style = document.createElement("style");
style.textContent = "abc</style><script>alert(1)</script>";
document.body.appendChild(style);
// Ensure that HTML entities are properly encoded inside <style>
document.body.serialize().should.equal(
'<style>abc&lt;/style><script>alert(1)</script></style>'
);
const html = document.serialize();
return alertFired(html).should.eventually.be.false('alert fired for: ' + html);
};
exports.styleMatchingClosingTagSkipsInsideCommentedContent = function() {
const document = domino.createDocument('');
const style = document.createElement("style");
style.textContent = "abc<!--</style>--><script>alert(1)</script>";
document.body.appendChild(style);
document.body.serialize().should.equal(
'<style>abc<!--&lt;/style>--><script>alert(1)</script></style>'
);
const html = document.serialize();
return alertFired(html).should.eventually.be.false('alert fired for: ' + html);
};
exports.styleMatchingClosingTagAfterClosingComment = function() {
const document = domino.createDocument('');
const style = document.createElement("style");
style.textContent = "abc--></style><script>alert(1)</script>";
document.body.appendChild(style);
// Ensure that HTML entities are properly encoded inside <style>
document.body.serialize().should.equal(
'<style>abc-->&lt;/style><script>alert(1)</script></style>'
);
const html = document.serialize();
return alertFired(html).should.eventually.be.false('alert fired for: ' + html);
};
exports.styleMatchingClosingTagSkipsUnclosedCommentedContent = function() {
const document = domino.createDocument('');
const style = document.createElement("style");
style.textContent = "abc<!--</style><script>alert(1)</script>";
document.body.appendChild(style);
document.body.serialize().should.equal(
'<style>abc<!--&lt;/style><script>alert(1)</script></style>'
);
const html = document.serialize();
return alertFired(html).should.eventually.be.false('alert fired for: ' + html);
};
exports.scriptMatchingClosingTagInRawText = function() {
const document = domino.createDocument('');
const script = document.createElement("script");
script.textContent = "abc</script><script>alert(1)</script>";
document.body.appendChild(script);
// Ensure that HTML entities are properly encoded inside <script>
// Note: the `</script>` is encoded in both places.
document.body.serialize().should.equal(
'<script>abc&lt;/script><script>alert(1)&lt;/script></script>'
);
const html = document.serialize();
return alertFired(html).should.eventually.be.false('alert fired for: ' + html);
};
exports.oneRawTextTagInsideAnotherOne = function() {
const document = domino.createDocument('');
const xmp = document.createElement("xmp");
const style = document.createElement("style");
xmp.textContent = "</style><script>alert(1)</script>";
style.appendChild(xmp);
document.body.appendChild(style);
document.body.serialize().should.equal(
'<style><xmp>&lt;/style><script>alert(1)</script></xmp></style>'
);
const html = document.serialize();
return alertFired(html).should.eventually.be.false('alert fired for: ' + html);
}
exports.xssInAttributeInsideRawTextTag = function() {
const document = domino.createDocument('');
const xmp = document.createElement("xmp");
const div = document.createElement("div");
div.title = "</xmp><script>alert(1)</script>";
xmp.appendChild(div);
document.body.appendChild(xmp);
document.body.serialize().should.equal(
'<xmp><div title="&lt;/xmp&gt;&lt;script&gt;alert(1)&lt;/script&gt;"></div></xmp>'
);
const html = document.serialize();
return alertFired(html).should.eventually.be.false('alert fired for: ' + html);
}
exports.commentNodeInsideRawTextTag = function() {
const document = domino.createDocument('');
const xmp = document.createElement("xmp");
const comment = document.createComment('</xmp><script>alert(1)</script>');
xmp.appendChild(comment);
document.body.appendChild(xmp);
document.body.serialize().should.equal(
'<xmp><!--&lt;/xmp><script>alert(1)</script>--></xmp>'
);
const html = document.serialize();
return alertFired(html).should.eventually.be.false('alert fired for: ' + html);
}
exports.alternativeEndTagForRawTextTag = function() {
const document = domino.createDocument('');
const style = document.createElement("style");
style.textContent = "</style /foobar><script>alert(1)</script>";
document.body.appendChild(style);
document.body.serialize().should.equal(
'<style>&lt;/style /foobar><script>alert(1)</script></style>'
);
const html = document.serialize();
return alertFired(html).should.eventually.be.false('alert fired for: ' + html);
}
exports.badCommentNode = function() {
const document = domino.createDocument('');
const comment = document.createComment('--><script>alert(1)</script>');
document.body.appendChild(comment);
document.body.serialize().should.equal(
'<!----&gt;<script>alert(1)</script>-->'
);
const html = document.serialize();
return alertFired(html).should.eventually.be.false('alert fired for: ' + html);
}
exports.anotherBadCommentNode = function() {
const document = domino.createDocument('');
const comment = document.createComment('--!><script>alert(1)</script>');
document.body.appendChild(comment);
document.body.serialize().should.equal(
'<!----!&gt;<script>alert(1)</script>-->'
);
const html = document.serialize();
return alertFired(html).should.eventually.be.false('alert fired for: ' + html);
}
exports.badProcessingInstruction = function() {
const document = domino.createDocument('');
const pi = document.createProcessingInstruction("bad", "><script>alert(1)</script>");
document.body.appendChild(pi);
document.body.serialize().should.equal(
'<?bad &gt;<script&gt;alert(1)</script&gt;?>'
);
const html = document.serialize();
return alertFired(html).should.eventually.be.false('alert fired for: ' + html);
}
exports.verifyEscapeMatchingClosingTag = function() {
const cases = [
['', 'style', ''], // no artifacts while processing an empty string
['abc', 'script', 'abc'], // no artifacts while processing a string without closing tags
['</style /foobar>abc', 'style', '&lt;/style /foobar>abc'],
['</xmp><script>alert(1)</script>', 'xmp', '&lt;/xmp><script>alert(1)</script>'],
['"</xmp>"', 'xmp', '"&lt;/xmp>"'],
// Raw content element inside another raw content element.
['<xmp></style><script>alert(1)</script></xmp>', 'style',
'<xmp>&lt;/style><script>alert(1)</script></xmp>'],
['abc</script><script>alert(1)&lt;/script>', 'script',
'abc&lt;/script><script>alert(1)&lt;/script>'],
// No changes to the content in case there are no matching closing tags.
['<xmp></style><script>alert(1)</script></xmp>', 'iframe',
'<xmp></style><script>alert(1)</script></xmp>'],
];
for (const [rawContent, parentTag, expected] of cases) {
NodeUtils.ɵescapeMatchingClosingTag(rawContent, parentTag).should.equal(expected);
}
}
exports.verifyEscapeClosingCommentTag = function() {
const cases = [
['', ''], // no artifacts while processing an empty string
['abc', 'abc'], // no artifacts while processing a string without closing tags
['a-->bc-->', 'a--&gt;bc--&gt;'],
['a--!>bc--!>', 'a--!&gt;bc--!&gt;'],
['a- -> b c - ->', 'a- -> b c - ->'],
['a- -!> b c - -!>', 'a- -!> b c - -!>'],
['<!--a--!> <!--b--!>', '<!--a--!&gt; <!--b--!&gt;'],
['<!--a--> <!--b-->', '<!--a--&gt; <!--b--&gt;'],
['<!--a--&lt; <!--b--&lt;', '<!--a--&lt; <!--b--&lt;'],
];
for (const [rawContent, expected] of cases) {
NodeUtils.ɵescapeClosingCommentTag(rawContent).should.equal(expected);
}
}
exports.verifyEscapeProcessingInstructionContent = function() {
const cases = [
['', ''], // no artifacts while processing an empty string
['abc', 'abc'], // no artifacts while processing a string without `>` chars
['>>>', '&gt;&gt;&gt;'],
['<<<', '<<<'],
['><script>alert(1)</script>', '&gt;<script&gt;alert(1)</script&gt;'],
['<!--a-->', '<!--a--&gt;'],
['">"', '"&gt;"'],
];
for (const [rawContent, expected] of cases) {
NodeUtils.ɵescapeProcessingInstructionContent(rawContent).should.equal(expected);
}
}