Skip to content

Commit

Permalink
Update implementation of TT enforcement for document.write(ln)
Browse files Browse the repository at this point in the history
https://bugs.webkit.org/show_bug.cgi?id=273819

Reviewed by Ryosuke Niwa.

This patch switches document.write and writeln to use a union IDL type and single call to default policy.

This brings it close to chromium behaviour.

See whatwg/html#10328

* LayoutTests/imported/w3c/web-platform-tests/trusted-types/Document-write-expected.txt:
* LayoutTests/imported/w3c/web-platform-tests/trusted-types/Document-write.html:
* LayoutTests/imported/w3c/web-platform-tests/trusted-types/block-string-assignment-to-Document-write-expected.txt:
* LayoutTests/imported/w3c/web-platform-tests/trusted-types/block-string-assignment-to-Document-write.html:
* Source/WebCore/dom/Document+HTML.idl:
* Source/WebCore/dom/Document.cpp:
(WebCore::Document::write):
(WebCore::Document::writeln):
* Source/WebCore/dom/Document.h:

Canonical link: https://commits.webkit.org/278501@main
  • Loading branch information
lukewarlow committed May 8, 2024
1 parent e3736ef commit e84b70e
Show file tree
Hide file tree
Showing 7 changed files with 229 additions and 13 deletions.
Original file line number Diff line number Diff line change
@@ -1,4 +1,11 @@
Quack, I want to be a duck!
abcdefghijkl

PASS document.write with html assigned via policy (successful transformation).
PASS document.writeln with html assigned via policy (successful transformation).
PASS document.write(TrustedHTML, TrustedHTML)
PASS document.writeln(TrustedHTML, TrustedHTML)
PASS document.write(TrustedHTML, String)
PASS document.writeln(TrustedHTML, String)
PASS document.write(String, String)
PASS document.writeln(String, String)

Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,64 @@
document.write(html);
assert_true(document.body.innerText.indexOf(RESULTS.HTML) !== -1);
}, "document.write with html assigned via policy (successful transformation).");

test(t => {
document.body.innerText = '';
let p = createHTML_policy(window, 1);
let html = p.createHTML(INPUTS.HTML);
document.writeln(html);
assert_true(document.body.innerText.indexOf(RESULTS.HTML) !== -1);
}, "document.writeln with html assigned via policy (successful transformation).");

test(t => {
document.body.innerText = '';
let p = createHTML_policy(window, 1);
let a = p.createHTML("abcdef");
let b = p.createHTML("ghijkl");
document.write(a, b);
assert_equals(document.body.innerText, "abcdefghijkl");
}, "document.write(TrustedHTML, TrustedHTML)");

test(t => {
document.body.innerText = '';
let p = createHTML_policy(window, 1);
let a = p.createHTML("abcdef");
let b = p.createHTML("ghijkl");
document.writeln(a, b);
assert_equals(document.body.innerText, "abcdefghijkl");
}, "document.writeln(TrustedHTML, TrustedHTML)");

test(t => {
document.body.innerText = '';
let p = createHTML_policy(window, 1);
let a = p.createHTML("abcdef");
let b = "ghijkl";
document.write(a, b);
assert_equals(document.body.innerText, "abcdefghijkl");
}, "document.write(TrustedHTML, String)");

test(t => {
document.body.innerText = '';
let p = createHTML_policy(window, 1);
let a = p.createHTML("abcdef");
let b = "ghijkl";
document.writeln(a, b);
assert_equals(document.body.innerText, "abcdefghijkl");
}, "document.writeln(TrustedHTML, String)");

test(t => {
document.body.innerText = '';
let a = "abcdef";
let b = "ghijkl";
document.write(a, b);
assert_equals(document.body.innerText, "abcdefghijkl");
}, "document.write(String, String)");

test(t => {
document.body.innerText = '';
let a = "abcdef";
let b = "ghijkl";
document.writeln(a, b);
assert_equals(document.body.innerText, "abcdefghijkl");
}, "document.writeln(String, String)");
</script>
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,28 @@ CONSOLE MESSAGE: This requires a TrustedHTML value else it violates the followin
CONSOLE MESSAGE: This requires a TrustedHTML value else it violates the following Content Security Policy directive: "require-trusted-types-for 'script'"
CONSOLE MESSAGE: This requires a TrustedHTML value else it violates the following Content Security Policy directive: "require-trusted-types-for 'script'"
CONSOLE MESSAGE: This requires a TrustedHTML value else it violates the following Content Security Policy directive: "require-trusted-types-for 'script'"
Quack, I want to be a duck!
CONSOLE MESSAGE: This requires a TrustedHTML value else it violates the following Content Security Policy directive: "require-trusted-types-for 'script'"
CONSOLE MESSAGE: This requires a TrustedHTML value else it violates the following Content Security Policy directive: "require-trusted-types-for 'script'"
CONSOLE MESSAGE: This requires a TrustedHTML value else it violates the following Content Security Policy directive: "require-trusted-types-for 'script'"
CONSOLE MESSAGE: This requires a TrustedHTML value else it violates the following Content Security Policy directive: "require-trusted-types-for 'script'"
abczxcvbnjkl

PASS document.write with html assigned via policy (successful URL transformation).
PASS document.write with multiple trusted arguments.
PASS document.writeln with html assigned via policy (successful URL transformation).
PASS document.writeln with multiple trusted arguments.
PASS `document.write(string)` throws
PASS `document.write(string, string)` throws
PASS `document.write(string, TrustedHTML)` throws
PASS `document.writeln(string)` throws
PASS `document.writeln(string, string)` throws
PASS `document.writeln(string, TrustedHTML)` throws
PASS `document.write(null)` throws
PASS `document.writeln(null)` throws
PASS `document.write(string)` observes default policy
PASS `document.write(string, string)` observes default policy
PASS `document.write(string, TrustedHTML)` observes default policy
PASS `document.writeln(string)` observes default policy
PASS `document.writeln(string, string)` observes default policy
PASS `document.writeln(string, TrustedHTML)` observes default policy

Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,14 @@
assert_equals(document.body.innerText, RESULTS.HTML);
}, "document.write with html assigned via policy (successful URL transformation).");

test(t => {
document.body.innerText = '';
let a = p.createHTML("abcdef");
let b = p.createHTML("ghijkl");
document.write(a,b);
assert_equals(document.body.innerText, "abcdefghijkl");
}, "document.write with multiple trusted arguments.");

// TrustedURL assignments do not throw. (Now for writeln.)
test(t => {
document.body.innerText = '';
Expand All @@ -26,6 +34,14 @@
assert_equals(document.body.innerText, RESULTS.HTML);
}, "document.writeln with html assigned via policy (successful URL transformation).");

test(t => {
document.body.innerText = '';
let a = p.createHTML("abcdef");
let b = p.createHTML("ghijkl");
document.writeln(a,b);
assert_equals(document.body.innerText, "abcdefghijkl");
}, "document.writeln with multiple trusted arguments.");

// String assignments throw.
test(t => {
const old = document.body.innerText;
Expand All @@ -35,6 +51,26 @@
assert_equals(document.body.innerText, old);
}, "`document.write(string)` throws");

test(t => {
const old = document.body.innerText;
assert_throws_js(TypeError, _ => {
let a = "abcdef";
let b = "ghijkl";
document.write(a, b);
});
assert_equals(document.body.innerText, old);
}, "`document.write(string, string)` throws");

test(t => {
const old = document.body.innerText;
assert_throws_js(TypeError, _ => {
let a = "abcdef";
let b = p.createHTML("ghijkl");
document.write(a, b);
});
assert_equals(document.body.innerText, old);
}, "`document.write(string, TrustedHTML)` throws");

// String assignments throw. (Now for writeln.)
test(t => {
const old = document.body.innerText;
Expand All @@ -44,6 +80,26 @@
assert_equals(document.body.innerText, old);
}, "`document.writeln(string)` throws");

test(t => {
const old = document.body.innerText;
assert_throws_js(TypeError, _ => {
let a = "abcdef";
let b = "ghijkl";
document.writeln(a, b);
});
assert_equals(document.body.innerText, old);
}, "`document.writeln(string, string)` throws");

test(t => {
const old = document.body.innerText;
assert_throws_js(TypeError, _ => {
let a = "abcdef";
let b = p.createHTML("ghijkl");
document.writeln(a, b);
});
assert_equals(document.body.innerText, old);
}, "`document.writeln(string, TrustedHTML)` throws");

// Null assignment throws.
test(t => {
const old = document.body.innerText;
Expand All @@ -63,7 +119,11 @@
}, "`document.writeln(null)` throws");

let default_policy = trustedTypes.createPolicy('default',
{ createHTML: createHTMLJS }, true );
{ createHTML: (html) => {
return html.replace("Hi", "Quack")
.replace("transformed", "a duck")
.replace("defghi", "zxcvbn")
} }, true );

// Default policy works.
test(t => {
Expand All @@ -72,10 +132,42 @@
assert_equals(document.body.innerText, RESULTS.HTML);
}, "`document.write(string)` observes default policy");

test(t => {
document.body.innerText = '';
let a = "abcdef";
let b = "ghijkl";
document.write(a, b);
assert_equals(document.body.innerText, "abczxcvbnjkl");
}, "`document.write(string, string)` observes default policy");

test(t => {
document.body.innerText = '';
let a = "abcdef";
let b = p.createHTML("ghijkl");
document.write(a, b);
assert_equals(document.body.innerText, "abczxcvbnjkl");
}, "`document.write(string, TrustedHTML)` observes default policy");

// Default policy works. (Now for writeln.)
test(t => {
document.body.innerText = '';
document.writeln(INPUTS.HTML);
assert_equals(document.body.innerText, RESULTS.HTML);
}, "`document.writeln(string)` observes default policy");

test(t => {
document.body.innerText = '';
let a = "abcdef";
let b = "ghijkl";
document.writeln(a, b);
assert_equals(document.body.innerText, "abczxcvbnjkl");
}, "`document.writeln(string, string)` observes default policy");

test(t => {
document.body.innerText = '';
let a = "abcdef";
let b = p.createHTML("ghijkl");
document.writeln(a, b);
assert_equals(document.body.innerText, "abczxcvbnjkl");
}, "`document.writeln(string, TrustedHTML)` observes default policy");
</script>
4 changes: 2 additions & 2 deletions Source/WebCore/dom/Document+HTML.idl
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,8 @@ partial interface Document {
[CEReactions=Needed, CallWith=EntryDocument, ImplementedAs=openForBindings] Document open(optional DOMString unused1, optional DOMString unused2); // both arguments are ignored.
[CallWith=ActiveWindow&FirstWindow, ImplementedAs=openForBindings] WindowProxy open(USVString url, [AtomString] DOMString name, DOMString features);
[CEReactions=Needed, ImplementedAs=closeForBindings] undefined close();
[CEReactions=Needed, CallWith=EntryDocument] undefined write(HTMLString... text);
[CEReactions=Needed, CallWith=EntryDocument] undefined writeln(HTMLString... text);
[CEReactions=Needed, CallWith=EntryDocument] undefined write((TrustedHTML or DOMString)... text);
[CEReactions=Needed, CallWith=EntryDocument] undefined writeln((TrustedHTML or DOMString)... text);

// user interaction
[ImplementedAs=windowProxy] readonly attribute WindowProxy? defaultView;
Expand Down
55 changes: 49 additions & 6 deletions Source/WebCore/dom/Document.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3937,28 +3937,71 @@ ExceptionOr<void> Document::write(Document* entryDocument, SegmentedString&& tex
return { };
}

ExceptionOr<void> Document::write(Document* entryDocument, FixedVector<String>&& strings)
ExceptionOr<void> Document::write(Document* entryDocument, FixedVector<std::variant<RefPtr<TrustedHTML>, String>>&& strings)
{
if (!isHTMLDocument() || m_throwOnDynamicMarkupInsertionCount)
return Exception { ExceptionCode::InvalidStateError };

auto isTrusted = true;
SegmentedString text;
for (auto& string : strings)
text.append(WTFMove(string));
for (auto& entry : strings) {
text.append(
WTF::switchOn(
WTFMove(entry),
[&isTrusted](const String& string) -> String {
isTrusted = false;
return string;
},
[](const RefPtr<TrustedHTML>& html) -> String {
return html->toString();
}
)
);
}

if (!isTrusted) {
auto stringValueHolder = trustedTypeCompliantString(TrustedType::TrustedHTML, *scriptExecutionContext(), text.toString(), "Document write"_s);
if (stringValueHolder.hasException())
return stringValueHolder.releaseException();
text.clear();
text.append(stringValueHolder.releaseReturnValue());
}

return write(entryDocument, WTFMove(text));
}

ExceptionOr<void> Document::writeln(Document* entryDocument, FixedVector<String>&& strings)
ExceptionOr<void> Document::writeln(Document* entryDocument, FixedVector<std::variant<RefPtr<TrustedHTML>, String>>&& strings)
{
if (!isHTMLDocument() || m_throwOnDynamicMarkupInsertionCount)
return Exception { ExceptionCode::InvalidStateError };

auto isTrusted = true;
SegmentedString text;
for (auto& string : strings)
text.append(WTFMove(string));
for (auto& entry : strings) {
text.append(
WTF::switchOn(
WTFMove(entry),
[&isTrusted](const String& string) -> String {
isTrusted = false;
return string;
},
[](const RefPtr<TrustedHTML>& html) -> String {
return html->toString();
}
)
);
}

if (!isTrusted) {
auto stringValueHolder = trustedTypeCompliantString(TrustedType::TrustedHTML, *scriptExecutionContext(), text.toString(), "Document writeln"_s);
if (stringValueHolder.hasException())
return stringValueHolder.releaseException();
text.clear();
text.append(stringValueHolder.releaseReturnValue());
}

text.append("\n"_s);

return write(entryDocument, WTFMove(text));
}

Expand Down
4 changes: 2 additions & 2 deletions Source/WebCore/dom/Document.h
Original file line number Diff line number Diff line change
Expand Up @@ -807,8 +807,8 @@ class Document
void cancelParsing();

ExceptionOr<void> write(Document* entryDocument, SegmentedString&&);
WEBCORE_EXPORT ExceptionOr<void> write(Document* entryDocument, FixedVector<String>&&);
WEBCORE_EXPORT ExceptionOr<void> writeln(Document* entryDocument, FixedVector<String>&&);
WEBCORE_EXPORT ExceptionOr<void> write(Document* entryDocument, FixedVector<std::variant<RefPtr<TrustedHTML>, String>>&&);
WEBCORE_EXPORT ExceptionOr<void> writeln(Document* entryDocument, FixedVector<std::variant<RefPtr<TrustedHTML>, String>>&&);

bool wellFormed() const { return m_wellFormed; }

Expand Down

0 comments on commit e84b70e

Please sign in to comment.