Clean up integrity checking code.
We check a resource's integrity disposition in a few places, with somewhat inconsistent formatting (checking "!passed" vs "failed", for instance). To make it simpler to add some functionality here, this patch makes a few small changes to align these callsites, and centralize the decision about whether or not to force integrity checks for cached/prefetched resources. No change in behavior is intended; the checks should have the same outcomes as in the status quo. Bug: 375343417 Change-Id: Iea10d3f730cc60359790070fcb06ce62acb97f48 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5965057 Reviewed-by: Yoav Weiss (@Shopify) <yoavweiss@chromium.org> Commit-Queue: Mike West <mkwst@chromium.org> Cr-Commit-Position: refs/heads/main@{#1374688}
This commit is contained in:

committed by
Chromium LUCI CQ

parent
bc79c91d8f
commit
2f7e8821db
third_party/blink/renderer
core
css
html
loader
script
platform
loader
@ -183,25 +183,24 @@ void RemoteFontFaceSource::NotifyFinished(Resource* resource) {
|
||||
auto* font = To<FontResource>(resource);
|
||||
histograms_.RecordRemoteFont(font);
|
||||
|
||||
// Refer to the comments in classic_pending_script.cc for the reason why
|
||||
// Refer to the comments in `Resource::ForceIntegrityChecks()`:
|
||||
// SRI checks should be done here in ResourceClient instead of
|
||||
// ResourceFetcher. SRI failure should behave as network error
|
||||
// (ErrorOccurred()). PreloadCache even caches network errors.
|
||||
// Font fetch itself doesn't support SRI but font preload does.
|
||||
// So, if the resource was preloaded we need to check
|
||||
// SRI failure and simulate network error if it happens.
|
||||
|
||||
if (resource->IsLinkPreload()) {
|
||||
bool force_integrity_checks = resource->ForceIntegrityChecks();
|
||||
if (force_integrity_checks) {
|
||||
SubresourceIntegrityHelper::DoReport(*execution_context,
|
||||
resource->IntegrityReportInfo());
|
||||
}
|
||||
|
||||
DCHECK(!custom_font_data_);
|
||||
// font->GetCustomFontData() returns nullptr if network error happened
|
||||
// (ErrorOccurred() is true). To simulate network error we don't update
|
||||
// custom_font_data_ to keep the nullptr value in case of SRI failures.
|
||||
if (!resource->IsLinkPreload() || resource->IntegrityDisposition() !=
|
||||
ResourceIntegrityDisposition::kFailed) {
|
||||
DCHECK(!custom_font_data_);
|
||||
if (resource->PassedIntegrityChecks() || !force_integrity_checks) {
|
||||
custom_font_data_ = font->GetCustomFontData();
|
||||
}
|
||||
url_ = resource->Url().GetString();
|
||||
|
@ -70,19 +70,14 @@ void LinkStyle::NotifyFinished(Resource* resource) {
|
||||
}
|
||||
|
||||
auto* cached_style_sheet = To<CSSStyleSheetResource>(resource);
|
||||
// See the comment in pending_script.cc about why this check is necessary
|
||||
// here, instead of in the resource fetcher. https://crbug.com/500701.
|
||||
if ((!cached_style_sheet->ErrorOccurred() &&
|
||||
!owner_->FastGetAttribute(html_names::kIntegrityAttr).empty() &&
|
||||
!cached_style_sheet->IntegrityMetadata().empty()) ||
|
||||
resource->IsLinkPreload()) {
|
||||
ResourceIntegrityDisposition disposition =
|
||||
cached_style_sheet->IntegrityDisposition();
|
||||
|
||||
resource->ForceIntegrityChecks()) {
|
||||
SubresourceIntegrityHelper::DoReport(
|
||||
*GetExecutionContext(), cached_style_sheet->IntegrityReportInfo());
|
||||
|
||||
if (disposition == ResourceIntegrityDisposition::kFailed) {
|
||||
if (!cached_style_sheet->PassedIntegrityChecks()) {
|
||||
loading_ = false;
|
||||
RemovePendingSheet();
|
||||
NotifyLoadedSheetAndAllCriticalSubresources(
|
||||
|
@ -84,10 +84,8 @@ LinkLoader::LinkLoader(LinkLoaderClient* client) : client_(client) {
|
||||
}
|
||||
|
||||
void LinkLoader::NotifyFinished(Resource* resource) {
|
||||
if (resource->ErrorOccurred() ||
|
||||
(resource->IsLinkPreload() &&
|
||||
resource->IntegrityDisposition() ==
|
||||
ResourceIntegrityDisposition::kFailed)) {
|
||||
if (resource->ErrorOccurred() || (resource->ForceIntegrityChecks() &&
|
||||
!resource->PassedIntegrityChecks())) {
|
||||
client_->LinkLoadingErrored();
|
||||
} else {
|
||||
client_->LinkLoaded();
|
||||
|
@ -46,8 +46,7 @@ bool ModuleScriptFetcher::WasModuleLoadSuccessful(
|
||||
|
||||
// <spec step="9">... response's type is "error" ...</spec>
|
||||
if (!resource || resource->ErrorOccurred() ||
|
||||
resource->IntegrityDisposition() !=
|
||||
ResourceIntegrityDisposition::kPassed) {
|
||||
!resource->PassedIntegrityChecks()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -433,17 +433,10 @@ void ClassicPendingScript::NotifyFinished(Resource* resource) {
|
||||
SubresourceIntegrityHelper::DoReport(*execution_context,
|
||||
resource->IntegrityReportInfo());
|
||||
|
||||
// It is possible to get back a script resource with integrity metadata
|
||||
// for a request with an empty integrity attribute. In that case, the
|
||||
// integrity check should be skipped, as the integrity may not have been
|
||||
// "meant" for this specific request. If the resource is being served from
|
||||
// the preload cache however, we know any associated integrity metadata and
|
||||
// checks were destined for this request, so we cannot skip the integrity
|
||||
// check.
|
||||
bool integrity_failure = false;
|
||||
if (!options_.GetIntegrityMetadata().empty() || resource->IsLinkPreload()) {
|
||||
integrity_failure = resource->IntegrityDisposition() !=
|
||||
ResourceIntegrityDisposition::kPassed;
|
||||
if (!options_.GetIntegrityMetadata().empty() ||
|
||||
resource->ForceIntegrityChecks()) {
|
||||
integrity_failure = !resource->PassedIntegrityChecks();
|
||||
}
|
||||
|
||||
if (intervened_) {
|
||||
|
@ -45,7 +45,8 @@ class PLATFORM_EXPORT IntegrityMetadata {
|
||||
|
||||
enum class ResourceIntegrityDisposition : uint8_t {
|
||||
kNotChecked = 0,
|
||||
kFailed,
|
||||
kNetworkError,
|
||||
kFailedIntegrityMetadata,
|
||||
kPassed
|
||||
};
|
||||
|
||||
|
@ -193,14 +193,15 @@ void Resource::SetLoader(ResourceLoader* loader) {
|
||||
void Resource::CheckResourceIntegrity() {
|
||||
// Skip the check and reuse the previous check result, especially on
|
||||
// successful revalidation.
|
||||
if (IntegrityDisposition() != ResourceIntegrityDisposition::kNotChecked)
|
||||
if (integrity_disposition_ != ResourceIntegrityDisposition::kNotChecked) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Loading error occurred? Then result is uncheckable.
|
||||
integrity_report_info_.Clear();
|
||||
if (ErrorOccurred()) {
|
||||
CHECK(!Data());
|
||||
integrity_disposition_ = ResourceIntegrityDisposition::kFailed;
|
||||
integrity_disposition_ = ResourceIntegrityDisposition::kNetworkError;
|
||||
return;
|
||||
}
|
||||
|
||||
@ -214,10 +215,11 @@ void Resource::CheckResourceIntegrity() {
|
||||
IntegrityMetadata(), Data(), Url(), *this, integrity_report_info_)) {
|
||||
integrity_disposition_ = ResourceIntegrityDisposition::kPassed;
|
||||
} else {
|
||||
integrity_disposition_ = ResourceIntegrityDisposition::kFailed;
|
||||
integrity_disposition_ =
|
||||
ResourceIntegrityDisposition::kFailedIntegrityMetadata;
|
||||
}
|
||||
|
||||
DCHECK_NE(IntegrityDisposition(), ResourceIntegrityDisposition::kNotChecked);
|
||||
DCHECK_NE(integrity_disposition_, ResourceIntegrityDisposition::kNotChecked);
|
||||
}
|
||||
|
||||
void Resource::NotifyFinished() {
|
||||
|
@ -338,9 +338,22 @@ class PLATFORM_EXPORT Resource : public GarbageCollected<Resource>,
|
||||
const IntegrityMetadataSet& IntegrityMetadata() const {
|
||||
return options_.integrity_metadata;
|
||||
}
|
||||
ResourceIntegrityDisposition IntegrityDisposition() const {
|
||||
return integrity_disposition_;
|
||||
bool PassedIntegrityChecks() const {
|
||||
return integrity_disposition_ == ResourceIntegrityDisposition::kPassed;
|
||||
}
|
||||
|
||||
// Caching makes it possible for a resource that was requested with integrity
|
||||
// metadata to be reused for a request that didn't itself require integrity
|
||||
// checks. In that case, the integrity check can be skipped, as the integrity
|
||||
// may not have been "meant" for this specific request. If the resource is
|
||||
// being served from the preload cache however, we know any associated
|
||||
// integrity metadata and checks were destined for this request, so we cannot
|
||||
// skip the integrity check.
|
||||
//
|
||||
// TODO(375343417): This will also be the case for server-initiated integrity
|
||||
// checks like `Identity-Digest`.
|
||||
bool ForceIntegrityChecks() const { return IsLinkPreload(); }
|
||||
|
||||
const SubresourceIntegrity::ReportInfo& IntegrityReportInfo() const {
|
||||
return integrity_report_info_;
|
||||
}
|
||||
|
Reference in New Issue
Block a user