UNPKG

chrome-devtools-frontend

Version:
297 lines (283 loc) • 13.3 kB
// Copyright 2020 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. import * as i18n from '../../core/i18n/i18n.js'; import type * as SDK from '../../core/sdk/sdk.js'; import * as Protocol from '../../generated/protocol.js'; import {Issue, IssueCategory, IssueKind} from './Issue.js'; import type {MarkdownIssueDescription} from './MarkdownIssueDescription.js'; const UIStrings = { /** *@description Label for the link for CORS Local Network Access issues */ corsLocalNetworkAccess: 'Local Network Access', /** *@description Label for the link for CORS private network issues */ corsPrivateNetworkAccess: 'Private Network Access', /** *@description Label for the link for CORS network issues */ CORS: 'Cross-Origin Resource Sharing (`CORS`)', } as const; const str_ = i18n.i18n.registerUIStrings('models/issues_manager/CorsIssue.ts', UIStrings); const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_); export const enum IssueCode { INSECURE_PRIVATE_NETWORK = 'CorsIssue::InsecurePrivateNetwork', INVALID_HEADER_VALUES = 'CorsIssue::InvalidHeaders', WILDCARD_ORIGN_NOT_ALLOWED = 'CorsIssue::WildcardOriginWithCredentials', PREFLIGHT_RESPONSE_INVALID = 'CorsIssue::PreflightResponseInvalid', ORIGIN_MISMATCH = 'CorsIssue::OriginMismatch', ALLOW_CREDENTIALS_REQUIRED = 'CorsIssue::AllowCredentialsRequired', METHOD_DISALLOWED_BY_PREFLIGHT_RESPONSE = 'CorsIssue::MethodDisallowedByPreflightResponse', HEADER_DISALLOWED_BY_PREFLIGHT_RESPONSE = 'CorsIssue::HeaderDisallowedByPreflightResponse', REDIRECT_CONTAINS_CREDENTIALS = 'CorsIssue::RedirectContainsCredentials', DISALLOWED_BY_MODE = 'CorsIssue::DisallowedByMode', CORS_DISABLED_SCHEME = 'CorsIssue::CorsDisabledScheme', // TODO(https://crbug.com/1263483): Remove this once it's removed from CDP. PREFLIGHT_MISSING_ALLOW_EXTERNAL = 'CorsIssue::PreflightMissingAllowExternal', // TODO(https://crbug.com/1263483): Remove this once it's removed from CDP. PREFLIGHT_INVALID_ALLOW_EXTERNAL = 'CorsIssue::PreflightInvalidAllowExternal', NO_CORS_REDIRECT_MODE_NOT_FOLLOW = 'CorsIssue::NoCorsRedirectModeNotFollow', INVALID_PRIVATE_NETWORK_ACCESS = 'CorsIssue::InvalidPrivateNetworkAccess', UNEXPECTED_PRIVATE_NETWORK_ACCESS = 'CorsIssue::UnexpectedPrivateNetworkAccess', PREFLIGHT_ALLOW_PRIVATE_NETWORK_ERROR = 'CorsIssue::PreflightAllowPrivateNetworkError', PREFLIGHT_MISSING_PRIVATE_NETWORK_ACCESS_ID = 'CorsIssue::PreflightMissingPrivateNetworkAccessId', PREFLIGHT_MISSING_PRIVATE_NETWORK_ACCESS_NAME = 'CorsIssue::PreflightMissingPrivateNetworkAccessName', PRIVATE_NETWORK_ACCESS_PERMISSION_UNAVAILABLE = 'CorsIssue::PrivateNetworkAccessPermissionUnavailable', PRIVATE_NETWORK_ACCESS_PERMISSION_DENIED = 'CorsIssue::PrivateNetworkAccessPermissionDenied', LOCAL_NETWORK_ACCESS_PERMISSION_DENIED = 'CorsIssue::LocalNetworkAccessPermissionDenied', } function getIssueCode(details: Protocol.Audits.CorsIssueDetails): IssueCode { switch (details.corsErrorStatus.corsError) { case Protocol.Network.CorsError.InvalidAllowMethodsPreflightResponse: case Protocol.Network.CorsError.InvalidAllowHeadersPreflightResponse: case Protocol.Network.CorsError.PreflightMissingAllowOriginHeader: case Protocol.Network.CorsError.PreflightMultipleAllowOriginValues: case Protocol.Network.CorsError.PreflightInvalidAllowOriginValue: case Protocol.Network.CorsError.MissingAllowOriginHeader: case Protocol.Network.CorsError.MultipleAllowOriginValues: case Protocol.Network.CorsError.InvalidAllowOriginValue: return IssueCode.INVALID_HEADER_VALUES; case Protocol.Network.CorsError.PreflightWildcardOriginNotAllowed: case Protocol.Network.CorsError.WildcardOriginNotAllowed: return IssueCode.WILDCARD_ORIGN_NOT_ALLOWED; case Protocol.Network.CorsError.PreflightInvalidStatus: case Protocol.Network.CorsError.PreflightDisallowedRedirect: case Protocol.Network.CorsError.InvalidResponse: return IssueCode.PREFLIGHT_RESPONSE_INVALID; case Protocol.Network.CorsError.AllowOriginMismatch: case Protocol.Network.CorsError.PreflightAllowOriginMismatch: return IssueCode.ORIGIN_MISMATCH; case Protocol.Network.CorsError.InvalidAllowCredentials: case Protocol.Network.CorsError.PreflightInvalidAllowCredentials: return IssueCode.ALLOW_CREDENTIALS_REQUIRED; case Protocol.Network.CorsError.MethodDisallowedByPreflightResponse: return IssueCode.METHOD_DISALLOWED_BY_PREFLIGHT_RESPONSE; case Protocol.Network.CorsError.HeaderDisallowedByPreflightResponse: return IssueCode.HEADER_DISALLOWED_BY_PREFLIGHT_RESPONSE; case Protocol.Network.CorsError.RedirectContainsCredentials: return IssueCode.REDIRECT_CONTAINS_CREDENTIALS; case Protocol.Network.CorsError.DisallowedByMode: return IssueCode.DISALLOWED_BY_MODE; case Protocol.Network.CorsError.CorsDisabledScheme: return IssueCode.CORS_DISABLED_SCHEME; case Protocol.Network.CorsError.PreflightMissingAllowExternal: return IssueCode.PREFLIGHT_MISSING_ALLOW_EXTERNAL; case Protocol.Network.CorsError.PreflightInvalidAllowExternal: return IssueCode.PREFLIGHT_INVALID_ALLOW_EXTERNAL; case Protocol.Network.CorsError.InsecurePrivateNetwork: return IssueCode.INSECURE_PRIVATE_NETWORK; case Protocol.Network.CorsError.NoCorsRedirectModeNotFollow: return IssueCode.NO_CORS_REDIRECT_MODE_NOT_FOLLOW; case Protocol.Network.CorsError.InvalidPrivateNetworkAccess: return IssueCode.INVALID_PRIVATE_NETWORK_ACCESS; case Protocol.Network.CorsError.UnexpectedPrivateNetworkAccess: return IssueCode.UNEXPECTED_PRIVATE_NETWORK_ACCESS; case Protocol.Network.CorsError.PreflightMissingAllowPrivateNetwork: case Protocol.Network.CorsError.PreflightInvalidAllowPrivateNetwork: return IssueCode.PREFLIGHT_ALLOW_PRIVATE_NETWORK_ERROR; case Protocol.Network.CorsError.PreflightMissingPrivateNetworkAccessId: return IssueCode.PREFLIGHT_MISSING_PRIVATE_NETWORK_ACCESS_ID; case Protocol.Network.CorsError.PreflightMissingPrivateNetworkAccessName: return IssueCode.PREFLIGHT_MISSING_PRIVATE_NETWORK_ACCESS_NAME; case Protocol.Network.CorsError.PrivateNetworkAccessPermissionUnavailable: return IssueCode.PRIVATE_NETWORK_ACCESS_PERMISSION_UNAVAILABLE; case Protocol.Network.CorsError.PrivateNetworkAccessPermissionDenied: return IssueCode.PRIVATE_NETWORK_ACCESS_PERMISSION_DENIED; case Protocol.Network.CorsError.LocalNetworkAccessPermissionDenied: return IssueCode.LOCAL_NETWORK_ACCESS_PERMISSION_DENIED; } } export class CorsIssue extends Issue<IssueCode> { #issueDetails: Protocol.Audits.CorsIssueDetails; constructor( issueDetails: Protocol.Audits.CorsIssueDetails, issuesModel: SDK.IssuesModel.IssuesModel, issueId: Protocol.Audits.IssueId|undefined) { super(getIssueCode(issueDetails), issuesModel, issueId); this.#issueDetails = issueDetails; } getCategory(): IssueCategory { return IssueCategory.CORS; } details(): Protocol.Audits.CorsIssueDetails { return this.#issueDetails; } getDescription(): MarkdownIssueDescription|null { switch (getIssueCode(this.#issueDetails)) { case IssueCode.INSECURE_PRIVATE_NETWORK: return { file: 'corsInsecurePrivateNetwork.md', links: [{ link: 'https://developer.chrome.com/blog/private-network-access-update', linkTitle: i18nString(UIStrings.corsPrivateNetworkAccess), }], }; case IssueCode.PREFLIGHT_ALLOW_PRIVATE_NETWORK_ERROR: return { file: 'corsPreflightAllowPrivateNetworkError.md', links: [{ link: 'https://developer.chrome.com/blog/private-network-access-update', linkTitle: i18nString(UIStrings.corsPrivateNetworkAccess), }], }; case IssueCode.INVALID_HEADER_VALUES: return { file: 'corsInvalidHeaderValues.md', links: [{ link: 'https://web.dev/cross-origin-resource-sharing', linkTitle: i18nString(UIStrings.CORS), }], }; case IssueCode.WILDCARD_ORIGN_NOT_ALLOWED: return { file: 'corsWildcardOriginNotAllowed.md', links: [{ link: 'https://web.dev/cross-origin-resource-sharing', linkTitle: i18nString(UIStrings.CORS), }], }; case IssueCode.PREFLIGHT_RESPONSE_INVALID: return { file: 'corsPreflightResponseInvalid.md', links: [{ link: 'https://web.dev/cross-origin-resource-sharing', linkTitle: i18nString(UIStrings.CORS), }], }; case IssueCode.ORIGIN_MISMATCH: return { file: 'corsOriginMismatch.md', links: [{ link: 'https://web.dev/cross-origin-resource-sharing', linkTitle: i18nString(UIStrings.CORS), }], }; case IssueCode.ALLOW_CREDENTIALS_REQUIRED: return { file: 'corsAllowCredentialsRequired.md', links: [{ link: 'https://web.dev/cross-origin-resource-sharing', linkTitle: i18nString(UIStrings.CORS), }], }; case IssueCode.METHOD_DISALLOWED_BY_PREFLIGHT_RESPONSE: return { file: 'corsMethodDisallowedByPreflightResponse.md', links: [{ link: 'https://web.dev/cross-origin-resource-sharing', linkTitle: i18nString(UIStrings.CORS), }], }; case IssueCode.HEADER_DISALLOWED_BY_PREFLIGHT_RESPONSE: return { file: 'corsHeaderDisallowedByPreflightResponse.md', links: [{ link: 'https://web.dev/cross-origin-resource-sharing', linkTitle: i18nString(UIStrings.CORS), }], }; case IssueCode.REDIRECT_CONTAINS_CREDENTIALS: return { file: 'corsRedirectContainsCredentials.md', links: [{ link: 'https://web.dev/cross-origin-resource-sharing', linkTitle: i18nString(UIStrings.CORS), }], }; case IssueCode.DISALLOWED_BY_MODE: return { file: 'corsDisallowedByMode.md', links: [{ link: 'https://web.dev/cross-origin-resource-sharing', linkTitle: i18nString(UIStrings.CORS), }], }; case IssueCode.CORS_DISABLED_SCHEME: return { file: 'corsDisabledScheme.md', links: [{ link: 'https://web.dev/cross-origin-resource-sharing', linkTitle: i18nString(UIStrings.CORS), }], }; case IssueCode.NO_CORS_REDIRECT_MODE_NOT_FOLLOW: return { file: 'corsNoCorsRedirectModeNotFollow.md', links: [{ link: 'https://web.dev/cross-origin-resource-sharing', linkTitle: i18nString(UIStrings.CORS), }], }; // TODO(1462857): Change the link after we have a blog post for PNA // permission prompt. case IssueCode.PREFLIGHT_MISSING_PRIVATE_NETWORK_ACCESS_ID: case IssueCode.PREFLIGHT_MISSING_PRIVATE_NETWORK_ACCESS_NAME: return { file: 'corsPrivateNetworkPermissionDenied.md', links: [{ link: 'https://developer.chrome.com/blog/private-network-access-update', linkTitle: i18nString(UIStrings.corsPrivateNetworkAccess), }], }; case IssueCode.LOCAL_NETWORK_ACCESS_PERMISSION_DENIED: return { file: 'corsLocalNetworkAccessPermissionDenied.md', links: [{ link: 'https://chromestatus.com/feature/5152728072060928', linkTitle: i18nString(UIStrings.corsLocalNetworkAccess), }], }; case IssueCode.PREFLIGHT_MISSING_ALLOW_EXTERNAL: case IssueCode.PREFLIGHT_INVALID_ALLOW_EXTERNAL: case IssueCode.INVALID_PRIVATE_NETWORK_ACCESS: case IssueCode.UNEXPECTED_PRIVATE_NETWORK_ACCESS: case IssueCode.PRIVATE_NETWORK_ACCESS_PERMISSION_UNAVAILABLE: case IssueCode.PRIVATE_NETWORK_ACCESS_PERMISSION_DENIED: return null; } } primaryKey(): string { return JSON.stringify(this.#issueDetails); } getKind(): IssueKind { if (this.#issueDetails.isWarning && (this.#issueDetails.corsErrorStatus.corsError === Protocol.Network.CorsError.InsecurePrivateNetwork || this.#issueDetails.corsErrorStatus.corsError === Protocol.Network.CorsError.PreflightMissingAllowPrivateNetwork || this.#issueDetails.corsErrorStatus.corsError === Protocol.Network.CorsError.PreflightInvalidAllowPrivateNetwork)) { return IssueKind.BREAKING_CHANGE; } return IssueKind.PAGE_ERROR; } static fromInspectorIssue(issuesModel: SDK.IssuesModel.IssuesModel, inspectorIssue: Protocol.Audits.InspectorIssue): CorsIssue[] { const corsIssueDetails = inspectorIssue.details.corsIssueDetails; if (!corsIssueDetails) { console.warn('Cors issue without details received.'); return []; } return [new CorsIssue(corsIssueDetails, issuesModel, inspectorIssue.issueId)]; } }