Commit 9d9cd7ee authored by Richard Townsend's avatar Richard Townsend Committed by Commit Bot

Add CSP meta detection to HTMLPreloadScanner::Scan

The content-security policy meta tag must be read before preloads can
be dispatched. This change allows the foreground HTML parser to use a
single HTMLPreloadScanner to read when this tag's been consumed.
It can then dispatch preloads at the correct time.

Bug: 901056

Change-Id: I70d0c75132b4203992f3b9d16db5c4442037ab13
Reviewed-on: https://chromium-review.googlesource.com/c/1320853
Commit-Queue: Richard Townsend <richard.townsend@arm.com>
Reviewed-by: default avatarCharlie Harrison <csharrison@chromium.org>
Cr-Commit-Position: refs/heads/master@{#608086}
parent 0661bc2f
...@@ -1211,8 +1211,9 @@ std::unique_ptr<HTMLPreloadScanner> HTMLDocumentParser::CreatePreloadScanner( ...@@ -1211,8 +1211,9 @@ std::unique_ptr<HTMLPreloadScanner> HTMLDocumentParser::CreatePreloadScanner(
} }
void HTMLDocumentParser::ScanAndPreload(HTMLPreloadScanner* scanner) { void HTMLDocumentParser::ScanAndPreload(HTMLPreloadScanner* scanner) {
PreloadRequestStream requests = bool seen_csp_meta_tag = false;
scanner->Scan(GetDocument()->ValidBaseElementURL(), nullptr); PreloadRequestStream requests = scanner->Scan(
GetDocument()->ValidBaseElementURL(), nullptr, seen_csp_meta_tag);
preloader_->TakeAndPreload(requests); preloader_->TakeAndPreload(requests);
} }
......
...@@ -949,7 +949,8 @@ void HTMLPreloadScanner::AppendToEnd(const SegmentedString& source) { ...@@ -949,7 +949,8 @@ void HTMLPreloadScanner::AppendToEnd(const SegmentedString& source) {
PreloadRequestStream HTMLPreloadScanner::Scan( PreloadRequestStream HTMLPreloadScanner::Scan(
const KURL& starting_base_element_url, const KURL& starting_base_element_url,
ViewportDescriptionWrapper* viewport) { ViewportDescriptionWrapper* viewport,
bool& has_csp_meta_tag) {
// HTMLTokenizer::updateStateFor only works on the main thread. // HTMLTokenizer::updateStateFor only works on the main thread.
DCHECK(IsMainThread()); DCHECK(IsMainThread());
...@@ -967,13 +968,14 @@ PreloadRequestStream HTMLPreloadScanner::Scan( ...@@ -967,13 +968,14 @@ PreloadRequestStream HTMLPreloadScanner::Scan(
if (token_.GetType() == HTMLToken::kStartTag) if (token_.GetType() == HTMLToken::kStartTag)
tokenizer_->UpdateStateFor( tokenizer_->UpdateStateFor(
AttemptStaticStringCreation(token_.GetName(), kLikely8Bit)); AttemptStaticStringCreation(token_.GetName(), kLikely8Bit));
bool is_csp_meta_tag = false; bool seen_csp_meta_tag = false;
scanner_.Scan(token_, source_, requests, viewport, &is_csp_meta_tag); scanner_.Scan(token_, source_, requests, viewport, &seen_csp_meta_tag);
has_csp_meta_tag |= seen_csp_meta_tag;
token_.Clear(); token_.Clear();
// Don't preload anything if a CSP meta tag is found. We should rarely find // Don't preload anything if a CSP meta tag is found. We should rarely find
// them here because the HTMLPreloadScanner is only used for the synchronous // them here because the HTMLPreloadScanner is only used for the synchronous
// parsing path. // parsing path.
if (is_csp_meta_tag) { if (seen_csp_meta_tag) {
// Reset the tokenizer, to avoid re-scanning tokens that we are about to // Reset the tokenizer, to avoid re-scanning tokens that we are about to
// start parsing. // start parsing.
source_.Clear(); source_.Clear();
......
...@@ -189,7 +189,8 @@ class CORE_EXPORT HTMLPreloadScanner { ...@@ -189,7 +189,8 @@ class CORE_EXPORT HTMLPreloadScanner {
void AppendToEnd(const SegmentedString&); void AppendToEnd(const SegmentedString&);
PreloadRequestStream Scan(const KURL& document_base_element_url, PreloadRequestStream Scan(const KURL& document_base_element_url,
ViewportDescriptionWrapper*); ViewportDescriptionWrapper*,
bool& has_csp_meta_tag);
private: private:
HTMLPreloadScanner(const HTMLParserOptions&, HTMLPreloadScanner(const HTMLParserOptions&,
......
...@@ -75,7 +75,9 @@ int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { ...@@ -75,7 +75,9 @@ int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
CString bytes = fuzzed_data.ConsumeRemainingBytes(); CString bytes = fuzzed_data.ConsumeRemainingBytes();
String decoded_bytes = decoder.Decode(bytes.data(), bytes.length()); String decoded_bytes = decoder.Decode(bytes.data(), bytes.length());
scanner->AppendToEnd(decoded_bytes); scanner->AppendToEnd(decoded_bytes);
PreloadRequestStream requests = scanner->Scan(document_url, nullptr); bool has_csp_meta_tag_unused = false;
PreloadRequestStream requests =
scanner->Scan(document_url, nullptr, has_csp_meta_tag_unused);
preloader.TakeAndPreload(requests); preloader.TakeAndPreload(requests);
return 0; return 0;
} }
......
...@@ -61,6 +61,12 @@ struct CORSTestCase { ...@@ -61,6 +61,12 @@ struct CORSTestCase {
network::mojom::FetchCredentialsMode credentials_mode; network::mojom::FetchCredentialsMode credentials_mode;
}; };
struct CSPTestCase {
const char* base_url;
const char* input_html;
bool should_see_csp_tag;
};
struct NonceTestCase { struct NonceTestCase {
const char* base_url; const char* base_url;
const char* input_html; const char* input_html;
...@@ -253,7 +259,8 @@ class HTMLPreloadScannerTest : public PageTestBase { ...@@ -253,7 +259,8 @@ class HTMLPreloadScannerTest : public PageTestBase {
HTMLMockHTMLResourcePreloader preloader; HTMLMockHTMLResourcePreloader preloader;
KURL base_url(test_case.base_url); KURL base_url(test_case.base_url);
scanner_->AppendToEnd(String(test_case.input_html)); scanner_->AppendToEnd(String(test_case.input_html));
PreloadRequestStream requests = scanner_->Scan(base_url, nullptr); PreloadRequestStream requests =
scanner_->Scan(base_url, nullptr, seen_csp_meta_tag_);
preloader.TakeAndPreload(requests); preloader.TakeAndPreload(requests);
preloader.PreloadRequestVerification( preloader.PreloadRequestVerification(
...@@ -265,7 +272,8 @@ class HTMLPreloadScannerTest : public PageTestBase { ...@@ -265,7 +272,8 @@ class HTMLPreloadScannerTest : public PageTestBase {
HTMLMockHTMLResourcePreloader preloader; HTMLMockHTMLResourcePreloader preloader;
KURL base_url(test_case.base_url); KURL base_url(test_case.base_url);
scanner_->AppendToEnd(String(test_case.input_html)); scanner_->AppendToEnd(String(test_case.input_html));
PreloadRequestStream requests = scanner_->Scan(base_url, nullptr); PreloadRequestStream requests =
scanner_->Scan(base_url, nullptr, seen_csp_meta_tag_);
preloader.TakeAndPreload(requests); preloader.TakeAndPreload(requests);
preloader.PreconnectRequestVerification(test_case.preconnected_host, preloader.PreconnectRequestVerification(test_case.preconnected_host,
test_case.cross_origin); test_case.cross_origin);
...@@ -275,7 +283,8 @@ class HTMLPreloadScannerTest : public PageTestBase { ...@@ -275,7 +283,8 @@ class HTMLPreloadScannerTest : public PageTestBase {
HTMLMockHTMLResourcePreloader preloader; HTMLMockHTMLResourcePreloader preloader;
KURL base_url(test_case.base_url); KURL base_url(test_case.base_url);
scanner_->AppendToEnd(String(test_case.input_html)); scanner_->AppendToEnd(String(test_case.input_html));
PreloadRequestStream requests = scanner_->Scan(base_url, nullptr); PreloadRequestStream requests =
scanner_->Scan(base_url, nullptr, seen_csp_meta_tag_);
preloader.TakeAndPreload(requests); preloader.TakeAndPreload(requests);
if (test_case.expected_referrer) { if (test_case.expected_referrer) {
...@@ -294,20 +303,29 @@ class HTMLPreloadScannerTest : public PageTestBase { ...@@ -294,20 +303,29 @@ class HTMLPreloadScannerTest : public PageTestBase {
HTMLMockHTMLResourcePreloader preloader; HTMLMockHTMLResourcePreloader preloader;
KURL base_url(test_case.base_url); KURL base_url(test_case.base_url);
scanner_->AppendToEnd(String(test_case.input_html)); scanner_->AppendToEnd(String(test_case.input_html));
PreloadRequestStream requests = scanner_->Scan(base_url, nullptr); PreloadRequestStream requests =
scanner_->Scan(base_url, nullptr, seen_csp_meta_tag_);
preloader.TakeAndPreload(requests); preloader.TakeAndPreload(requests);
preloader.CORSRequestVerification(&GetDocument(), test_case.request_mode, preloader.CORSRequestVerification(&GetDocument(), test_case.request_mode,
test_case.credentials_mode); test_case.credentials_mode);
} }
void Test(CSPTestCase test_case) {
HTMLMockHTMLResourcePreloader preloader;
KURL base_url(test_case.base_url);
seen_csp_meta_tag_ = false;
scanner_->AppendToEnd(String(test_case.input_html));
scanner_->Scan(base_url, nullptr, seen_csp_meta_tag_);
EXPECT_EQ(test_case.should_see_csp_tag, seen_csp_meta_tag_);
}
void Test(NonceTestCase test_case) { void Test(NonceTestCase test_case) {
HTMLMockHTMLResourcePreloader preloader; HTMLMockHTMLResourcePreloader preloader;
KURL base_url(test_case.base_url); KURL base_url(test_case.base_url);
scanner_->AppendToEnd(String(test_case.input_html)); scanner_->AppendToEnd(String(test_case.input_html));
PreloadRequestStream requests = scanner_->Scan(base_url, nullptr); PreloadRequestStream requests =
scanner_->Scan(base_url, nullptr, seen_csp_meta_tag_);
preloader.TakeAndPreload(requests); preloader.TakeAndPreload(requests);
preloader.NonceRequestVerification(test_case.nonce); preloader.NonceRequestVerification(test_case.nonce);
} }
...@@ -315,7 +333,8 @@ class HTMLPreloadScannerTest : public PageTestBase { ...@@ -315,7 +333,8 @@ class HTMLPreloadScannerTest : public PageTestBase {
HTMLMockHTMLResourcePreloader preloader; HTMLMockHTMLResourcePreloader preloader;
KURL base_url(test_case.base_url); KURL base_url(test_case.base_url);
scanner_->AppendToEnd(String(test_case.input_html)); scanner_->AppendToEnd(String(test_case.input_html));
PreloadRequestStream requests = scanner_->Scan(base_url, nullptr); PreloadRequestStream requests =
scanner_->Scan(base_url, nullptr, seen_csp_meta_tag_);
preloader.TakeAndPreload(requests); preloader.TakeAndPreload(requests);
preloader.ContextVerification(test_case.is_image_set); preloader.ContextVerification(test_case.is_image_set);
...@@ -326,7 +345,8 @@ class HTMLPreloadScannerTest : public PageTestBase { ...@@ -326,7 +345,8 @@ class HTMLPreloadScannerTest : public PageTestBase {
HTMLMockHTMLResourcePreloader preloader; HTMLMockHTMLResourcePreloader preloader;
KURL base_url("http://example.test/"); KURL base_url("http://example.test/");
scanner_->AppendToEnd(String(test_case.input_html)); scanner_->AppendToEnd(String(test_case.input_html));
PreloadRequestStream requests = scanner_->Scan(base_url, nullptr); PreloadRequestStream requests =
scanner_->Scan(base_url, nullptr, seen_csp_meta_tag_);
preloader.TakeAndPreload(requests); preloader.TakeAndPreload(requests);
preloader.CheckNumberOfIntegrityConstraints( preloader.CheckNumberOfIntegrityConstraints(
...@@ -335,6 +355,7 @@ class HTMLPreloadScannerTest : public PageTestBase { ...@@ -335,6 +355,7 @@ class HTMLPreloadScannerTest : public PageTestBase {
private: private:
std::unique_ptr<HTMLPreloadScanner> scanner_; std::unique_ptr<HTMLPreloadScanner> scanner_;
bool seen_csp_meta_tag_ = false;
}; };
TEST_F(HTMLPreloadScannerTest, testImages) { TEST_F(HTMLPreloadScannerTest, testImages) {
...@@ -889,6 +910,22 @@ TEST_F(HTMLPreloadScannerTest, testCORS) { ...@@ -889,6 +910,22 @@ TEST_F(HTMLPreloadScannerTest, testCORS) {
} }
} }
TEST_F(HTMLPreloadScannerTest, testCSP) {
CSPTestCase test_cases[] = {
{"http://example.test",
"<meta http-equiv=\"Content-Security-Policy\" content=\"default-src "
"https:\">",
true},
{"http://example.test",
"<meta name=\"viewport\" content=\"width=device-width\">", false},
{"http://example.test", "<img src=\"example.gif\">", false}};
for (const auto& test_case : test_cases) {
SCOPED_TRACE(test_case.input_html);
Test(test_case);
}
}
TEST_F(HTMLPreloadScannerTest, testNonce) { TEST_F(HTMLPreloadScannerTest, testNonce) {
NonceTestCase test_cases[] = { NonceTestCase test_cases[] = {
{"http://example.test", "<script src='/script'></script>", ""}, {"http://example.test", "<script src='/script'></script>", ""},
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment