blob: 342f4a9ede951c959e91d3970ba7ddfb60df8ae9 [file]
// 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 {assert} from 'chai';
import * as SDK from './sdk.js';
describe('SecurityOrigin', () => {
describe('create', () => {
it('creates standard origin matching scheme, host, and port', () => {
const origin1 = SDK.SecurityOrigin.SecurityOrigin.create('https://example.com/path1');
const origin2 = SDK.SecurityOrigin.SecurityOrigin.create('https://example.com/path2');
const originDifferent = SDK.SecurityOrigin.SecurityOrigin.create('https://other.com/path1');
assert.isFalse(origin1.isOpaque());
assert.isTrue(origin1.isSameOriginWith(origin2));
assert.isFalse(origin1.isSameOriginWith(originDifferent));
assert.isFalse(origin1.isSameOriginWith(null));
});
it('isolates file:// URLs by full path', () => {
const file1 = SDK.SecurityOrigin.SecurityOrigin.create('file:///tmp/attacker.html');
const file1Duplicate = SDK.SecurityOrigin.SecurityOrigin.create('file:///tmp/attacker.html');
const file2 = SDK.SecurityOrigin.SecurityOrigin.create('file:///tmp/victim.html');
assert.isFalse(file1.isOpaque());
assert.isTrue(file1.isSameOriginWith(file1Duplicate));
assert.isFalse(file1.isSameOriginWith(file2));
});
it('isolates file:// URLs on different UNC hosts', () => {
const unc1 = SDK.SecurityOrigin.SecurityOrigin.create('file://server1/share/file.html');
const unc2 = SDK.SecurityOrigin.SecurityOrigin.create('file://server2/share/file.html');
assert.isFalse(unc1.isOpaque());
assert.isFalse(unc1.isSameOriginWith(unc2));
});
it('treats opaque URLs as unique opaque origins that do not match each other', () => {
const data1 = SDK.SecurityOrigin.SecurityOrigin.create('data:text/html,hello');
const data2 = SDK.SecurityOrigin.SecurityOrigin.create('data:text/html,hello');
const aboutBlank = SDK.SecurityOrigin.SecurityOrigin.create('about:blank');
const nullOrigin = SDK.SecurityOrigin.SecurityOrigin.create('null');
const emptyOrigin = SDK.SecurityOrigin.SecurityOrigin.create('');
const detachedOrigin = SDK.SecurityOrigin.SecurityOrigin.create('detached');
assert.isTrue(data1.isOpaque());
assert.isTrue(data2.isOpaque());
assert.isTrue(aboutBlank.isOpaque());
assert.isTrue(nullOrigin.isOpaque());
assert.isTrue(emptyOrigin.isOpaque());
assert.isTrue(detachedOrigin.isOpaque());
assert.isFalse(data1.isSameOriginWith(data2));
assert.isFalse(data1.isSameOriginWith(aboutBlank));
assert.isFalse(nullOrigin.isSameOriginWith(emptyOrigin));
assert.isFalse(detachedOrigin.isSameOriginWith(emptyOrigin));
});
it('normalizes imported artifact schemes and hostnames to lowercase', () => {
const harLower = SDK.SecurityOrigin.SecurityOrigin.create('imported-har://example.com');
const harUpper = SDK.SecurityOrigin.SecurityOrigin.create('IMPORTED-HAR://EXAMPLE.COM/other');
const traceLower = SDK.SecurityOrigin.SecurityOrigin.create('imported-trace://example.com');
const traceUpper = SDK.SecurityOrigin.SecurityOrigin.create('IMPORTED-TRACE://EXAMPLE.COM/other');
assert.isFalse(harUpper.isOpaque());
assert.isTrue(harLower.isSameOriginWith(harUpper));
assert.isTrue(harUpper.isSameOriginWith(harLower));
assert.strictEqual(harUpper.siteId(), 'imported-har://example.com');
assert.isFalse(traceUpper.isOpaque());
assert.isTrue(traceLower.isSameOriginWith(traceUpper));
assert.isTrue(traceUpper.isSameOriginWith(traceLower));
assert.strictEqual(traceUpper.siteId(), 'imported-trace://example.com');
});
it('ignores paths, query strings, and fragments for imported artifact origins', () => {
const harBase = SDK.SecurityOrigin.SecurityOrigin.create('imported-har://example.com');
const harWithPath = SDK.SecurityOrigin.SecurityOrigin.create('imported-har://example.com/api/data?filter=1#hash');
const traceBase = SDK.SecurityOrigin.SecurityOrigin.create('imported-trace://example.com');
const traceWithPath =
SDK.SecurityOrigin.SecurityOrigin.create('imported-trace://example.com/trace.json?token=123#sec');
const traceQueryOnly = SDK.SecurityOrigin.SecurityOrigin.create('imported-trace://example.com?token=123');
const traceHashOnly = SDK.SecurityOrigin.SecurityOrigin.create('imported-trace://example.com#sec');
assert.isTrue(harBase.isSameOriginWith(harWithPath));
assert.strictEqual(harWithPath.siteId(), 'imported-har://example.com');
assert.isTrue(traceBase.isSameOriginWith(traceWithPath));
assert.isTrue(traceBase.isSameOriginWith(traceQueryOnly));
assert.isTrue(traceBase.isSameOriginWith(traceHashOnly));
assert.strictEqual(traceWithPath.siteId(), 'imported-trace://example.com');
assert.strictEqual(traceQueryOnly.siteId(), 'imported-trace://example.com');
assert.strictEqual(traceHashOnly.siteId(), 'imported-trace://example.com');
});
it('isolates imported artifact schemes from live web origins and other artifact schemes', () => {
const har = SDK.SecurityOrigin.SecurityOrigin.create('imported-har://example.com/api/data');
const harOtherDomain = SDK.SecurityOrigin.SecurityOrigin.create('imported-har://other.com/api/data');
const trace = SDK.SecurityOrigin.SecurityOrigin.create('imported-trace://example.com/trace.json');
const traceOtherDomain = SDK.SecurityOrigin.SecurityOrigin.create('imported-trace://other.com');
const livePage = SDK.SecurityOrigin.SecurityOrigin.create('https://example.com/api/data');
// Live page isolation
assert.isFalse(har.isSameOriginWith(livePage));
assert.isFalse(livePage.isSameOriginWith(har));
assert.isFalse(trace.isSameOriginWith(livePage));
assert.isFalse(livePage.isSameOriginWith(trace));
// Cross-artifact scheme isolation
assert.isFalse(trace.isSameOriginWith(har));
assert.isFalse(har.isSameOriginWith(trace));
// Different domain isolation
assert.isFalse(har.isSameOriginWith(harOtherDomain));
assert.isFalse(trace.isSameOriginWith(traceOtherDomain));
});
it('treats imported artifact schemes with empty host or missing authority as opaque', () => {
const emptyHar1 = SDK.SecurityOrigin.SecurityOrigin.create('imported-har://');
const emptyHar2 = SDK.SecurityOrigin.SecurityOrigin.create('imported-har://');
const emptyTrace1 = SDK.SecurityOrigin.SecurityOrigin.create('imported-trace://');
const emptyTrace2 = SDK.SecurityOrigin.SecurityOrigin.create('imported-trace://');
const pathOnlyHar = SDK.SecurityOrigin.SecurityOrigin.create('imported-har:///path/to/data.har');
const pathOnlyTrace = SDK.SecurityOrigin.SecurityOrigin.create('imported-trace:///path/to/trace.json');
const noAuthorityHar = SDK.SecurityOrigin.SecurityOrigin.create('imported-har:example.com');
const noAuthorityTrace = SDK.SecurityOrigin.SecurityOrigin.create('imported-trace:example.com');
assert.isTrue(emptyHar1.isOpaque());
assert.isTrue(emptyHar2.isOpaque());
assert.isFalse(emptyHar1.isSameOriginWith(emptyHar2));
assert.isTrue(emptyTrace1.isOpaque());
assert.isTrue(emptyTrace2.isOpaque());
assert.isFalse(emptyTrace1.isSameOriginWith(emptyTrace2));
assert.isTrue(pathOnlyHar.isOpaque());
assert.isTrue(pathOnlyTrace.isOpaque());
assert.isTrue(noAuthorityHar.isOpaque());
assert.isTrue(noAuthorityTrace.isOpaque());
});
it('does not treat URLs with hostnames containing detached as opaque', () => {
const origin1 = SDK.SecurityOrigin.SecurityOrigin.create('https://detached.example.com/page.html');
const origin2 = SDK.SecurityOrigin.SecurityOrigin.create('https://detached.example.com/other.html');
assert.isFalse(origin1.isOpaque());
assert.isTrue(origin1.isSameOriginWith(origin2));
});
});
describe('createUniqueOpaque', () => {
it('creates unique instances that only match themselves', () => {
const opaque1 = SDK.SecurityOrigin.SecurityOrigin.createUniqueOpaque();
const opaque2 = SDK.SecurityOrigin.SecurityOrigin.createUniqueOpaque();
assert.isTrue(opaque1.isOpaque());
assert.isTrue(opaque2.isOpaque());
assert.isTrue(opaque1.isSameOriginWith(opaque1));
assert.isFalse(opaque1.isSameOriginWith(opaque2));
});
});
describe('siteId', () => {
it('returns the URL origin string for standard origins', () => {
const origin = SDK.SecurityOrigin.SecurityOrigin.create('https://example.com:8080/path');
assert.strictEqual(origin.siteId(), 'https://example.com:8080');
});
it('returns the full file URL for file origins', () => {
const origin = SDK.SecurityOrigin.SecurityOrigin.create('file:///tmp/index.html');
assert.strictEqual(origin.siteId(), 'file:///tmp/index.html');
});
it('returns the UUID for opaque origins', () => {
const origin = SDK.SecurityOrigin.SecurityOrigin.create('data:text/html,test');
assert.isNotEmpty(origin.siteId());
assert.isFalse(origin.siteId().startsWith('data:'));
});
});
describe('isFile', () => {
it('returns true for file URLs and false for other origins', () => {
const fileOrigin = SDK.SecurityOrigin.SecurityOrigin.create('file:///tmp/index.html');
const webOrigin = SDK.SecurityOrigin.SecurityOrigin.create('https://example.com');
const opaqueOrigin = SDK.SecurityOrigin.SecurityOrigin.create('data:text/html,test');
assert.isTrue(fileOrigin.isFile());
assert.isFalse(webOrigin.isFile());
assert.isFalse(opaqueOrigin.isFile());
});
});
describe('createForImportedTrace', () => {
it('creates an imported-trace origin for a standard web URL', () => {
const origin = SDK.SecurityOrigin.SecurityOrigin.createForImportedTrace('https://example.com/trace/path');
const expectedOrigin = SDK.SecurityOrigin.SecurityOrigin.create('imported-trace://example.com');
assert.isTrue(origin.isSameOriginWith(expectedOrigin));
assert.isFalse(origin.isOpaque());
});
it('creates an imported-trace origin preserving port for URLs with port', () => {
const origin = SDK.SecurityOrigin.SecurityOrigin.createForImportedTrace('http://localhost:8080/app');
const expectedOrigin = SDK.SecurityOrigin.SecurityOrigin.create('imported-trace://localhost:8080');
assert.isTrue(origin.isSameOriginWith(expectedOrigin));
assert.isFalse(origin.isOpaque());
});
it('isolates imported trace origin from live web origins', () => {
const traceOrigin = SDK.SecurityOrigin.SecurityOrigin.createForImportedTrace('https://example.com');
const webOrigin = SDK.SecurityOrigin.SecurityOrigin.create('https://example.com');
assert.isFalse(traceOrigin.isSameOriginWith(webOrigin));
});
it('returns an opaque origin when URL is null or undefined', () => {
const nullOrigin = SDK.SecurityOrigin.SecurityOrigin.createForImportedTrace(null);
const undefinedOrigin = SDK.SecurityOrigin.SecurityOrigin.createForImportedTrace(undefined);
assert.isTrue(nullOrigin.isOpaque());
assert.isTrue(undefinedOrigin.isOpaque());
assert.isFalse(nullOrigin.isSameOriginWith(undefinedOrigin));
});
it('returns an opaque origin when URL is empty or invalid', () => {
const emptyOrigin = SDK.SecurityOrigin.SecurityOrigin.createForImportedTrace('');
const invalidOrigin = SDK.SecurityOrigin.SecurityOrigin.createForImportedTrace('invalid:url');
assert.isTrue(emptyOrigin.isOpaque());
assert.isTrue(invalidOrigin.isOpaque());
assert.isFalse(emptyOrigin.isSameOriginWith(invalidOrigin));
});
it('returns an opaque origin for local files without host', () => {
const origin = SDK.SecurityOrigin.SecurityOrigin.createForImportedTrace('file:///tmp/trace.json');
assert.isTrue(origin.isOpaque());
});
it('returns an opaque origin for data or about URLs', () => {
const dataOrigin = SDK.SecurityOrigin.SecurityOrigin.createForImportedTrace('data:text/html,<html></html>');
const aboutOrigin = SDK.SecurityOrigin.SecurityOrigin.createForImportedTrace('about:blank');
assert.isTrue(dataOrigin.isOpaque());
assert.isTrue(aboutOrigin.isOpaque());
assert.isFalse(dataOrigin.isSameOriginWith(aboutOrigin));
});
});
});