diff --git a/config/gni/all_devtools_files.gni b/config/gni/all_devtools_files.gni index bac60f10f1..32eb34b1fe 100644 --- a/config/gni/all_devtools_files.gni +++ b/config/gni/all_devtools_files.gni @@ -214,6 +214,7 @@ all_devtools_files = [ "front_end/models/issues_manager/descriptions/corsAllowCredentialsRequired.md", "front_end/models/issues_manager/descriptions/corsDisallowedByMode.md", "front_end/models/issues_manager/descriptions/corsDisabledScheme.md", + "front_end/models/issues_manager/descriptions/corsNoCorsRedirectModeNotFollow.md", "front_end/models/issues_manager/descriptions/corsHeaderDisallowedByPreflightResponse.md", "front_end/models/issues_manager/descriptions/corsInvalidHeaderValues.md", "front_end/models/issues_manager/descriptions/corsMethodDisallowedByPreflightResponse.md", diff --git a/config/gni/devtools_grd_files.gni b/config/gni/devtools_grd_files.gni index 5b8f46d501..4657e1584c 100644 --- a/config/gni/devtools_grd_files.gni +++ b/config/gni/devtools_grd_files.gni @@ -253,6 +253,7 @@ grd_files_release_sources = [ "front_end/models/issues_manager/descriptions/corsInsecurePrivateNetworkPreflight.md", "front_end/models/issues_manager/descriptions/corsInvalidHeaderValues.md", "front_end/models/issues_manager/descriptions/corsMethodDisallowedByPreflightResponse.md", + "front_end/models/issues_manager/descriptions/corsNoCorsRedirectModeNotFollow.md", "front_end/models/issues_manager/descriptions/corsOriginMismatch.md", "front_end/models/issues_manager/descriptions/corsPreflightResponseInvalid.md", "front_end/models/issues_manager/descriptions/corsRedirectContainsCredentials.md", diff --git a/front_end/models/issues_manager/BUILD.gn b/front_end/models/issues_manager/BUILD.gn index ae3a19362e..3e04174066 100644 --- a/front_end/models/issues_manager/BUILD.gn +++ b/front_end/models/issues_manager/BUILD.gn @@ -52,6 +52,7 @@ devtools_issue_description_files = [ "corsInsecurePrivateNetworkPreflight.md", "corsInvalidHeaderValues.md", "corsMethodDisallowedByPreflightResponse.md", + "corsNoCorsRedirectModeNotFollow.md", "corsOriginMismatch.md", "corsPreflightResponseInvalid.md", "corsRedirectContainsCredentials.md", diff --git a/front_end/models/issues_manager/CorsIssue.ts b/front_end/models/issues_manager/CorsIssue.ts index d66ccdcf47..631bf74e51 100644 --- a/front_end/models/issues_manager/CorsIssue.ts +++ b/front_end/models/issues_manager/CorsIssue.ts @@ -41,7 +41,7 @@ export enum IssueCode { PreflightMissingAllowExternal = 'CorsIssue::PreflightMissingAllowExternal', PreflightInvalidAllowExternal = 'CorsIssue::PreflightInvalidAllowExternal', InvalidResponse = 'CorsIssue::InvalidResponse', - NoCorsRedirectModeNotFollow = 'NoCorsRedirectModeNotFollow', + NoCorsRedirectModeNotFollow = 'CorsIssue::NoCorsRedirectModeNotFollow', } function getIssueCode(details: Protocol.Audits.CorsIssueDetails): IssueCode { @@ -205,10 +205,17 @@ export class CorsIssue extends Issue { linkTitle: i18nString(UIStrings.CORS), }], }; + case IssueCode.NoCorsRedirectModeNotFollow: + return { + file: 'corsNoCorsRedirectModeNotFollow.md', + links: [{ + link: 'https://web.dev/cross-origin-resource-sharing', + linkTitle: i18nString(UIStrings.CORS), + }], + }; case IssueCode.PreflightMissingAllowExternal: case IssueCode.PreflightInvalidAllowExternal: case IssueCode.InvalidResponse: - case IssueCode.NoCorsRedirectModeNotFollow: return null; } } diff --git a/front_end/models/issues_manager/descriptions/corsNoCorsRedirectModeNotFollow.md b/front_end/models/issues_manager/descriptions/corsNoCorsRedirectModeNotFollow.md new file mode 100644 index 0000000000..270aca56fe --- /dev/null +++ b/front_end/models/issues_manager/descriptions/corsNoCorsRedirectModeNotFollow.md @@ -0,0 +1,5 @@ +# Ensure no-cors requests configure redirect mode follow + +A cross-origin resource sharing (CORS) request was blocked because it was configured to use request mode `no-cors` but did not use the redirect mode `follow`. + +To fix this issue, ensure that whenever the request mode `no-cors` is set then the redirect mode is set to `follow`. diff --git a/front_end/panels/issues/CorsIssueDetailsView.ts b/front_end/panels/issues/CorsIssueDetailsView.ts index af90333e1e..ce08b7537b 100644 --- a/front_end/panels/issues/CorsIssueDetailsView.ts +++ b/front_end/panels/issues/CorsIssueDetailsView.ts @@ -199,9 +199,11 @@ export class CorsIssueDetailsView extends AffectedResourcesView { this.appendColumnTitle(header, i18nString(UIStrings.sourceLocation)); this.appendColumnTitle(header, i18nString(UIStrings.unsupportedScheme)); break; + case IssuesManager.CorsIssue.IssueCode.NoCorsRedirectModeNotFollow: + this.appendColumnTitle(header, i18nString(UIStrings.sourceLocation)); + break; default: - Platform.assertUnhandled(issueCode); } @@ -403,11 +405,15 @@ export class CorsIssueDetailsView extends AffectedResourcesView { this.appendSourceLocation(element, details.location, issue.model()?.getTargetIfNotDisposed()); this.appendIssueDetailCell(element, details.corsErrorStatus.failedParameter ?? '', 'code-example'); break; + case IssuesManager.CorsIssue.IssueCode.NoCorsRedirectModeNotFollow: + element.appendChild(this.createRequestCell(details.request)); + this.appendStatus(element, details.isWarning); + this.appendSourceLocation(element, details.location, issue.model()?.getTargetIfNotDisposed()); + break; default: element.appendChild(this.createRequestCell(details.request)); this.appendStatus(element, details.isWarning); - Platform.assertUnhandled(issueCode); break; diff --git a/test/e2e/issues/cors-issues_test.ts b/test/e2e/issues/cors-issues_test.ts index 72c3cf86d5..cf2b0bc899 100644 --- a/test/e2e/issues/cors-issues_test.ts +++ b/test/e2e/issues/cors-issues_test.ts @@ -448,4 +448,39 @@ describe('CORS issues', async () => { 'webdav', ]); }); + + it('should display CORS issues that are misconfiguring the redirect mode', async () => { + await goToResource('empty.html'); + const {target} = getBrowserAndPages(); + await target.evaluate(async () => { + try { + const url = new URL('/', document.location.toString()) + .toString() + .replace('https://localhost', 'webdav://devtools.oopif.test'); + await fetch(url, {mode: 'no-cors', redirect: 'manual'}); + } catch (e) { + } + }); + await navigateToIssuesTab(); + await expandIssue(); + const issueElement = await getIssueByTitle('Ensure no-cors requests configure redirect mode follow'); + assertNotNull(issueElement); + const section = await getResourcesElement('request', issueElement, '.cors-issue-affected-resource-label'); + const text = await section.label.evaluate(el => el.textContent); + assert.strictEqual(text, '1 request'); + await ensureResourceSectionIsExpanded(section); + const table = await extractTableFromResourceSection(section.content); + assertNotNull(table); + assert.strictEqual(table.length, 2); + assert.deepEqual(table[0], [ + 'Request', + 'Status', + 'Source Location', + ]); + assertMatchArray(table[1], [ + /^devtools.oopif.test.*\//, + 'blocked', + /.*:\d+/, + ]); + }); });