Skip to content
Closed
Changes from 1 commit
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
158 changes: 158 additions & 0 deletions fetch/dangling-markup-mitigation.tentative.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
<!DOCTYPE html>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="./resources/helper.js"></script>
<body>
<script>
function readableURL(url) {
return url.replace(/\n/g, "\\n").replace(/\r/g, "\\r").replace(/\t/g, "\\t");
}

var should_load = [
`/images/green-1x1.png`,
`/images/gre\nen-1x1.png`,
`/images/gre\ten-1x1.png`,
`/images/gre\ren-1x1.png`,
`/images/green-1x1.png?img=<`,
`/images/green-1x1.png?img=&lt;`,
`/images/green-1x1.png?img=%3C`,
`/images/gr\neen-1x1.png?img=%3C`,
`/images/gr\reen-1x1.png?img=%3C`,
`/images/gr\teen-1x1.png?img=%3C`,
`/images/green-1x1.png?img=&#10;`,
`/images/gr\neen-1x1.png?img=&#10;`,
`/images/gr\reen-1x1.png?img=&#10;`,
`/images/gr\teen-1x1.png?img=&#10;`,
];
should_load.forEach(url => async_test(t => {
fetch(url)
.then(t.step_func_done(r => {
assert_equals(r.status, 200);
}))
.catch(t.unreached_func("Fetch should succeed."));
}, "Fetch: " + readableURL(url)));

var should_block = [
`/images/gre\nen-1x1.png?img=<`,
`/images/gre\ren-1x1.png?img=<`,
`/images/gre\ten-1x1.png?img=<`,
`/images/green-1x1.png?<\n=block`,
`/images/green-1x1.png?<\r=block`,
`/images/green-1x1.png?<\t=block`,
];
should_block.forEach(url => async_test(t => {
fetch(url)
.then(t.unreached_func("Fetch should fail."))
.catch(t.step_func_done());
}, "Fetch: " + readableURL(url)));


// For each of the following tests, we'll inject a frame containing the HTML we'd like to poke at
// as a `srcdoc` attribute. Because we're injecting markup via `srcdoc`, we need to entity-escape
// the content we'd like to treat as "raw" (e.g. `\n` => `&#10;`, `<` => `&lt;`), and
// double-escape the "escaped" content.
var rawBrace = "&lt;";
var escapedBrace = "&amp;lt;";
var rawNewline = "&#10;";
var escapedNewline = "&amp;#10;";

function appendFrameAndGetElement(test, frame) {
return new Promise((resolve, reject) => {
frame.onload = test.step_func(_ => {
frame.onload = null;
resolve(frame.contentDocument.querySelector('#dangling'));
});
document.body.appendChild(frame);
});
}

function assert_img_loaded(test, frame) {
appendFrameAndGetElement(test, frame)
.then(test.step_func_done(img => {
assert_equals(img.naturalHeight, 1, "Height");
frame.remove();
}));
}

function assert_img_not_loaded(test, frame) {
appendFrameAndGetElement(test, frame)
.then(test.step_func_done(img => {
assert_equals(img.naturalHeight, 0, "Height");
assert_equals(img.naturalWidth, 0, "Width");
}));
}

function createFrame(markup) {
var i = document.createElement('iframe');
i.srcdoc = `${markup}sekrit`;
return i;
}

// The following resources should not be blocked, as their URLs do not contain both a `\n` and `<`
// character in the body of the URL.
var should_load = [
// Brace alone doesn't block:
`<img id="dangling" src="/images/green-1x1.png?img=${rawBrace}b">`,

// Newline alone doesn't block:
`<img id="dangling" src="/images/green-1x1.png?img=${rawNewline}b">`,

// Entity-escaped characters don't trigger blocking:
`<img id="dangling" src="/images/green-1x1.png?img=${escapedNewline}b">`,
`<img id="dangling" src="/images/green-1x1.png?img=${escapedBrace}b">`,
`<img id="dangling" src="/images/green-1x1.png?img=${escapedNewline}b${escapedBrace}c">`,

// Leading and trailing whitespace is stripped:
`
<img id="dangling" src="
/images/green-1x1.png?img=
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mikewest, the lint is unhappy about trailing whitespace here and in one other place, but I'm not certain if it's intentional.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As long as it's not unhappy about trailing \n or \r, removing it should be fine.

">
`,
`
<img id="dangling" src="
/images/green-1x1.png?img=${escapedBrace}
">
`,
`
<img id="dangling" src="
/images/green-1x1.png?img=${escapedNewline}
">
`,

// Data URLs don't trigger blocking:
`<img id="dangling" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=">`,
`<img id="dangling" src="data:image/png;base64,${rawNewline}iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=">`,
`<img id="dangling" src="data:image/png;base64,i${rawNewline}VBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=">`,
`<img id="dangling" src="data:image/svg+xml;utf8,
<svg width='1' height='1' xmlns='http://www.w3.org/2000/svg'>
<rect width='100%' height='100%' fill='rebeccapurple'/>
<rect x='10%' y='10%' width='80%' height='80%' fill='lightgreen'/>
</svg>">`
];

should_load.forEach(markup => {
async_test(t => {
var i = createFrame(`${markup} <element attr="" another=''>`);
assert_img_loaded(t, i);
}, readableURL(markup));
});

// The following resources should be blocked, as their URLs contain both `\n` and `<` characters:
var should_block = [
`<img id="dangling" src="/images/green-1x1.png?img=${rawNewline}${rawBrace}b">`,
`<img id="dangling" src="/images/green-1x1.png?img=${rawBrace}${rawNewline}b">`,
`
<img id="dangling" src="/images/green-1x1.png?img=
${rawBrace}
${rawNewline}b
">
`,
];

should_block.forEach(markup => {
async_test(t => {
var i = createFrame(`${markup}`);
assert_img_not_loaded(t, i);
}, readableURL(markup));
});
</script>