Commit 0c45ffd2 authored by Matt Falkenhagen's avatar Matt Falkenhagen Committed by Commit Bot

Disallow access to opaque CSS responses.

Bug: 848786
Change-Id: Ie53fbf644afdd76d7c65649a05c939c63d89b4ec
Reviewed-on: https://chromium-review.googlesource.com/1088335Reviewed-by: default avatarKouhei Ueno <kouhei@chromium.org>
Commit-Queue: Matt Falkenhagen <falken@chromium.org>
Cr-Commit-Position: refs/heads/master@{#565537}
parent bf42268f
This is a testharness.js-based test.
PASS setup global state
PASS MIME checking of CSS resources fetched via service worker when Content-Type is not set.
FAIL Same-origin policy for access to CSS resources fetched via service worker assert_throws: function "() => {
f.contentDocument.styleSheets[0].cssRules[0].cssText;
}" did not throw
PASS cleanup global state
Harness: the test ran to completion.
...@@ -154,6 +154,7 @@ CSSStyleSheet* CSSStyleSheet::CreateInline(Node& owner_node, ...@@ -154,6 +154,7 @@ CSSStyleSheet* CSSStyleSheet::CreateInline(Node& owner_node,
const WTF::TextEncoding& encoding) { const WTF::TextEncoding& encoding) {
CSSParserContext* parser_context = CSSParserContext::Create( CSSParserContext* parser_context = CSSParserContext::Create(
owner_node.GetDocument(), owner_node.GetDocument().BaseURL(), owner_node.GetDocument(), owner_node.GetDocument().BaseURL(),
false /* is_opaque_response_from_service_worker */,
owner_node.GetDocument().GetReferrerPolicy(), encoding); owner_node.GetDocument().GetReferrerPolicy(), encoding);
StyleSheetContents* sheet = StyleSheetContents* sheet =
StyleSheetContents::Create(base_url.GetString(), parser_context); StyleSheetContents::Create(base_url.GetString(), parser_context);
...@@ -317,6 +318,12 @@ void CSSStyleSheet::ClearOwnerNode() { ...@@ -317,6 +318,12 @@ void CSSStyleSheet::ClearOwnerNode() {
bool CSSStyleSheet::CanAccessRules() const { bool CSSStyleSheet::CanAccessRules() const {
if (enable_rule_access_for_inspector_) if (enable_rule_access_for_inspector_)
return true; return true;
// Opaque responses should never be accessible, mod DevTools. See comments for
// IsOpaqueResponseFromServiceWorker().
if (contents_->IsOpaqueResponseFromServiceWorker())
return false;
if (is_inline_stylesheet_) if (is_inline_stylesheet_)
return true; return true;
KURL base_url = contents_->BaseURL(); KURL base_url = contents_->BaseURL();
......
...@@ -27,9 +27,9 @@ CSSParserContext* CSSParserContext::Create(const ExecutionContext& context) { ...@@ -27,9 +27,9 @@ CSSParserContext* CSSParserContext::Create(const ExecutionContext& context) {
policy_disposition = kCheckContentSecurityPolicy; policy_disposition = kCheckContentSecurityPolicy;
return new CSSParserContext( return new CSSParserContext(
context.Url(), WTF::TextEncoding(), kHTMLStandardMode, kHTMLStandardMode, context.Url(), false /* is_opaque_response_from_service_worker */,
kLiveProfile, referrer, true, false, context.GetSecureContextMode(), WTF::TextEncoding(), kHTMLStandardMode, kHTMLStandardMode, kLiveProfile,
policy_disposition, referrer, true, false, context.GetSecureContextMode(), policy_disposition,
context.IsDocument() ? &ToDocument(context) : nullptr); context.IsDocument() ? &ToDocument(context) : nullptr);
} }
...@@ -54,8 +54,9 @@ CSSParserContext* CSSParserContext::Create( ...@@ -54,8 +54,9 @@ CSSParserContext* CSSParserContext::Create(
const CSSParserContext* other, const CSSParserContext* other,
const Document* use_counter_document) { const Document* use_counter_document) {
return new CSSParserContext( return new CSSParserContext(
other->base_url_, other->charset_, other->mode_, other->match_mode_, other->base_url_, other->is_opaque_response_from_service_worker_,
other->profile_, other->referrer_, other->is_html_document_, other->charset_, other->mode_, other->match_mode_, other->profile_,
other->referrer_, other->is_html_document_,
other->use_legacy_background_size_shorthand_behavior_, other->use_legacy_background_size_shorthand_behavior_,
other->secure_context_mode_, other->should_check_content_security_policy_, other->secure_context_mode_, other->should_check_content_security_policy_,
use_counter_document); use_counter_document);
...@@ -65,11 +66,13 @@ CSSParserContext* CSSParserContext::Create( ...@@ -65,11 +66,13 @@ CSSParserContext* CSSParserContext::Create(
CSSParserContext* CSSParserContext::Create( CSSParserContext* CSSParserContext::Create(
const CSSParserContext* other, const CSSParserContext* other,
const KURL& base_url, const KURL& base_url,
bool is_opaque_response_from_service_worker,
ReferrerPolicy referrer_policy, ReferrerPolicy referrer_policy,
const WTF::TextEncoding& charset, const WTF::TextEncoding& charset,
const Document* use_counter_document) { const Document* use_counter_document) {
return new CSSParserContext( return new CSSParserContext(
base_url, charset, other->mode_, other->match_mode_, other->profile_, base_url, is_opaque_response_from_service_worker, charset, other->mode_,
other->match_mode_, other->profile_,
Referrer(base_url.StrippedForUseAsReferrer(), referrer_policy), Referrer(base_url.StrippedForUseAsReferrer(), referrer_policy),
other->is_html_document_, other->is_html_document_,
other->use_legacy_background_size_shorthand_behavior_, other->use_legacy_background_size_shorthand_behavior_,
...@@ -83,23 +86,26 @@ CSSParserContext* CSSParserContext::Create( ...@@ -83,23 +86,26 @@ CSSParserContext* CSSParserContext::Create(
SecureContextMode secure_context_mode, SecureContextMode secure_context_mode,
SelectorProfile profile, SelectorProfile profile,
const Document* use_counter_document) { const Document* use_counter_document) {
return new CSSParserContext(KURL(), WTF::TextEncoding(), mode, mode, profile, return new CSSParserContext(
Referrer(), false, false, secure_context_mode, KURL(), false /* is_opaque_response_from_service_worker */,
kDoNotCheckContentSecurityPolicy, WTF::TextEncoding(), mode, mode, profile, Referrer(), false, false,
use_counter_document); secure_context_mode, kDoNotCheckContentSecurityPolicy,
use_counter_document);
} }
// static // static
CSSParserContext* CSSParserContext::Create(const Document& document) { CSSParserContext* CSSParserContext::Create(const Document& document) {
return CSSParserContext::Create(document, document.BaseURL(), return CSSParserContext::Create(
document.GetReferrerPolicy(), document, document.BaseURL(),
WTF::TextEncoding(), kLiveProfile); false /* is_opaque_response_from_service_worker */,
document.GetReferrerPolicy(), WTF::TextEncoding(), kLiveProfile);
} }
// static // static
CSSParserContext* CSSParserContext::Create( CSSParserContext* CSSParserContext::Create(
const Document& document, const Document& document,
const KURL& base_url_override, const KURL& base_url_override,
bool is_opaque_response_from_service_worker,
ReferrerPolicy referrer_policy_override, ReferrerPolicy referrer_policy_override,
const WTF::TextEncoding& charset, const WTF::TextEncoding& charset,
SelectorProfile profile) { SelectorProfile profile) {
...@@ -131,13 +137,15 @@ CSSParserContext* CSSParserContext::Create( ...@@ -131,13 +137,15 @@ CSSParserContext* CSSParserContext::Create(
policy_disposition = kCheckContentSecurityPolicy; policy_disposition = kCheckContentSecurityPolicy;
return new CSSParserContext( return new CSSParserContext(
base_url_override, charset, mode, match_mode, profile, referrer, base_url_override, is_opaque_response_from_service_worker, charset, mode,
document.IsHTMLDocument(), use_legacy_background_size_shorthand_behavior, match_mode, profile, referrer, document.IsHTMLDocument(),
use_legacy_background_size_shorthand_behavior,
document.GetSecureContextMode(), policy_disposition, &document); document.GetSecureContextMode(), policy_disposition, &document);
} }
CSSParserContext::CSSParserContext( CSSParserContext::CSSParserContext(
const KURL& base_url, const KURL& base_url,
bool is_opaque_response_from_service_worker,
const WTF::TextEncoding& charset, const WTF::TextEncoding& charset,
CSSParserMode mode, CSSParserMode mode,
CSSParserMode match_mode, CSSParserMode match_mode,
...@@ -149,6 +157,8 @@ CSSParserContext::CSSParserContext( ...@@ -149,6 +157,8 @@ CSSParserContext::CSSParserContext(
ContentSecurityPolicyDisposition policy_disposition, ContentSecurityPolicyDisposition policy_disposition,
const Document* use_counter_document) const Document* use_counter_document)
: base_url_(base_url), : base_url_(base_url),
is_opaque_response_from_service_worker_(
is_opaque_response_from_service_worker),
charset_(charset), charset_(charset),
mode_(mode), mode_(mode),
match_mode_(match_mode), match_mode_(match_mode),
...@@ -190,6 +200,10 @@ const CSSParserContext* StrictCSSParserContext( ...@@ -190,6 +200,10 @@ const CSSParserContext* StrictCSSParserContext(
return context; return context;
} }
bool CSSParserContext::IsOpaqueResponseFromServiceWorker() const {
return is_opaque_response_from_service_worker_;
}
bool CSSParserContext::IsSecureContext() const { bool CSSParserContext::IsSecureContext() const {
return secure_context_mode_ == SecureContextMode::kSecureContext; return secure_context_mode_ == SecureContextMode::kSecureContext;
} }
......
...@@ -43,6 +43,7 @@ class CORE_EXPORT CSSParserContext ...@@ -43,6 +43,7 @@ class CORE_EXPORT CSSParserContext
static CSSParserContext* Create(const CSSParserContext* other, static CSSParserContext* Create(const CSSParserContext* other,
const KURL& base_url_override, const KURL& base_url_override,
bool is_opaque_response_from_service_worker,
ReferrerPolicy referrer_policy_override, ReferrerPolicy referrer_policy_override,
const WTF::TextEncoding& charset_override, const WTF::TextEncoding& charset_override,
const Document* use_counter_document); const Document* use_counter_document);
...@@ -56,6 +57,7 @@ class CORE_EXPORT CSSParserContext ...@@ -56,6 +57,7 @@ class CORE_EXPORT CSSParserContext
static CSSParserContext* Create( static CSSParserContext* Create(
const Document&, const Document&,
const KURL& base_url_override, const KURL& base_url_override,
bool is_opaque_response_from_service_worker,
ReferrerPolicy referrer_policy_override, ReferrerPolicy referrer_policy_override,
const WTF::TextEncoding& charset = WTF::TextEncoding(), const WTF::TextEncoding& charset = WTF::TextEncoding(),
SelectorProfile = kLiveProfile); SelectorProfile = kLiveProfile);
...@@ -75,6 +77,9 @@ class CORE_EXPORT CSSParserContext ...@@ -75,6 +77,9 @@ class CORE_EXPORT CSSParserContext
bool IsHTMLDocument() const { return is_html_document_; } bool IsHTMLDocument() const { return is_html_document_; }
bool IsLiveProfile() const { return profile_ == kLiveProfile; } bool IsLiveProfile() const { return profile_ == kLiveProfile; }
// See documentation in StyleSheetContents for this function.
bool IsOpaqueResponseFromServiceWorker() const;
bool IsSecureContext() const; bool IsSecureContext() const;
// This quirk is to maintain compatibility with Android apps built on // This quirk is to maintain compatibility with Android apps built on
...@@ -109,6 +114,7 @@ class CORE_EXPORT CSSParserContext ...@@ -109,6 +114,7 @@ class CORE_EXPORT CSSParserContext
private: private:
CSSParserContext(const KURL& base_url, CSSParserContext(const KURL& base_url,
bool is_opaque_response_from_service_worker,
const WTF::TextEncoding& charset, const WTF::TextEncoding& charset,
CSSParserMode, CSSParserMode,
CSSParserMode match_mode, CSSParserMode match_mode,
...@@ -121,6 +127,7 @@ class CORE_EXPORT CSSParserContext ...@@ -121,6 +127,7 @@ class CORE_EXPORT CSSParserContext
const Document* use_counter_document); const Document* use_counter_document);
KURL base_url_; KURL base_url_;
const bool is_opaque_response_from_service_worker_;
WTF::TextEncoding charset_; WTF::TextEncoding charset_;
CSSParserMode mode_; CSSParserMode mode_;
CSSParserMode match_mode_; CSSParserMode match_mode_;
......
...@@ -534,8 +534,10 @@ SelectorQuery* SelectorQueryCache::Add(const AtomicString& selectors, ...@@ -534,8 +534,10 @@ SelectorQuery* SelectorQueryCache::Add(const AtomicString& selectors,
CSSSelectorList selector_list = CSSParser::ParseSelector( CSSSelectorList selector_list = CSSParser::ParseSelector(
CSSParserContext::Create( CSSParserContext::Create(
document, document.BaseURL(), document.GetReferrerPolicy(), document, document.BaseURL(),
WTF::TextEncoding(), CSSParserContext::kSnapshotProfile), false /* is_opaque_response_from_service_worker */,
document.GetReferrerPolicy(), WTF::TextEncoding(),
CSSParserContext::kSnapshotProfile),
nullptr, selectors); nullptr, selectors);
if (!selector_list.First()) { if (!selector_list.First()) {
......
...@@ -68,9 +68,11 @@ TEST(SelectorQueryTest, NotMatchingPseudoElement) { ...@@ -68,9 +68,11 @@ TEST(SelectorQueryTest, NotMatchingPseudoElement) {
"<body><style>span::before { content: 'X' }</style><span></span></body>"); "<body><style>span::before { content: 'X' }</style><span></span></body>");
CSSSelectorList selector_list = CSSParser::ParseSelector( CSSSelectorList selector_list = CSSParser::ParseSelector(
CSSParserContext::Create(*document, NullURL(), kReferrerPolicyDefault, CSSParserContext::Create(
WTF::TextEncoding(), *document, NullURL(),
CSSParserContext::kSnapshotProfile), false /* is_opaque_response_from_service_worker */,
kReferrerPolicyDefault, WTF::TextEncoding(),
CSSParserContext::kSnapshotProfile),
nullptr, "span::before"); nullptr, "span::before");
std::unique_ptr<SelectorQuery> query = std::unique_ptr<SelectorQuery> query =
SelectorQuery::Adopt(std::move(selector_list)); SelectorQuery::Adopt(std::move(selector_list));
...@@ -78,9 +80,11 @@ TEST(SelectorQueryTest, NotMatchingPseudoElement) { ...@@ -78,9 +80,11 @@ TEST(SelectorQueryTest, NotMatchingPseudoElement) {
EXPECT_EQ(nullptr, elm); EXPECT_EQ(nullptr, elm);
selector_list = CSSParser::ParseSelector( selector_list = CSSParser::ParseSelector(
CSSParserContext::Create(*document, NullURL(), kReferrerPolicyDefault, CSSParserContext::Create(
WTF::TextEncoding(), *document, NullURL(),
CSSParserContext::kSnapshotProfile), false /* is_opaque_response_from_service_worker */,
kReferrerPolicyDefault, WTF::TextEncoding(),
CSSParserContext::kSnapshotProfile),
nullptr, "span"); nullptr, "span");
query = SelectorQuery::Adopt(std::move(selector_list)); query = SelectorQuery::Adopt(std::move(selector_list));
elm = query->QueryFirst(*document); elm = query->QueryFirst(*document);
...@@ -97,9 +101,11 @@ TEST(SelectorQueryTest, LastOfTypeNotFinishedParsing) { ...@@ -97,9 +101,11 @@ TEST(SelectorQueryTest, LastOfTypeNotFinishedParsing) {
document->body()->BeginParsingChildren(); document->body()->BeginParsingChildren();
CSSSelectorList selector_list = CSSParser::ParseSelector( CSSSelectorList selector_list = CSSParser::ParseSelector(
CSSParserContext::Create(*document, NullURL(), kReferrerPolicyDefault, CSSParserContext::Create(
WTF::TextEncoding(), *document, NullURL(),
CSSParserContext::kSnapshotProfile), false /* is_opaque_response_from_service_worker */,
kReferrerPolicyDefault, WTF::TextEncoding(),
CSSParserContext::kSnapshotProfile),
nullptr, "p:last-of-type"); nullptr, "p:last-of-type");
std::unique_ptr<SelectorQuery> query = std::unique_ptr<SelectorQuery> query =
SelectorQuery::Adopt(std::move(selector_list)); SelectorQuery::Adopt(std::move(selector_list));
......
...@@ -78,10 +78,11 @@ void StyleRuleImport::NotifyFinished(Resource* resource) { ...@@ -78,10 +78,11 @@ void StyleRuleImport::NotifyFinished(Resource* resource) {
document = parent_style_sheet_->SingleOwnerDocument(); document = parent_style_sheet_->SingleOwnerDocument();
context = parent_style_sheet_->ParserContext(); context = parent_style_sheet_->ParserContext();
} }
context = context = CSSParserContext::Create(
CSSParserContext::Create(context, cached_style_sheet->GetResponse().Url(), context, cached_style_sheet->GetResponse().Url(),
cached_style_sheet->GetReferrerPolicy(), cached_style_sheet->GetResponse().IsOpaqueResponseFromServiceWorker(),
cached_style_sheet->Encoding(), document); cached_style_sheet->GetReferrerPolicy(), cached_style_sheet->Encoding(),
document);
style_sheet_ = style_sheet_ =
StyleSheetContents::Create(this, cached_style_sheet->Url(), context); StyleSheetContents::Create(this, cached_style_sheet->Url(), context);
......
...@@ -135,12 +135,34 @@ class CORE_EXPORT StyleSheetContents ...@@ -135,12 +135,34 @@ class CORE_EXPORT StyleSheetContents
StyleRuleImport* OwnerRule() const { return owner_rule_; } StyleRuleImport* OwnerRule() const { return owner_rule_; }
void ClearOwnerRule() { owner_rule_ = nullptr; } void ClearOwnerRule() { owner_rule_ = nullptr; }
// Note that href is the URL that started the redirect chain that led to // The URL that started the redirect chain that led to this
// this style sheet. This property probably isn't useful for much except // style sheet. This property probably isn't useful for much except the
// the JavaScript binding (which needs to use this value for security). // JavaScript binding (which needs to use this value for security).
String OriginalURL() const { return original_url_; } String OriginalURL() const { return original_url_; }
// The final request URL after redirects. WARNING: Be careful when
// using this for security checks. It can be different from the actual
// response URL if a service worker is involved. See
// IsOpaqueResponseFromServiceWorker().
const KURL& BaseURL() const { return parser_context_->BaseURL(); } const KURL& BaseURL() const { return parser_context_->BaseURL(); }
// True if a service worker intercepted the request for this style sheet and
// returned an opaque response. This context should NOT have access to the
// contents, regardless of BaseURL().
//
// For example:
// 1. Page at a.com requests a.com/style.css.
// 2. Service worker responds with b.com/style.css (without CORS).
// 3. The BaseURL() is "a.com/style.css" but this context is should not have
// access to contents.
//
// You might ask why we don't change BaseURL() to be the actual response URL.
// In fact, the spec says we should! See crbug.com/553535. But we would still
// need this "is opaque" bit, since in step 2 above the service worker might
// have used CORS to get a non-opaque response from b.com.
bool IsOpaqueResponseFromServiceWorker() const {
return parser_context_->IsOpaqueResponseFromServiceWorker();
}
unsigned RuleCount() const; unsigned RuleCount() const;
StyleRuleBase* RuleAt(unsigned index) const; StyleRuleBase* RuleAt(unsigned index) const;
......
...@@ -202,6 +202,7 @@ void ProcessingInstruction::NotifyFinished(Resource* resource) { ...@@ -202,6 +202,7 @@ void ProcessingInstruction::NotifyFinished(Resource* resource) {
CSSStyleSheetResource* style_resource = ToCSSStyleSheetResource(resource); CSSStyleSheetResource* style_resource = ToCSSStyleSheetResource(resource);
CSSParserContext* parser_context = CSSParserContext::Create( CSSParserContext* parser_context = CSSParserContext::Create(
GetDocument(), style_resource->GetResponse().Url(), GetDocument(), style_resource->GetResponse().Url(),
style_resource->GetResponse().IsOpaqueResponseFromServiceWorker(),
style_resource->GetReferrerPolicy(), style_resource->Encoding()); style_resource->GetReferrerPolicy(), style_resource->Encoding());
StyleSheetContents* new_sheet = StyleSheetContents* new_sheet =
......
...@@ -92,6 +92,7 @@ void LinkStyle::NotifyFinished(Resource* resource) { ...@@ -92,6 +92,7 @@ void LinkStyle::NotifyFinished(Resource* resource) {
CSSParserContext* parser_context = CSSParserContext::Create( CSSParserContext* parser_context = CSSParserContext::Create(
GetDocument(), cached_style_sheet->GetResponse().Url(), GetDocument(), cached_style_sheet->GetResponse().Url(),
cached_style_sheet->GetResponse().IsOpaqueResponseFromServiceWorker(),
cached_style_sheet->GetReferrerPolicy(), cached_style_sheet->Encoding()); cached_style_sheet->GetReferrerPolicy(), cached_style_sheet->Encoding());
if (StyleSheetContents* parsed_sheet = if (StyleSheetContents* parsed_sheet =
......
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