UNPKG

chrome-devtools-frontend

Version:
108 lines (100 loc) 4.62 kB
// Copyright 2026 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. import * as Common from '../../core/common/common.js'; import type * as Platform from '../../core/platform/platform.js'; /** * Returns true if the origin is considered opaque and should be blocked from * AI assistance to prevent potential data leakage. * * @param origin The origin string to check. * @returns True if the origin is opaque and blocked. * @see https://crbug.com/513732588 */ export function isOpaqueOrigin(origin: string): boolean { /** * Origins starting with 'about' (like about:blank or about:srcdoc) are * considered opaque. 'about://' is the sentinel used by DevTools * ParsedURL.securityOrigin() for these. * * We also treat empty origins, 'undefined', and 'null' as opaque to prevent * bypasses when URL parsing fails or variables are uninitialized. */ const lower = origin.toLowerCase(); return lower === '' || lower === 'null' || lower === 'data:' || lower.startsWith('about') || lower.startsWith('detached') || lower.startsWith('undefined'); } /** * Extracts the origin from a context URL or identifier. * Handles special cases like "detached" nodes, trace identifiers, * opaque blob URLs, and isolates local file paths. * * @param contextURL The context URL or trace/node identifier. * @returns The extracted origin string. */ export function extractContextOrigin(contextURL: string): string { if (isOpaqueOrigin(contextURL)) { return contextURL; } if (contextURL.startsWith('trace-')) { return contextURL; } // If a blob URL has an opaque inner origin (e.g. blob:null/uuid, blob:about:blank), // it won't contain "://". We classify these as opaque ('null') to prevent cross-origin leaks. if (/^blob:/i.test(contextURL)) { const innerURL = contextURL.substring(5); if (!innerURL.includes('://')) { return 'null'; } } // Local files collapse to a generic "file://" origin by default. We isolate them // by appending the normalized path, ensuring distinct files are treated as different origins. if (/^file:\/\//i.test(contextURL)) { const parsed = Common.ParsedURL.ParsedURL.fromString(contextURL as Platform.DevToolsPath.UrlString); if (parsed) { // Include host and port (authority) to preserve server names in Windows UNC paths // (e.g. file://server/path) so different network shares are isolated. const authority = parsed.host + (parsed.port ? ':' + parsed.port : ''); return 'file://' + authority + parsed.path; } // Fallback to 'null' (opaque) if parsing fails. This prevents malformed file URLs // from bypassing isolation by collapsing to the same 'file://' string. return 'null'; } return Common.ParsedURL.ParsedURL.extractOrigin(contextURL as Platform.DevToolsPath.UrlString); } /** * Determines if two origins are equivalent and safe to be used together. * Opaque origins are never equivalent to anything, not even themselves. */ export function areOriginsEquivalent(origin1: string, origin2: string): boolean { if (isOpaqueOrigin(origin1) || isOpaqueOrigin(origin2)) { return false; } return origin1 === origin2; } /** * Validates if the source code contents of a file (identified by targetURL) can be retrieved * and shared with the AI, based on the origin of the active trace context. * * This function handles the following branches: * 1. **Opaque origins**: If either the trace origin or target file origin is opaque * (e.g., data URLs, about:blank, sandboxed frames), access is blocked. * 2. **Fresh Recordings**: For live page recordings, a strict same-origin comparison * (scheme, host, and port matching) is enforced. * * @param targetURL The URL of the file to be read. * @param traceOrigin The allowed origin of the trace context. * @returns true if reading the file is permitted; false otherwise. */ export function canResourceContentsBeReadForTrace(targetURL: string, traceOrigin: string): boolean { // We explicitly block all file:// URLs. While we want to allow users to debug // traces on local sites, allowing file:// access poses security risks (e.g., // reading local files like /etc/passwd via prompt injection) because file:// // origins are not sufficiently isolated from each other in DevTools' origin model. if (traceOrigin.startsWith('file://') || targetURL.startsWith('file://')) { return false; } const targetOrigin = extractContextOrigin(targetURL); return areOriginsEquivalent(targetOrigin, traceOrigin); }