11cb0ef41Sopenharmony_ci'use strict'; 21cb0ef41Sopenharmony_ci 31cb0ef41Sopenharmony_ci// #region imports 41cb0ef41Sopenharmony_ciconst { 51cb0ef41Sopenharmony_ci ArrayIsArray, 61cb0ef41Sopenharmony_ci ArrayPrototypeSort, 71cb0ef41Sopenharmony_ci ObjectCreate, 81cb0ef41Sopenharmony_ci ObjectEntries, 91cb0ef41Sopenharmony_ci ObjectFreeze, 101cb0ef41Sopenharmony_ci ObjectKeys, 111cb0ef41Sopenharmony_ci ObjectSetPrototypeOf, 121cb0ef41Sopenharmony_ci RegExpPrototypeExec, 131cb0ef41Sopenharmony_ci SafeMap, 141cb0ef41Sopenharmony_ci SafeSet, 151cb0ef41Sopenharmony_ci RegExpPrototypeSymbolReplace, 161cb0ef41Sopenharmony_ci StringPrototypeEndsWith, 171cb0ef41Sopenharmony_ci StringPrototypeStartsWith, 181cb0ef41Sopenharmony_ci Symbol, 191cb0ef41Sopenharmony_ci} = primordials; 201cb0ef41Sopenharmony_ciconst { 211cb0ef41Sopenharmony_ci ERR_MANIFEST_ASSERT_INTEGRITY, 221cb0ef41Sopenharmony_ci ERR_MANIFEST_INVALID_RESOURCE_FIELD, 231cb0ef41Sopenharmony_ci ERR_MANIFEST_INVALID_SPECIFIER, 241cb0ef41Sopenharmony_ci ERR_MANIFEST_UNKNOWN_ONERROR, 251cb0ef41Sopenharmony_ci} = require('internal/errors').codes; 261cb0ef41Sopenharmony_cilet debug = require('internal/util/debuglog').debuglog('policy', (fn) => { 271cb0ef41Sopenharmony_ci debug = fn; 281cb0ef41Sopenharmony_ci}); 291cb0ef41Sopenharmony_ciconst SRI = require('internal/policy/sri'); 301cb0ef41Sopenharmony_ciconst { URL } = require('internal/url'); 311cb0ef41Sopenharmony_ciconst { internalVerifyIntegrity } = internalBinding('crypto'); 321cb0ef41Sopenharmony_ciconst kRelativeURLStringPattern = /^\.{0,2}\//; 331cb0ef41Sopenharmony_ciconst { getOptionValue } = require('internal/options'); 341cb0ef41Sopenharmony_ciconst shouldAbortOnUncaughtException = getOptionValue( 351cb0ef41Sopenharmony_ci '--abort-on-uncaught-exception', 361cb0ef41Sopenharmony_ci); 371cb0ef41Sopenharmony_ciconst { abort, exit, _rawDebug } = process; 381cb0ef41Sopenharmony_ci// #endregion 391cb0ef41Sopenharmony_ci 401cb0ef41Sopenharmony_ci// #region constants 411cb0ef41Sopenharmony_ci// From https://url.spec.whatwg.org/#special-scheme 421cb0ef41Sopenharmony_ciconst kSpecialSchemes = new SafeSet([ 431cb0ef41Sopenharmony_ci 'file:', 441cb0ef41Sopenharmony_ci 'ftp:', 451cb0ef41Sopenharmony_ci 'http:', 461cb0ef41Sopenharmony_ci 'https:', 471cb0ef41Sopenharmony_ci 'ws:', 481cb0ef41Sopenharmony_ci 'wss:', 491cb0ef41Sopenharmony_ci]); 501cb0ef41Sopenharmony_ci 511cb0ef41Sopenharmony_ci/** 521cb0ef41Sopenharmony_ci * @type {symbol} 531cb0ef41Sopenharmony_ci */ 541cb0ef41Sopenharmony_ciconst kCascade = Symbol('cascade'); 551cb0ef41Sopenharmony_ci/** 561cb0ef41Sopenharmony_ci * @type {symbol} 571cb0ef41Sopenharmony_ci */ 581cb0ef41Sopenharmony_ciconst kFallThrough = Symbol('fall through'); 591cb0ef41Sopenharmony_ci 601cb0ef41Sopenharmony_cifunction REACTION_THROW(error) { 611cb0ef41Sopenharmony_ci throw error; 621cb0ef41Sopenharmony_ci} 631cb0ef41Sopenharmony_ci 641cb0ef41Sopenharmony_cifunction REACTION_EXIT(error) { 651cb0ef41Sopenharmony_ci REACTION_LOG(error); 661cb0ef41Sopenharmony_ci if (shouldAbortOnUncaughtException) { 671cb0ef41Sopenharmony_ci abort(); 681cb0ef41Sopenharmony_ci } 691cb0ef41Sopenharmony_ci exit(1); 701cb0ef41Sopenharmony_ci} 711cb0ef41Sopenharmony_ci 721cb0ef41Sopenharmony_cifunction REACTION_LOG(error) { 731cb0ef41Sopenharmony_ci _rawDebug(error.stack); 741cb0ef41Sopenharmony_ci} 751cb0ef41Sopenharmony_ci 761cb0ef41Sopenharmony_ci// #endregion 771cb0ef41Sopenharmony_ci 781cb0ef41Sopenharmony_ci// #region DependencyMapperInstance 791cb0ef41Sopenharmony_ciclass DependencyMapperInstance { 801cb0ef41Sopenharmony_ci /** 811cb0ef41Sopenharmony_ci * @type {string} 821cb0ef41Sopenharmony_ci */ 831cb0ef41Sopenharmony_ci href; 841cb0ef41Sopenharmony_ci /** 851cb0ef41Sopenharmony_ci * @type {DependencyMap | undefined} 861cb0ef41Sopenharmony_ci */ 871cb0ef41Sopenharmony_ci #dependencies; 881cb0ef41Sopenharmony_ci /** 891cb0ef41Sopenharmony_ci * @type {PatternDependencyMap | undefined} 901cb0ef41Sopenharmony_ci */ 911cb0ef41Sopenharmony_ci #patternDependencies; 921cb0ef41Sopenharmony_ci /** 931cb0ef41Sopenharmony_ci * @type {DependencyMapperInstance | null | undefined} 941cb0ef41Sopenharmony_ci */ 951cb0ef41Sopenharmony_ci #parentDependencyMapper; 961cb0ef41Sopenharmony_ci /** 971cb0ef41Sopenharmony_ci * @type {boolean} 981cb0ef41Sopenharmony_ci */ 991cb0ef41Sopenharmony_ci #normalized = false; 1001cb0ef41Sopenharmony_ci /** 1011cb0ef41Sopenharmony_ci * @type {boolean} 1021cb0ef41Sopenharmony_ci */ 1031cb0ef41Sopenharmony_ci cascade; 1041cb0ef41Sopenharmony_ci /** 1051cb0ef41Sopenharmony_ci * @type {boolean} 1061cb0ef41Sopenharmony_ci */ 1071cb0ef41Sopenharmony_ci allowSameHREFScope; 1081cb0ef41Sopenharmony_ci /** 1091cb0ef41Sopenharmony_ci * @param {string} parentHREF 1101cb0ef41Sopenharmony_ci * @param {DependencyMap | undefined} dependencies 1111cb0ef41Sopenharmony_ci * @param {boolean} cascade 1121cb0ef41Sopenharmony_ci * @param {boolean} allowSameHREFScope 1131cb0ef41Sopenharmony_ci */ 1141cb0ef41Sopenharmony_ci constructor( 1151cb0ef41Sopenharmony_ci parentHREF, 1161cb0ef41Sopenharmony_ci dependencies, 1171cb0ef41Sopenharmony_ci cascade = false, 1181cb0ef41Sopenharmony_ci allowSameHREFScope = false) { 1191cb0ef41Sopenharmony_ci this.href = parentHREF; 1201cb0ef41Sopenharmony_ci if (dependencies === kFallThrough || 1211cb0ef41Sopenharmony_ci dependencies === undefined || 1221cb0ef41Sopenharmony_ci dependencies === null) { 1231cb0ef41Sopenharmony_ci this.#dependencies = dependencies; 1241cb0ef41Sopenharmony_ci this.#patternDependencies = undefined; 1251cb0ef41Sopenharmony_ci } else { 1261cb0ef41Sopenharmony_ci const patterns = []; 1271cb0ef41Sopenharmony_ci const keys = ObjectKeys(dependencies); 1281cb0ef41Sopenharmony_ci for (let i = 0; i < keys.length; i++) { 1291cb0ef41Sopenharmony_ci const key = keys[i]; 1301cb0ef41Sopenharmony_ci if (StringPrototypeEndsWith(key, '*')) { 1311cb0ef41Sopenharmony_ci const target = RegExpPrototypeExec(/^([^*]*)\*([^*]*)$/); 1321cb0ef41Sopenharmony_ci if (!target) { 1331cb0ef41Sopenharmony_ci throw new ERR_MANIFEST_INVALID_SPECIFIER( 1341cb0ef41Sopenharmony_ci this.href, 1351cb0ef41Sopenharmony_ci `${target}, pattern needs to have a single trailing "*" in target`, 1361cb0ef41Sopenharmony_ci ); 1371cb0ef41Sopenharmony_ci } 1381cb0ef41Sopenharmony_ci const prefix = target[1]; 1391cb0ef41Sopenharmony_ci const suffix = target[2]; 1401cb0ef41Sopenharmony_ci patterns.push([ 1411cb0ef41Sopenharmony_ci target.slice(0, -1), 1421cb0ef41Sopenharmony_ci [prefix, suffix], 1431cb0ef41Sopenharmony_ci ]); 1441cb0ef41Sopenharmony_ci } 1451cb0ef41Sopenharmony_ci } 1461cb0ef41Sopenharmony_ci ArrayPrototypeSort(patterns, (a, b) => { 1471cb0ef41Sopenharmony_ci return a[0] < b[0] ? -1 : 1; 1481cb0ef41Sopenharmony_ci }); 1491cb0ef41Sopenharmony_ci this.#dependencies = dependencies; 1501cb0ef41Sopenharmony_ci this.#patternDependencies = patterns; 1511cb0ef41Sopenharmony_ci } 1521cb0ef41Sopenharmony_ci this.cascade = cascade; 1531cb0ef41Sopenharmony_ci this.allowSameHREFScope = allowSameHREFScope; 1541cb0ef41Sopenharmony_ci ObjectFreeze(this); 1551cb0ef41Sopenharmony_ci } 1561cb0ef41Sopenharmony_ci /** 1571cb0ef41Sopenharmony_ci * 1581cb0ef41Sopenharmony_ci * @param {string} normalizedSpecifier 1591cb0ef41Sopenharmony_ci * @param {Set<string>} conditions 1601cb0ef41Sopenharmony_ci * @param {Manifest} manifest 1611cb0ef41Sopenharmony_ci * @returns {URL | typeof kFallThrough | null} 1621cb0ef41Sopenharmony_ci */ 1631cb0ef41Sopenharmony_ci _resolveAlreadyNormalized(normalizedSpecifier, conditions, manifest) { 1641cb0ef41Sopenharmony_ci let dependencies = this.#dependencies; 1651cb0ef41Sopenharmony_ci debug(this.href, 'resolving', normalizedSpecifier); 1661cb0ef41Sopenharmony_ci if (dependencies === kFallThrough) return true; 1671cb0ef41Sopenharmony_ci if (dependencies !== undefined && typeof dependencies === 'object') { 1681cb0ef41Sopenharmony_ci const normalized = this.#normalized; 1691cb0ef41Sopenharmony_ci if (normalized !== true) { 1701cb0ef41Sopenharmony_ci /** 1711cb0ef41Sopenharmony_ci * @type {Record<string, string>} 1721cb0ef41Sopenharmony_ci */ 1731cb0ef41Sopenharmony_ci const normalizedDependencyMap = ObjectCreate(null); 1741cb0ef41Sopenharmony_ci for (let specifier in dependencies) { 1751cb0ef41Sopenharmony_ci const target = dependencies[specifier]; 1761cb0ef41Sopenharmony_ci specifier = canonicalizeSpecifier(specifier, manifest.href); 1771cb0ef41Sopenharmony_ci normalizedDependencyMap[specifier] = target; 1781cb0ef41Sopenharmony_ci } 1791cb0ef41Sopenharmony_ci ObjectFreeze(normalizedDependencyMap); 1801cb0ef41Sopenharmony_ci dependencies = normalizedDependencyMap; 1811cb0ef41Sopenharmony_ci this.#dependencies = normalizedDependencyMap; 1821cb0ef41Sopenharmony_ci this.#normalized = true; 1831cb0ef41Sopenharmony_ci } 1841cb0ef41Sopenharmony_ci debug(dependencies); 1851cb0ef41Sopenharmony_ci if (normalizedSpecifier in dependencies === true) { 1861cb0ef41Sopenharmony_ci const to = searchDependencies( 1871cb0ef41Sopenharmony_ci this.href, 1881cb0ef41Sopenharmony_ci dependencies[normalizedSpecifier], 1891cb0ef41Sopenharmony_ci conditions, 1901cb0ef41Sopenharmony_ci ); 1911cb0ef41Sopenharmony_ci debug({ to }); 1921cb0ef41Sopenharmony_ci if (to === true) { 1931cb0ef41Sopenharmony_ci return true; 1941cb0ef41Sopenharmony_ci } 1951cb0ef41Sopenharmony_ci let ret; 1961cb0ef41Sopenharmony_ci if (parsedURLs && parsedURLs.has(to)) { 1971cb0ef41Sopenharmony_ci ret = parsedURLs.get(to); 1981cb0ef41Sopenharmony_ci } else if (RegExpPrototypeExec(kRelativeURLStringPattern, to) !== null) { 1991cb0ef41Sopenharmony_ci ret = resolve(to, manifest.href); 2001cb0ef41Sopenharmony_ci } else { 2011cb0ef41Sopenharmony_ci ret = resolve(to); 2021cb0ef41Sopenharmony_ci } 2031cb0ef41Sopenharmony_ci return ret; 2041cb0ef41Sopenharmony_ci } 2051cb0ef41Sopenharmony_ci } 2061cb0ef41Sopenharmony_ci const { cascade } = this; 2071cb0ef41Sopenharmony_ci if (cascade !== true) { 2081cb0ef41Sopenharmony_ci return null; 2091cb0ef41Sopenharmony_ci } 2101cb0ef41Sopenharmony_ci let parentDependencyMapper = this.#parentDependencyMapper; 2111cb0ef41Sopenharmony_ci if (parentDependencyMapper === undefined) { 2121cb0ef41Sopenharmony_ci parentDependencyMapper = manifest.getScopeDependencyMapper( 2131cb0ef41Sopenharmony_ci this.href, 2141cb0ef41Sopenharmony_ci this.allowSameHREFScope, 2151cb0ef41Sopenharmony_ci ); 2161cb0ef41Sopenharmony_ci this.#parentDependencyMapper = parentDependencyMapper; 2171cb0ef41Sopenharmony_ci } 2181cb0ef41Sopenharmony_ci if (parentDependencyMapper === null) { 2191cb0ef41Sopenharmony_ci return null; 2201cb0ef41Sopenharmony_ci } 2211cb0ef41Sopenharmony_ci return parentDependencyMapper._resolveAlreadyNormalized( 2221cb0ef41Sopenharmony_ci normalizedSpecifier, 2231cb0ef41Sopenharmony_ci conditions, 2241cb0ef41Sopenharmony_ci manifest, 2251cb0ef41Sopenharmony_ci ); 2261cb0ef41Sopenharmony_ci } 2271cb0ef41Sopenharmony_ci} 2281cb0ef41Sopenharmony_ci 2291cb0ef41Sopenharmony_ciconst kArbitraryDependencies = new DependencyMapperInstance( 2301cb0ef41Sopenharmony_ci 'arbitrary dependencies', 2311cb0ef41Sopenharmony_ci kFallThrough, 2321cb0ef41Sopenharmony_ci false, 2331cb0ef41Sopenharmony_ci true, 2341cb0ef41Sopenharmony_ci); 2351cb0ef41Sopenharmony_ciconst kNoDependencies = new DependencyMapperInstance( 2361cb0ef41Sopenharmony_ci 'no dependencies', 2371cb0ef41Sopenharmony_ci null, 2381cb0ef41Sopenharmony_ci false, 2391cb0ef41Sopenharmony_ci true, 2401cb0ef41Sopenharmony_ci); 2411cb0ef41Sopenharmony_ci/** 2421cb0ef41Sopenharmony_ci * @param {string} href 2431cb0ef41Sopenharmony_ci * @param {JSONDependencyMap} dependencies 2441cb0ef41Sopenharmony_ci * @param {boolean} cascade 2451cb0ef41Sopenharmony_ci * @param {boolean} allowSameHREFScope 2461cb0ef41Sopenharmony_ci * @param {Map<string | null | undefined, DependencyMapperInstance>} store 2471cb0ef41Sopenharmony_ci */ 2481cb0ef41Sopenharmony_ciconst insertDependencyMap = ( 2491cb0ef41Sopenharmony_ci href, 2501cb0ef41Sopenharmony_ci dependencies, 2511cb0ef41Sopenharmony_ci cascade, 2521cb0ef41Sopenharmony_ci allowSameHREFScope, 2531cb0ef41Sopenharmony_ci store, 2541cb0ef41Sopenharmony_ci) => { 2551cb0ef41Sopenharmony_ci if (cascade !== undefined && typeof cascade !== 'boolean') { 2561cb0ef41Sopenharmony_ci throw new ERR_MANIFEST_INVALID_RESOURCE_FIELD(href, 'cascade'); 2571cb0ef41Sopenharmony_ci } 2581cb0ef41Sopenharmony_ci if (dependencies === true) { 2591cb0ef41Sopenharmony_ci store.set(href, kArbitraryDependencies); 2601cb0ef41Sopenharmony_ci return; 2611cb0ef41Sopenharmony_ci } 2621cb0ef41Sopenharmony_ci if (dependencies === null || dependencies === undefined) { 2631cb0ef41Sopenharmony_ci store.set( 2641cb0ef41Sopenharmony_ci href, 2651cb0ef41Sopenharmony_ci cascade ? 2661cb0ef41Sopenharmony_ci new DependencyMapperInstance(href, null, true, allowSameHREFScope) : 2671cb0ef41Sopenharmony_ci kNoDependencies, 2681cb0ef41Sopenharmony_ci ); 2691cb0ef41Sopenharmony_ci return; 2701cb0ef41Sopenharmony_ci } 2711cb0ef41Sopenharmony_ci if (objectButNotArray(dependencies)) { 2721cb0ef41Sopenharmony_ci store.set( 2731cb0ef41Sopenharmony_ci href, 2741cb0ef41Sopenharmony_ci new DependencyMapperInstance( 2751cb0ef41Sopenharmony_ci href, 2761cb0ef41Sopenharmony_ci dependencies, 2771cb0ef41Sopenharmony_ci cascade, 2781cb0ef41Sopenharmony_ci allowSameHREFScope, 2791cb0ef41Sopenharmony_ci ), 2801cb0ef41Sopenharmony_ci ); 2811cb0ef41Sopenharmony_ci return; 2821cb0ef41Sopenharmony_ci } 2831cb0ef41Sopenharmony_ci throw new ERR_MANIFEST_INVALID_RESOURCE_FIELD(href, 'dependencies'); 2841cb0ef41Sopenharmony_ci}; 2851cb0ef41Sopenharmony_ci/** 2861cb0ef41Sopenharmony_ci * Finds the longest key within `this.#scopeDependencies` that covers a 2871cb0ef41Sopenharmony_ci * specific HREF 2881cb0ef41Sopenharmony_ci * @param {string} href 2891cb0ef41Sopenharmony_ci * @param {ScopeStore} scopeStore 2901cb0ef41Sopenharmony_ci * @returns {null | string} 2911cb0ef41Sopenharmony_ci */ 2921cb0ef41Sopenharmony_cifunction findScopeHREF(href, scopeStore, allowSame) { 2931cb0ef41Sopenharmony_ci let protocol; 2941cb0ef41Sopenharmony_ci if (href !== '') { 2951cb0ef41Sopenharmony_ci // default URL parser does some stuff to special urls... skip if this is 2961cb0ef41Sopenharmony_ci // just the protocol 2971cb0ef41Sopenharmony_ci if (RegExpPrototypeExec(/^[^:]*[:]$/, href) !== null) { 2981cb0ef41Sopenharmony_ci protocol = href; 2991cb0ef41Sopenharmony_ci } else { 3001cb0ef41Sopenharmony_ci let currentURL = new URL(href); 3011cb0ef41Sopenharmony_ci const normalizedHREF = currentURL.href; 3021cb0ef41Sopenharmony_ci protocol = currentURL.protocol; 3031cb0ef41Sopenharmony_ci // Non-opaque blobs adopt origins 3041cb0ef41Sopenharmony_ci if (protocol === 'blob:' && currentURL.origin !== 'null') { 3051cb0ef41Sopenharmony_ci currentURL = new URL(currentURL.origin); 3061cb0ef41Sopenharmony_ci protocol = currentURL.protocol; 3071cb0ef41Sopenharmony_ci } 3081cb0ef41Sopenharmony_ci // Only a few schemes are hierarchical 3091cb0ef41Sopenharmony_ci if (kSpecialSchemes.has(currentURL.protocol)) { 3101cb0ef41Sopenharmony_ci // Make first '..' act like '.' 3111cb0ef41Sopenharmony_ci if (!StringPrototypeEndsWith(currentURL.pathname, '/')) { 3121cb0ef41Sopenharmony_ci currentURL.pathname += '/'; 3131cb0ef41Sopenharmony_ci } 3141cb0ef41Sopenharmony_ci let lastHREF; 3151cb0ef41Sopenharmony_ci let currentHREF = currentURL.href; 3161cb0ef41Sopenharmony_ci do { 3171cb0ef41Sopenharmony_ci if (scopeStore.has(currentHREF)) { 3181cb0ef41Sopenharmony_ci if (allowSame || currentHREF !== normalizedHREF) { 3191cb0ef41Sopenharmony_ci return currentHREF; 3201cb0ef41Sopenharmony_ci } 3211cb0ef41Sopenharmony_ci } 3221cb0ef41Sopenharmony_ci lastHREF = currentHREF; 3231cb0ef41Sopenharmony_ci currentURL = new URL('..', currentURL); 3241cb0ef41Sopenharmony_ci currentHREF = currentURL.href; 3251cb0ef41Sopenharmony_ci } while (lastHREF !== currentHREF); 3261cb0ef41Sopenharmony_ci } 3271cb0ef41Sopenharmony_ci } 3281cb0ef41Sopenharmony_ci } 3291cb0ef41Sopenharmony_ci if (scopeStore.has(protocol)) { 3301cb0ef41Sopenharmony_ci if (allowSame || protocol !== href) return protocol; 3311cb0ef41Sopenharmony_ci } 3321cb0ef41Sopenharmony_ci if (scopeStore.has('')) { 3331cb0ef41Sopenharmony_ci if (allowSame || '' !== href) return ''; 3341cb0ef41Sopenharmony_ci } 3351cb0ef41Sopenharmony_ci return null; 3361cb0ef41Sopenharmony_ci} 3371cb0ef41Sopenharmony_ci// #endregion 3381cb0ef41Sopenharmony_ci 3391cb0ef41Sopenharmony_ci/** 3401cb0ef41Sopenharmony_ci * @typedef {Record<string, string> | typeof kFallThrough} DependencyMap 3411cb0ef41Sopenharmony_ci * @typedef {Array<[string, [string, string]]>} PatternDependencyMap 3421cb0ef41Sopenharmony_ci * @typedef {Record<string, string> | null | true} JSONDependencyMap 3431cb0ef41Sopenharmony_ci */ 3441cb0ef41Sopenharmony_ci/** 3451cb0ef41Sopenharmony_ci * @typedef {Map<string, any>} ScopeStore 3461cb0ef41Sopenharmony_ci * @typedef {(specifier: string) => true | URL} DependencyMapper 3471cb0ef41Sopenharmony_ci * @typedef {boolean | string | SRI[] | typeof kCascade} Integrity 3481cb0ef41Sopenharmony_ci */ 3491cb0ef41Sopenharmony_ci 3501cb0ef41Sopenharmony_ciclass Manifest { 3511cb0ef41Sopenharmony_ci #defaultDependencies; 3521cb0ef41Sopenharmony_ci /** 3531cb0ef41Sopenharmony_ci * @type {string} 3541cb0ef41Sopenharmony_ci */ 3551cb0ef41Sopenharmony_ci href; 3561cb0ef41Sopenharmony_ci /** 3571cb0ef41Sopenharmony_ci * @type {(err: Error) => void} 3581cb0ef41Sopenharmony_ci * 3591cb0ef41Sopenharmony_ci * Performs default action for what happens when a manifest encounters 3601cb0ef41Sopenharmony_ci * a violation such as abort()ing or exiting the process, throwing the error, 3611cb0ef41Sopenharmony_ci * or logging the error. 3621cb0ef41Sopenharmony_ci */ 3631cb0ef41Sopenharmony_ci #reaction; 3641cb0ef41Sopenharmony_ci /** 3651cb0ef41Sopenharmony_ci * @type {Map<string, DependencyMapperInstance>} 3661cb0ef41Sopenharmony_ci * 3671cb0ef41Sopenharmony_ci * Used to find where a dependency is located. 3681cb0ef41Sopenharmony_ci * 3691cb0ef41Sopenharmony_ci * This stores functions to lazily calculate locations as needed. 3701cb0ef41Sopenharmony_ci * `true` is used to signify that the location is not specified 3711cb0ef41Sopenharmony_ci * by the manifest and default resolution should be allowed. 3721cb0ef41Sopenharmony_ci * 3731cb0ef41Sopenharmony_ci * The functions return `null` to signify that a dependency is 3741cb0ef41Sopenharmony_ci * not found 3751cb0ef41Sopenharmony_ci */ 3761cb0ef41Sopenharmony_ci #resourceDependencies = new SafeMap(); 3771cb0ef41Sopenharmony_ci /** 3781cb0ef41Sopenharmony_ci * @type {Map<string, Integrity>} 3791cb0ef41Sopenharmony_ci * 3801cb0ef41Sopenharmony_ci * Used to compare a resource to the content body at the resource. 3811cb0ef41Sopenharmony_ci * `true` is used to signify that all integrities are allowed, otherwise, 3821cb0ef41Sopenharmony_ci * SRI strings are parsed to compare with the body. 3831cb0ef41Sopenharmony_ci * 3841cb0ef41Sopenharmony_ci * This stores strings instead of eagerly parsing SRI strings 3851cb0ef41Sopenharmony_ci * and only converts them to SRI data structures when needed. 3861cb0ef41Sopenharmony_ci * This avoids needing to parse all SRI strings at startup even 3871cb0ef41Sopenharmony_ci * if some never end up being used. 3881cb0ef41Sopenharmony_ci */ 3891cb0ef41Sopenharmony_ci #resourceIntegrities = new SafeMap(); 3901cb0ef41Sopenharmony_ci /** 3911cb0ef41Sopenharmony_ci * @type {ScopeStore} 3921cb0ef41Sopenharmony_ci * 3931cb0ef41Sopenharmony_ci * Used to compare a resource to the content body at the resource. 3941cb0ef41Sopenharmony_ci * `true` is used to signify that all integrities are allowed, otherwise, 3951cb0ef41Sopenharmony_ci * SRI strings are parsed to compare with the body. 3961cb0ef41Sopenharmony_ci * 3971cb0ef41Sopenharmony_ci * Separate from #resourceDependencies due to conflicts with things like 3981cb0ef41Sopenharmony_ci * `blob:` being both a scope and a resource potentially as well as 3991cb0ef41Sopenharmony_ci * `file:` being parsed to `file:///` instead of remaining host neutral. 4001cb0ef41Sopenharmony_ci */ 4011cb0ef41Sopenharmony_ci #scopeDependencies = new SafeMap(); 4021cb0ef41Sopenharmony_ci /** 4031cb0ef41Sopenharmony_ci * @type {Map<string, boolean | null | typeof kCascade>} 4041cb0ef41Sopenharmony_ci * 4051cb0ef41Sopenharmony_ci * Used to allow arbitrary loading within a scope 4061cb0ef41Sopenharmony_ci */ 4071cb0ef41Sopenharmony_ci #scopeIntegrities = new SafeMap(); 4081cb0ef41Sopenharmony_ci /** 4091cb0ef41Sopenharmony_ci * `obj` should match the policy file format described in the docs 4101cb0ef41Sopenharmony_ci * it is expected to not have prototype pollution issues either by reassigning 4111cb0ef41Sopenharmony_ci * the prototype to `null` for values or by running prior to any user code. 4121cb0ef41Sopenharmony_ci * 4131cb0ef41Sopenharmony_ci * `manifestURL` is a URL to resolve relative locations against. 4141cb0ef41Sopenharmony_ci * @param {object} obj 4151cb0ef41Sopenharmony_ci * @param {string} manifestHREF 4161cb0ef41Sopenharmony_ci */ 4171cb0ef41Sopenharmony_ci constructor(obj, manifestHREF) { 4181cb0ef41Sopenharmony_ci this.href = manifestHREF; 4191cb0ef41Sopenharmony_ci const scopes = this.#scopeDependencies; 4201cb0ef41Sopenharmony_ci const integrities = this.#resourceIntegrities; 4211cb0ef41Sopenharmony_ci const resourceDependencies = this.#resourceDependencies; 4221cb0ef41Sopenharmony_ci let reaction = REACTION_THROW; 4231cb0ef41Sopenharmony_ci 4241cb0ef41Sopenharmony_ci if (objectButNotArray(obj) && 'onerror' in obj) { 4251cb0ef41Sopenharmony_ci const behavior = obj.onerror; 4261cb0ef41Sopenharmony_ci if (behavior === 'exit') { 4271cb0ef41Sopenharmony_ci reaction = REACTION_EXIT; 4281cb0ef41Sopenharmony_ci } else if (behavior === 'log') { 4291cb0ef41Sopenharmony_ci reaction = REACTION_LOG; 4301cb0ef41Sopenharmony_ci } else if (behavior !== 'throw') { 4311cb0ef41Sopenharmony_ci throw new ERR_MANIFEST_UNKNOWN_ONERROR(behavior); 4321cb0ef41Sopenharmony_ci } 4331cb0ef41Sopenharmony_ci } 4341cb0ef41Sopenharmony_ci 4351cb0ef41Sopenharmony_ci this.#reaction = reaction; 4361cb0ef41Sopenharmony_ci const jsonResourcesEntries = ObjectEntries( 4371cb0ef41Sopenharmony_ci obj.resources ?? ObjectCreate(null), 4381cb0ef41Sopenharmony_ci ); 4391cb0ef41Sopenharmony_ci const jsonScopesEntries = ObjectEntries(obj.scopes ?? ObjectCreate(null)); 4401cb0ef41Sopenharmony_ci const defaultDependencies = obj.dependencies ?? ObjectCreate(null); 4411cb0ef41Sopenharmony_ci 4421cb0ef41Sopenharmony_ci this.#defaultDependencies = new DependencyMapperInstance( 4431cb0ef41Sopenharmony_ci 'default', 4441cb0ef41Sopenharmony_ci defaultDependencies === true ? kFallThrough : defaultDependencies, 4451cb0ef41Sopenharmony_ci false, 4461cb0ef41Sopenharmony_ci ); 4471cb0ef41Sopenharmony_ci 4481cb0ef41Sopenharmony_ci for (let i = 0; i < jsonResourcesEntries.length; i++) { 4491cb0ef41Sopenharmony_ci const { 0: originalHREF, 1: descriptor } = jsonResourcesEntries[i]; 4501cb0ef41Sopenharmony_ci const { cascade, dependencies, integrity } = descriptor; 4511cb0ef41Sopenharmony_ci const href = resolve(originalHREF, manifestHREF).href; 4521cb0ef41Sopenharmony_ci 4531cb0ef41Sopenharmony_ci if (typeof integrity !== 'undefined') { 4541cb0ef41Sopenharmony_ci debug('Manifest contains integrity for resource %s', originalHREF); 4551cb0ef41Sopenharmony_ci if (typeof integrity === 'string') { 4561cb0ef41Sopenharmony_ci integrities.set(href, integrity); 4571cb0ef41Sopenharmony_ci } else if (integrity === true) { 4581cb0ef41Sopenharmony_ci integrities.set(href, true); 4591cb0ef41Sopenharmony_ci } else { 4601cb0ef41Sopenharmony_ci throw new ERR_MANIFEST_INVALID_RESOURCE_FIELD(href, 'integrity'); 4611cb0ef41Sopenharmony_ci } 4621cb0ef41Sopenharmony_ci } else { 4631cb0ef41Sopenharmony_ci integrities.set(href, cascade === true ? kCascade : false); 4641cb0ef41Sopenharmony_ci } 4651cb0ef41Sopenharmony_ci insertDependencyMap( 4661cb0ef41Sopenharmony_ci href, 4671cb0ef41Sopenharmony_ci dependencies, 4681cb0ef41Sopenharmony_ci cascade, 4691cb0ef41Sopenharmony_ci true, 4701cb0ef41Sopenharmony_ci resourceDependencies, 4711cb0ef41Sopenharmony_ci ); 4721cb0ef41Sopenharmony_ci } 4731cb0ef41Sopenharmony_ci 4741cb0ef41Sopenharmony_ci const scopeIntegrities = this.#scopeIntegrities; 4751cb0ef41Sopenharmony_ci for (let i = 0; i < jsonScopesEntries.length; i++) { 4761cb0ef41Sopenharmony_ci const { 0: originalHREF, 1: descriptor } = jsonScopesEntries[i]; 4771cb0ef41Sopenharmony_ci const { cascade, dependencies, integrity } = descriptor; 4781cb0ef41Sopenharmony_ci const href = emptyOrProtocolOrResolve(originalHREF, manifestHREF); 4791cb0ef41Sopenharmony_ci if (typeof integrity !== 'undefined') { 4801cb0ef41Sopenharmony_ci debug('Manifest contains integrity for scope %s', originalHREF); 4811cb0ef41Sopenharmony_ci if (integrity === true) { 4821cb0ef41Sopenharmony_ci scopeIntegrities.set(href, true); 4831cb0ef41Sopenharmony_ci } else { 4841cb0ef41Sopenharmony_ci throw new ERR_MANIFEST_INVALID_RESOURCE_FIELD(href, 'integrity'); 4851cb0ef41Sopenharmony_ci } 4861cb0ef41Sopenharmony_ci } else { 4871cb0ef41Sopenharmony_ci scopeIntegrities.set(href, cascade === true ? kCascade : false); 4881cb0ef41Sopenharmony_ci } 4891cb0ef41Sopenharmony_ci insertDependencyMap(href, dependencies, cascade, false, scopes); 4901cb0ef41Sopenharmony_ci } 4911cb0ef41Sopenharmony_ci 4921cb0ef41Sopenharmony_ci ObjectFreeze(this); 4931cb0ef41Sopenharmony_ci } 4941cb0ef41Sopenharmony_ci 4951cb0ef41Sopenharmony_ci /** 4961cb0ef41Sopenharmony_ci * @param {string} requester 4971cb0ef41Sopenharmony_ci * @returns {{resolve: any, reaction: (err: any) => void}} 4981cb0ef41Sopenharmony_ci */ 4991cb0ef41Sopenharmony_ci getDependencyMapper(requester) { 5001cb0ef41Sopenharmony_ci const requesterHREF = `${requester}`; 5011cb0ef41Sopenharmony_ci const dependencies = this.#resourceDependencies; 5021cb0ef41Sopenharmony_ci /** 5031cb0ef41Sopenharmony_ci * @type {DependencyMapperInstance} 5041cb0ef41Sopenharmony_ci */ 5051cb0ef41Sopenharmony_ci const instance = ( 5061cb0ef41Sopenharmony_ci dependencies.has(requesterHREF) ? 5071cb0ef41Sopenharmony_ci dependencies.get(requesterHREF) ?? null : 5081cb0ef41Sopenharmony_ci this.getScopeDependencyMapper(requesterHREF, true) 5091cb0ef41Sopenharmony_ci ) ?? this.#defaultDependencies; 5101cb0ef41Sopenharmony_ci return { 5111cb0ef41Sopenharmony_ci resolve: (specifier, conditions) => { 5121cb0ef41Sopenharmony_ci const normalizedSpecifier = canonicalizeSpecifier( 5131cb0ef41Sopenharmony_ci specifier, 5141cb0ef41Sopenharmony_ci requesterHREF, 5151cb0ef41Sopenharmony_ci ); 5161cb0ef41Sopenharmony_ci const result = instance._resolveAlreadyNormalized( 5171cb0ef41Sopenharmony_ci normalizedSpecifier, 5181cb0ef41Sopenharmony_ci conditions, 5191cb0ef41Sopenharmony_ci this, 5201cb0ef41Sopenharmony_ci ); 5211cb0ef41Sopenharmony_ci if (result === kFallThrough) return true; 5221cb0ef41Sopenharmony_ci return result; 5231cb0ef41Sopenharmony_ci }, 5241cb0ef41Sopenharmony_ci reaction: this.#reaction, 5251cb0ef41Sopenharmony_ci }; 5261cb0ef41Sopenharmony_ci } 5271cb0ef41Sopenharmony_ci 5281cb0ef41Sopenharmony_ci mightAllow(url, onreact) { 5291cb0ef41Sopenharmony_ci const href = `${url}`; 5301cb0ef41Sopenharmony_ci debug('Checking for entry of %s', href); 5311cb0ef41Sopenharmony_ci if (StringPrototypeStartsWith(href, 'node:')) { 5321cb0ef41Sopenharmony_ci return true; 5331cb0ef41Sopenharmony_ci } 5341cb0ef41Sopenharmony_ci if (this.#resourceIntegrities.has(href)) { 5351cb0ef41Sopenharmony_ci return true; 5361cb0ef41Sopenharmony_ci } 5371cb0ef41Sopenharmony_ci let scope = findScopeHREF(href, this.#scopeIntegrities, true); 5381cb0ef41Sopenharmony_ci while (scope !== null) { 5391cb0ef41Sopenharmony_ci if (this.#scopeIntegrities.has(scope)) { 5401cb0ef41Sopenharmony_ci const entry = this.#scopeIntegrities.get(scope); 5411cb0ef41Sopenharmony_ci if (entry === true) { 5421cb0ef41Sopenharmony_ci return true; 5431cb0ef41Sopenharmony_ci } else if (entry !== kCascade) { 5441cb0ef41Sopenharmony_ci break; 5451cb0ef41Sopenharmony_ci } 5461cb0ef41Sopenharmony_ci } 5471cb0ef41Sopenharmony_ci const nextScope = findScopeHREF( 5481cb0ef41Sopenharmony_ci new URL('..', scope), 5491cb0ef41Sopenharmony_ci this.#scopeIntegrities, 5501cb0ef41Sopenharmony_ci false, 5511cb0ef41Sopenharmony_ci ); 5521cb0ef41Sopenharmony_ci if (!nextScope || nextScope === scope) { 5531cb0ef41Sopenharmony_ci break; 5541cb0ef41Sopenharmony_ci } 5551cb0ef41Sopenharmony_ci scope = nextScope; 5561cb0ef41Sopenharmony_ci } 5571cb0ef41Sopenharmony_ci if (onreact) { 5581cb0ef41Sopenharmony_ci this.#reaction(onreact()); 5591cb0ef41Sopenharmony_ci } 5601cb0ef41Sopenharmony_ci return false; 5611cb0ef41Sopenharmony_ci } 5621cb0ef41Sopenharmony_ci 5631cb0ef41Sopenharmony_ci assertIntegrity(url, content) { 5641cb0ef41Sopenharmony_ci const href = `${url}`; 5651cb0ef41Sopenharmony_ci debug('Checking integrity of %s', href); 5661cb0ef41Sopenharmony_ci const realIntegrities = new SafeMap(); 5671cb0ef41Sopenharmony_ci const integrities = this.#resourceIntegrities; 5681cb0ef41Sopenharmony_ci function processEntry(href) { 5691cb0ef41Sopenharmony_ci let integrityEntries = integrities.get(href); 5701cb0ef41Sopenharmony_ci if (integrityEntries === true) return true; 5711cb0ef41Sopenharmony_ci if (typeof integrityEntries === 'string') { 5721cb0ef41Sopenharmony_ci const sri = ObjectFreeze(SRI.parse(integrityEntries)); 5731cb0ef41Sopenharmony_ci integrities.set(href, sri); 5741cb0ef41Sopenharmony_ci integrityEntries = sri; 5751cb0ef41Sopenharmony_ci } 5761cb0ef41Sopenharmony_ci return integrityEntries; 5771cb0ef41Sopenharmony_ci } 5781cb0ef41Sopenharmony_ci if (integrities.has(href)) { 5791cb0ef41Sopenharmony_ci const integrityEntries = processEntry(href); 5801cb0ef41Sopenharmony_ci if (integrityEntries === true) return true; 5811cb0ef41Sopenharmony_ci if (ArrayIsArray(integrityEntries)) { 5821cb0ef41Sopenharmony_ci // Avoid clobbered Symbol.iterator 5831cb0ef41Sopenharmony_ci for (let i = 0; i < integrityEntries.length; i++) { 5841cb0ef41Sopenharmony_ci const { algorithm, value: expected } = integrityEntries[i]; 5851cb0ef41Sopenharmony_ci // TODO(tniessen): the content should not be passed as a string in the 5861cb0ef41Sopenharmony_ci // first place, see https://github.com/nodejs/node/issues/39707 5871cb0ef41Sopenharmony_ci const mismatchedIntegrity = internalVerifyIntegrity(algorithm, content, expected); 5881cb0ef41Sopenharmony_ci if (mismatchedIntegrity === undefined) { 5891cb0ef41Sopenharmony_ci return true; 5901cb0ef41Sopenharmony_ci } 5911cb0ef41Sopenharmony_ci realIntegrities.set(algorithm, mismatchedIntegrity); 5921cb0ef41Sopenharmony_ci } 5931cb0ef41Sopenharmony_ci } 5941cb0ef41Sopenharmony_ci 5951cb0ef41Sopenharmony_ci if (integrityEntries !== kCascade) { 5961cb0ef41Sopenharmony_ci const error = new ERR_MANIFEST_ASSERT_INTEGRITY(url, realIntegrities); 5971cb0ef41Sopenharmony_ci this.#reaction(error); 5981cb0ef41Sopenharmony_ci } 5991cb0ef41Sopenharmony_ci } 6001cb0ef41Sopenharmony_ci let scope = findScopeHREF(href, this.#scopeIntegrities, true); 6011cb0ef41Sopenharmony_ci while (scope !== null) { 6021cb0ef41Sopenharmony_ci if (this.#scopeIntegrities.has(scope)) { 6031cb0ef41Sopenharmony_ci const entry = this.#scopeIntegrities.get(scope); 6041cb0ef41Sopenharmony_ci if (entry === true) { 6051cb0ef41Sopenharmony_ci return true; 6061cb0ef41Sopenharmony_ci } else if (entry !== kCascade) { 6071cb0ef41Sopenharmony_ci break; 6081cb0ef41Sopenharmony_ci } 6091cb0ef41Sopenharmony_ci } 6101cb0ef41Sopenharmony_ci const nextScope = findScopeHREF(scope, this.#scopeDependencies, false); 6111cb0ef41Sopenharmony_ci if (!nextScope) { 6121cb0ef41Sopenharmony_ci break; 6131cb0ef41Sopenharmony_ci } 6141cb0ef41Sopenharmony_ci scope = nextScope; 6151cb0ef41Sopenharmony_ci } 6161cb0ef41Sopenharmony_ci const error = new ERR_MANIFEST_ASSERT_INTEGRITY(url, realIntegrities); 6171cb0ef41Sopenharmony_ci this.#reaction(error); 6181cb0ef41Sopenharmony_ci } 6191cb0ef41Sopenharmony_ci /** 6201cb0ef41Sopenharmony_ci * @param {string} href 6211cb0ef41Sopenharmony_ci * @param {boolean} allowSameHREFScope 6221cb0ef41Sopenharmony_ci * @returns {DependencyMapperInstance | null} 6231cb0ef41Sopenharmony_ci */ 6241cb0ef41Sopenharmony_ci getScopeDependencyMapper(href, allowSameHREFScope) { 6251cb0ef41Sopenharmony_ci if (href === null) { 6261cb0ef41Sopenharmony_ci return this.#defaultDependencies; 6271cb0ef41Sopenharmony_ci } 6281cb0ef41Sopenharmony_ci /** @type {string | null} */ 6291cb0ef41Sopenharmony_ci const scopeHREF = findScopeHREF( 6301cb0ef41Sopenharmony_ci href, 6311cb0ef41Sopenharmony_ci this.#scopeDependencies, 6321cb0ef41Sopenharmony_ci allowSameHREFScope, 6331cb0ef41Sopenharmony_ci ); 6341cb0ef41Sopenharmony_ci if (scopeHREF === null) return this.#defaultDependencies; 6351cb0ef41Sopenharmony_ci return this.#scopeDependencies.get(scopeHREF); 6361cb0ef41Sopenharmony_ci } 6371cb0ef41Sopenharmony_ci} 6381cb0ef41Sopenharmony_ci 6391cb0ef41Sopenharmony_ci// Lock everything down to avoid problems even if reference is leaked somehow 6401cb0ef41Sopenharmony_ciObjectSetPrototypeOf(Manifest, null); 6411cb0ef41Sopenharmony_ciObjectSetPrototypeOf(Manifest.prototype, null); 6421cb0ef41Sopenharmony_ciObjectFreeze(Manifest); 6431cb0ef41Sopenharmony_ciObjectFreeze(Manifest.prototype); 6441cb0ef41Sopenharmony_cimodule.exports = ObjectFreeze({ Manifest }); 6451cb0ef41Sopenharmony_ci 6461cb0ef41Sopenharmony_ci// #region URL utils 6471cb0ef41Sopenharmony_ci 6481cb0ef41Sopenharmony_ci/** 6491cb0ef41Sopenharmony_ci * Attempts to canonicalize relative URL strings against a base URL string 6501cb0ef41Sopenharmony_ci * Does not perform I/O 6511cb0ef41Sopenharmony_ci * If not able to canonicalize, returns the original specifier 6521cb0ef41Sopenharmony_ci * 6531cb0ef41Sopenharmony_ci * This effectively removes the possibility of the return value being a relative 6541cb0ef41Sopenharmony_ci * URL string 6551cb0ef41Sopenharmony_ci * @param {string} specifier 6561cb0ef41Sopenharmony_ci * @param {string} base 6571cb0ef41Sopenharmony_ci * @returns {string} 6581cb0ef41Sopenharmony_ci */ 6591cb0ef41Sopenharmony_cifunction canonicalizeSpecifier(specifier, base) { 6601cb0ef41Sopenharmony_ci try { 6611cb0ef41Sopenharmony_ci if (RegExpPrototypeExec(kRelativeURLStringPattern, specifier) !== null) { 6621cb0ef41Sopenharmony_ci return resolve(specifier, base).href; 6631cb0ef41Sopenharmony_ci } 6641cb0ef41Sopenharmony_ci return resolve(specifier).href; 6651cb0ef41Sopenharmony_ci } catch { 6661cb0ef41Sopenharmony_ci // Continue regardless of error. 6671cb0ef41Sopenharmony_ci } 6681cb0ef41Sopenharmony_ci return specifier; 6691cb0ef41Sopenharmony_ci} 6701cb0ef41Sopenharmony_ci 6711cb0ef41Sopenharmony_ci/** 6721cb0ef41Sopenharmony_ci * Does a special allowance for scopes to be non-valid URLs 6731cb0ef41Sopenharmony_ci * that are only protocol strings or the empty string 6741cb0ef41Sopenharmony_ci * @param {string} resourceHREF 6751cb0ef41Sopenharmony_ci * @param {string} [base] 6761cb0ef41Sopenharmony_ci * @returns {string} 6771cb0ef41Sopenharmony_ci */ 6781cb0ef41Sopenharmony_ciconst emptyOrProtocolOrResolve = (resourceHREF, base) => { 6791cb0ef41Sopenharmony_ci if (resourceHREF === '') return ''; 6801cb0ef41Sopenharmony_ci if (StringPrototypeEndsWith(resourceHREF, ':')) { 6811cb0ef41Sopenharmony_ci // URL parse will trim these anyway, save the compute 6821cb0ef41Sopenharmony_ci resourceHREF = RegExpPrototypeSymbolReplace( 6831cb0ef41Sopenharmony_ci // eslint-disable-next-line 6841cb0ef41Sopenharmony_ci /^[\x00-\x1F\x20]|\x09\x0A\x0D|[\x00-\x1F\x20]$/g, 6851cb0ef41Sopenharmony_ci resourceHREF, 6861cb0ef41Sopenharmony_ci '', 6871cb0ef41Sopenharmony_ci ); 6881cb0ef41Sopenharmony_ci if (RegExpPrototypeExec(/^[a-zA-Z][a-zA-Z+\-.]*:$/, resourceHREF) !== null) { 6891cb0ef41Sopenharmony_ci return resourceHREF; 6901cb0ef41Sopenharmony_ci } 6911cb0ef41Sopenharmony_ci } 6921cb0ef41Sopenharmony_ci return resolve(resourceHREF, base).href; 6931cb0ef41Sopenharmony_ci}; 6941cb0ef41Sopenharmony_ci 6951cb0ef41Sopenharmony_ci/** 6961cb0ef41Sopenharmony_ci * @type {Map<string, URL>} 6971cb0ef41Sopenharmony_ci */ 6981cb0ef41Sopenharmony_cilet parsedURLs; 6991cb0ef41Sopenharmony_ci/** 7001cb0ef41Sopenharmony_ci * Resolves a valid url string and uses the parsed cache to avoid double parsing 7011cb0ef41Sopenharmony_ci * costs. 7021cb0ef41Sopenharmony_ci * @param {string} originalHREF 7031cb0ef41Sopenharmony_ci * @param {string} [base] 7041cb0ef41Sopenharmony_ci * @returns {Readonly<URL>} 7051cb0ef41Sopenharmony_ci */ 7061cb0ef41Sopenharmony_ciconst resolve = (originalHREF, base) => { 7071cb0ef41Sopenharmony_ci parsedURLs = parsedURLs ?? new SafeMap(); 7081cb0ef41Sopenharmony_ci if (parsedURLs.has(originalHREF)) { 7091cb0ef41Sopenharmony_ci return parsedURLs.get(originalHREF); 7101cb0ef41Sopenharmony_ci } else if (RegExpPrototypeExec(kRelativeURLStringPattern, originalHREF) !== null) { 7111cb0ef41Sopenharmony_ci const resourceURL = new URL(originalHREF, base); 7121cb0ef41Sopenharmony_ci parsedURLs.set(resourceURL.href, resourceURL); 7131cb0ef41Sopenharmony_ci return resourceURL; 7141cb0ef41Sopenharmony_ci } 7151cb0ef41Sopenharmony_ci const resourceURL = new URL(originalHREF); 7161cb0ef41Sopenharmony_ci parsedURLs.set(originalHREF, resourceURL); 7171cb0ef41Sopenharmony_ci return resourceURL; 7181cb0ef41Sopenharmony_ci}; 7191cb0ef41Sopenharmony_ci 7201cb0ef41Sopenharmony_ci// #endregion 7211cb0ef41Sopenharmony_ci 7221cb0ef41Sopenharmony_ci/** 7231cb0ef41Sopenharmony_ci * @param {any} o 7241cb0ef41Sopenharmony_ci * @returns {o is object} 7251cb0ef41Sopenharmony_ci */ 7261cb0ef41Sopenharmony_cifunction objectButNotArray(o) { 7271cb0ef41Sopenharmony_ci return o && typeof o === 'object' && !ArrayIsArray(o); 7281cb0ef41Sopenharmony_ci} 7291cb0ef41Sopenharmony_ci 7301cb0ef41Sopenharmony_cifunction searchDependencies(href, target, conditions) { 7311cb0ef41Sopenharmony_ci if (objectButNotArray(target)) { 7321cb0ef41Sopenharmony_ci const keys = ObjectKeys(target); 7331cb0ef41Sopenharmony_ci for (let i = 0; i < keys.length; i++) { 7341cb0ef41Sopenharmony_ci const key = keys[i]; 7351cb0ef41Sopenharmony_ci if (conditions.has(key)) { 7361cb0ef41Sopenharmony_ci const ret = searchDependencies(href, target[key], conditions); 7371cb0ef41Sopenharmony_ci if (ret != null) { 7381cb0ef41Sopenharmony_ci return ret; 7391cb0ef41Sopenharmony_ci } 7401cb0ef41Sopenharmony_ci } 7411cb0ef41Sopenharmony_ci } 7421cb0ef41Sopenharmony_ci } else if (typeof target === 'string') { 7431cb0ef41Sopenharmony_ci return target; 7441cb0ef41Sopenharmony_ci } else if (target === true) { 7451cb0ef41Sopenharmony_ci return target; 7461cb0ef41Sopenharmony_ci } else { 7471cb0ef41Sopenharmony_ci throw new ERR_MANIFEST_INVALID_RESOURCE_FIELD(href, 'dependencies'); 7481cb0ef41Sopenharmony_ci } 7491cb0ef41Sopenharmony_ci return null; 7501cb0ef41Sopenharmony_ci} 751