CVE-2026-113075
Overview
Changed Functions
| Function | Change | Notes |
|---|---|---|
iffront_end/models/bindings/ResourceScriptMapping.ts |
modified |
Files Changed
front_end/models/persistence/NetworkPersistenceManager.tstest/e2e/console/console-errors.test.tsfront_end/models/bindings/NetworkProject.tsfront_end/models/bindings/ResourceScriptMapping.tsfront_end/models/persistence/NetworkPersistenceManager.test.tsfront_end/models/bindings/ResourceScriptMapping.test.tsfront_end/models/persistence/BUILD.gn
Patch
From 0e5527264722c85d3771c38762ce63e9485dc165 Mon Sep 17 00:00:00 2001 From: Danil Somsikov <[email protected]> Date: Mon, 21 Sep 2026 02:42:10 -0700 Subject: [PATCH] Prevent sourceURL annotations from spoofing origins and being locally overridden Scripts can specify sourceURL annotations and source maps that are fully controlled by the page. This change ensures that: - HTTP and HTTPS sourceURL annotations must match the origin of the script frame to prevent cross-origin resource spoofing. - sourceURL annotations cannot evict existing UISourceCodes fetched directly from the network. - UISourceCodes generated from sourceURL annotations or source maps cannot be saved as local overrides, preventing override directory poisoning. Bug: 553931271 Change-Id: Ib9ac1c265445980585706830385697ee6d6d29d1 Reviewed-on: https://chromium-review.googlesource.com/c/devtools/devtools-frontend/+/8421102 Commit-Queue: Danil Somsikov <[email protected]> Reviewed-by: Simon Zünd <[email protected]> Auto-Submit: Danil Somsikov <[email protected]> Reviewed-by: Philip Pfaffe <[email protected]> --- diff --git a/front_end/models/bindings/NetworkProject.ts b/front_end/models/bindings/NetworkProject.ts index 03a5bec..6866340 100644 --- a/front_end/models/bindings/NetworkProject.ts +++ b/front_end/models/bindings/NetworkProject.ts @@ -15,6 +15,7 @@ export class NetworkProjectManager extends Common.ObjectWrapper.ObjectWrapper<EventTypes> { readonly #projectToTargetMap = new WeakMap<Workspace.Workspace.Project, SDK.Target.Target>(); + readonly #sourceURLSynthesizedUISourceCodes = new WeakSet<Workspace.UISourceCode.UISourceCode>(); static instance({forceNew}: { forceNew: boolean, @@ -41,6 +42,14 @@ getTargetForUISourceCode(uiSourceCode: Workspace.UISourceCode.UISourceCode): SDK.Target.Target|null { return this.#projectToTargetMap.get(uiSourceCode.project()) ?? null; } + + setSourceURLSynthesized(uiSourceCode: Workspace.UISourceCode.UISourceCode): void { + this.#sourceURLSynthesizedUISourceCodes.add(uiSourceCode); + } + + isSourceURLSynthesized(uiSourceCode: Workspace.UISourceCode.UISourceCode): boolean { + return this.#sourceURLSynthesizedUISourceCodes.has(uiSourceCode); + } } export const enum Events { @@ -59,6 +68,27 @@ } export class NetworkProject { + /** + * Records that `uiSourceCode` was synthesized from a `//# sourceURL=` annotation, + * and therefore doesn't correspond to an actual network resource. + */ + static setSourceURLSynthesized(uiSourceCode: Workspace.UISourceCode.UISourceCode): void { + // eslint-disable-next-line @devtools/no-instance-of-migrated-singletons + NetworkProjectManager.instance().setSourceURLSynthesized(uiSourceCode); + } + + /** + * Whether `uiSourceCode` was synthesized from a `//# sourceURL=` annotation. + * + * Both the URL and the content of such a source are fully controlled by the page, + * so it must never be treated like a genuine network resource (for example it must + * not be persisted as a local override, see b/553931271). + */ + static isSourceURLSynthesized(uiSourceCode: Workspace.UISourceCode.UISourceCode): boolean { + // eslint-disable-next-line @devtools/no-instance-of-migrated-singletons + return NetworkProjectManager.instance().isSourceURLSynthesized(uiSourceCode); + } + static resolveFrame(uiSourceCode: Workspace.UISourceCode.UISourceCode, frameId: Protocol.Page.FrameId): SDK.ResourceTreeModel.ResourceTreeFrame|null { const target = NetworkProject.targetForUISourceCode(uiSourceCode); diff --git a/front_end/models/bindings/ResourceScriptMapping.test.ts b/front_end/models/bindings/ResourceScriptMapping.test.ts index 8c073d2..f296fdf 100644 --- a/front_end/models/bindings/ResourceScriptMapping.test.ts +++ b/front_end/models/bindings/ResourceScriptMapping.test.ts @@ -7,7 +7,9 @@ import * as Platform from '../../core/platform/platform.js'; import * as SDK from '../../core/sdk/sdk.js'; import * as TextUtils from '../../core/text_utils/text_utils.js'; +import {setupLocaleHooks} from '../../testing/LocaleHelpers.js'; import {MockDebuggerBackend} from '../../testing/MockScopeChain.js'; +import {getMainFrame, SECURITY_ORIGIN} from '../../testing/ResourceTreeHelpers.js'; import {setupRuntimeHooks} from '../../testing/RuntimeHelpers.js'; import {setupSettingsHooks} from '../../testing/SettingsHelpers.js'; @@ -16,6 +18,7 @@ const {urlString} = Platform.DevToolsPath; describe('ResourceScriptMapping', () => { + setupLocaleHooks(); setupRuntimeHooks(); setupSettingsHooks(); @@ -73,7 +76,9 @@ }); it('maps UI locations in inline scripts with sourceURL', async () => { - const script = await backend.addScript(target, {content: contentWithSourceUrl, url, hasSourceURL: true}, null); + getMainFrame(target); + const script = await backend.addScript( + target, {content: contentWithSourceUrl, url: `${SECURITY_ORIGIN}/example.js`, hasSourceURL: true}, null); const uiSourceCode = resourceScriptMapping.uiSourceCodeForScript(script); assert.exists(uiSourceCode); @@ -109,4 +114,85 @@ assert.isNull(uiSourceCode); }); + + describe('for scripts with a `//# sourceURL` annotation', () => { + const content = contentWithSourceUrl; + + it('creates a UISourceCode for same-origin URLs', async () => { + getMainFrame(target); + + const script = await backend.addScript( + target, {content, url: `${SECURITY_ORIGIN}/from-source-url.js`, hasSourceURL: true}, null); + + const uiSourceCode = resourceScriptMapping.uiSourceCodeForScript(script); + assert.exists(uiSourceCode); + assert.isTrue(Bindings.NetworkProject.NetworkProject.isSourceURLSynthesized(uiSourceCode)); + }); + + it('ignores annotations claiming a cross-origin URL', async () => { + getMainFrame(target); + + const script = await backend.addScript( + target, {content, url: 'https://not-example.com/from-source-url.js', hasSourceURL: true}, null); + + assert.isNull(resourceScriptMapping.uiSourceCodeForScript(script)); + }); + + it('accepts annotations with schemes that cannot be network resources', async () => { + getMainFrame(target); + + const script = await backend.addScript( + target, {content, url: 'webpack-internal:///./src/index.js', hasSourceURL: true}, null); + + assert.exists(resourceScriptMapping.uiSourceCodeForScript(script)); + }); + + it('does not evict the UISourceCode of a script that was fetched from the network', async () => { + getMainFrame(target); + const networkURL = `${SECURITY_ORIGIN}/example.js`; + const networkScript = await backend.addScript( + target, {content: contentWithoutSourceUrl, url: networkURL, hasSourceURL: false}, null); + const networkUISourceCode = resourceScriptMapping.uiSourceCodeForScript(networkScript); + assert.exists(networkUISourceCode); + + const spoofingScript = await backend.addScript(target, {content, url: networkURL, hasSourceURL: true}, null); + + assert.isNull(resourceScriptMapping.uiSourceCodeForScript(spoofingScript)); + assert.strictEqual(resourceScriptMapping.uiSourceCodeForScript(networkScript), networkUISourceCode); + assert.isFalse(Bindings.NetworkProject.NetworkProject.isSourceURLSynthesized(networkUISourceCode)); + }); + + it('falls back to the inspected origin when the frame is unknown', async () => { + target.setInspectedURL(urlString`${`${SECURITY_ORIGIN}/index.html`}`); + + const sameOrigin = await backend.addScript( + target, {content, url: `${SECURITY_ORIGIN}/from-source-url.js`, hasSourceURL: true}, null); + const crossOrigin = await backend.addScript( + target, {content, url: 'https://not-example.com/from-source-url.js', hasSourceURL: true}, null); + + assert.exists(resourceScriptMapping.uiSourceCodeForScript(sameOrigin)); + assert.isNull(resourceScriptMapping.uiSourceCodeForScript(crossOrigin)); + }); + + it('ignores annotations when no origin can be established', async () => { + // Neither a frame nor an inspected URL: we can't tell whether the annotation + // spoofs another origin, so it must not be used. + const script = await backend.addScript( + target, {content, url: `${SECURITY_ORIGIN}/from-source-url.js`, hasSourceURL: true}, null); + + assert.isNull(resourceScriptMapping.uiSourceCodeForScript(script)); + }); + + it('replaces the UISourceCode of an earlier script with the same annotation', async () => { + getMainFrame(target); + const sourceURL = `${SECURITY_ORIGIN}/from-source-url.js`; + const oldScript = await backend.addScript(target, {content, url: sourceURL, hasSourceURL: true}, null); + assert.exists(resourceScriptMapping.uiSourceCodeForScript(oldScript)); + + const newScript = await backend.addScript(target, {content, url: sourceURL, hasSourceURL: true}, null); + + assert.exists(resourceScriptMapping.uiSourceCodeForScript(newScript)); + assert.isNull(resourceScriptMapping.uiSourceCodeForScript(oldScript)); + }); + }); }); diff --git a/front_end/models/bindings/ResourceScriptMapping.ts b/front_end/models/bindings/ResourceScriptMapping.ts index e01c6f7..185ba78 100644 --- a/front_end/models/bindings/ResourceScriptMapping.ts +++ b/front_end/models/bindings/ResourceScriptMapping.ts @@ -14,6 +14,19 @@ import {NetworkProject} from './NetworkProject.js'; import {metadataForURL} from './ResourceUtils.js'; +/** + * The security origin of the URL that `target` (or its closest ancestor that has + * one) is inspecting, or `null` if none of them has an inspected URL yet. + */ +function inspectedSecurityOrigin(target: SDK.Target.Target): SDK.SecurityOrigin.SecurityOrigin|null { + for (let current: SDK.Target.Target|null = target; current; current = current.parentTarget()) { + if (current.inspectedURL()) { + return current.inspectedSecurityOrigin(); + } + } + return null; +} + export class ResourceScriptMapping implements DebuggerSourceMapping { readonly debuggerModel: SDK.DebuggerModel.DebuggerModel; #workspace: Workspace.Workspace.WorkspaceImpl; @@ -197,6 +210,19 @@ // Try to resolve `//# sourceURL=` annotations relative to // the base URL, according to the sourcemap specification. url = SDK.SourceMapManager.SourceMapManager.resolveRelativeSourceURL(script.debuggerModel.target(), url); + + // `//# sourceURL=` annotations are fully controlled by the page, so don't let + // them impersonate a resource of a different origin (b/553931271). + if (!this.#isTrustworthySourceURL(script, url)) { + return; + } + + // Don't let a synthetic source evict the `UISourceCode` of a script that was + // actually fetched from the network under the same URL. + const previousUISourceCode = this.project(script).uiSourceCodeForURL(url); + if (previousUISourceCode && !NetworkProject.isSourceURLSynthesized(previousUISourceCode)) { + return; + } } else { // Ignore inline <script>s without `//# sourceURL` annotation here. if (script.isInlineScript()) { @@ -225,6 +251,9 @@ // Create UISourceCode. const originalContentProvider = script.originalContentProvider(); const uiSourceCode = project.createUISourceCode(url, originalContentProvider.contentType()); + if (script.hasSourceURL) { + NetworkProject.setSourceURLSynthesized(uiSourceCode); + } NetworkProject.setInitialFrameAttribution(uiSourceCode, script.frameId); const metadata = metadataForURL(this.debuggerModel.target(), script.frameId, url); @@ -238,6 +267,39 @@ void this.debuggerWorkspaceBinding.updateLocations(script); } + /** + * Whether the (already resolved) `//# sourceURL=` annotation `url` of `script` may + * be used as-is. + * + * Only `http(s)` URLs can collide with genuine network resources, so an origin is + * only enforced for those. Annotations using other schemes (e.g. `webpack-internal://`, + * `snippet://` or `chrome-extension://` for content scripts) cannot impersonate a + * network resource and are always accepted. + * + * The origin of the script's frame is authoritative. For targets that don't have a + * frame (e.g. workers) we fall back to the inspected URL of the target (or of its + * closest ancestor that has one). If no origin can be established at all, the + * annotation is rejected, since we cannot rule out that it spoofs another origin. + */ + #isTrustworthySourceURL(script: SDK.Script.Script, url: Platform.DevToolsPath.UrlString): boolean { + if (script.isContentScript()) { + return true; + } + const parsedURL = Common.ParsedURL.ParsedURL.fromString(url); + if (!parsedURL || !['http', 'https'].includes(parsedURL.scheme)) { + return true; + } + const target = script.debuggerModel.target(); + const frame = script.frameId ? + target.model(SDK.ResourceTreeModel.ResourceTreeModel)?.frameForId(script.frameId) ?? null : + null; + const securityOrigin = frame ? frame.securityOrigin() : inspectedSecurityOrigin(target); + if (!securityOrigin) { + return false; + } + return SDK.SecurityOrigin.SecurityOrigin.create(url).isSameOriginWith(securityOrigin); + } + scriptFile(uiSourceCode: Workspace.UISourceCode.UISourceCode): ResourceScriptFile|null { return this.#uiSourceCodeToScriptFile.get(uiSourceCode) || null; } diff --git a/front_end/models/persistence/NetworkPersistenceManager.test.ts b/front_end/models/persistence/NetworkPersistenceManager.test.ts index 3b12287..5cde2c7 100644 --- a/front_end/models/persistence/NetworkPersistenceManager.test.ts +++ b/front_end/models/persistence/NetworkPersistenceManager.test.ts @@ -17,6 +17,7 @@ import {setupSettingsHooks} from '../../testing/SettingsHelpers.js'; import {createTarget} from '../../testing/TargetHelpers.js'; import {createFileSystemUISourceCode} from '../../testing/UISourceCodeHelpers.js'; +import * as Bindings from '../bindings/bindings.js'; import * as Formatter from '../formatter/formatter.js'; import * as Persistence from '../persistence/persistence.js'; import * as Workspace from '../workspace/workspace.js'; @@ -127,6 +128,48 @@ assert.isTrue(saveSpy.notCalled, 'should not attempt to save override'); }); + it('does not persist sources that come from a source map', async () => { + const url = 'http://www.example.com/src/script.ts'; + const resourceType = Common.ResourceType.resourceTypes.SourceMapScript; + + const {uiSourceCode} = setUpEnvironmentWithUISourceCode(url, resourceType); + const networkPersistenceManager = await createWorkspaceProject(urlString`file:///path/to/overrides`, []); + const overridesProject = networkPersistenceManager.project(); + assert.exists(overridesProject); + const createFileSpy = sinon.spy(overridesProject, 'createFile'); + + // The context menu still offers "Override content" for source mapped files, but it + // redirects to the deployed file instead of persisting the source mapped one. + assert.isTrue(networkPersistenceManager.isUISourceCodeOverridable(uiSourceCode)); + + const actual = await networkPersistenceManager.setupAndStartLocalOverrides(uiSourceCode); + await networkPersistenceManager.saveUISourceCodeForOverrides(uiSourceCode); + + createFileSpy.restore(); + + assert.isFalse(actual, 'should not allow override'); + assert.isTrue(createFileSpy.notCalled, 'should not write an override file'); + }); + + it('does not allow overrides for sources synthesized from a `//# sourceURL` annotation', async () => { + const url = 'http://www.example.com/script.js'; + const resourceType = Common.ResourceType.resourceTypes.Script; + + const {uiSourceCode} = setUpEnvironmentWithUISourceCode(url, resourceType); + Bindings.NetworkProject.NetworkProject.setSourceURLSynthesized(uiSourceCode); + const networkPersistenceManager = await createWorkspaceProject(urlString`file:///path/to/overrides`, []); + + assert.isFalse(networkPersistenceManager.isUISourceCodeOverridable(uiSourceCode)); + + const saveSpy = sinon.spy(networkPersistenceManager, 'saveUISourceCodeForOverrides'); + const actual = await networkPersistenceManager.setupAndStartLocalOverrides(uiSourceCode); + + saveSpy.restore(); + + assert.isFalse(actual, 'should not allow override'); + assert.isTrue(saveSpy.notCalled, 'should not attempt to save override'); + }); + it('does not save override on working copy committed for data URLs', async () => { const url = 'data:/../victim.com/script.js'; const resourceType = Common.ResourceType.resourceTypes.Script; diff --git a/front_end/models/persistence/NetworkPersistenceManager.ts b/front_end/models/persistence/NetworkPersistenceManager.ts index 59e4284..80d91a5 100644 --- a/front_end/models/persistence/NetworkPersistenceManager.ts +++ b/front_end/models/persistence/NetworkPersistenceManager.ts @@ -9,6 +9,7 @@ import * as SDK from '../../core/sdk/sdk.js'; import * as TextUtils from '../../core/text_utils/text_utils.js'; import * as Protocol from '../../generated/protocol.js'; +import * as Bindings from '../bindings/bindings.js'; import * as Breakpoints from '../breakpoints/breakpoints.js'; import * as Workspace from '../workspace/workspace.js'; @@ -431,8 +432,32 @@ } isUISourceCodeOverridable(uiSourceCode: Workspace.UISourceCode.UISourceCode): boolean { - return uiSourceCode.project().type() === Workspace.Workspace.projectTypes.Network && - !NetworkPersistenceManager.isForbiddenNetworkUrl(uiSourceCode.url()); + if (uiSourceCode.project().type() !== Workspace.Workspace.projectTypes.Network) { + return false; + } + if (NetworkPersistenceManager.isForbiddenNetworkUrl(uiSourceCode.url())) { + return false; + } + // A `//# sourceURL=` annotation is fully controlled by the page and doesn't refer + // to an actual network resource, so there is nothing to override here. Persisting + // it would poison the overrides folder with a file that masquerades as a genuine + // resource (b/553931271). + if (Bindings.NetworkProject.NetworkProject.isSourceURLSynthesized(uiSourceCode)) { + return false; + } + return true; + } + + /** + * Whether the contents of `uiSourceCode` may be written into the overrides folder. + * + * Sources that originate from a source map are overridable in the sense that the + * deployed resource they are mapped from can be overridden (see + * `PersistenceActions`), but their own URL and content are page controlled and + * must never be persisted themselves (b/553931271). + */ + #canPersistUISourceCodeAsOverride(uiSourceCode: Workspace.UISourceCode.UISourceCode): boolean { + return this.isUISourceCodeOverridable(uiSourceCode) && !uiSourceCode.contentType().isFromSourceMap(); } #isUISourceCodeAlreadyOverridden(uiSourceCode: Workspace.UISourceCode.UISourceCode): boolean { @@ -445,12 +470,12 @@ } #canSaveUISourceCodeForOverrides(uiSourceCode: Workspace.UISourceCode.UISourceCode): boolean { - return this.#active && this.isUISourceCodeOverridable(uiSourceCode) && + return this.#active && this.#canPersistUISourceCodeAsOverride(uiSourceCode) && ... (file diff truncated)
Original Bug Report
DevTools Local Overrides Spoofing via Unvalidated UISourceCode URLs
Project Fortify, an experimental security project, has identified the following potential security issue. If you’re a feature owner CC-ed on this bug, please do your best to review these reports. Please see https://chromium.googlesource.com/chromium/src/+/main/docs/security/ai-generated-security-bugs-faq.md for more information.
Overview: DevTools Local Overrides determines override file paths and persisted contents based on a UISourceCode’s reported URL and content without verifying provenance from an actual network response. Attacker-controlled scripts can forge UISourceCode URLs and content via //# sourceURL annotations and source maps. If a developer initiates local overrides on a mapped asset, attacker-controlled code can be persisted to disk and intercepted for genuine first-party requests across sessions.
Affected files:
third_party/devtools-frontend/src/front_end/models/persistence/NetworkPersistenceManager.tsthird_party/devtools-frontend/src/front_end/panels/sources/PersistenceActions.tsthird_party/devtools-frontend/src/front_end/models/bindings/ResourceScriptMapping.tsthird_party/devtools-frontend/src/front_end/models/bindings/CompilerScriptMapping.tsthird_party/devtools-frontend/src/front_end/models/bindings/DebuggerWorkspaceBinding.ts
Estimated timestamp from git blame: 2017-10-18
Summary
DevTools Local Overrides allows developers to save local modifications of network resources to disk and intercept future network requests to serve the local copies. However, DevTools determines which URL to override and what content to save based entirely on UISourceCode.url() and UISourceCode.requestContentData(), without validating that the UISourceCode is backed by a verified network response for that origin.
Because //# sourceURL and source map comments allow web scripts to declare arbitrary URLs in projectTypes.Network projects, an attacker-injected script can replace genuine workspace resources and redirect override actions, causing DevTools to persist malicious content as a persistent local override for a first-party origin.
Affected Code
third_party/devtools-frontend/src/front_end/models/persistence/NetworkPersistenceManager.tsthird_party/devtools-frontend/src/front_end/models/bindings/ResourceScriptMapping.tsthird_party/devtools-frontend/src/front_end/models/bindings/CompilerScriptMapping.tsthird_party/devtools-frontend/src/front_end/panels/sources/PersistenceActions.tsthird_party/devtools-frontend/src/front_end/models/bindings/DebuggerWorkspaceBinding.ts
Root Cause Analysis
- Unchecked Overridability:
NetworkPersistenceManager.isUISourceCodeOverridable(NetworkPersistenceManager.ts:433-436) only verifies that the project type isWorkspace.Workspace.projectTypes.Networkand that the URL is not in a restricted list (e.g.,chrome:URLs). It does not verify the backing resource or network provenance. - Workspace Poisoning via Source Comments: In
ResourceScriptMapping.ts:186-239, when a script hasscript.hasSourceURL, DevTools resolves the URL withSourceMapManager.resolveRelativeSourceURL()(which keeps absolute URLs intact).ResourceScriptMappingassigns this to aprojectTypes.Networkproject and deletes any previous genuineUISourceCoderegistered at that URL (ResourceScriptMapping.ts:215-223). - Source-Map Redirection Deception: In
PersistenceActions.ts:150-161and226-248, right-clicking a source-mapped original and selecting “Override content” resolves the deployed script viaDebuggerWorkspaceBinding.uiSourceCodeForScript(script).DebuggerWorkspaceBindingprioritizesResourceScriptMapping(DebuggerWorkspaceBinding.ts:602-608), returning the attacker-controlledUISourceCode. - Persistence & Interception: When the user confirms the override dialog,
NetworkPersistenceManager.setupAndStartLocalOverridescommits and saves the file viasaveUISourceCodeForOverrides(NetworkPersistenceManager.ts:487-508), writing the attacker’s script text to the local overrides folder under the victim domain path. DevTools then registers a CDPFetch.enableinterception pattern (NetworkPersistenceManager.ts:753-790), causing future genuine network requests for that first-party URL to be intercepted and served with the attacker’s payload.
Potential Attack Steps
(Note: These are suggested/potential reproduction steps based on static analysis, as automated live execution has not been run.)
- An attacker gains script execution on
https://victim.example(e.g., via a compromised third-party script/ad) or hosts a page inspected by a developer. - The script executes an evaluated bundle or loads an external script containing:
where
eval(realBundle + backdoorPayload + "\n//# sourceMappingURL=data:application/json;base64," + btoa(mapJson) + "\n//# sourceURL=https://victim.example/static/main.min.js");mapJsondefinessources: ["https://victim.example/src/app.ts"]. - The developer opens DevTools, views
https://victim.example/src/app.tsunder the Sources panel, and selects Override content. - DevTools displays the expected dialog: "‘app.ts’ is a source mapped file and cannot be overridden. Override ‘main.min.js’ instead?".
- Upon developer confirmation, DevTools writes the attacker’s payload to
overrides/victim.example/static/main.min.jsand enables local overrides. - On subsequent visits or reloads with DevTools open, requests for
https://victim.example/static/main.min.jsare fulfilled with the local override, running the backdoor in the victim’s origin.
Suggested Fix
- Ensure
isUISourceCodeOverridableandsaveUISourceCodeForOverridesverify that the targetUISourceCodeoriginates from an actual network response (e.g., backed by an activeSDK.NetworkRequest.NetworkRequestmatching the URL) rather than purely fromscript.sourceURLcomments or synthetic source-map entries. - Prevent scripts marked with
hasSourceURLfrom evicting or overriding legitimate network-fetchedUISourceCodeinstances inResourceScriptMapping.
Evaluated with Chrome root at commit: 796d95f11afe18561709217227313319d6660322
Results so far have been promising, but there can be wrong deductions. Feel free to adjust as follows:
- If you are familiar with the severity guidelines, you may adjust the severity.
- If this is a false positive, and there’s no work to be done, please close as WAI.
- If there is work to do here but not a vulnerability, please change the issue type to Task/Bug/FR.
Data from false positives will be used to improve accuracy over time. And please feel free to reach out to me directly if you have concerns or feedback on the project.