From 2d6cee41ecb7eb5bce9574eff96de7060886cfdf Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Thu, 13 Aug 2026 08:55:02 +0900 Subject: [PATCH 01/50] test(supply-chain): expose indirect CommonJS authority gap --- scripts/javascript-runtime-authority.test.mjs | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/scripts/javascript-runtime-authority.test.mjs b/scripts/javascript-runtime-authority.test.mjs index 7ca3e2a3..c3003034 100644 --- a/scripts/javascript-runtime-authority.test.mjs +++ b/scripts/javascript-runtime-authority.test.mjs @@ -41,6 +41,32 @@ test('reports executable module authority with actionable specifiers', () => { ); }); +test('reports statically recognizable indirect CommonJS loader invocations', () => { + const source = [ + "const comma = (0, require)('comma-package');", + "const called = require.call(undefined, 'call-package');", + "const moduleRequired = module.require('module-package');", + "const elementRequired = module['require']('element-package');", + 'const computed = module.require(runtimePackageName);', + 'const object = { require() { return "method-only"; } };', + 'const benign = object.require("not-the-commonjs-loader");', + 'void [comma, called, moduleRequired, elementRequired, computed, benign];', + ].join('\n'); + + assert.deepEqual( + findRuntimeModuleAuthority(source, 'indirect-authority.js').map( + ({ kind, specifier }) => ({ kind, specifier }), + ), + [ + { kind: 'commonjs-require', specifier: 'comma-package' }, + { kind: 'commonjs-require', specifier: 'call-package' }, + { kind: 'commonjs-require', specifier: 'module-package' }, + { kind: 'commonjs-require', specifier: 'element-package' }, + { kind: 'commonjs-require', specifier: undefined }, + ], + ); +}); + test('fails closed when emitted JavaScript is syntactically invalid', () => { assert.throws( () => findRuntimeModuleAuthority('const = ;', 'broken.js'), From f616d0d776f4a186b4ecd52784fc142189e71690 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Thu, 13 Aug 2026 09:00:38 +0900 Subject: [PATCH 02/50] fix(supply-chain): reject indirect CommonJS loaders --- scripts/javascript-runtime-authority.mjs | 96 ++++++++++++++++++++---- 1 file changed, 82 insertions(+), 14 deletions(-) diff --git a/scripts/javascript-runtime-authority.mjs b/scripts/javascript-runtime-authority.mjs index 8ee68338..a3a455cd 100644 --- a/scripts/javascript-runtime-authority.mjs +++ b/scripts/javascript-runtime-authority.mjs @@ -5,11 +5,11 @@ import ts from 'typescript'; * * Parsing the artifact instead of scanning raw text deliberately ignores comments, * string literals, and template text that merely mention `require()` or `import()`. - * Actual static imports/re-exports, bare CommonJS `require()` calls, and dynamic - * `import()` calls remain fail-closed findings. Literal module specifiers are - * preserved in diagnostics so a verifier failure identifies the dependency edge - * that escaped bundling; computed specifiers remain `undefined` and therefore do - * not acquire an invented interpretation. + * Actual static imports/re-exports, statically recognizable CommonJS loader calls, + * and dynamic `import()` calls remain fail-closed findings. Literal module + * specifiers are preserved in diagnostics so a verifier failure identifies the + * dependency edge that escaped bundling; computed specifiers remain `undefined` + * and therefore do not acquire an invented interpretation. * * @param {string} source JavaScript source emitted into the packed artifact. * @param {string} [filename='bundle.js'] Diagnostic filename for parse failures. @@ -42,6 +42,74 @@ export function findRuntimeModuleAuthority(source, filename = 'bundle.js') { : undefined; } + /** Remove syntax-only parentheses without evaluating the expression. */ + function unwrapParentheses(expression) { + let current = expression; + while (ts.isParenthesizedExpression(current)) { + current = current.expression; + } + return current; + } + + /** Return a statically written member name without evaluating property input. */ + function staticMemberName(expression) { + if (ts.isPropertyAccessExpression(expression)) { + return expression.name.text; + } + if ( + ts.isElementAccessExpression(expression) && + expression.argumentExpression && + ts.isStringLiteralLike(expression.argumentExpression) + ) { + return expression.argumentExpression.text; + } + return undefined; + } + + /** Identify direct CommonJS loader values without resolving aliases or scope. */ + function isCommonJsLoaderExpression(expression) { + const current = unwrapParentheses(expression); + if (ts.isIdentifier(current)) { + return current.text === 'require'; + } + if ( + ts.isBinaryExpression(current) && + current.operatorToken.kind === ts.SyntaxKind.CommaToken + ) { + return isCommonJsLoaderExpression(current.right); + } + if ( + (ts.isPropertyAccessExpression(current) || + ts.isElementAccessExpression(current)) && + staticMemberName(current) === 'require' + ) { + const receiver = unwrapParentheses(current.expression); + return ts.isIdentifier(receiver) && receiver.text === 'module'; + } + return false; + } + + /** + * Return the package argument for one recognizable CommonJS loader call. + * A wrapper object distinguishes a computed or missing argument from no match. + */ + function commonJsInvocation(node) { + if (isCommonJsLoaderExpression(node.expression)) { + return { argument: node.arguments[0] }; + } + + const callee = unwrapParentheses(node.expression); + if ( + (ts.isPropertyAccessExpression(callee) || + ts.isElementAccessExpression(callee)) && + staticMemberName(callee) === 'call' && + isCommonJsLoaderExpression(callee.expression) + ) { + return { argument: node.arguments[1] }; + } + return null; + } + /** @param {import('typescript').Node} node Parsed JavaScript node. */ function visit(node) { if (ts.isImportDeclaration(node) && node.moduleSpecifier) { @@ -63,15 +131,15 @@ export function findRuntimeModuleAuthority(source, filename = 'bundle.js') { offset: node.getStart(sourceFile), specifier: literalSpecifier(node.arguments[0]), }); - } else if ( - ts.isIdentifier(node.expression) && - node.expression.text === 'require' - ) { - findings.push({ - kind: 'commonjs-require', - offset: node.getStart(sourceFile), - specifier: literalSpecifier(node.arguments[0]), - }); + } else { + const invocation = commonJsInvocation(node); + if (invocation) { + findings.push({ + kind: 'commonjs-require', + offset: node.getStart(sourceFile), + specifier: literalSpecifier(invocation.argument), + }); + } } } From dc3d47b1bb818ec0592619c5c6ee73e8da09a8c6 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 14 Aug 2026 09:34:06 +0900 Subject: [PATCH 03/50] test(supply-chain): detect CommonJS resolver authority --- scripts/javascript-runtime-authority.test.mjs | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/scripts/javascript-runtime-authority.test.mjs b/scripts/javascript-runtime-authority.test.mjs index c3003034..eb293ec7 100644 --- a/scripts/javascript-runtime-authority.test.mjs +++ b/scripts/javascript-runtime-authority.test.mjs @@ -67,6 +67,28 @@ test('reports statically recognizable indirect CommonJS loader invocations', () ); }); +test('reports statically recognizable CommonJS resolver authority', () => { + const source = [ + "const direct = require.resolve('resolve-package');", + "const element = require['resolve']('element-resolve-package');", + 'const computed = require.resolve(runtimePackageName);', + 'const object = { resolve() { return "method-only"; } };', + 'const benign = object.resolve("not-the-commonjs-resolver");', + 'void [direct, element, computed, benign];', + ].join('\n'); + + assert.deepEqual( + findRuntimeModuleAuthority(source, 'resolver-authority.js').map( + ({ kind, specifier }) => ({ kind, specifier }), + ), + [ + { kind: 'commonjs-resolve', specifier: 'resolve-package' }, + { kind: 'commonjs-resolve', specifier: 'element-resolve-package' }, + { kind: 'commonjs-resolve', specifier: undefined }, + ], + ); +}); + test('fails closed when emitted JavaScript is syntactically invalid', () => { assert.throws( () => findRuntimeModuleAuthority('const = ;', 'broken.js'), From 7a13161d3afc4dade60854f067d8b5c758b053bf Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 14 Aug 2026 09:43:29 +0900 Subject: [PATCH 04/50] fix(supply-chain): detect CommonJS resolver authority --- scripts/javascript-runtime-authority.mjs | 44 +++++++++++++++++++----- 1 file changed, 35 insertions(+), 9 deletions(-) diff --git a/scripts/javascript-runtime-authority.mjs b/scripts/javascript-runtime-authority.mjs index a3a455cd..7385b651 100644 --- a/scripts/javascript-runtime-authority.mjs +++ b/scripts/javascript-runtime-authority.mjs @@ -5,11 +5,11 @@ import ts from 'typescript'; * * Parsing the artifact instead of scanning raw text deliberately ignores comments, * string literals, and template text that merely mention `require()` or `import()`. - * Actual static imports/re-exports, statically recognizable CommonJS loader calls, - * and dynamic `import()` calls remain fail-closed findings. Literal module - * specifiers are preserved in diagnostics so a verifier failure identifies the - * dependency edge that escaped bundling; computed specifiers remain `undefined` - * and therefore do not acquire an invented interpretation. + * Actual static imports/re-exports, statically recognizable CommonJS loader and + * resolver calls, and dynamic `import()` calls remain fail-closed findings. + * Literal module specifiers are preserved in diagnostics so a verifier failure + * identifies the dependency edge that escaped bundling; computed specifiers remain + * `undefined` and therefore do not acquire an invented interpretation. * * @param {string} source JavaScript source emitted into the packed artifact. * @param {string} [filename='bundle.js'] Diagnostic filename for parse failures. @@ -89,6 +89,23 @@ export function findRuntimeModuleAuthority(source, filename = 'bundle.js') { return false; } + /** + * Return the package argument for one recognizable CommonJS resolver call. + * Arbitrary object methods named `resolve` remain outside this authority model. + */ + function commonJsResolverInvocation(node) { + const callee = unwrapParentheses(node.expression); + if ( + (ts.isPropertyAccessExpression(callee) || + ts.isElementAccessExpression(callee)) && + staticMemberName(callee) === 'resolve' && + isCommonJsLoaderExpression(callee.expression) + ) { + return { argument: node.arguments[0] }; + } + return null; + } + /** * Return the package argument for one recognizable CommonJS loader call. * A wrapper object distinguishes a computed or missing argument from no match. @@ -132,13 +149,22 @@ export function findRuntimeModuleAuthority(source, filename = 'bundle.js') { specifier: literalSpecifier(node.arguments[0]), }); } else { - const invocation = commonJsInvocation(node); - if (invocation) { + const resolverInvocation = commonJsResolverInvocation(node); + if (resolverInvocation) { findings.push({ - kind: 'commonjs-require', + kind: 'commonjs-resolve', offset: node.getStart(sourceFile), - specifier: literalSpecifier(invocation.argument), + specifier: literalSpecifier(resolverInvocation.argument), }); + } else { + const invocation = commonJsInvocation(node); + if (invocation) { + findings.push({ + kind: 'commonjs-require', + offset: node.getStart(sourceFile), + specifier: literalSpecifier(invocation.argument), + }); + } } } } From a4152db03576f2e886b4b14613800a4ce430f4b4 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 14 Aug 2026 12:25:15 +0900 Subject: [PATCH 05/50] test(supply-chain): expose indirect CommonJS resolver authority --- scripts/javascript-runtime-authority.test.mjs | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/scripts/javascript-runtime-authority.test.mjs b/scripts/javascript-runtime-authority.test.mjs index eb293ec7..f81d84f7 100644 --- a/scripts/javascript-runtime-authority.test.mjs +++ b/scripts/javascript-runtime-authority.test.mjs @@ -89,6 +89,34 @@ test('reports statically recognizable CommonJS resolver authority', () => { ); }); +test('reports statically recognizable indirect CommonJS resolver invocations', () => { + const source = [ + "const comma = (0, require.resolve)('comma-resolve-package');", + "const called = require.resolve.call(undefined, 'call-resolve-package');", + "const elementCalled = require['resolve']['call'](undefined, 'element-call-resolve-package');", + 'const computed = require.resolve.call(undefined, runtimePackageName);', + 'const object = { resolve() { return "method-only"; } };', + 'const benignComma = (0, object.resolve)("not-the-commonjs-resolver");', + 'const benignCall = object.resolve.call(undefined, "also-not-the-commonjs-resolver");', + 'void [comma, called, elementCalled, computed, benignComma, benignCall];', + ].join('\n'); + + assert.deepEqual( + findRuntimeModuleAuthority(source, 'indirect-resolver-authority.js').map( + ({ kind, specifier }) => ({ kind, specifier }), + ), + [ + { kind: 'commonjs-resolve', specifier: 'comma-resolve-package' }, + { kind: 'commonjs-resolve', specifier: 'call-resolve-package' }, + { + kind: 'commonjs-resolve', + specifier: 'element-call-resolve-package', + }, + { kind: 'commonjs-resolve', specifier: undefined }, + ], + ); +}); + test('fails closed when emitted JavaScript is syntactically invalid', () => { assert.throws( () => findRuntimeModuleAuthority('const = ;', 'broken.js'), From 2193959432099d418e471d9b02683fff9e7112d6 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 14 Aug 2026 12:29:25 +0900 Subject: [PATCH 06/50] fix(supply-chain): detect indirect CommonJS resolver invocations --- scripts/javascript-runtime-authority.mjs | 29 ++++++++++++++++++++---- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/scripts/javascript-runtime-authority.mjs b/scripts/javascript-runtime-authority.mjs index 7385b651..296f8f6a 100644 --- a/scripts/javascript-runtime-authority.mjs +++ b/scripts/javascript-runtime-authority.mjs @@ -89,19 +89,40 @@ export function findRuntimeModuleAuthority(source, filename = 'bundle.js') { return false; } + /** Identify statically recognizable CommonJS resolver values. */ + function isCommonJsResolverExpression(expression) { + const current = unwrapParentheses(expression); + if ( + ts.isBinaryExpression(current) && + current.operatorToken.kind === ts.SyntaxKind.CommaToken + ) { + return isCommonJsResolverExpression(current.right); + } + return ( + (ts.isPropertyAccessExpression(current) || + ts.isElementAccessExpression(current)) && + staticMemberName(current) === 'resolve' && + isCommonJsLoaderExpression(current.expression) + ); + } + /** * Return the package argument for one recognizable CommonJS resolver call. * Arbitrary object methods named `resolve` remain outside this authority model. */ function commonJsResolverInvocation(node) { + if (isCommonJsResolverExpression(node.expression)) { + return { argument: node.arguments[0] }; + } + const callee = unwrapParentheses(node.expression); if ( (ts.isPropertyAccessExpression(callee) || ts.isElementAccessExpression(callee)) && - staticMemberName(callee) === 'resolve' && - isCommonJsLoaderExpression(callee.expression) + staticMemberName(callee) === 'call' && + isCommonJsResolverExpression(callee.expression) ) { - return { argument: node.arguments[0] }; + return { argument: node.arguments[1] }; } return null; } @@ -174,4 +195,4 @@ export function findRuntimeModuleAuthority(source, filename = 'bundle.js') { visit(sourceFile); return Object.freeze(findings.map((finding) => Object.freeze(finding))); -} +} \ No newline at end of file From 1a78422433df7f2329ac6dc5ad2eb34b380b2aac Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 14 Aug 2026 13:03:17 +0900 Subject: [PATCH 07/50] test(supply-chain): expose require.main loader authority gap --- scripts/javascript-runtime-authority.test.mjs | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/scripts/javascript-runtime-authority.test.mjs b/scripts/javascript-runtime-authority.test.mjs index f81d84f7..ce666f5a 100644 --- a/scripts/javascript-runtime-authority.test.mjs +++ b/scripts/javascript-runtime-authority.test.mjs @@ -67,6 +67,30 @@ test('reports statically recognizable indirect CommonJS loader invocations', () ); }); +test('reports CommonJS loader authority reached through require.main', () => { + const source = [ + "const direct = require.main.require('main-package');", + "const element = require['main']['require']('element-main-package');", + "const called = require.main.require.call(undefined, 'call-main-package');", + 'const computed = require.main.require(runtimePackageName);', + 'const object = { main: { require() { return "method-only"; } } };', + 'const benign = object.main.require("not-the-commonjs-loader");', + 'void [direct, element, called, computed, benign];', + ].join('\n'); + + assert.deepEqual( + findRuntimeModuleAuthority(source, 'require-main-authority.js').map( + ({ kind, specifier }) => ({ kind, specifier }), + ), + [ + { kind: 'commonjs-require', specifier: 'main-package' }, + { kind: 'commonjs-require', specifier: 'element-main-package' }, + { kind: 'commonjs-require', specifier: 'call-main-package' }, + { kind: 'commonjs-require', specifier: undefined }, + ], + ); +}); + test('reports statically recognizable CommonJS resolver authority', () => { const source = [ "const direct = require.resolve('resolve-package');", From d2b52eca59f8b11def3e8714b8841538da6a8419 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 14 Aug 2026 13:06:46 +0900 Subject: [PATCH 08/50] fix(supply-chain): detect require.main loader authority --- scripts/javascript-runtime-authority.mjs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/scripts/javascript-runtime-authority.mjs b/scripts/javascript-runtime-authority.mjs index 296f8f6a..5405dac7 100644 --- a/scripts/javascript-runtime-authority.mjs +++ b/scripts/javascript-runtime-authority.mjs @@ -84,7 +84,15 @@ export function findRuntimeModuleAuthority(source, filename = 'bundle.js') { staticMemberName(current) === 'require' ) { const receiver = unwrapParentheses(current.expression); - return ts.isIdentifier(receiver) && receiver.text === 'module'; + if (ts.isIdentifier(receiver) && receiver.text === 'module') { + return true; + } + return ( + (ts.isPropertyAccessExpression(receiver) || + ts.isElementAccessExpression(receiver)) && + staticMemberName(receiver) === 'main' && + isCommonJsLoaderExpression(receiver.expression) + ); } return false; } @@ -195,4 +203,4 @@ export function findRuntimeModuleAuthority(source, filename = 'bundle.js') { visit(sourceFile); return Object.freeze(findings.map((finding) => Object.freeze(finding))); -} \ No newline at end of file +} From 0f3f1d06a2ad0961f2bc7b4e7293c8d9c854d04a Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 14 Aug 2026 17:06:10 +0900 Subject: [PATCH 09/50] test(package): expose CommonJS apply authority gap --- scripts/javascript-runtime-authority.test.mjs | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/scripts/javascript-runtime-authority.test.mjs b/scripts/javascript-runtime-authority.test.mjs index ce666f5a..eb1342a6 100644 --- a/scripts/javascript-runtime-authority.test.mjs +++ b/scripts/javascript-runtime-authority.test.mjs @@ -141,6 +141,33 @@ test('reports statically recognizable indirect CommonJS resolver invocations', ( ); }); +test('reports statically recognizable CommonJS apply invocations', () => { + const source = [ + "const required = require.apply(undefined, ['apply-package']);", + "const moduleRequired = module['require']['apply'](undefined, ['module-apply-package']);", + 'const mainRequired = require.main.require.apply(undefined, [runtimePackageName]);', + "const resolved = require.resolve.apply(undefined, ['apply-resolve-package']);", + 'const computedResolved = require[\'resolve\'][\'apply\'](undefined, resolverArguments);', + 'const object = { require() {}, resolve() {} };', + "const benignRequire = object.require.apply(undefined, ['not-commonjs']);", + "const benignResolve = object.resolve.apply(undefined, ['not-commonjs-resolve']);", + 'void [required, moduleRequired, mainRequired, resolved, computedResolved, benignRequire, benignResolve];', + ].join('\n'); + + assert.deepEqual( + findRuntimeModuleAuthority(source, 'apply-authority.js').map( + ({ kind, specifier }) => ({ kind, specifier }), + ), + [ + { kind: 'commonjs-require', specifier: 'apply-package' }, + { kind: 'commonjs-require', specifier: 'module-apply-package' }, + { kind: 'commonjs-require', specifier: undefined }, + { kind: 'commonjs-resolve', specifier: 'apply-resolve-package' }, + { kind: 'commonjs-resolve', specifier: undefined }, + ], + ); +}); + test('fails closed when emitted JavaScript is syntactically invalid', () => { assert.throws( () => findRuntimeModuleAuthority('const = ;', 'broken.js'), From 9dc017e787e6d4e3d49416141a39937325a3129d Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 14 Aug 2026 17:10:54 +0900 Subject: [PATCH 10/50] fix(package): detect CommonJS apply authority --- scripts/javascript-runtime-authority.mjs | 56 +++++++++++++++++++----- 1 file changed, 46 insertions(+), 10 deletions(-) diff --git a/scripts/javascript-runtime-authority.mjs b/scripts/javascript-runtime-authority.mjs index 5405dac7..484086b5 100644 --- a/scripts/javascript-runtime-authority.mjs +++ b/scripts/javascript-runtime-authority.mjs @@ -114,6 +114,24 @@ export function findRuntimeModuleAuthority(source, filename = 'bundle.js') { ); } + /** + * Return the first statically written `.apply()` payload argument. + * Non-array, computed, missing, and spread argument lists still identify + * executable authority but deliberately yield an unknown module specifier. + */ + function commonJsApplyArgument(node) { + const argumentList = node.arguments[1]; + if (!argumentList) { + return undefined; + } + const current = unwrapParentheses(argumentList); + if (!ts.isArrayLiteralExpression(current)) { + return undefined; + } + const first = current.elements[0]; + return first && !ts.isSpreadElement(first) ? first : undefined; + } + /** * Return the package argument for one recognizable CommonJS resolver call. * Arbitrary object methods named `resolve` remain outside this authority model. @@ -125,12 +143,21 @@ export function findRuntimeModuleAuthority(source, filename = 'bundle.js') { const callee = unwrapParentheses(node.expression); if ( - (ts.isPropertyAccessExpression(callee) || - ts.isElementAccessExpression(callee)) && - staticMemberName(callee) === 'call' && - isCommonJsResolverExpression(callee.expression) + ts.isPropertyAccessExpression(callee) || + ts.isElementAccessExpression(callee) ) { - return { argument: node.arguments[1] }; + const invocationMethod = staticMemberName(callee); + if ( + (invocationMethod === 'call' || invocationMethod === 'apply') && + isCommonJsResolverExpression(callee.expression) + ) { + return { + argument: + invocationMethod === 'call' + ? node.arguments[1] + : commonJsApplyArgument(node), + }; + } } return null; } @@ -146,12 +173,21 @@ export function findRuntimeModuleAuthority(source, filename = 'bundle.js') { const callee = unwrapParentheses(node.expression); if ( - (ts.isPropertyAccessExpression(callee) || - ts.isElementAccessExpression(callee)) && - staticMemberName(callee) === 'call' && - isCommonJsLoaderExpression(callee.expression) + ts.isPropertyAccessExpression(callee) || + ts.isElementAccessExpression(callee) ) { - return { argument: node.arguments[1] }; + const invocationMethod = staticMemberName(callee); + if ( + (invocationMethod === 'call' || invocationMethod === 'apply') && + isCommonJsLoaderExpression(callee.expression) + ) { + return { + argument: + invocationMethod === 'call' + ? node.arguments[1] + : commonJsApplyArgument(node), + }; + } } return null; } From b5a18634b2ee88a7798d82d228cdf4f88cf04ef7 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 14 Aug 2026 18:30:23 +0900 Subject: [PATCH 11/50] test(supply-chain): detect Reflect.apply CommonJS authority --- scripts/javascript-runtime-authority.test.mjs | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/scripts/javascript-runtime-authority.test.mjs b/scripts/javascript-runtime-authority.test.mjs index eb1342a6..b4118de7 100644 --- a/scripts/javascript-runtime-authority.test.mjs +++ b/scripts/javascript-runtime-authority.test.mjs @@ -168,6 +168,33 @@ test('reports statically recognizable CommonJS apply invocations', () => { ); }); +test('reports CommonJS authority invoked through Reflect.apply', () => { + const source = [ + "const required = Reflect.apply(require, undefined, ['reflect-package']);", + "const moduleRequired = Reflect['apply'](module.require, module, ['reflect-module-package']);", + 'const mainRequired = Reflect.apply(require.main.require, require.main, [runtimePackageName]);', + "const resolved = Reflect.apply(require.resolve, require, ['reflect-resolve-package']);", + 'const computedResolved = Reflect.apply(require[\'resolve\'], require, resolverArguments);', + 'const object = { require() {}, resolve() {} };', + "const benignRequire = Reflect.apply(object.require, object, ['not-commonjs']);", + "const benignResolve = Reflect.apply(object.resolve, object, ['not-commonjs-resolve']);", + 'void [required, moduleRequired, mainRequired, resolved, computedResolved, benignRequire, benignResolve];', + ].join('\n'); + + assert.deepEqual( + findRuntimeModuleAuthority(source, 'reflect-apply-authority.js').map( + ({ kind, specifier }) => ({ kind, specifier }), + ), + [ + { kind: 'commonjs-require', specifier: 'reflect-package' }, + { kind: 'commonjs-require', specifier: 'reflect-module-package' }, + { kind: 'commonjs-require', specifier: undefined }, + { kind: 'commonjs-resolve', specifier: 'reflect-resolve-package' }, + { kind: 'commonjs-resolve', specifier: undefined }, + ], + ); +}); + test('fails closed when emitted JavaScript is syntactically invalid', () => { assert.throws( () => findRuntimeModuleAuthority('const = ;', 'broken.js'), From ff3459f48a5884e4894d742a949336d6b3ebce36 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 14 Aug 2026 18:33:45 +0900 Subject: [PATCH 12/50] fix(supply-chain): detect Reflect.apply CommonJS authority --- scripts/javascript-runtime-authority.mjs | 37 ++++++++++++++++++++++-- 1 file changed, 34 insertions(+), 3 deletions(-) diff --git a/scripts/javascript-runtime-authority.mjs b/scripts/javascript-runtime-authority.mjs index 484086b5..8529dcc2 100644 --- a/scripts/javascript-runtime-authority.mjs +++ b/scripts/javascript-runtime-authority.mjs @@ -114,13 +114,30 @@ export function findRuntimeModuleAuthority(source, filename = 'bundle.js') { ); } + /** Identify the built-in `Reflect.apply` without resolving aliases or receivers. */ + function isReflectApplyExpression(expression) { + const current = unwrapParentheses(expression); + if ( + !ts.isPropertyAccessExpression(current) && + !ts.isElementAccessExpression(current) + ) { + return false; + } + const receiver = unwrapParentheses(current.expression); + return ( + staticMemberName(current) === 'apply' && + ts.isIdentifier(receiver) && + receiver.text === 'Reflect' + ); + } + /** - * Return the first statically written `.apply()` payload argument. + * Return the first statically written apply payload argument. * Non-array, computed, missing, and spread argument lists still identify * executable authority but deliberately yield an unknown module specifier. */ - function commonJsApplyArgument(node) { - const argumentList = node.arguments[1]; + function commonJsApplyArgument(node, argumentListIndex = 1) { + const argumentList = node.arguments[argumentListIndex]; if (!argumentList) { return undefined; } @@ -141,6 +158,13 @@ export function findRuntimeModuleAuthority(source, filename = 'bundle.js') { return { argument: node.arguments[0] }; } + if (isReflectApplyExpression(node.expression)) { + const target = node.arguments[0]; + if (target && isCommonJsResolverExpression(target)) { + return { argument: commonJsApplyArgument(node, 2) }; + } + } + const callee = unwrapParentheses(node.expression); if ( ts.isPropertyAccessExpression(callee) || @@ -171,6 +195,13 @@ export function findRuntimeModuleAuthority(source, filename = 'bundle.js') { return { argument: node.arguments[0] }; } + if (isReflectApplyExpression(node.expression)) { + const target = node.arguments[0]; + if (target && isCommonJsLoaderExpression(target)) { + return { argument: commonJsApplyArgument(node, 2) }; + } + } + const callee = unwrapParentheses(node.expression); if ( ts.isPropertyAccessExpression(callee) || From f63836d2a37a7c9616a9ec16a9ddae0a5d9d158c Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 14 Aug 2026 19:05:08 +0900 Subject: [PATCH 13/50] test(supply-chain): expose bound CommonJS authority --- src/javascriptRuntimeAuthorityBind.test.mjs | 31 +++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 src/javascriptRuntimeAuthorityBind.test.mjs diff --git a/src/javascriptRuntimeAuthorityBind.test.mjs b/src/javascriptRuntimeAuthorityBind.test.mjs new file mode 100644 index 00000000..d5aa106d --- /dev/null +++ b/src/javascriptRuntimeAuthorityBind.test.mjs @@ -0,0 +1,31 @@ +import { describe, expect, it } from 'vitest'; + +import { findRuntimeModuleAuthority } from '../scripts/javascript-runtime-authority.mjs'; + +describe('packed JavaScript CommonJS bind authority', () => { + it('reports statically recognizable bound CommonJS loader and resolver invocations', () => { + const source = [ + "const required = require.bind(undefined)('bind-package');", + "const prebound = module.require.bind(module, 'prebound-module-package')();", + 'const mainRequired = require.main.require.bind(require.main)(runtimePackageName);', + "const resolved = require.resolve.bind(require)('bind-resolve-package');", + 'const computedResolved = require[\'resolve\'][\'bind\'](require)(runtimePackageName);', + 'const object = { require() {}, resolve() {} };', + "const benignRequire = object.require.bind(object)('not-commonjs');", + "const benignResolve = object.resolve.bind(object)('not-commonjs-resolve');", + 'void [required, prebound, mainRequired, resolved, computedResolved, benignRequire, benignResolve];', + ].join('\n'); + + expect( + findRuntimeModuleAuthority(source, 'bind-authority.js').map( + ({ kind, specifier }) => ({ kind, specifier }), + ), + ).toEqual([ + { kind: 'commonjs-require', specifier: 'bind-package' }, + { kind: 'commonjs-require', specifier: 'prebound-module-package' }, + { kind: 'commonjs-require', specifier: undefined }, + { kind: 'commonjs-resolve', specifier: 'bind-resolve-package' }, + { kind: 'commonjs-resolve', specifier: undefined }, + ]); + }); +}); From a415a014b5ab793d44d3c2dd73ebb7d285d116c4 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 14 Aug 2026 19:07:58 +0900 Subject: [PATCH 14/50] test(supply-chain): exercise bound CommonJS authority in CI --- src/javascriptRuntimeAuthorityBind.test.ts | 46 ++++++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 src/javascriptRuntimeAuthorityBind.test.ts diff --git a/src/javascriptRuntimeAuthorityBind.test.ts b/src/javascriptRuntimeAuthorityBind.test.ts new file mode 100644 index 00000000..36d3c441 --- /dev/null +++ b/src/javascriptRuntimeAuthorityBind.test.ts @@ -0,0 +1,46 @@ +import { execFileSync } from 'node:child_process'; +import { resolve } from 'node:path'; +import { pathToFileURL } from 'node:url'; + +import { describe, expect, it } from 'vitest'; + +function scanWithRepositoryAuthority(source: string): unknown { + const scannerUrl = pathToFileURL( + resolve(process.cwd(), 'scripts/javascript-runtime-authority.mjs'), + ).href; + const program = [ + `import { findRuntimeModuleAuthority } from ${JSON.stringify(scannerUrl)};`, + `const source = ${JSON.stringify(source)};`, + "const findings = findRuntimeModuleAuthority(source, 'bind-authority.js').map(({ kind, specifier }) => ({ kind, specifier }));", + 'process.stdout.write(JSON.stringify(findings));', + ].join('\n'); + return JSON.parse( + execFileSync(process.execPath, ['--input-type=module', '--eval', program], { + encoding: 'utf8', + }), + ) as unknown; +} + +describe('packed JavaScript CommonJS bind authority', () => { + it('reports statically recognizable bound CommonJS loader and resolver invocations', () => { + const source = [ + "const required = require.bind(undefined)('bind-package');", + "const prebound = module.require.bind(module, 'prebound-module-package')();", + 'const mainRequired = require.main.require.bind(require.main)(runtimePackageName);', + "const resolved = require.resolve.bind(require)('bind-resolve-package');", + "const computedResolved = require['resolve']['bind'](require)(runtimePackageName);", + 'const object = { require() {}, resolve() {} };', + "const benignRequire = object.require.bind(object)('not-commonjs');", + "const benignResolve = object.resolve.bind(object)('not-commonjs-resolve');", + 'void [required, prebound, mainRequired, resolved, computedResolved, benignRequire, benignResolve];', + ].join('\n'); + + expect(scanWithRepositoryAuthority(source)).toEqual([ + { kind: 'commonjs-require', specifier: 'bind-package' }, + { kind: 'commonjs-require', specifier: 'prebound-module-package' }, + { kind: 'commonjs-require', specifier: undefined }, + { kind: 'commonjs-resolve', specifier: 'bind-resolve-package' }, + { kind: 'commonjs-resolve', specifier: undefined }, + ]); + }); +}); From 586acff015b6e25e6f256e4becd796d4d58155af Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 14 Aug 2026 19:11:34 +0900 Subject: [PATCH 15/50] test(supply-chain): remove unused bind harness --- src/javascriptRuntimeAuthorityBind.test.mjs | 31 --------------------- 1 file changed, 31 deletions(-) delete mode 100644 src/javascriptRuntimeAuthorityBind.test.mjs diff --git a/src/javascriptRuntimeAuthorityBind.test.mjs b/src/javascriptRuntimeAuthorityBind.test.mjs deleted file mode 100644 index d5aa106d..00000000 --- a/src/javascriptRuntimeAuthorityBind.test.mjs +++ /dev/null @@ -1,31 +0,0 @@ -import { describe, expect, it } from 'vitest'; - -import { findRuntimeModuleAuthority } from '../scripts/javascript-runtime-authority.mjs'; - -describe('packed JavaScript CommonJS bind authority', () => { - it('reports statically recognizable bound CommonJS loader and resolver invocations', () => { - const source = [ - "const required = require.bind(undefined)('bind-package');", - "const prebound = module.require.bind(module, 'prebound-module-package')();", - 'const mainRequired = require.main.require.bind(require.main)(runtimePackageName);', - "const resolved = require.resolve.bind(require)('bind-resolve-package');", - 'const computedResolved = require[\'resolve\'][\'bind\'](require)(runtimePackageName);', - 'const object = { require() {}, resolve() {} };', - "const benignRequire = object.require.bind(object)('not-commonjs');", - "const benignResolve = object.resolve.bind(object)('not-commonjs-resolve');", - 'void [required, prebound, mainRequired, resolved, computedResolved, benignRequire, benignResolve];', - ].join('\n'); - - expect( - findRuntimeModuleAuthority(source, 'bind-authority.js').map( - ({ kind, specifier }) => ({ kind, specifier }), - ), - ).toEqual([ - { kind: 'commonjs-require', specifier: 'bind-package' }, - { kind: 'commonjs-require', specifier: 'prebound-module-package' }, - { kind: 'commonjs-require', specifier: undefined }, - { kind: 'commonjs-resolve', specifier: 'bind-resolve-package' }, - { kind: 'commonjs-resolve', specifier: undefined }, - ]); - }); -}); From 3b2535422feb814a35e4a196f96ac2094d73c29d Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 14 Aug 2026 19:12:40 +0900 Subject: [PATCH 16/50] fix(supply-chain): detect bound CommonJS authority --- scripts/javascript-runtime-authority.mjs | 44 ++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/scripts/javascript-runtime-authority.mjs b/scripts/javascript-runtime-authority.mjs index 8529dcc2..8b7ff4a4 100644 --- a/scripts/javascript-runtime-authority.mjs +++ b/scripts/javascript-runtime-authority.mjs @@ -149,6 +149,34 @@ export function findRuntimeModuleAuthority(source, filename = 'bundle.js') { return first && !ts.isSpreadElement(first) ? first : undefined; } + /** + * Recognize an immediately invoked statically written `.bind` call whose + * receiver is already known module authority. The bound receiver is never + * evaluated. A package argument bound after `thisArg` takes precedence over + * the first argument supplied to the resulting function. + */ + function commonJsBoundInvocation(node, authorityPredicate) { + const bindCall = unwrapParentheses(node.expression); + if (!ts.isCallExpression(bindCall)) { + return null; + } + const bindCallee = unwrapParentheses(bindCall.expression); + if ( + (!ts.isPropertyAccessExpression(bindCallee) && + !ts.isElementAccessExpression(bindCallee)) || + staticMemberName(bindCallee) !== 'bind' || + !authorityPredicate(bindCallee.expression) + ) { + return null; + } + return { + argument: + bindCall.arguments.length > 1 + ? bindCall.arguments[1] + : node.arguments[0], + }; + } + /** * Return the package argument for one recognizable CommonJS resolver call. * Arbitrary object methods named `resolve` remain outside this authority model. @@ -158,6 +186,14 @@ export function findRuntimeModuleAuthority(source, filename = 'bundle.js') { return { argument: node.arguments[0] }; } + const boundInvocation = commonJsBoundInvocation( + node, + isCommonJsResolverExpression, + ); + if (boundInvocation) { + return boundInvocation; + } + if (isReflectApplyExpression(node.expression)) { const target = node.arguments[0]; if (target && isCommonJsResolverExpression(target)) { @@ -195,6 +231,14 @@ export function findRuntimeModuleAuthority(source, filename = 'bundle.js') { return { argument: node.arguments[0] }; } + const boundInvocation = commonJsBoundInvocation( + node, + isCommonJsLoaderExpression, + ); + if (boundInvocation) { + return boundInvocation; + } + if (isReflectApplyExpression(node.expression)) { const target = node.arguments[0]; if (target && isCommonJsLoaderExpression(target)) { From b7be216922681c33765a18696b36da2998880faa Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 15 Aug 2026 20:04:11 +0900 Subject: [PATCH 17/50] test(supply-chain): cover composed bound CommonJS authority --- scripts/javascript-runtime-authority.test.mjs | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/scripts/javascript-runtime-authority.test.mjs b/scripts/javascript-runtime-authority.test.mjs index b4118de7..7c205540 100644 --- a/scripts/javascript-runtime-authority.test.mjs +++ b/scripts/javascript-runtime-authority.test.mjs @@ -195,6 +195,33 @@ test('reports CommonJS authority invoked through Reflect.apply', () => { ); }); +test('reports bound CommonJS authority composed through call, apply, and Reflect.apply', () => { + const source = [ + "const called = require.bind(undefined, 'bound-call-package').call(undefined);", + "const applied = module.require.bind(module).apply(module, ['bound-apply-package']);", + "const reflected = Reflect.apply(require.main.require.bind(require.main, 'reflect-bound-package'), require.main, []);", + "const resolved = require.resolve.bind(require, 'bound-resolve-package').call(undefined);", + "const reflectedResolved = Reflect.apply(require['resolve'].bind(require), require, [runtimePackageName]);", + 'const object = { require() {}, resolve() {} };', + "const benignCalled = object.require.bind(object, 'not-commonjs').call(object);", + "const benignReflected = Reflect.apply(object.resolve.bind(object, 'not-commonjs-resolve'), object, []);", + 'void [called, applied, reflected, resolved, reflectedResolved, benignCalled, benignReflected];', + ].join('\n'); + + assert.deepEqual( + findRuntimeModuleAuthority(source, 'bound-composition-authority.js').map( + ({ kind, specifier }) => ({ kind, specifier }), + ), + [ + { kind: 'commonjs-require', specifier: 'bound-call-package' }, + { kind: 'commonjs-require', specifier: 'bound-apply-package' }, + { kind: 'commonjs-require', specifier: 'reflect-bound-package' }, + { kind: 'commonjs-resolve', specifier: 'bound-resolve-package' }, + { kind: 'commonjs-resolve', specifier: undefined }, + ], + ); +}); + test('fails closed when emitted JavaScript is syntactically invalid', () => { assert.throws( () => findRuntimeModuleAuthority('const = ;', 'broken.js'), From da16556a0e4d85d2ca8998f8b4527f58c1d7bc78 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 15 Aug 2026 20:08:37 +0900 Subject: [PATCH 18/50] fix(supply-chain): detect composed bound CommonJS authority --- scripts/javascript-runtime-authority.mjs | 131 +++++++++++++++++------ 1 file changed, 101 insertions(+), 30 deletions(-) diff --git a/scripts/javascript-runtime-authority.mjs b/scripts/javascript-runtime-authority.mjs index 8b7ff4a4..41c307cb 100644 --- a/scripts/javascript-runtime-authority.mjs +++ b/scripts/javascript-runtime-authority.mjs @@ -150,13 +150,11 @@ export function findRuntimeModuleAuthority(source, filename = 'bundle.js') { } /** - * Recognize an immediately invoked statically written `.bind` call whose - * receiver is already known module authority. The bound receiver is never - * evaluated. A package argument bound after `thisArg` takes precedence over - * the first argument supplied to the resulting function. + * Recognize a statically written `.bind` expression whose receiver is already + * known module authority. The bound receiver and arguments are never evaluated. */ - function commonJsBoundInvocation(node, authorityPredicate) { - const bindCall = unwrapParentheses(node.expression); + function commonJsBoundExpression(expression, authorityPredicate) { + const bindCall = unwrapParentheses(expression); if (!ts.isCallExpression(bindCall)) { return null; } @@ -170,13 +168,38 @@ export function findRuntimeModuleAuthority(source, filename = 'bundle.js') { return null; } return { - argument: - bindCall.arguments.length > 1 - ? bindCall.arguments[1] - : node.arguments[0], + hasArgument: bindCall.arguments.length > 1, + argument: bindCall.arguments[1], }; } + /** Preserve an explicitly bound package argument even when it is computed. */ + function commonJsBoundArgument(boundExpression, fallbackArgument) { + return boundExpression.hasArgument + ? boundExpression.argument + : fallbackArgument; + } + + /** + * Recognize an immediately invoked statically written `.bind` call whose + * receiver is already known module authority. A package argument bound after + * `thisArg` takes precedence over the first invocation argument. + */ + function commonJsBoundInvocation(node, authorityPredicate) { + const boundExpression = commonJsBoundExpression( + node.expression, + authorityPredicate, + ); + return boundExpression + ? { + argument: commonJsBoundArgument( + boundExpression, + node.arguments[0], + ), + } + : null; + } + /** * Return the package argument for one recognizable CommonJS resolver call. * Arbitrary object methods named `resolve` remain outside this authority model. @@ -199,6 +222,20 @@ export function findRuntimeModuleAuthority(source, filename = 'bundle.js') { if (target && isCommonJsResolverExpression(target)) { return { argument: commonJsApplyArgument(node, 2) }; } + if (target) { + const boundTarget = commonJsBoundExpression( + target, + isCommonJsResolverExpression, + ); + if (boundTarget) { + return { + argument: commonJsBoundArgument( + boundTarget, + commonJsApplyArgument(node, 2), + ), + }; + } + } } const callee = unwrapParentheses(node.expression); @@ -207,16 +244,26 @@ export function findRuntimeModuleAuthority(source, filename = 'bundle.js') { ts.isElementAccessExpression(callee) ) { const invocationMethod = staticMemberName(callee); - if ( - (invocationMethod === 'call' || invocationMethod === 'apply') && - isCommonJsResolverExpression(callee.expression) - ) { - return { - argument: - invocationMethod === 'call' - ? node.arguments[1] - : commonJsApplyArgument(node), - }; + if (invocationMethod === 'call' || invocationMethod === 'apply') { + const fallbackArgument = + invocationMethod === 'call' + ? node.arguments[1] + : commonJsApplyArgument(node); + if (isCommonJsResolverExpression(callee.expression)) { + return { argument: fallbackArgument }; + } + const boundReceiver = commonJsBoundExpression( + callee.expression, + isCommonJsResolverExpression, + ); + if (boundReceiver) { + return { + argument: commonJsBoundArgument( + boundReceiver, + fallbackArgument, + ), + }; + } } } return null; @@ -244,6 +291,20 @@ export function findRuntimeModuleAuthority(source, filename = 'bundle.js') { if (target && isCommonJsLoaderExpression(target)) { return { argument: commonJsApplyArgument(node, 2) }; } + if (target) { + const boundTarget = commonJsBoundExpression( + target, + isCommonJsLoaderExpression, + ); + if (boundTarget) { + return { + argument: commonJsBoundArgument( + boundTarget, + commonJsApplyArgument(node, 2), + ), + }; + } + } } const callee = unwrapParentheses(node.expression); @@ -252,16 +313,26 @@ export function findRuntimeModuleAuthority(source, filename = 'bundle.js') { ts.isElementAccessExpression(callee) ) { const invocationMethod = staticMemberName(callee); - if ( - (invocationMethod === 'call' || invocationMethod === 'apply') && - isCommonJsLoaderExpression(callee.expression) - ) { - return { - argument: - invocationMethod === 'call' - ? node.arguments[1] - : commonJsApplyArgument(node), - }; + if (invocationMethod === 'call' || invocationMethod === 'apply') { + const fallbackArgument = + invocationMethod === 'call' + ? node.arguments[1] + : commonJsApplyArgument(node); + if (isCommonJsLoaderExpression(callee.expression)) { + return { argument: fallbackArgument }; + } + const boundReceiver = commonJsBoundExpression( + callee.expression, + isCommonJsLoaderExpression, + ); + if (boundReceiver) { + return { + argument: commonJsBoundArgument( + boundReceiver, + fallbackArgument, + ), + }; + } } } return null; From 8f907dbb91da22ce47e39bf9346b8e494fb65db9 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 15 Aug 2026 20:12:50 +0900 Subject: [PATCH 19/50] docs(doctoring): ground CommonJS authority verification --- docs/doctoring/commonjs-runtime-authority.md | 31 ++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 docs/doctoring/commonjs-runtime-authority.md diff --git a/docs/doctoring/commonjs-runtime-authority.md b/docs/doctoring/commonjs-runtime-authority.md new file mode 100644 index 00000000..eea95074 --- /dev/null +++ b/docs/doctoring/commonjs-runtime-authority.md @@ -0,0 +1,31 @@ +# CommonJS Runtime-Authority Verification + +Status: active-PR evidence for PR #290. This note does not describe protected-main shipped behavior until the PR is merged. + +## Decision boundary + +Inkspan release verification treats executable module lookup or loading authority left in a packed JavaScript artifact as a packaging failure. The verifier parses emitted JavaScript with the TypeScript AST and classifies only statically recognizable syntax. It does not execute artifact code, resolve arbitrary aliases, evaluate receiver expressions, or infer computed member names. + +The current active-PR scanner covers static imports/re-exports, dynamic `import(...)`, direct and statically indirect CommonJS `require` forms, `module.require`, `require.main.require`, `require.resolve`, `.call`, `.apply`, `Reflect.apply`, and statically composed `.bind` invocations when the bound target is already a recognized CommonJS loader or resolver. Literal package arguments are retained as actionable evidence; computed arguments remain unknown rather than being guessed. Ordinary object methods merely named `require` or `resolve` remain outside the authority model. + +This is a release-evidence boundary, not a runtime loader. It adds no filesystem, network, credential, persistence, deployment, model, or host authority to Inkspan. + +## Standards basis + +Node.js documents `require()` as the CommonJS module loader, `require.main` as the entry-module reference for CommonJS entry points, and `require.resolve()` as using the internal `require()` resolution machinery without loading the resolved module. These semantics make both loading and resolution relevant executable authority in an artifact expected to be self-contained. + +ECMA-262 defines `Function.prototype.bind` as producing a bound function whose target and leading arguments are retained for subsequent calls. It also defines `Reflect.apply(target, thisArgument, argumentsList)` as invoking a callable target with an argument list. Therefore, a verifier that recognizes only direct `require(...)` or `require.resolve(...)` calls can miss equivalent authority when those same callable values are invoked through standard `bind`, `call`/`apply`, or `Reflect.apply` composition. + +The verifier intentionally remains syntax-bounded. ECMAScript permits arbitrary aliasing and computation; attempting whole-program resolution in this release check would enlarge the trusted implementation and risk unsound guesses. Unknown computed package arguments are therefore reported as executable authority without an invented specifier. + +## Assurance implications + +The regression suite must include positive cases for each supported syntax family and negative controls for ordinary objects with similarly named methods. Invalid emitted JavaScript fails closed. Any extension to the recognized syntax must be test-first and must preserve the no-execution/no-alias-evaluation invariant. + +Release acceptance must use the exact packed artifact produced from the exact candidate head. Passing source tests on a predecessor, a different checkout SHA, or a status-only/model-only signal is not evidence that the packed artifact is free of runtime module authority. + +## References + +Ecma International. (2026). *ECMA-262: ECMAScript® 2026 language specification* (17th ed.). https://262.ecma-international.org/ + +Node.js contributors. (2026). *Modules: CommonJS modules* (Node.js v26.5.1 documentation). Node.js. https://nodejs.org/api/modules.html From 7cbf98b0882300d420156587cd44c2d55e599626 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sun, 16 Aug 2026 07:03:34 +0900 Subject: [PATCH 20/50] test(supply-chain): expose constructible CommonJS loader bypass --- ...avascriptRuntimeAuthorityConstruct.test.ts | 56 +++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 src/javascriptRuntimeAuthorityConstruct.test.ts diff --git a/src/javascriptRuntimeAuthorityConstruct.test.ts b/src/javascriptRuntimeAuthorityConstruct.test.ts new file mode 100644 index 00000000..b37f27aa --- /dev/null +++ b/src/javascriptRuntimeAuthorityConstruct.test.ts @@ -0,0 +1,56 @@ +import { execFileSync } from 'node:child_process'; +import { resolve } from 'node:path'; +import { pathToFileURL } from 'node:url'; + +import { describe, expect, it } from 'vitest'; + +function scanWithRepositoryAuthority(source: string): unknown { + const scannerUrl = pathToFileURL( + resolve(process.cwd(), 'scripts/javascript-runtime-authority.mjs'), + ).href; + const program = [ + `import { findRuntimeModuleAuthority } from ${JSON.stringify(scannerUrl)};`, + `const source = ${JSON.stringify(source)};`, + "const findings = findRuntimeModuleAuthority(source, 'construct-authority.js').map(({ kind, specifier }) => ({ kind, specifier }));", + 'process.stdout.write(JSON.stringify(findings));', + ].join('\n'); + return JSON.parse( + execFileSync(process.execPath, ['--input-type=module', '--eval', program], { + encoding: 'utf8', + }), + ) as unknown; +} + +describe('packed JavaScript CommonJS constructor authority', () => { + it('reports statically recognizable CommonJS loaders invoked through new', () => { + const source = [ + "const direct = new require('construct-package');", + "const parenthesized = new (require)('parenthesized-construct-package');", + "const comma = new (0, require)('comma-construct-package');", + 'const computed = new require(runtimePackageName);', + 'const object = { require() { return {}; } };', + "const benign = new object.require('not-commonjs');", + 'void [direct, parenthesized, comma, computed, benign];', + ].join('\n'); + + expect(scanWithRepositoryAuthority(source)).toEqual([ + { kind: 'commonjs-require', specifier: 'construct-package' }, + { kind: 'commonjs-require', specifier: 'parenthesized-construct-package' }, + { kind: 'commonjs-require', specifier: 'comma-construct-package' }, + { kind: 'commonjs-require', specifier: undefined }, + ]); + }); + + it('proves the supported Node CommonJS require function can load through new', () => { + const result = execFileSync( + process.execPath, + [ + '--eval', + "const loaded = new require('node:path'); process.stdout.write(String(loaded === require('node:path')));", + ], + { encoding: 'utf8' }, + ); + + expect(result).toBe('true'); + }); +}); From 30475e4e76f13a0787600be1b59c4ac8f8ca0d9a Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sun, 16 Aug 2026 07:08:35 +0900 Subject: [PATCH 21/50] fix(supply-chain): detect constructed CommonJS authority --- scripts/javascript-runtime-authority.mjs | 47 ++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/scripts/javascript-runtime-authority.mjs b/scripts/javascript-runtime-authority.mjs index 41c307cb..0a7c6d8b 100644 --- a/scripts/javascript-runtime-authority.mjs +++ b/scripts/javascript-runtime-authority.mjs @@ -200,6 +200,29 @@ export function findRuntimeModuleAuthority(source, filename = 'bundle.js') { : null; } + /** + * Recognize statically written constructor use of known CommonJS authority. + * The constructor target and package argument are inspected syntactically only; + * aliases, arbitrary receivers, and computed property names remain unresolved. + */ + function commonJsConstructorInvocation(node, authorityPredicate) { + if (authorityPredicate(node.expression)) { + return { argument: node.arguments?.[0] }; + } + const boundExpression = commonJsBoundExpression( + node.expression, + authorityPredicate, + ); + return boundExpression + ? { + argument: commonJsBoundArgument( + boundExpression, + node.arguments?.[0], + ), + } + : null; + } + /** * Return the package argument for one recognizable CommonJS resolver call. * Arbitrary object methods named `resolve` remain outside this authority model. @@ -352,6 +375,30 @@ export function findRuntimeModuleAuthority(source, filename = 'bundle.js') { offset: node.getStart(sourceFile), specifier: literalSpecifier(node.moduleSpecifier), }); + } else if (ts.isNewExpression(node)) { + const resolverInvocation = commonJsConstructorInvocation( + node, + isCommonJsResolverExpression, + ); + if (resolverInvocation) { + findings.push({ + kind: 'commonjs-resolve', + offset: node.getStart(sourceFile), + specifier: literalSpecifier(resolverInvocation.argument), + }); + } else { + const invocation = commonJsConstructorInvocation( + node, + isCommonJsLoaderExpression, + ); + if (invocation) { + findings.push({ + kind: 'commonjs-require', + offset: node.getStart(sourceFile), + specifier: literalSpecifier(invocation.argument), + }); + } + } } else if (ts.isCallExpression(node)) { if (node.expression.kind === ts.SyntaxKind.ImportKeyword) { findings.push({ From a1025eaefa67c9ddc1616dc0426db122b0342c53 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sun, 16 Aug 2026 07:15:12 +0900 Subject: [PATCH 22/50] test(supply-chain): expose Reflect.construct loader bypass --- ...ptRuntimeAuthorityReflectConstruct.test.ts | 55 +++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 src/javascriptRuntimeAuthorityReflectConstruct.test.ts diff --git a/src/javascriptRuntimeAuthorityReflectConstruct.test.ts b/src/javascriptRuntimeAuthorityReflectConstruct.test.ts new file mode 100644 index 00000000..63fb07f4 --- /dev/null +++ b/src/javascriptRuntimeAuthorityReflectConstruct.test.ts @@ -0,0 +1,55 @@ +import { execFileSync } from 'node:child_process'; +import { resolve } from 'node:path'; +import { pathToFileURL } from 'node:url'; + +import { describe, expect, it } from 'vitest'; + +function scanWithRepositoryAuthority(source: string): unknown { + const scannerUrl = pathToFileURL( + resolve(process.cwd(), 'scripts/javascript-runtime-authority.mjs'), + ).href; + const program = [ + `import { findRuntimeModuleAuthority } from ${JSON.stringify(scannerUrl)};`, + `const source = ${JSON.stringify(source)};`, + "const findings = findRuntimeModuleAuthority(source, 'reflect-construct-authority.js').map(({ kind, specifier }) => ({ kind, specifier }));", + 'process.stdout.write(JSON.stringify(findings));', + ].join('\n'); + return JSON.parse( + execFileSync(process.execPath, ['--input-type=module', '--eval', program], { + encoding: 'utf8', + }), + ) as unknown; +} + +describe('packed JavaScript Reflect.construct CommonJS authority', () => { + it('reports statically recognizable CommonJS loaders and resolvers used as constructors', () => { + const source = [ + "Reflect.construct(require, ['reflect-package']);", + "Reflect['construct'](require.resolve, ['reflect-resolve-package']);", + 'Reflect.construct((0, require), [runtimePackageName]);', + "Reflect.construct(require.bind(undefined, 'bound-reflect-package'), []);", + 'const object = { construct() { return {}; } };', + "object.construct(require, ['not-reflect']);", + ].join('\n'); + + expect(scanWithRepositoryAuthority(source)).toEqual([ + { kind: 'commonjs-require', specifier: 'reflect-package' }, + { kind: 'commonjs-resolve', specifier: 'reflect-resolve-package' }, + { kind: 'commonjs-require', specifier: undefined }, + { kind: 'commonjs-require', specifier: 'bound-reflect-package' }, + ]); + }); + + it('proves the supported Node CommonJS require function is constructible through Reflect.construct', () => { + const result = execFileSync( + process.execPath, + [ + '--eval', + "const loaded = Reflect.construct(require, ['node:path']); process.stdout.write(String(loaded === require('node:path')));", + ], + { encoding: 'utf8' }, + ); + + expect(result).toBe('true'); + }); +}); From f2fab24999e1502720e2a9535dae1cf85a37f9ed Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sun, 16 Aug 2026 07:20:13 +0900 Subject: [PATCH 23/50] fix(supply-chain): detect Reflect.construct module authority --- scripts/javascript-runtime-authority.mjs | 59 ++++++++++++++++++++++++ 1 file changed, 59 insertions(+) diff --git a/scripts/javascript-runtime-authority.mjs b/scripts/javascript-runtime-authority.mjs index 0a7c6d8b..2596c827 100644 --- a/scripts/javascript-runtime-authority.mjs +++ b/scripts/javascript-runtime-authority.mjs @@ -131,6 +131,23 @@ export function findRuntimeModuleAuthority(source, filename = 'bundle.js') { ); } + /** Identify the built-in `Reflect.construct` without resolving aliases. */ + function isReflectConstructExpression(expression) { + const current = unwrapParentheses(expression); + if ( + !ts.isPropertyAccessExpression(current) && + !ts.isElementAccessExpression(current) + ) { + return false; + } + const receiver = unwrapParentheses(current.expression); + return ( + staticMemberName(current) === 'construct' && + ts.isIdentifier(receiver) && + receiver.text === 'Reflect' + ); + } + /** * Return the first statically written apply payload argument. * Non-array, computed, missing, and spread argument lists still identify @@ -223,6 +240,32 @@ export function findRuntimeModuleAuthority(source, filename = 'bundle.js') { : null; } + /** + * Recognize a `Reflect.construct` call whose constructor target is already + * known module authority. The argument-list array is inspected statically only. + */ + function commonJsReflectConstructInvocation(node, authorityPredicate) { + if (!isReflectConstructExpression(node.expression)) { + return null; + } + const target = node.arguments[0]; + if (target && authorityPredicate(target)) { + return { argument: commonJsApplyArgument(node, 1) }; + } + if (!target) { + return null; + } + const boundTarget = commonJsBoundExpression(target, authorityPredicate); + return boundTarget + ? { + argument: commonJsBoundArgument( + boundTarget, + commonJsApplyArgument(node, 1), + ), + } + : null; + } + /** * Return the package argument for one recognizable CommonJS resolver call. * Arbitrary object methods named `resolve` remain outside this authority model. @@ -240,6 +283,14 @@ export function findRuntimeModuleAuthority(source, filename = 'bundle.js') { return boundInvocation; } + const reflectConstructInvocation = commonJsReflectConstructInvocation( + node, + isCommonJsResolverExpression, + ); + if (reflectConstructInvocation) { + return reflectConstructInvocation; + } + if (isReflectApplyExpression(node.expression)) { const target = node.arguments[0]; if (target && isCommonJsResolverExpression(target)) { @@ -309,6 +360,14 @@ export function findRuntimeModuleAuthority(source, filename = 'bundle.js') { return boundInvocation; } + const reflectConstructInvocation = commonJsReflectConstructInvocation( + node, + isCommonJsLoaderExpression, + ); + if (reflectConstructInvocation) { + return reflectConstructInvocation; + } + if (isReflectApplyExpression(node.expression)) { const target = node.arguments[0]; if (target && isCommonJsLoaderExpression(target)) { From 17897e08447eef91d8f6eef03fb09bcc2aa035cb Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 18 Aug 2026 09:50:13 -0700 Subject: [PATCH 24/50] test(docs): require constructor authority doctoring --- src/javascriptRuntimeAuthorityConstruct.test.ts | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/javascriptRuntimeAuthorityConstruct.test.ts b/src/javascriptRuntimeAuthorityConstruct.test.ts index b37f27aa..c468bebf 100644 --- a/src/javascriptRuntimeAuthorityConstruct.test.ts +++ b/src/javascriptRuntimeAuthorityConstruct.test.ts @@ -1,4 +1,5 @@ import { execFileSync } from 'node:child_process'; +import { readFileSync } from 'node:fs'; import { resolve } from 'node:path'; import { pathToFileURL } from 'node:url'; @@ -53,4 +54,15 @@ describe('packed JavaScript CommonJS constructor authority', () => { expect(result).toBe('true'); }); + + it('keeps doctoring aligned with constructor and Reflect.construct authority', () => { + const doctoring = readFileSync( + resolve(process.cwd(), 'docs/doctoring/commonjs-runtime-authority.md'), + 'utf8', + ); + + expect(doctoring).toContain('constructor invocation'); + expect(doctoring).toContain('Reflect.construct'); + expect(doctoring).toContain('no-execution/no-alias-evaluation invariant'); + }); }); From 73d34d0ca9288777d533b32a790b7ad55b95eee3 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 18 Aug 2026 09:51:14 -0700 Subject: [PATCH 25/50] docs(supply-chain): align constructor authority doctoring --- docs/doctoring/commonjs-runtime-authority.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/doctoring/commonjs-runtime-authority.md b/docs/doctoring/commonjs-runtime-authority.md index eea95074..e6966f1a 100644 --- a/docs/doctoring/commonjs-runtime-authority.md +++ b/docs/doctoring/commonjs-runtime-authority.md @@ -6,7 +6,7 @@ Status: active-PR evidence for PR #290. This note does not describe protected-ma Inkspan release verification treats executable module lookup or loading authority left in a packed JavaScript artifact as a packaging failure. The verifier parses emitted JavaScript with the TypeScript AST and classifies only statically recognizable syntax. It does not execute artifact code, resolve arbitrary aliases, evaluate receiver expressions, or infer computed member names. -The current active-PR scanner covers static imports/re-exports, dynamic `import(...)`, direct and statically indirect CommonJS `require` forms, `module.require`, `require.main.require`, `require.resolve`, `.call`, `.apply`, `Reflect.apply`, and statically composed `.bind` invocations when the bound target is already a recognized CommonJS loader or resolver. Literal package arguments are retained as actionable evidence; computed arguments remain unknown rather than being guessed. Ordinary object methods merely named `require` or `resolve` remain outside the authority model. +The current active-PR scanner covers static imports/re-exports, dynamic `import(...)`, direct and statically indirect CommonJS `require` forms, `module.require`, `require.main.require`, `require.resolve`, `.call`, `.apply`, `Reflect.apply`, statically composed `.bind` invocations, constructor invocation through `new`, and `Reflect.construct` when the target is already a recognized CommonJS loader or resolver. Literal package arguments are retained as actionable evidence; computed arguments remain unknown rather than being guessed. Ordinary object methods merely named `require` or `resolve` remain outside the authority model. This is a release-evidence boundary, not a runtime loader. It adds no filesystem, network, credential, persistence, deployment, model, or host authority to Inkspan. @@ -14,7 +14,7 @@ This is a release-evidence boundary, not a runtime loader. It adds no filesystem Node.js documents `require()` as the CommonJS module loader, `require.main` as the entry-module reference for CommonJS entry points, and `require.resolve()` as using the internal `require()` resolution machinery without loading the resolved module. These semantics make both loading and resolution relevant executable authority in an artifact expected to be self-contained. -ECMA-262 defines `Function.prototype.bind` as producing a bound function whose target and leading arguments are retained for subsequent calls. It also defines `Reflect.apply(target, thisArgument, argumentsList)` as invoking a callable target with an argument list. Therefore, a verifier that recognizes only direct `require(...)` or `require.resolve(...)` calls can miss equivalent authority when those same callable values are invoked through standard `bind`, `call`/`apply`, or `Reflect.apply` composition. +ECMA-262 defines `Function.prototype.bind` as producing a bound function whose target and leading arguments are retained for subsequent calls. It defines `Reflect.apply(target, thisArgument, argumentsList)` as invoking a callable target with an argument list, ordinary `new` expressions as constructor invocation, and `Reflect.construct(target, argumentsList, newTarget)` as construction through an explicitly supplied target. Therefore, a verifier that recognizes only direct `require(...)` or `require.resolve(...)` calls can miss equivalent authority when those same callable values are invoked through standard `bind`, `call`/`apply`, `Reflect.apply`, constructor invocation, or `Reflect.construct` composition. The verifier intentionally remains syntax-bounded. ECMAScript permits arbitrary aliasing and computation; attempting whole-program resolution in this release check would enlarge the trusted implementation and risk unsound guesses. Unknown computed package arguments are therefore reported as executable authority without an invented specifier. From 46c06a22dd32ddc9502f2f20e1b7032bb1050a8f Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 18 Aug 2026 11:10:43 -0700 Subject: [PATCH 26/50] test(supply-chain): reject retained bound CommonJS authority --- ...scriptRuntimeAuthorityBindCreation.test.ts | 47 +++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 src/javascriptRuntimeAuthorityBindCreation.test.ts diff --git a/src/javascriptRuntimeAuthorityBindCreation.test.ts b/src/javascriptRuntimeAuthorityBindCreation.test.ts new file mode 100644 index 00000000..f3ecc089 --- /dev/null +++ b/src/javascriptRuntimeAuthorityBindCreation.test.ts @@ -0,0 +1,47 @@ +import { execFileSync } from 'node:child_process'; +import { resolve } from 'node:path'; +import { pathToFileURL } from 'node:url'; + +import { describe, expect, it } from 'vitest'; + +/** Run the repository runtime-authority scanner against one emitted JS fixture. */ +function scanWithRepositoryAuthority(source: string): unknown { + const scannerUrl = pathToFileURL( + resolve(process.cwd(), 'scripts/javascript-runtime-authority.mjs'), + ).href; + const program = [ + `import { findRuntimeModuleAuthority } from ${JSON.stringify(scannerUrl)};`, + `const source = ${JSON.stringify(source)};`, + "const findings = findRuntimeModuleAuthority(source, 'bound-capability.js').map(({ kind, specifier }) => ({ kind, specifier }));", + 'process.stdout.write(JSON.stringify(findings));', + ].join('\n'); + return JSON.parse( + execFileSync(process.execPath, ['--input-type=module', '--eval', program], { + encoding: 'utf8', + }), + ) as unknown; +} + +describe('packed JavaScript retained CommonJS bind authority', () => { + it('reports directly recognizable bound loader and resolver capabilities before invocation', () => { + const source = [ + "const direct = require.bind(undefined, 'bound-package');", + "const resolver = require.resolve.bind(require, 'bound-resolve-package');", + 'const computed = module.require.bind(module, runtimePackageName);', + "const main = require.main.require.bind(require.main, 'main-bound-package');", + 'const late = require.bind(undefined);', + 'const object = { require() {}, resolve() {} };', + "const benignLoader = object.require.bind(object, 'not-commonjs');", + "const benignResolver = object.resolve.bind(object, 'not-commonjs-resolve');", + 'void [direct, resolver, computed, main, late, benignLoader, benignResolver];', + ].join('\n'); + + expect(scanWithRepositoryAuthority(source)).toEqual([ + { kind: 'commonjs-require', specifier: 'bound-package' }, + { kind: 'commonjs-resolve', specifier: 'bound-resolve-package' }, + { kind: 'commonjs-require', specifier: undefined }, + { kind: 'commonjs-require', specifier: 'main-bound-package' }, + { kind: 'commonjs-require', specifier: undefined }, + ]); + }); +}); From d2da97114020b78a934a81fae28d288c3b82cc27 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 18 Aug 2026 11:14:24 -0700 Subject: [PATCH 27/50] fix(supply-chain): detect retained bound CommonJS authority --- scripts/javascript-runtime-authority.mjs | 31 ++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/scripts/javascript-runtime-authority.mjs b/scripts/javascript-runtime-authority.mjs index 2596c827..4e16c4ec 100644 --- a/scripts/javascript-runtime-authority.mjs +++ b/scripts/javascript-runtime-authority.mjs @@ -34,6 +34,7 @@ export function findRuntimeModuleAuthority(source, filename = 'bundle.js') { } const findings = []; + const consumedBoundCalls = new WeakSet(); /** Return a literal module specifier without evaluating computed expressions. */ function literalSpecifier(expression) { @@ -185,6 +186,7 @@ export function findRuntimeModuleAuthority(source, filename = 'bundle.js') { return null; } return { + bindCall, hasArgument: bindCall.arguments.length > 1, argument: bindCall.arguments[1], }; @@ -192,6 +194,7 @@ export function findRuntimeModuleAuthority(source, filename = 'bundle.js') { /** Preserve an explicitly bound package argument even when it is computed. */ function commonJsBoundArgument(boundExpression, fallbackArgument) { + consumedBoundCalls.add(boundExpression.bindCall); return boundExpression.hasArgument ? boundExpression.argument : fallbackArgument; @@ -481,6 +484,34 @@ export function findRuntimeModuleAuthority(source, filename = 'bundle.js') { offset: node.getStart(sourceFile), specifier: literalSpecifier(invocation.argument), }); + } else if (!consumedBoundCalls.has(node)) { + const boundResolver = commonJsBoundExpression( + node, + isCommonJsResolverExpression, + ); + if (boundResolver) { + findings.push({ + kind: 'commonjs-resolve', + offset: node.getStart(sourceFile), + specifier: literalSpecifier( + boundResolver.hasArgument ? boundResolver.argument : undefined, + ), + }); + } else { + const boundLoader = commonJsBoundExpression( + node, + isCommonJsLoaderExpression, + ); + if (boundLoader) { + findings.push({ + kind: 'commonjs-require', + offset: node.getStart(sourceFile), + specifier: literalSpecifier( + boundLoader.hasArgument ? boundLoader.argument : undefined, + ), + }); + } + } } } } From 36d7b281d1d53ab5f76963c60e8b33f81d742a43 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 18 Aug 2026 11:18:28 -0700 Subject: [PATCH 28/50] docs(supply-chain): bind retained CommonJS authority contract --- docs/doctoring/commonjs-runtime-authority.md | 6 +++--- src/javascriptRuntimeAuthorityBindCreation.test.ts | 11 +++++++++++ 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/docs/doctoring/commonjs-runtime-authority.md b/docs/doctoring/commonjs-runtime-authority.md index e6966f1a..df987e0c 100644 --- a/docs/doctoring/commonjs-runtime-authority.md +++ b/docs/doctoring/commonjs-runtime-authority.md @@ -6,7 +6,7 @@ Status: active-PR evidence for PR #290. This note does not describe protected-ma Inkspan release verification treats executable module lookup or loading authority left in a packed JavaScript artifact as a packaging failure. The verifier parses emitted JavaScript with the TypeScript AST and classifies only statically recognizable syntax. It does not execute artifact code, resolve arbitrary aliases, evaluate receiver expressions, or infer computed member names. -The current active-PR scanner covers static imports/re-exports, dynamic `import(...)`, direct and statically indirect CommonJS `require` forms, `module.require`, `require.main.require`, `require.resolve`, `.call`, `.apply`, `Reflect.apply`, statically composed `.bind` invocations, constructor invocation through `new`, and `Reflect.construct` when the target is already a recognized CommonJS loader or resolver. Literal package arguments are retained as actionable evidence; computed arguments remain unknown rather than being guessed. Ordinary object methods merely named `require` or `resolve` remain outside the authority model. +The current active-PR scanner covers static imports/re-exports, dynamic `import(...)`, direct and statically indirect CommonJS `require` forms, `module.require`, `require.main.require`, `require.resolve`, `.call`, `.apply`, `Reflect.apply`, retained `.bind` capabilities, statically composed `.bind` invocations, constructor invocation through `new`, and `Reflect.construct` when the target is already a recognized CommonJS loader or resolver. A directly recognizable bound loader or resolver is therefore rejected when it is retained for later use, before later invocation occurs. Literal package arguments are retained as actionable evidence; computed or not-yet-supplied package arguments remain unknown rather than being guessed. Ordinary object methods merely named `require` or `resolve` remain outside the authority model. This is a release-evidence boundary, not a runtime loader. It adds no filesystem, network, credential, persistence, deployment, model, or host authority to Inkspan. @@ -14,13 +14,13 @@ This is a release-evidence boundary, not a runtime loader. It adds no filesystem Node.js documents `require()` as the CommonJS module loader, `require.main` as the entry-module reference for CommonJS entry points, and `require.resolve()` as using the internal `require()` resolution machinery without loading the resolved module. These semantics make both loading and resolution relevant executable authority in an artifact expected to be self-contained. -ECMA-262 defines `Function.prototype.bind` as producing a bound function whose target and leading arguments are retained for subsequent calls. It defines `Reflect.apply(target, thisArgument, argumentsList)` as invoking a callable target with an argument list, ordinary `new` expressions as constructor invocation, and `Reflect.construct(target, argumentsList, newTarget)` as construction through an explicitly supplied target. Therefore, a verifier that recognizes only direct `require(...)` or `require.resolve(...)` calls can miss equivalent authority when those same callable values are invoked through standard `bind`, `call`/`apply`, `Reflect.apply`, constructor invocation, or `Reflect.construct` composition. +ECMA-262 defines `Function.prototype.bind` as producing a bound function whose target and leading arguments are retained for subsequent calls. That retained callable is already executable loader/resolver authority before later invocation, so a packed artifact that stores a directly recognizable `require.bind(...)` or `require.resolve.bind(...)` capability has not eliminated the authority merely because the call happens later. ECMA-262 also defines `Reflect.apply(target, thisArgument, argumentsList)` as invoking a callable target with an argument list, ordinary `new` expressions as constructor invocation, and `Reflect.construct(target, argumentsList, newTarget)` as construction through an explicitly supplied target. Therefore, a verifier that recognizes only direct `require(...)` or `require.resolve(...)` calls can miss equivalent authority retained or invoked through standard `bind`, `call`/`apply`, `Reflect.apply`, constructor invocation, or `Reflect.construct` composition. The verifier intentionally remains syntax-bounded. ECMAScript permits arbitrary aliasing and computation; attempting whole-program resolution in this release check would enlarge the trusted implementation and risk unsound guesses. Unknown computed package arguments are therefore reported as executable authority without an invented specifier. ## Assurance implications -The regression suite must include positive cases for each supported syntax family and negative controls for ordinary objects with similarly named methods. Invalid emitted JavaScript fails closed. Any extension to the recognized syntax must be test-first and must preserve the no-execution/no-alias-evaluation invariant. +The regression suite must include positive cases for each supported syntax family, including retained bound loader/resolver capabilities before later invocation, and negative controls for ordinary objects with similarly named methods. Invalid emitted JavaScript fails closed. Any extension to the recognized syntax must be test-first and must preserve the no-execution/no-alias-evaluation invariant. Release acceptance must use the exact packed artifact produced from the exact candidate head. Passing source tests on a predecessor, a different checkout SHA, or a status-only/model-only signal is not evidence that the packed artifact is free of runtime module authority. diff --git a/src/javascriptRuntimeAuthorityBindCreation.test.ts b/src/javascriptRuntimeAuthorityBindCreation.test.ts index f3ecc089..a97af331 100644 --- a/src/javascriptRuntimeAuthorityBindCreation.test.ts +++ b/src/javascriptRuntimeAuthorityBindCreation.test.ts @@ -1,4 +1,5 @@ import { execFileSync } from 'node:child_process'; +import { readFileSync } from 'node:fs'; import { resolve } from 'node:path'; import { pathToFileURL } from 'node:url'; @@ -44,4 +45,14 @@ describe('packed JavaScript retained CommonJS bind authority', () => { { kind: 'commonjs-require', specifier: undefined }, ]); }); + + it('keeps doctoring aligned with retained bound capabilities', () => { + const doctoring = readFileSync( + resolve(process.cwd(), 'docs/doctoring/commonjs-runtime-authority.md'), + 'utf8', + ); + + expect(doctoring).toContain('retained `.bind` capabilities'); + expect(doctoring).toContain('before later invocation'); + }); }); From e8423ed705cab04ee7565d7f2179cdb55e8b5f33 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 18 Aug 2026 20:10:55 -0700 Subject: [PATCH 29/50] test(supply-chain): reject module.main runtime loaders --- ...vascriptRuntimeAuthorityModuleMain.test.ts | 41 +++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 src/javascriptRuntimeAuthorityModuleMain.test.ts diff --git a/src/javascriptRuntimeAuthorityModuleMain.test.ts b/src/javascriptRuntimeAuthorityModuleMain.test.ts new file mode 100644 index 00000000..aee7a93e --- /dev/null +++ b/src/javascriptRuntimeAuthorityModuleMain.test.ts @@ -0,0 +1,41 @@ +import { execFileSync } from 'node:child_process'; +import { resolve } from 'node:path'; +import { pathToFileURL } from 'node:url'; + +import { describe, expect, it } from 'vitest'; + +function scanWithRepositoryAuthority(source: string): unknown { + const scannerUrl = pathToFileURL( + resolve(process.cwd(), 'scripts/javascript-runtime-authority.mjs'), + ).href; + const program = [ + `import { findRuntimeModuleAuthority } from ${JSON.stringify(scannerUrl)};`, + `const source = ${JSON.stringify(source)};`, + "const findings = findRuntimeModuleAuthority(source, 'module-main-authority.js').map(({ kind, specifier }) => ({ kind, specifier }));", + 'process.stdout.write(JSON.stringify(findings));', + ].join('\n'); + return JSON.parse( + execFileSync(process.execPath, ['--input-type=module', '--eval', program], { + encoding: 'utf8', + }), + ) as unknown; +} + +describe('packed JavaScript module.main CommonJS authority', () => { + it('reports statically recognizable module.main loaders without promoting arbitrary object members', () => { + const source = [ + "const direct = module.main.require('module-main-package');", + "const element = module['main']['require']('module-main-element-package');", + "const called = module.main.require.call(module.main, 'module-main-call-package');", + 'const object = { main: { require() {} } };', + "const benign = object.main.require('not-commonjs');", + 'void [direct, element, called, benign];', + ].join('\n'); + + expect(scanWithRepositoryAuthority(source)).toEqual([ + { kind: 'commonjs-require', specifier: 'module-main-package' }, + { kind: 'commonjs-require', specifier: 'module-main-element-package' }, + { kind: 'commonjs-require', specifier: 'module-main-call-package' }, + ]); + }); +}); From 1fd873026741b30c89aa7561ac8c98e7981620b6 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 18 Aug 2026 20:12:37 -0700 Subject: [PATCH 30/50] fix(supply-chain): detect module.main loaders --- scripts/javascript-runtime-authority.mjs | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/scripts/javascript-runtime-authority.mjs b/scripts/javascript-runtime-authority.mjs index 4e16c4ec..af74f86e 100644 --- a/scripts/javascript-runtime-authority.mjs +++ b/scripts/javascript-runtime-authority.mjs @@ -88,12 +88,17 @@ export function findRuntimeModuleAuthority(source, filename = 'bundle.js') { if (ts.isIdentifier(receiver) && receiver.text === 'module') { return true; } - return ( + if ( (ts.isPropertyAccessExpression(receiver) || ts.isElementAccessExpression(receiver)) && - staticMemberName(receiver) === 'main' && - isCommonJsLoaderExpression(receiver.expression) - ); + staticMemberName(receiver) === 'main' + ) { + const mainReceiver = unwrapParentheses(receiver.expression); + return ( + (ts.isIdentifier(mainReceiver) && mainReceiver.text === 'module') || + isCommonJsLoaderExpression(mainReceiver) + ); + } } return false; } From 8bb513a7f62afd1c7a21c286d7d4505c91d9777c Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 18 Aug 2026 20:17:29 -0700 Subject: [PATCH 31/50] revert(supply-chain): keep module.main outside CommonJS authority --- scripts/javascript-runtime-authority.mjs | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/scripts/javascript-runtime-authority.mjs b/scripts/javascript-runtime-authority.mjs index af74f86e..4e16c4ec 100644 --- a/scripts/javascript-runtime-authority.mjs +++ b/scripts/javascript-runtime-authority.mjs @@ -88,17 +88,12 @@ export function findRuntimeModuleAuthority(source, filename = 'bundle.js') { if (ts.isIdentifier(receiver) && receiver.text === 'module') { return true; } - if ( + return ( (ts.isPropertyAccessExpression(receiver) || ts.isElementAccessExpression(receiver)) && - staticMemberName(receiver) === 'main' - ) { - const mainReceiver = unwrapParentheses(receiver.expression); - return ( - (ts.isIdentifier(mainReceiver) && mainReceiver.text === 'module') || - isCommonJsLoaderExpression(mainReceiver) - ); - } + staticMemberName(receiver) === 'main' && + isCommonJsLoaderExpression(receiver.expression) + ); } return false; } From 781104fc65fdbacbb83d530c40e7374a3bc4b9c0 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 18 Aug 2026 20:18:17 -0700 Subject: [PATCH 32/50] test(supply-chain): keep Node main authority syntax-bounded --- ...vascriptRuntimeAuthorityModuleMain.test.ts | 21 ++++++++++--------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/src/javascriptRuntimeAuthorityModuleMain.test.ts b/src/javascriptRuntimeAuthorityModuleMain.test.ts index aee7a93e..9663c725 100644 --- a/src/javascriptRuntimeAuthorityModuleMain.test.ts +++ b/src/javascriptRuntimeAuthorityModuleMain.test.ts @@ -11,7 +11,7 @@ function scanWithRepositoryAuthority(source: string): unknown { const program = [ `import { findRuntimeModuleAuthority } from ${JSON.stringify(scannerUrl)};`, `const source = ${JSON.stringify(source)};`, - "const findings = findRuntimeModuleAuthority(source, 'module-main-authority.js').map(({ kind, specifier }) => ({ kind, specifier }));", + "const findings = findRuntimeModuleAuthority(source, 'main-module-authority.js').map(({ kind, specifier }) => ({ kind, specifier }));", 'process.stdout.write(JSON.stringify(findings));', ].join('\n'); return JSON.parse( @@ -21,21 +21,22 @@ function scanWithRepositoryAuthority(source: string): unknown { ) as unknown; } -describe('packed JavaScript module.main CommonJS authority', () => { - it('reports statically recognizable module.main loaders without promoting arbitrary object members', () => { +describe('packed JavaScript CommonJS main-module authority', () => { + it('recognizes require.main loaders without inventing a module.main authority surface', () => { const source = [ - "const direct = module.main.require('module-main-package');", - "const element = module['main']['require']('module-main-element-package');", - "const called = module.main.require.call(module.main, 'module-main-call-package');", + "const direct = require.main.require('require-main-package');", + "const element = require['main']['require']('require-main-element-package');", + "const called = require.main.require.call(require.main, 'require-main-call-package');", + "const nonexistentNodeSurface = module.main.require('not-node-commonjs-authority');", 'const object = { main: { require() {} } };', "const benign = object.main.require('not-commonjs');", - 'void [direct, element, called, benign];', + 'void [direct, element, called, nonexistentNodeSurface, benign];', ].join('\n'); expect(scanWithRepositoryAuthority(source)).toEqual([ - { kind: 'commonjs-require', specifier: 'module-main-package' }, - { kind: 'commonjs-require', specifier: 'module-main-element-package' }, - { kind: 'commonjs-require', specifier: 'module-main-call-package' }, + { kind: 'commonjs-require', specifier: 'require-main-package' }, + { kind: 'commonjs-require', specifier: 'require-main-element-package' }, + { kind: 'commonjs-require', specifier: 'require-main-call-package' }, ]); }); }); From 9c7282317e60ba3ca5144fa6557117b82b3a015e Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Wed, 19 Aug 2026 00:11:44 -0700 Subject: [PATCH 33/50] test(supply-chain): detect Node builtin module authority --- ...scriptRuntimeAuthorityNodeBuiltin.test.mjs | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 src/javascriptRuntimeAuthorityNodeBuiltin.test.mjs diff --git a/src/javascriptRuntimeAuthorityNodeBuiltin.test.mjs b/src/javascriptRuntimeAuthorityNodeBuiltin.test.mjs new file mode 100644 index 00000000..1590231b --- /dev/null +++ b/src/javascriptRuntimeAuthorityNodeBuiltin.test.mjs @@ -0,0 +1,26 @@ +import { describe, expect, it } from 'vitest'; + +import { findRuntimeModuleAuthority } from '../scripts/javascript-runtime-authority.mjs'; + +describe('Node built-in runtime module authority', () => { + it('reports statically recognizable process.getBuiltinModule calls', () => { + const source = [ + "const fs = process.getBuiltinModule('node:fs');", + "const path = globalThis.process.getBuiltinModule('node:path');", + 'const unknown = process.getBuiltinModule(runtimeBuiltinName);', + 'const object = { getBuiltinModule() { return "method-only"; } };', + 'const benign = object.getBuiltinModule("node:crypto");', + 'void [fs, path, unknown, benign];', + ].join('\n'); + + expect( + findRuntimeModuleAuthority(source, 'node-builtins.js').map( + ({ kind, specifier }) => ({ kind, specifier }), + ), + ).toEqual([ + { kind: 'node-builtin-module', specifier: 'node:fs' }, + { kind: 'node-builtin-module', specifier: 'node:path' }, + { kind: 'node-builtin-module', specifier: undefined }, + ]); + }); +}); From ddea3ec9eaee335540f845974472385c2d035a7a Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Wed, 19 Aug 2026 00:15:05 -0700 Subject: [PATCH 34/50] fix(supply-chain): detect Node builtin module loads --- scripts/javascript-runtime-authority.mjs | 41 +++++++++++++++++++++--- 1 file changed, 37 insertions(+), 4 deletions(-) diff --git a/scripts/javascript-runtime-authority.mjs b/scripts/javascript-runtime-authority.mjs index 4e16c4ec..bb13a532 100644 --- a/scripts/javascript-runtime-authority.mjs +++ b/scripts/javascript-runtime-authority.mjs @@ -6,10 +6,11 @@ import ts from 'typescript'; * Parsing the artifact instead of scanning raw text deliberately ignores comments, * string literals, and template text that merely mention `require()` or `import()`. * Actual static imports/re-exports, statically recognizable CommonJS loader and - * resolver calls, and dynamic `import()` calls remain fail-closed findings. - * Literal module specifiers are preserved in diagnostics so a verifier failure - * identifies the dependency edge that escaped bundling; computed specifiers remain - * `undefined` and therefore do not acquire an invented interpretation. + * resolver calls, Node built-in module loads, and dynamic `import()` calls remain + * fail-closed findings. Literal module specifiers are preserved in diagnostics so a + * verifier failure identifies the dependency edge that escaped bundling; computed + * specifiers remain `undefined` and therefore do not acquire an invented + * interpretation. * * @param {string} source JavaScript source emitted into the packed artifact. * @param {string} [filename='bundle.js'] Diagnostic filename for parse failures. @@ -67,6 +68,32 @@ export function findRuntimeModuleAuthority(source, filename = 'bundle.js') { return undefined; } + /** Identify Node's ambient synchronous built-in module loader syntax. */ + function isNodeBuiltinModuleExpression(expression) { + const current = unwrapParentheses(expression); + if ( + (!ts.isPropertyAccessExpression(current) && + !ts.isElementAccessExpression(current)) || + staticMemberName(current) !== 'getBuiltinModule' + ) { + return false; + } + + const receiver = unwrapParentheses(current.expression); + if (ts.isIdentifier(receiver) && receiver.text === 'process') { + return true; + } + if ( + (ts.isPropertyAccessExpression(receiver) || + ts.isElementAccessExpression(receiver)) && + staticMemberName(receiver) === 'process' + ) { + const root = unwrapParentheses(receiver.expression); + return ts.isIdentifier(root) && root.text === 'globalThis'; + } + return false; + } + /** Identify direct CommonJS loader values without resolving aliases or scope. */ function isCommonJsLoaderExpression(expression) { const current = unwrapParentheses(expression); @@ -468,6 +495,12 @@ export function findRuntimeModuleAuthority(source, filename = 'bundle.js') { offset: node.getStart(sourceFile), specifier: literalSpecifier(node.arguments[0]), }); + } else if (isNodeBuiltinModuleExpression(node.expression)) { + findings.push({ + kind: 'node-builtin-module', + offset: node.getStart(sourceFile), + specifier: literalSpecifier(node.arguments[0]), + }); } else { const resolverInvocation = commonJsResolverInvocation(node); if (resolverInvocation) { From f118bcf27b2592edef2f9a2d63731f1fe667065d Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Wed, 19 Aug 2026 00:15:50 -0700 Subject: [PATCH 35/50] docs(supply-chain): document Node builtin authority --- docs/doctoring/commonjs-runtime-authority.md | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/docs/doctoring/commonjs-runtime-authority.md b/docs/doctoring/commonjs-runtime-authority.md index df987e0c..3c1ebd4c 100644 --- a/docs/doctoring/commonjs-runtime-authority.md +++ b/docs/doctoring/commonjs-runtime-authority.md @@ -6,7 +6,9 @@ Status: active-PR evidence for PR #290. This note does not describe protected-ma Inkspan release verification treats executable module lookup or loading authority left in a packed JavaScript artifact as a packaging failure. The verifier parses emitted JavaScript with the TypeScript AST and classifies only statically recognizable syntax. It does not execute artifact code, resolve arbitrary aliases, evaluate receiver expressions, or infer computed member names. -The current active-PR scanner covers static imports/re-exports, dynamic `import(...)`, direct and statically indirect CommonJS `require` forms, `module.require`, `require.main.require`, `require.resolve`, `.call`, `.apply`, `Reflect.apply`, retained `.bind` capabilities, statically composed `.bind` invocations, constructor invocation through `new`, and `Reflect.construct` when the target is already a recognized CommonJS loader or resolver. A directly recognizable bound loader or resolver is therefore rejected when it is retained for later use, before later invocation occurs. Literal package arguments are retained as actionable evidence; computed or not-yet-supplied package arguments remain unknown rather than being guessed. Ordinary object methods merely named `require` or `resolve` remain outside the authority model. +The current active-PR scanner covers static imports/re-exports, dynamic `import(...)`, direct and statically indirect CommonJS `require` forms, `module.require`, `require.main.require`, `require.resolve`, `.call`, `.apply`, `Reflect.apply`, retained `.bind` capabilities, statically composed `.bind` invocations, constructor invocation through `new`, `Reflect.construct` when the target is already a recognized CommonJS loader or resolver, and direct Node `process.getBuiltinModule(...)` / `globalThis.process.getBuiltinModule(...)` calls. A directly recognizable bound CommonJS loader or resolver is therefore rejected when it is retained for later use, before later invocation occurs. Literal module specifiers are retained as actionable evidence; computed or missing specifiers remain unknown rather than being guessed. Ordinary object methods merely named `require`, `resolve`, or `getBuiltinModule` remain outside the authority model. + +The `process.getBuiltinModule(...)` classification is intentionally narrow: it recognizes only the ambient `process` spelling and the explicit `globalThis.process` spelling documented by Node.js. It does not resolve aliases, arbitrary receivers, or computed member names, and it does not infer that a similarly named method on another object has Node module authority. This is a release-evidence boundary, not a runtime loader. It adds no filesystem, network, credential, persistence, deployment, model, or host authority to Inkspan. @@ -14,13 +16,15 @@ This is a release-evidence boundary, not a runtime loader. It adds no filesystem Node.js documents `require()` as the CommonJS module loader, `require.main` as the entry-module reference for CommonJS entry points, and `require.resolve()` as using the internal `require()` resolution machinery without loading the resolved module. These semantics make both loading and resolution relevant executable authority in an artifact expected to be self-contained. +Node.js also documents `process.getBuiltinModule(id)` as a globally available synchronous way to load a Node built-in module, including the explicit `globalThis.process.getBuiltinModule(...)` form for environment-conditional access. Because the packed Markdown artifact is required to be self-contained and free of executable runtime module authority, leaving that syntax in the artifact is a packaging failure even though the target is a Node built-in rather than an external package. + ECMA-262 defines `Function.prototype.bind` as producing a bound function whose target and leading arguments are retained for subsequent calls. That retained callable is already executable loader/resolver authority before later invocation, so a packed artifact that stores a directly recognizable `require.bind(...)` or `require.resolve.bind(...)` capability has not eliminated the authority merely because the call happens later. ECMA-262 also defines `Reflect.apply(target, thisArgument, argumentsList)` as invoking a callable target with an argument list, ordinary `new` expressions as constructor invocation, and `Reflect.construct(target, argumentsList, newTarget)` as construction through an explicitly supplied target. Therefore, a verifier that recognizes only direct `require(...)` or `require.resolve(...)` calls can miss equivalent authority retained or invoked through standard `bind`, `call`/`apply`, `Reflect.apply`, constructor invocation, or `Reflect.construct` composition. -The verifier intentionally remains syntax-bounded. ECMAScript permits arbitrary aliasing and computation; attempting whole-program resolution in this release check would enlarge the trusted implementation and risk unsound guesses. Unknown computed package arguments are therefore reported as executable authority without an invented specifier. +The verifier intentionally remains syntax-bounded. ECMAScript permits arbitrary aliasing and computation; attempting whole-program resolution in this release check would enlarge the trusted implementation and risk unsound guesses. Unknown computed module arguments are therefore reported as executable authority without an invented specifier when the authority surface itself is statically recognizable. ## Assurance implications -The regression suite must include positive cases for each supported syntax family, including retained bound loader/resolver capabilities before later invocation, and negative controls for ordinary objects with similarly named methods. Invalid emitted JavaScript fails closed. Any extension to the recognized syntax must be test-first and must preserve the no-execution/no-alias-evaluation invariant. +The regression suite must include positive cases for each supported syntax family, including retained bound CommonJS loader/resolver capabilities before later invocation and Node built-in loading through both documented ambient process spellings, plus negative controls for ordinary objects with similarly named methods. Invalid emitted JavaScript fails closed. Any extension to the recognized syntax must be test-first and must preserve the no-execution/no-alias-evaluation invariant. Release acceptance must use the exact packed artifact produced from the exact candidate head. Passing source tests on a predecessor, a different checkout SHA, or a status-only/model-only signal is not evidence that the packed artifact is free of runtime module authority. @@ -29,3 +33,5 @@ Release acceptance must use the exact packed artifact produced from the exact ca Ecma International. (2026). *ECMA-262: ECMAScript® 2026 language specification* (17th ed.). https://262.ecma-international.org/ Node.js contributors. (2026). *Modules: CommonJS modules* (Node.js v26.5.1 documentation). Node.js. https://nodejs.org/api/modules.html + +Node.js contributors. (2026). *Process* (Node.js v26.7.0 documentation). Node.js. https://nodejs.org/api/process.html From 51d7bcf652431caf1572ae7a4169906f104f9b91 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Wed, 19 Aug 2026 10:06:58 -0700 Subject: [PATCH 36/50] test: expose indirect Node built-in loader authority --- ...scriptRuntimeAuthorityNodeBuiltin.test.mjs | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/javascriptRuntimeAuthorityNodeBuiltin.test.mjs b/src/javascriptRuntimeAuthorityNodeBuiltin.test.mjs index 1590231b..f0501129 100644 --- a/src/javascriptRuntimeAuthorityNodeBuiltin.test.mjs +++ b/src/javascriptRuntimeAuthorityNodeBuiltin.test.mjs @@ -23,4 +23,23 @@ describe('Node built-in runtime module authority', () => { { kind: 'node-builtin-module', specifier: undefined }, ]); }); + + it('reports direct Function.prototype.call invocation of the ambient loader', () => { + const source = [ + "const fs = process.getBuiltinModule.call(undefined, 'node:fs');", + 'const path = globalThis.process.getBuiltinModule.call(null, runtimeBuiltinName);', + 'const object = { getBuiltinModule() { return "method-only"; } };', + 'const benign = object.getBuiltinModule.call(null, "node:crypto");', + 'void [fs, path, benign];', + ].join('\n'); + + expect( + findRuntimeModuleAuthority(source, 'node-builtins-call.js').map( + ({ kind, specifier }) => ({ kind, specifier }), + ), + ).toEqual([ + { kind: 'node-builtin-module', specifier: 'node:fs' }, + { kind: 'node-builtin-module', specifier: undefined }, + ]); + }); }); From 2b2f2a1f62c3b2581557b10fd1d16d198c2a439d Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Wed, 19 Aug 2026 10:09:16 -0700 Subject: [PATCH 37/50] fix(supply-chain): detect call-invoked Node built-in loader --- scripts/javascript-runtime-authority.mjs | 81 +++++++++++++++--------- 1 file changed, 51 insertions(+), 30 deletions(-) diff --git a/scripts/javascript-runtime-authority.mjs b/scripts/javascript-runtime-authority.mjs index bb13a532..9bce744d 100644 --- a/scripts/javascript-runtime-authority.mjs +++ b/scripts/javascript-runtime-authority.mjs @@ -94,6 +94,24 @@ export function findRuntimeModuleAuthority(source, filename = 'bundle.js') { return false; } + /** Return the module argument for direct or `.call` ambient built-in loading. */ + function nodeBuiltinModuleInvocation(node) { + if (isNodeBuiltinModuleExpression(node.expression)) { + return { argument: node.arguments[0] }; + } + + const callee = unwrapParentheses(node.expression); + if ( + (ts.isPropertyAccessExpression(callee) || + ts.isElementAccessExpression(callee)) && + staticMemberName(callee) === 'call' && + isNodeBuiltinModuleExpression(callee.expression) + ) { + return { argument: node.arguments[1] }; + } + return null; + } + /** Identify direct CommonJS loader values without resolving aliases or scope. */ function isCommonJsLoaderExpression(expression) { const current = unwrapParentheses(expression); @@ -495,54 +513,57 @@ export function findRuntimeModuleAuthority(source, filename = 'bundle.js') { offset: node.getStart(sourceFile), specifier: literalSpecifier(node.arguments[0]), }); - } else if (isNodeBuiltinModuleExpression(node.expression)) { - findings.push({ - kind: 'node-builtin-module', - offset: node.getStart(sourceFile), - specifier: literalSpecifier(node.arguments[0]), - }); } else { - const resolverInvocation = commonJsResolverInvocation(node); - if (resolverInvocation) { + const nodeBuiltinInvocation = nodeBuiltinModuleInvocation(node); + if (nodeBuiltinInvocation) { findings.push({ - kind: 'commonjs-resolve', + kind: 'node-builtin-module', offset: node.getStart(sourceFile), - specifier: literalSpecifier(resolverInvocation.argument), + specifier: literalSpecifier(nodeBuiltinInvocation.argument), }); } else { - const invocation = commonJsInvocation(node); - if (invocation) { + const resolverInvocation = commonJsResolverInvocation(node); + if (resolverInvocation) { findings.push({ - kind: 'commonjs-require', + kind: 'commonjs-resolve', offset: node.getStart(sourceFile), - specifier: literalSpecifier(invocation.argument), + specifier: literalSpecifier(resolverInvocation.argument), }); - } else if (!consumedBoundCalls.has(node)) { - const boundResolver = commonJsBoundExpression( - node, - isCommonJsResolverExpression, - ); - if (boundResolver) { + } else { + const invocation = commonJsInvocation(node); + if (invocation) { findings.push({ - kind: 'commonjs-resolve', + kind: 'commonjs-require', offset: node.getStart(sourceFile), - specifier: literalSpecifier( - boundResolver.hasArgument ? boundResolver.argument : undefined, - ), + specifier: literalSpecifier(invocation.argument), }); - } else { - const boundLoader = commonJsBoundExpression( + } else if (!consumedBoundCalls.has(node)) { + const boundResolver = commonJsBoundExpression( node, - isCommonJsLoaderExpression, + isCommonJsResolverExpression, ); - if (boundLoader) { + if (boundResolver) { findings.push({ - kind: 'commonjs-require', + kind: 'commonjs-resolve', offset: node.getStart(sourceFile), specifier: literalSpecifier( - boundLoader.hasArgument ? boundLoader.argument : undefined, + boundResolver.hasArgument ? boundResolver.argument : undefined, ), }); + } else { + const boundLoader = commonJsBoundExpression( + node, + isCommonJsLoaderExpression, + ); + if (boundLoader) { + findings.push({ + kind: 'commonjs-require', + offset: node.getStart(sourceFile), + specifier: literalSpecifier( + boundLoader.hasArgument ? boundLoader.argument : undefined, + ), + }); + } } } } From 4304a57ff44c8a37a78bedacd6ecfbb77ab1f6e2 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Wed, 19 Aug 2026 10:09:58 -0700 Subject: [PATCH 38/50] docs: record call-invoked Node built-in authority --- docs/doctoring/commonjs-runtime-authority.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/doctoring/commonjs-runtime-authority.md b/docs/doctoring/commonjs-runtime-authority.md index 3c1ebd4c..52ef0ae3 100644 --- a/docs/doctoring/commonjs-runtime-authority.md +++ b/docs/doctoring/commonjs-runtime-authority.md @@ -6,9 +6,9 @@ Status: active-PR evidence for PR #290. This note does not describe protected-ma Inkspan release verification treats executable module lookup or loading authority left in a packed JavaScript artifact as a packaging failure. The verifier parses emitted JavaScript with the TypeScript AST and classifies only statically recognizable syntax. It does not execute artifact code, resolve arbitrary aliases, evaluate receiver expressions, or infer computed member names. -The current active-PR scanner covers static imports/re-exports, dynamic `import(...)`, direct and statically indirect CommonJS `require` forms, `module.require`, `require.main.require`, `require.resolve`, `.call`, `.apply`, `Reflect.apply`, retained `.bind` capabilities, statically composed `.bind` invocations, constructor invocation through `new`, `Reflect.construct` when the target is already a recognized CommonJS loader or resolver, and direct Node `process.getBuiltinModule(...)` / `globalThis.process.getBuiltinModule(...)` calls. A directly recognizable bound CommonJS loader or resolver is therefore rejected when it is retained for later use, before later invocation occurs. Literal module specifiers are retained as actionable evidence; computed or missing specifiers remain unknown rather than being guessed. Ordinary object methods merely named `require`, `resolve`, or `getBuiltinModule` remain outside the authority model. +The current active-PR scanner covers static imports/re-exports, dynamic `import(...)`, direct and statically indirect CommonJS `require` forms, `module.require`, `require.main.require`, `require.resolve`, `.call`, `.apply`, `Reflect.apply`, retained `.bind` capabilities, statically composed `.bind` invocations, constructor invocation through `new`, `Reflect.construct` when the target is already a recognized CommonJS loader or resolver, and Node `process.getBuiltinModule(...)` / `globalThis.process.getBuiltinModule(...)` calls including direct `Function.prototype.call` invocation of those exact ambient loader expressions. A directly recognizable bound CommonJS loader or resolver is therefore rejected when it is retained for later use, before later invocation occurs. Literal module specifiers are retained as actionable evidence; computed or missing specifiers remain unknown rather than being guessed. Ordinary object methods merely named `require`, `resolve`, or `getBuiltinModule` remain outside the authority model. -The `process.getBuiltinModule(...)` classification is intentionally narrow: it recognizes only the ambient `process` spelling and the explicit `globalThis.process` spelling documented by Node.js. It does not resolve aliases, arbitrary receivers, or computed member names, and it does not infer that a similarly named method on another object has Node module authority. +The `process.getBuiltinModule(...)` classification is intentionally narrow: it recognizes only the ambient `process` spelling and the explicit `globalThis.process` spelling documented by Node.js, plus `.call(...)` when its receiver is one of those exact loader expressions. It does not resolve aliases, arbitrary receivers, or computed member names, and it does not infer that a similarly named method on another object has Node module authority. This is a release-evidence boundary, not a runtime loader. It adds no filesystem, network, credential, persistence, deployment, model, or host authority to Inkspan. @@ -18,13 +18,13 @@ Node.js documents `require()` as the CommonJS module loader, `require.main` as t Node.js also documents `process.getBuiltinModule(id)` as a globally available synchronous way to load a Node built-in module, including the explicit `globalThis.process.getBuiltinModule(...)` form for environment-conditional access. Because the packed Markdown artifact is required to be self-contained and free of executable runtime module authority, leaving that syntax in the artifact is a packaging failure even though the target is a Node built-in rather than an external package. -ECMA-262 defines `Function.prototype.bind` as producing a bound function whose target and leading arguments are retained for subsequent calls. That retained callable is already executable loader/resolver authority before later invocation, so a packed artifact that stores a directly recognizable `require.bind(...)` or `require.resolve.bind(...)` capability has not eliminated the authority merely because the call happens later. ECMA-262 also defines `Reflect.apply(target, thisArgument, argumentsList)` as invoking a callable target with an argument list, ordinary `new` expressions as constructor invocation, and `Reflect.construct(target, argumentsList, newTarget)` as construction through an explicitly supplied target. Therefore, a verifier that recognizes only direct `require(...)` or `require.resolve(...)` calls can miss equivalent authority retained or invoked through standard `bind`, `call`/`apply`, `Reflect.apply`, constructor invocation, or `Reflect.construct` composition. +ECMA-262 defines `Function.prototype.bind` as producing a bound function whose target and leading arguments are retained for subsequent calls. That retained callable is already executable loader/resolver authority before later invocation, so a packed artifact that stores a directly recognizable `require.bind(...)` or `require.resolve.bind(...)` capability has not eliminated the authority merely because the call happens later. ECMA-262 also defines `Function.prototype.call` as invoking its callable receiver with an explicit `this` value and following arguments, `Reflect.apply(target, thisArgument, argumentsList)` as invoking a callable target with an argument list, ordinary `new` expressions as constructor invocation, and `Reflect.construct(target, argumentsList, newTarget)` as construction through an explicitly supplied target. Therefore, a verifier that recognizes only direct loader calls can miss equivalent statically recognizable authority invoked through standard `call`/`apply`/`Reflect.apply` or retained/composed through other standard invocation forms. The verifier intentionally remains syntax-bounded. ECMAScript permits arbitrary aliasing and computation; attempting whole-program resolution in this release check would enlarge the trusted implementation and risk unsound guesses. Unknown computed module arguments are therefore reported as executable authority without an invented specifier when the authority surface itself is statically recognizable. ## Assurance implications -The regression suite must include positive cases for each supported syntax family, including retained bound CommonJS loader/resolver capabilities before later invocation and Node built-in loading through both documented ambient process spellings, plus negative controls for ordinary objects with similarly named methods. Invalid emitted JavaScript fails closed. Any extension to the recognized syntax must be test-first and must preserve the no-execution/no-alias-evaluation invariant. +The regression suite must include positive cases for each supported syntax family, including retained bound CommonJS loader/resolver capabilities before later invocation and Node built-in loading through both documented ambient process spellings, direct calls, and exact-receiver `.call(...)` invocation, plus negative controls for ordinary objects with similarly named methods. Invalid emitted JavaScript fails closed. Any extension to the recognized syntax must be test-first and must preserve the no-execution/no-alias-evaluation invariant. Release acceptance must use the exact packed artifact produced from the exact candidate head. Passing source tests on a predecessor, a different checkout SHA, or a status-only/model-only signal is not evidence that the packed artifact is free of runtime module authority. From d7455ae81d895c0bce41547ea01057382c6ce7a9 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Wed, 19 Aug 2026 10:10:49 -0700 Subject: [PATCH 39/50] test: cover computed Node built-in call authority --- src/javascriptRuntimeAuthorityNodeBuiltin.test.mjs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/javascriptRuntimeAuthorityNodeBuiltin.test.mjs b/src/javascriptRuntimeAuthorityNodeBuiltin.test.mjs index f0501129..f943219f 100644 --- a/src/javascriptRuntimeAuthorityNodeBuiltin.test.mjs +++ b/src/javascriptRuntimeAuthorityNodeBuiltin.test.mjs @@ -28,9 +28,12 @@ describe('Node built-in runtime module authority', () => { const source = [ "const fs = process.getBuiltinModule.call(undefined, 'node:fs');", 'const path = globalThis.process.getBuiltinModule.call(null, runtimeBuiltinName);', + "const util = process['getBuiltinModule']['call'](undefined, 'node:util');", + "const os = globalThis['process']['getBuiltinModule'].call(null, 'node:os');", 'const object = { getBuiltinModule() { return "method-only"; } };', 'const benign = object.getBuiltinModule.call(null, "node:crypto");', - 'void [fs, path, benign];', + 'const benignComputed = object["getBuiltinModule"]["call"](null, "node:url");', + 'void [fs, path, util, os, benign, benignComputed];', ].join('\n'); expect( @@ -40,6 +43,8 @@ describe('Node built-in runtime module authority', () => { ).toEqual([ { kind: 'node-builtin-module', specifier: 'node:fs' }, { kind: 'node-builtin-module', specifier: undefined }, + { kind: 'node-builtin-module', specifier: 'node:util' }, + { kind: 'node-builtin-module', specifier: 'node:os' }, ]); }); }); From eb045c08650d527c64d053055c153ea04fff04de Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Wed, 19 Aug 2026 10:18:01 -0700 Subject: [PATCH 40/50] test: expose indirect Node built-in loader authority --- ...scriptRuntimeAuthorityNodeBuiltin.test.mjs | 42 ++++++++++++++++--- 1 file changed, 37 insertions(+), 5 deletions(-) diff --git a/src/javascriptRuntimeAuthorityNodeBuiltin.test.mjs b/src/javascriptRuntimeAuthorityNodeBuiltin.test.mjs index f943219f..53e21fce 100644 --- a/src/javascriptRuntimeAuthorityNodeBuiltin.test.mjs +++ b/src/javascriptRuntimeAuthorityNodeBuiltin.test.mjs @@ -24,20 +24,25 @@ describe('Node built-in runtime module authority', () => { ]); }); - it('reports direct Function.prototype.call invocation of the ambient loader', () => { + it('reports Function.prototype call/apply and Reflect.apply invocation of the ambient loader', () => { const source = [ "const fs = process.getBuiltinModule.call(undefined, 'node:fs');", 'const path = globalThis.process.getBuiltinModule.call(null, runtimeBuiltinName);', "const util = process['getBuiltinModule']['call'](undefined, 'node:util');", "const os = globalThis['process']['getBuiltinModule'].call(null, 'node:os');", + "const crypto = process.getBuiltinModule.apply(undefined, ['node:crypto']);", + 'const unknownApply = globalThis.process.getBuiltinModule.apply(null, [runtimeBuiltinName]);', + "const url = Reflect.apply(process.getBuiltinModule, undefined, ['node:url']);", + 'const unknownReflect = Reflect.apply(globalThis.process.getBuiltinModule, null, runtimeBuiltinArgs);', 'const object = { getBuiltinModule() { return "method-only"; } };', - 'const benign = object.getBuiltinModule.call(null, "node:crypto");', - 'const benignComputed = object["getBuiltinModule"]["call"](null, "node:url");', - 'void [fs, path, util, os, benign, benignComputed];', + 'const benignCall = object.getBuiltinModule.call(null, "node:buffer");', + 'const benignApply = object.getBuiltinModule.apply(null, ["node:assert"]);', + 'const benignReflect = Reflect.apply(object.getBuiltinModule, null, ["node:zlib"]);', + 'void [fs, path, util, os, crypto, unknownApply, url, unknownReflect, benignCall, benignApply, benignReflect];', ].join('\n'); expect( - findRuntimeModuleAuthority(source, 'node-builtins-call.js').map( + findRuntimeModuleAuthority(source, 'node-builtins-indirect.js').map( ({ kind, specifier }) => ({ kind, specifier }), ), ).toEqual([ @@ -45,6 +50,33 @@ describe('Node built-in runtime module authority', () => { { kind: 'node-builtin-module', specifier: undefined }, { kind: 'node-builtin-module', specifier: 'node:util' }, { kind: 'node-builtin-module', specifier: 'node:os' }, + { kind: 'node-builtin-module', specifier: 'node:crypto' }, + { kind: 'node-builtin-module', specifier: undefined }, + { kind: 'node-builtin-module', specifier: 'node:url' }, + { kind: 'node-builtin-module', specifier: undefined }, + ]); + }); + + it('reports retained and immediately invoked bound ambient loaders', () => { + const source = [ + "const fsLoader = process.getBuiltinModule.bind(undefined, 'node:fs');", + 'const unknownLoader = globalThis.process.getBuiltinModule.bind(null, runtimeBuiltinName);', + "const path = process.getBuiltinModule.bind(undefined)('node:path');", + 'const unknown = globalThis.process.getBuiltinModule.bind(null)(runtimeBuiltinName);', + 'const object = { getBuiltinModule() { return "method-only"; } };', + 'const benign = object.getBuiltinModule.bind(null, "node:crypto");', + 'void [fsLoader, unknownLoader, path, unknown, benign];', + ].join('\n'); + + expect( + findRuntimeModuleAuthority(source, 'node-builtins-bind.js').map( + ({ kind, specifier }) => ({ kind, specifier }), + ), + ).toEqual([ + { kind: 'node-builtin-module', specifier: 'node:fs' }, + { kind: 'node-builtin-module', specifier: undefined }, + { kind: 'node-builtin-module', specifier: 'node:path' }, + { kind: 'node-builtin-module', specifier: undefined }, ]); }); }); From 9143549b24722cafeeb92fbd0e2116418e5d8bf2 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Wed, 19 Aug 2026 10:19:13 -0700 Subject: [PATCH 41/50] fix(supply-chain): reject indirect Node built-in loaders --- scripts/javascript-runtime-authority.mjs | 100 +++++++++++++++++++---- 1 file changed, 84 insertions(+), 16 deletions(-) diff --git a/scripts/javascript-runtime-authority.mjs b/scripts/javascript-runtime-authority.mjs index 9bce744d..627228b4 100644 --- a/scripts/javascript-runtime-authority.mjs +++ b/scripts/javascript-runtime-authority.mjs @@ -94,20 +94,68 @@ export function findRuntimeModuleAuthority(source, filename = 'bundle.js') { return false; } - /** Return the module argument for direct or `.call` ambient built-in loading. */ + /** Return the module argument for recognizable ambient built-in loading. */ function nodeBuiltinModuleInvocation(node) { if (isNodeBuiltinModuleExpression(node.expression)) { return { argument: node.arguments[0] }; } + const boundInvocation = commonJsBoundInvocation( + node, + isNodeBuiltinModuleExpression, + ); + if (boundInvocation) { + return boundInvocation; + } + + if (isReflectApplyExpression(node.expression)) { + const target = node.arguments[0]; + if (target && isNodeBuiltinModuleExpression(target)) { + return { argument: commonJsApplyArgument(node, 2) }; + } + if (target) { + const boundTarget = commonJsBoundExpression( + target, + isNodeBuiltinModuleExpression, + ); + if (boundTarget) { + return { + argument: commonJsBoundArgument( + boundTarget, + commonJsApplyArgument(node, 2), + ), + }; + } + } + } + const callee = unwrapParentheses(node.expression); if ( - (ts.isPropertyAccessExpression(callee) || - ts.isElementAccessExpression(callee)) && - staticMemberName(callee) === 'call' && - isNodeBuiltinModuleExpression(callee.expression) + ts.isPropertyAccessExpression(callee) || + ts.isElementAccessExpression(callee) ) { - return { argument: node.arguments[1] }; + const invocationMethod = staticMemberName(callee); + if (invocationMethod === 'call' || invocationMethod === 'apply') { + const fallbackArgument = + invocationMethod === 'call' + ? node.arguments[1] + : commonJsApplyArgument(node); + if (isNodeBuiltinModuleExpression(callee.expression)) { + return { argument: fallbackArgument }; + } + const boundReceiver = commonJsBoundExpression( + callee.expression, + isNodeBuiltinModuleExpression, + ); + if (boundReceiver) { + return { + argument: commonJsBoundArgument( + boundReceiver, + fallbackArgument, + ), + }; + } + } } return null; } @@ -538,31 +586,51 @@ export function findRuntimeModuleAuthority(source, filename = 'bundle.js') { specifier: literalSpecifier(invocation.argument), }); } else if (!consumedBoundCalls.has(node)) { - const boundResolver = commonJsBoundExpression( + const boundNodeBuiltin = commonJsBoundExpression( node, - isCommonJsResolverExpression, + isNodeBuiltinModuleExpression, ); - if (boundResolver) { + if (boundNodeBuiltin) { findings.push({ - kind: 'commonjs-resolve', + kind: 'node-builtin-module', offset: node.getStart(sourceFile), specifier: literalSpecifier( - boundResolver.hasArgument ? boundResolver.argument : undefined, + boundNodeBuiltin.hasArgument + ? boundNodeBuiltin.argument + : undefined, ), }); } else { - const boundLoader = commonJsBoundExpression( + const boundResolver = commonJsBoundExpression( node, - isCommonJsLoaderExpression, + isCommonJsResolverExpression, ); - if (boundLoader) { + if (boundResolver) { findings.push({ - kind: 'commonjs-require', + kind: 'commonjs-resolve', offset: node.getStart(sourceFile), specifier: literalSpecifier( - boundLoader.hasArgument ? boundLoader.argument : undefined, + boundResolver.hasArgument + ? boundResolver.argument + : undefined, ), }); + } else { + const boundLoader = commonJsBoundExpression( + node, + isCommonJsLoaderExpression, + ); + if (boundLoader) { + findings.push({ + kind: 'commonjs-require', + offset: node.getStart(sourceFile), + specifier: literalSpecifier( + boundLoader.hasArgument + ? boundLoader.argument + : undefined, + ), + }); + } } } } From 5750e638aa96e5b8a51312967dc87ec506fddd81 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Wed, 19 Aug 2026 10:19:54 -0700 Subject: [PATCH 42/50] docs: record indirect Node built-in authority --- docs/doctoring/commonjs-runtime-authority.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/doctoring/commonjs-runtime-authority.md b/docs/doctoring/commonjs-runtime-authority.md index 52ef0ae3..0a5f55c7 100644 --- a/docs/doctoring/commonjs-runtime-authority.md +++ b/docs/doctoring/commonjs-runtime-authority.md @@ -4,11 +4,11 @@ Status: active-PR evidence for PR #290. This note does not describe protected-ma ## Decision boundary -Inkspan release verification treats executable module lookup or loading authority left in a packed JavaScript artifact as a packaging failure. The verifier parses emitted JavaScript with the TypeScript AST and classifies only statically recognizable syntax. It does not execute artifact code, resolve arbitrary aliases, evaluate receiver expressions, or infer computed member names. +Inkspan release verification treats executable module lookup or loading authority left in a packed JavaScript artifact as a packaging failure. The verifier parses emitted JavaScript with the TypeScript AST and classifies only statically recognizable syntax. It does not execute artifact code, resolve arbitrary aliases, evaluate receiver expressions, or infer dynamic computed member names. -The current active-PR scanner covers static imports/re-exports, dynamic `import(...)`, direct and statically indirect CommonJS `require` forms, `module.require`, `require.main.require`, `require.resolve`, `.call`, `.apply`, `Reflect.apply`, retained `.bind` capabilities, statically composed `.bind` invocations, constructor invocation through `new`, `Reflect.construct` when the target is already a recognized CommonJS loader or resolver, and Node `process.getBuiltinModule(...)` / `globalThis.process.getBuiltinModule(...)` calls including direct `Function.prototype.call` invocation of those exact ambient loader expressions. A directly recognizable bound CommonJS loader or resolver is therefore rejected when it is retained for later use, before later invocation occurs. Literal module specifiers are retained as actionable evidence; computed or missing specifiers remain unknown rather than being guessed. Ordinary object methods merely named `require`, `resolve`, or `getBuiltinModule` remain outside the authority model. +The current active-PR scanner covers static imports/re-exports, dynamic `import(...)`, direct and statically indirect CommonJS `require` forms, `module.require`, `require.main.require`, `require.resolve`, `.call`, `.apply`, `Reflect.apply`, retained `.bind` capabilities, statically composed `.bind` invocations, constructor invocation through `new`, `Reflect.construct` when the target is already a recognized CommonJS loader or resolver, and Node `process.getBuiltinModule(...)` / `globalThis.process.getBuiltinModule(...)` authority through direct calls, exact-receiver `.call(...)` / `.apply(...)`, `Reflect.apply(...)`, retained `.bind(...)` capabilities, and immediately invoked bound capabilities. A directly recognizable bound CommonJS or Node built-in loader/resolver is therefore rejected when it is retained for later use, before later invocation occurs. Literal module specifiers are retained as actionable evidence; computed or missing specifiers remain unknown rather than being guessed. Static string-literal element access may identify an already-supported member spelling. Ordinary object methods merely named `require`, `resolve`, or `getBuiltinModule` remain outside the authority model. -The `process.getBuiltinModule(...)` classification is intentionally narrow: it recognizes only the ambient `process` spelling and the explicit `globalThis.process` spelling documented by Node.js, plus `.call(...)` when its receiver is one of those exact loader expressions. It does not resolve aliases, arbitrary receivers, or computed member names, and it does not infer that a similarly named method on another object has Node module authority. +The `process.getBuiltinModule(...)` classification is intentionally narrow: it recognizes only the ambient `process` spelling and the explicit `globalThis.process` spelling documented by Node.js, plus standard invocation/retention syntax when the receiver/target is one of those exact loader expressions. It does not resolve aliases, arbitrary receivers, or dynamic computed member names, and it does not infer that a similarly named method on another object has Node module authority. This is a release-evidence boundary, not a runtime loader. It adds no filesystem, network, credential, persistence, deployment, model, or host authority to Inkspan. @@ -16,15 +16,15 @@ This is a release-evidence boundary, not a runtime loader. It adds no filesystem Node.js documents `require()` as the CommonJS module loader, `require.main` as the entry-module reference for CommonJS entry points, and `require.resolve()` as using the internal `require()` resolution machinery without loading the resolved module. These semantics make both loading and resolution relevant executable authority in an artifact expected to be self-contained. -Node.js also documents `process.getBuiltinModule(id)` as a globally available synchronous way to load a Node built-in module, including the explicit `globalThis.process.getBuiltinModule(...)` form for environment-conditional access. Because the packed Markdown artifact is required to be self-contained and free of executable runtime module authority, leaving that syntax in the artifact is a packaging failure even though the target is a Node built-in rather than an external package. +Node.js also documents `process.getBuiltinModule(id)` as a globally available synchronous way to load a Node built-in module, including the explicit `globalThis.process.getBuiltinModule(...)` form for environment-conditional access. Because the packed Markdown artifact is required to be self-contained and free of executable runtime module authority, leaving that authority in the artifact is a packaging failure even though the target is a Node built-in rather than an external package. -ECMA-262 defines `Function.prototype.bind` as producing a bound function whose target and leading arguments are retained for subsequent calls. That retained callable is already executable loader/resolver authority before later invocation, so a packed artifact that stores a directly recognizable `require.bind(...)` or `require.resolve.bind(...)` capability has not eliminated the authority merely because the call happens later. ECMA-262 also defines `Function.prototype.call` as invoking its callable receiver with an explicit `this` value and following arguments, `Reflect.apply(target, thisArgument, argumentsList)` as invoking a callable target with an argument list, ordinary `new` expressions as constructor invocation, and `Reflect.construct(target, argumentsList, newTarget)` as construction through an explicitly supplied target. Therefore, a verifier that recognizes only direct loader calls can miss equivalent statically recognizable authority invoked through standard `call`/`apply`/`Reflect.apply` or retained/composed through other standard invocation forms. +ECMA-262 defines `Function.prototype.bind` as producing a bound function whose target and leading arguments are retained for subsequent calls. That retained callable is already executable loader/resolver authority before later invocation, so a packed artifact that stores a directly recognizable `require.bind(...)`, `require.resolve.bind(...)`, or ambient `process.getBuiltinModule.bind(...)` capability has not eliminated the authority merely because the call happens later. ECMA-262 also defines `Function.prototype.call` and `Function.prototype.apply` as invoking their callable receiver with explicit receiver/argument data, `Reflect.apply(target, thisArgument, argumentsList)` as invoking a callable target with an argument list, ordinary `new` expressions as constructor invocation, and `Reflect.construct(target, argumentsList, newTarget)` as construction through an explicitly supplied target. Therefore, a verifier that recognizes only direct loader calls can miss equivalent statically recognizable authority invoked through standard `call`/`apply`/`Reflect.apply` or retained/composed through standard `bind` forms. The verifier intentionally remains syntax-bounded. ECMAScript permits arbitrary aliasing and computation; attempting whole-program resolution in this release check would enlarge the trusted implementation and risk unsound guesses. Unknown computed module arguments are therefore reported as executable authority without an invented specifier when the authority surface itself is statically recognizable. ## Assurance implications -The regression suite must include positive cases for each supported syntax family, including retained bound CommonJS loader/resolver capabilities before later invocation and Node built-in loading through both documented ambient process spellings, direct calls, and exact-receiver `.call(...)` invocation, plus negative controls for ordinary objects with similarly named methods. Invalid emitted JavaScript fails closed. Any extension to the recognized syntax must be test-first and must preserve the no-execution/no-alias-evaluation invariant. +The regression suite must include positive cases for each supported syntax family, including retained bound CommonJS loader/resolver capabilities before later invocation and Node built-in loading through both documented ambient process spellings, direct calls, exact-receiver `.call(...)` / `.apply(...)`, `Reflect.apply(...)`, retained `.bind(...)`, and immediate bound invocation, plus negative controls for ordinary objects with similarly named methods. Invalid emitted JavaScript fails closed. Any extension to the recognized syntax must be test-first and must preserve the no-execution/no-alias-evaluation invariant. Release acceptance must use the exact packed artifact produced from the exact candidate head. Passing source tests on a predecessor, a different checkout SHA, or a status-only/model-only signal is not evidence that the packed artifact is free of runtime module authority. From a0eaac01e392ea8a3e4dec987b330c141cfc3e80 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Wed, 19 Aug 2026 14:31:51 -0700 Subject: [PATCH 43/50] test(supply-chain): expose comma-indirected built-in loader gap --- ...scriptRuntimeAuthorityNodeBuiltin.test.mjs | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/src/javascriptRuntimeAuthorityNodeBuiltin.test.mjs b/src/javascriptRuntimeAuthorityNodeBuiltin.test.mjs index 53e21fce..2a6cd942 100644 --- a/src/javascriptRuntimeAuthorityNodeBuiltin.test.mjs +++ b/src/javascriptRuntimeAuthorityNodeBuiltin.test.mjs @@ -24,6 +24,29 @@ describe('Node built-in runtime module authority', () => { ]); }); + it('reports comma-indirected ambient built-in loaders without promoting ordinary object methods', () => { + const source = [ + "const fs = (0, process.getBuiltinModule)('node:fs');", + "const path = (0, globalThis.process.getBuiltinModule)('node:path');", + 'const unknown = (0, process.getBuiltinModule)(runtimeBuiltinName);', + "const util = (0, globalThis['process']['getBuiltinModule'])('node:util');", + 'const object = { getBuiltinModule() { return "method-only"; } };', + 'const benign = (0, object.getBuiltinModule)("node:crypto");', + 'void [fs, path, unknown, util, benign];', + ].join('\n'); + + expect( + findRuntimeModuleAuthority(source, 'node-builtins-comma-indirect.js').map( + ({ kind, specifier }) => ({ kind, specifier }), + ), + ).toEqual([ + { kind: 'node-builtin-module', specifier: 'node:fs' }, + { kind: 'node-builtin-module', specifier: 'node:path' }, + { kind: 'node-builtin-module', specifier: undefined }, + { kind: 'node-builtin-module', specifier: 'node:util' }, + ]); + }); + it('reports Function.prototype call/apply and Reflect.apply invocation of the ambient loader', () => { const source = [ "const fs = process.getBuiltinModule.call(undefined, 'node:fs');", From 55cf0f0e1b4829d6cc7d845f2bdd2207892cd0f9 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Wed, 19 Aug 2026 14:34:19 -0700 Subject: [PATCH 44/50] fix(supply-chain): catch comma-indirected built-in loaders --- scripts/javascript-runtime-authority.mjs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/scripts/javascript-runtime-authority.mjs b/scripts/javascript-runtime-authority.mjs index 627228b4..eb674eca 100644 --- a/scripts/javascript-runtime-authority.mjs +++ b/scripts/javascript-runtime-authority.mjs @@ -71,6 +71,12 @@ export function findRuntimeModuleAuthority(source, filename = 'bundle.js') { /** Identify Node's ambient synchronous built-in module loader syntax. */ function isNodeBuiltinModuleExpression(expression) { const current = unwrapParentheses(expression); + if ( + ts.isBinaryExpression(current) && + current.operatorToken.kind === ts.SyntaxKind.CommaToken + ) { + return isNodeBuiltinModuleExpression(current.right); + } if ( (!ts.isPropertyAccessExpression(current) && !ts.isElementAccessExpression(current)) || From c7436b447f80a952cf330b925f0ff560bb8dd4d4 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Wed, 19 Aug 2026 14:35:35 -0700 Subject: [PATCH 45/50] docs(supply-chain): record comma-indirected built-in authority --- docs/doctoring/commonjs-runtime-authority.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/doctoring/commonjs-runtime-authority.md b/docs/doctoring/commonjs-runtime-authority.md index 0a5f55c7..352cc5f4 100644 --- a/docs/doctoring/commonjs-runtime-authority.md +++ b/docs/doctoring/commonjs-runtime-authority.md @@ -6,9 +6,9 @@ Status: active-PR evidence for PR #290. This note does not describe protected-ma Inkspan release verification treats executable module lookup or loading authority left in a packed JavaScript artifact as a packaging failure. The verifier parses emitted JavaScript with the TypeScript AST and classifies only statically recognizable syntax. It does not execute artifact code, resolve arbitrary aliases, evaluate receiver expressions, or infer dynamic computed member names. -The current active-PR scanner covers static imports/re-exports, dynamic `import(...)`, direct and statically indirect CommonJS `require` forms, `module.require`, `require.main.require`, `require.resolve`, `.call`, `.apply`, `Reflect.apply`, retained `.bind` capabilities, statically composed `.bind` invocations, constructor invocation through `new`, `Reflect.construct` when the target is already a recognized CommonJS loader or resolver, and Node `process.getBuiltinModule(...)` / `globalThis.process.getBuiltinModule(...)` authority through direct calls, exact-receiver `.call(...)` / `.apply(...)`, `Reflect.apply(...)`, retained `.bind(...)` capabilities, and immediately invoked bound capabilities. A directly recognizable bound CommonJS or Node built-in loader/resolver is therefore rejected when it is retained for later use, before later invocation occurs. Literal module specifiers are retained as actionable evidence; computed or missing specifiers remain unknown rather than being guessed. Static string-literal element access may identify an already-supported member spelling. Ordinary object methods merely named `require`, `resolve`, or `getBuiltinModule` remain outside the authority model. +The current active-PR scanner covers static imports/re-exports, dynamic `import(...)`, direct and statically indirect CommonJS `require` forms, `module.require`, `require.main.require`, `require.resolve`, `.call`, `.apply`, `Reflect.apply`, retained `.bind` capabilities, statically composed `.bind` invocations, constructor invocation through `new`, `Reflect.construct` when the target is already a recognized CommonJS loader or resolver, and Node `process.getBuiltinModule(...)` / `globalThis.process.getBuiltinModule(...)` authority through direct calls, comma-indirected calls whose right operand is one of those exact ambient loader expressions, exact-receiver `.call(...)` / `.apply(...)`, `Reflect.apply(...)`, retained `.bind(...)` capabilities, and immediately invoked bound capabilities. A directly recognizable bound CommonJS or Node built-in loader/resolver is therefore rejected when it is retained for later use, before later invocation occurs. Literal module specifiers are retained as actionable evidence; computed or missing specifiers remain unknown rather than being guessed. Static string-literal element access may identify an already-supported member spelling. Ordinary object methods merely named `require`, `resolve`, or `getBuiltinModule` remain outside the authority model. -The `process.getBuiltinModule(...)` classification is intentionally narrow: it recognizes only the ambient `process` spelling and the explicit `globalThis.process` spelling documented by Node.js, plus standard invocation/retention syntax when the receiver/target is one of those exact loader expressions. It does not resolve aliases, arbitrary receivers, or dynamic computed member names, and it does not infer that a similarly named method on another object has Node module authority. +The `process.getBuiltinModule(...)` classification is intentionally narrow: it recognizes only the ambient `process` spelling and the explicit `globalThis.process` spelling documented by Node.js, plus syntax-only comma indirection that preserves one of those exact expressions as the right operand, and standard invocation/retention syntax when the receiver/target is one of those exact loader expressions. It does not resolve aliases, arbitrary receivers, or dynamic computed member names, and it does not infer that a similarly named method on another object has Node module authority. This is a release-evidence boundary, not a runtime loader. It adds no filesystem, network, credential, persistence, deployment, model, or host authority to Inkspan. @@ -24,7 +24,7 @@ The verifier intentionally remains syntax-bounded. ECMAScript permits arbitrary ## Assurance implications -The regression suite must include positive cases for each supported syntax family, including retained bound CommonJS loader/resolver capabilities before later invocation and Node built-in loading through both documented ambient process spellings, direct calls, exact-receiver `.call(...)` / `.apply(...)`, `Reflect.apply(...)`, retained `.bind(...)`, and immediate bound invocation, plus negative controls for ordinary objects with similarly named methods. Invalid emitted JavaScript fails closed. Any extension to the recognized syntax must be test-first and must preserve the no-execution/no-alias-evaluation invariant. +The regression suite must include positive cases for each supported syntax family, including retained bound CommonJS loader/resolver capabilities before later invocation and Node built-in loading through both documented ambient process spellings, direct and comma-indirected calls, exact-receiver `.call(...)` / `.apply(...)`, `Reflect.apply(...)`, retained `.bind(...)`, and immediate bound invocation, plus negative controls for ordinary objects with similarly named methods. Invalid emitted JavaScript fails closed. Any extension to the recognized syntax must be test-first and must preserve the no-execution/no-alias-evaluation invariant. Release acceptance must use the exact packed artifact produced from the exact candidate head. Passing source tests on a predecessor, a different checkout SHA, or a status-only/model-only signal is not evidence that the packed artifact is free of runtime module authority. From 7d7926e4e7149c6cb2bd40b785ced27e4a360e1b Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Wed, 19 Aug 2026 14:37:42 -0700 Subject: [PATCH 46/50] test(supply-chain): make built-in authority regression discoverable --- ...ascriptRuntimeAuthorityNodeBuiltin.test.ts | 128 ++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 src/javascriptRuntimeAuthorityNodeBuiltin.test.ts diff --git a/src/javascriptRuntimeAuthorityNodeBuiltin.test.ts b/src/javascriptRuntimeAuthorityNodeBuiltin.test.ts new file mode 100644 index 00000000..150f9b2a --- /dev/null +++ b/src/javascriptRuntimeAuthorityNodeBuiltin.test.ts @@ -0,0 +1,128 @@ +import { execFileSync } from 'node:child_process'; +import { resolve } from 'node:path'; +import { pathToFileURL } from 'node:url'; + +import { describe, expect, it } from 'vitest'; + +function scanWithRepositoryAuthority( + source: string, + filename: string, +): unknown { + const scannerUrl = pathToFileURL( + resolve(process.cwd(), 'scripts/javascript-runtime-authority.mjs'), + ).href; + const program = [ + `import { findRuntimeModuleAuthority } from ${JSON.stringify(scannerUrl)};`, + `const source = ${JSON.stringify(source)};`, + `const findings = findRuntimeModuleAuthority(source, ${JSON.stringify(filename)}).map(({ kind, specifier }) => ({ kind, specifier }));`, + 'process.stdout.write(JSON.stringify(findings));', + ].join('\n'); + return JSON.parse( + execFileSync(process.execPath, ['--input-type=module', '--eval', program], { + encoding: 'utf8', + }), + ) as unknown; +} + +describe('Node built-in runtime module authority', () => { + it('reports statically recognizable process.getBuiltinModule calls', () => { + const source = [ + "const fs = process.getBuiltinModule('node:fs');", + "const path = globalThis.process.getBuiltinModule('node:path');", + 'const unknown = process.getBuiltinModule(runtimeBuiltinName);', + 'const object = { getBuiltinModule() { return "method-only"; } };', + 'const benign = object.getBuiltinModule("node:crypto");', + 'void [fs, path, unknown, benign];', + ].join('\n'); + + expect(scanWithRepositoryAuthority(source, 'node-builtins.js')).toEqual([ + { kind: 'node-builtin-module', specifier: 'node:fs' }, + { kind: 'node-builtin-module', specifier: 'node:path' }, + { kind: 'node-builtin-module', specifier: undefined }, + ]); + }); + + it('reports comma-indirected ambient built-in loaders without promoting ordinary object methods', () => { + const source = [ + "const fs = (0, process.getBuiltinModule)('node:fs');", + "const path = (0, globalThis.process.getBuiltinModule)('node:path');", + 'const unknown = (0, process.getBuiltinModule)(runtimeBuiltinName);', + "const util = (0, globalThis['process']['getBuiltinModule'])('node:util');", + 'const object = { getBuiltinModule() { return "method-only"; } };', + 'const benign = (0, object.getBuiltinModule)("node:crypto");', + 'void [fs, path, unknown, util, benign];', + ].join('\n'); + + expect( + scanWithRepositoryAuthority(source, 'node-builtins-comma-indirect.js'), + ).toEqual([ + { kind: 'node-builtin-module', specifier: 'node:fs' }, + { kind: 'node-builtin-module', specifier: 'node:path' }, + { kind: 'node-builtin-module', specifier: undefined }, + { kind: 'node-builtin-module', specifier: 'node:util' }, + ]); + }); + + it('proves the supported Node runtime can load through comma indirection', () => { + const result = execFileSync( + process.execPath, + [ + '--eval', + "const loaded = (0, process.getBuiltinModule)('node:path'); process.stdout.write(String(loaded === require('node:path')));", + ], + { encoding: 'utf8' }, + ); + + expect(result).toBe('true'); + }); + + it('reports Function.prototype call/apply and Reflect.apply invocation of the ambient loader', () => { + const source = [ + "const fs = process.getBuiltinModule.call(undefined, 'node:fs');", + 'const path = globalThis.process.getBuiltinModule.call(null, runtimeBuiltinName);', + "const util = process['getBuiltinModule']['call'](undefined, 'node:util');", + "const os = globalThis['process']['getBuiltinModule'].call(null, 'node:os');", + "const crypto = process.getBuiltinModule.apply(undefined, ['node:crypto']);", + 'const unknownApply = globalThis.process.getBuiltinModule.apply(null, [runtimeBuiltinName]);', + "const url = Reflect.apply(process.getBuiltinModule, undefined, ['node:url']);", + 'const unknownReflect = Reflect.apply(globalThis.process.getBuiltinModule, null, runtimeBuiltinArgs);', + 'const object = { getBuiltinModule() { return "method-only"; } };', + 'const benignCall = object.getBuiltinModule.call(null, "node:buffer");', + 'const benignApply = object.getBuiltinModule.apply(null, ["node:assert"]);', + 'const benignReflect = Reflect.apply(object.getBuiltinModule, null, ["node:zlib"]);', + 'void [fs, path, util, os, crypto, unknownApply, url, unknownReflect, benignCall, benignApply, benignReflect];', + ].join('\n'); + + expect( + scanWithRepositoryAuthority(source, 'node-builtins-indirect.js'), + ).toEqual([ + { kind: 'node-builtin-module', specifier: 'node:fs' }, + { kind: 'node-builtin-module', specifier: undefined }, + { kind: 'node-builtin-module', specifier: 'node:util' }, + { kind: 'node-builtin-module', specifier: 'node:os' }, + { kind: 'node-builtin-module', specifier: 'node:crypto' }, + { kind: 'node-builtin-module', specifier: undefined }, + { kind: 'node-builtin-module', specifier: 'node:url' }, + { kind: 'node-builtin-module', specifier: undefined }, + ]); + }); + + it('reports retained and immediately invoked bound ambient loaders', () => { + const source = [ + "const fsLoader = process.getBuiltinModule.bind(undefined, 'node:fs');", + 'const unknownLoader = globalThis.process.getBuiltinModule.bind(null, runtimeBuiltinName);', + "const path = process.getBuiltinModule.bind(undefined)('node:path');", + 'const unknown = globalThis.process.getBuiltinModule.bind(null)(runtimeBuiltinName);', + 'const object = { getBuiltinModule() { return "method-only"; } };', + 'const benign = object.getBuiltinModule.bind(null, "node:crypto");', + 'void [fsLoader, unknownLoader, path, unknown, benign];', + ].join('\n'); + + expect(scanWithRepositoryAuthority(source, 'node-builtins-bind.js')).toEqual([ + { kind: 'node-builtin-module', specifier: 'node:fs' }, + { kind: 'node-builtin-module', specifier: undefined }, + { kind: 'node-builtin-module', specifier: 'node:path' }, + { kind: 'node-builtin-module', specifier: undefined }, + ]); + }); +}); From 097ffbd230aaeaf9ceec978e66bab723b119f23d Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Wed, 19 Aug 2026 14:37:57 -0700 Subject: [PATCH 47/50] test(supply-chain): remove undiscovered mjs regression --- ...scriptRuntimeAuthorityNodeBuiltin.test.mjs | 105 ------------------ 1 file changed, 105 deletions(-) delete mode 100644 src/javascriptRuntimeAuthorityNodeBuiltin.test.mjs diff --git a/src/javascriptRuntimeAuthorityNodeBuiltin.test.mjs b/src/javascriptRuntimeAuthorityNodeBuiltin.test.mjs deleted file mode 100644 index 2a6cd942..00000000 --- a/src/javascriptRuntimeAuthorityNodeBuiltin.test.mjs +++ /dev/null @@ -1,105 +0,0 @@ -import { describe, expect, it } from 'vitest'; - -import { findRuntimeModuleAuthority } from '../scripts/javascript-runtime-authority.mjs'; - -describe('Node built-in runtime module authority', () => { - it('reports statically recognizable process.getBuiltinModule calls', () => { - const source = [ - "const fs = process.getBuiltinModule('node:fs');", - "const path = globalThis.process.getBuiltinModule('node:path');", - 'const unknown = process.getBuiltinModule(runtimeBuiltinName);', - 'const object = { getBuiltinModule() { return "method-only"; } };', - 'const benign = object.getBuiltinModule("node:crypto");', - 'void [fs, path, unknown, benign];', - ].join('\n'); - - expect( - findRuntimeModuleAuthority(source, 'node-builtins.js').map( - ({ kind, specifier }) => ({ kind, specifier }), - ), - ).toEqual([ - { kind: 'node-builtin-module', specifier: 'node:fs' }, - { kind: 'node-builtin-module', specifier: 'node:path' }, - { kind: 'node-builtin-module', specifier: undefined }, - ]); - }); - - it('reports comma-indirected ambient built-in loaders without promoting ordinary object methods', () => { - const source = [ - "const fs = (0, process.getBuiltinModule)('node:fs');", - "const path = (0, globalThis.process.getBuiltinModule)('node:path');", - 'const unknown = (0, process.getBuiltinModule)(runtimeBuiltinName);', - "const util = (0, globalThis['process']['getBuiltinModule'])('node:util');", - 'const object = { getBuiltinModule() { return "method-only"; } };', - 'const benign = (0, object.getBuiltinModule)("node:crypto");', - 'void [fs, path, unknown, util, benign];', - ].join('\n'); - - expect( - findRuntimeModuleAuthority(source, 'node-builtins-comma-indirect.js').map( - ({ kind, specifier }) => ({ kind, specifier }), - ), - ).toEqual([ - { kind: 'node-builtin-module', specifier: 'node:fs' }, - { kind: 'node-builtin-module', specifier: 'node:path' }, - { kind: 'node-builtin-module', specifier: undefined }, - { kind: 'node-builtin-module', specifier: 'node:util' }, - ]); - }); - - it('reports Function.prototype call/apply and Reflect.apply invocation of the ambient loader', () => { - const source = [ - "const fs = process.getBuiltinModule.call(undefined, 'node:fs');", - 'const path = globalThis.process.getBuiltinModule.call(null, runtimeBuiltinName);', - "const util = process['getBuiltinModule']['call'](undefined, 'node:util');", - "const os = globalThis['process']['getBuiltinModule'].call(null, 'node:os');", - "const crypto = process.getBuiltinModule.apply(undefined, ['node:crypto']);", - 'const unknownApply = globalThis.process.getBuiltinModule.apply(null, [runtimeBuiltinName]);', - "const url = Reflect.apply(process.getBuiltinModule, undefined, ['node:url']);", - 'const unknownReflect = Reflect.apply(globalThis.process.getBuiltinModule, null, runtimeBuiltinArgs);', - 'const object = { getBuiltinModule() { return "method-only"; } };', - 'const benignCall = object.getBuiltinModule.call(null, "node:buffer");', - 'const benignApply = object.getBuiltinModule.apply(null, ["node:assert"]);', - 'const benignReflect = Reflect.apply(object.getBuiltinModule, null, ["node:zlib"]);', - 'void [fs, path, util, os, crypto, unknownApply, url, unknownReflect, benignCall, benignApply, benignReflect];', - ].join('\n'); - - expect( - findRuntimeModuleAuthority(source, 'node-builtins-indirect.js').map( - ({ kind, specifier }) => ({ kind, specifier }), - ), - ).toEqual([ - { kind: 'node-builtin-module', specifier: 'node:fs' }, - { kind: 'node-builtin-module', specifier: undefined }, - { kind: 'node-builtin-module', specifier: 'node:util' }, - { kind: 'node-builtin-module', specifier: 'node:os' }, - { kind: 'node-builtin-module', specifier: 'node:crypto' }, - { kind: 'node-builtin-module', specifier: undefined }, - { kind: 'node-builtin-module', specifier: 'node:url' }, - { kind: 'node-builtin-module', specifier: undefined }, - ]); - }); - - it('reports retained and immediately invoked bound ambient loaders', () => { - const source = [ - "const fsLoader = process.getBuiltinModule.bind(undefined, 'node:fs');", - 'const unknownLoader = globalThis.process.getBuiltinModule.bind(null, runtimeBuiltinName);', - "const path = process.getBuiltinModule.bind(undefined)('node:path');", - 'const unknown = globalThis.process.getBuiltinModule.bind(null)(runtimeBuiltinName);', - 'const object = { getBuiltinModule() { return "method-only"; } };', - 'const benign = object.getBuiltinModule.bind(null, "node:crypto");', - 'void [fsLoader, unknownLoader, path, unknown, benign];', - ].join('\n'); - - expect( - findRuntimeModuleAuthority(source, 'node-builtins-bind.js').map( - ({ kind, specifier }) => ({ kind, specifier }), - ), - ).toEqual([ - { kind: 'node-builtin-module', specifier: 'node:fs' }, - { kind: 'node-builtin-module', specifier: undefined }, - { kind: 'node-builtin-module', specifier: 'node:path' }, - { kind: 'node-builtin-module', specifier: undefined }, - ]); - }); -}); From 1c3b55d7eecedcba7b00ce372652a29327152e81 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 22 Aug 2026 07:14:33 -0700 Subject: [PATCH 48/50] test(supply-chain): expose global.process builtin loader gap --- ...tRuntimeAuthorityNodeBuiltinGlobal.test.ts | 57 +++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 src/javascriptRuntimeAuthorityNodeBuiltinGlobal.test.ts diff --git a/src/javascriptRuntimeAuthorityNodeBuiltinGlobal.test.ts b/src/javascriptRuntimeAuthorityNodeBuiltinGlobal.test.ts new file mode 100644 index 00000000..c0821f4a --- /dev/null +++ b/src/javascriptRuntimeAuthorityNodeBuiltinGlobal.test.ts @@ -0,0 +1,57 @@ +import { execFileSync } from 'node:child_process'; +import { resolve } from 'node:path'; +import { pathToFileURL } from 'node:url'; + +import { describe, expect, it } from 'vitest'; + +function scanWithRepositoryAuthority( + source: string, + filename: string, +): unknown { + const scannerUrl = pathToFileURL( + resolve(process.cwd(), 'scripts/javascript-runtime-authority.mjs'), + ).href; + const program = [ + `import { findRuntimeModuleAuthority } from ${JSON.stringify(scannerUrl)};`, + `const source = ${JSON.stringify(source)};`, + `const findings = findRuntimeModuleAuthority(source, ${JSON.stringify(filename)}).map(({ kind, specifier }) => ({ kind, specifier }));`, + 'process.stdout.write(JSON.stringify(findings));', + ].join('\n'); + return JSON.parse( + execFileSync(process.execPath, ['--input-type=module', '--eval', program], { + encoding: 'utf8', + }), + ) as unknown; +} + +describe('Node global.process built-in runtime module authority', () => { + it('reports exact ambient global.process loaders without promoting ordinary objects', () => { + const source = [ + "const fs = global.process.getBuiltinModule('node:fs');", + "const util = global['process']['getBuiltinModule']('node:util');", + 'const ordinaryGlobal = { process: { getBuiltinModule() { return "method-only"; } } };', + 'const benign = ordinaryGlobal.process.getBuiltinModule("node:crypto");', + 'void [fs, util, benign];', + ].join('\n'); + + expect( + scanWithRepositoryAuthority(source, 'node-builtins-global-process.js'), + ).toEqual([ + { kind: 'node-builtin-module', specifier: 'node:fs' }, + { kind: 'node-builtin-module', specifier: 'node:util' }, + ]); + }); + + it('proves the supported Node runtime exposes global.process as the ambient process', () => { + const result = execFileSync( + process.execPath, + [ + '--eval', + "const loaded = global.process.getBuiltinModule('node:path'); process.stdout.write(String(global.process === process && loaded === require('node:path')));", + ], + { encoding: 'utf8' }, + ); + + expect(result).toBe('true'); + }); +}); From 10985038dcd191ebed86a680ad32c423ca61e467 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 22 Aug 2026 07:18:59 -0700 Subject: [PATCH 49/50] fix(supply-chain): recognize ambient global.process loader --- scripts/javascript-runtime-authority.mjs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/scripts/javascript-runtime-authority.mjs b/scripts/javascript-runtime-authority.mjs index eb674eca..d6268149 100644 --- a/scripts/javascript-runtime-authority.mjs +++ b/scripts/javascript-runtime-authority.mjs @@ -95,7 +95,10 @@ export function findRuntimeModuleAuthority(source, filename = 'bundle.js') { staticMemberName(receiver) === 'process' ) { const root = unwrapParentheses(receiver.expression); - return ts.isIdentifier(root) && root.text === 'globalThis'; + return ( + ts.isIdentifier(root) && + (root.text === 'globalThis' || root.text === 'global') + ); } return false; } From 8aa1ab94809fe2f3b0655a70e5de48d0f04dfffe Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 22 Aug 2026 07:23:23 -0700 Subject: [PATCH 50/50] docs(supply-chain): align global.process authority contract --- docs/doctoring/commonjs-runtime-authority.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/doctoring/commonjs-runtime-authority.md b/docs/doctoring/commonjs-runtime-authority.md index 352cc5f4..71957f93 100644 --- a/docs/doctoring/commonjs-runtime-authority.md +++ b/docs/doctoring/commonjs-runtime-authority.md @@ -6,9 +6,9 @@ Status: active-PR evidence for PR #290. This note does not describe protected-ma Inkspan release verification treats executable module lookup or loading authority left in a packed JavaScript artifact as a packaging failure. The verifier parses emitted JavaScript with the TypeScript AST and classifies only statically recognizable syntax. It does not execute artifact code, resolve arbitrary aliases, evaluate receiver expressions, or infer dynamic computed member names. -The current active-PR scanner covers static imports/re-exports, dynamic `import(...)`, direct and statically indirect CommonJS `require` forms, `module.require`, `require.main.require`, `require.resolve`, `.call`, `.apply`, `Reflect.apply`, retained `.bind` capabilities, statically composed `.bind` invocations, constructor invocation through `new`, `Reflect.construct` when the target is already a recognized CommonJS loader or resolver, and Node `process.getBuiltinModule(...)` / `globalThis.process.getBuiltinModule(...)` authority through direct calls, comma-indirected calls whose right operand is one of those exact ambient loader expressions, exact-receiver `.call(...)` / `.apply(...)`, `Reflect.apply(...)`, retained `.bind(...)` capabilities, and immediately invoked bound capabilities. A directly recognizable bound CommonJS or Node built-in loader/resolver is therefore rejected when it is retained for later use, before later invocation occurs. Literal module specifiers are retained as actionable evidence; computed or missing specifiers remain unknown rather than being guessed. Static string-literal element access may identify an already-supported member spelling. Ordinary object methods merely named `require`, `resolve`, or `getBuiltinModule` remain outside the authority model. +The current active-PR scanner covers static imports/re-exports, dynamic `import(...)`, direct and statically indirect CommonJS `require` forms, `module.require`, `require.main.require`, `require.resolve`, `.call`, `.apply`, `Reflect.apply`, retained `.bind` capabilities, statically composed `.bind` invocations, constructor invocation through `new`, `Reflect.construct` when the target is already a recognized CommonJS loader or resolver, and Node `process.getBuiltinModule(...)` / `globalThis.process.getBuiltinModule(...)` / `global.process.getBuiltinModule(...)` authority through direct calls, comma-indirected calls whose right operand is one of those exact ambient loader expressions, exact-receiver `.call(...)` / `.apply(...)`, `Reflect.apply(...)`, retained `.bind(...)` capabilities, and immediately invoked bound capabilities. A directly recognizable bound CommonJS or Node built-in loader/resolver is therefore rejected when it is retained for later use, before later invocation occurs. Literal module specifiers are retained as actionable evidence; computed or missing specifiers remain unknown rather than being guessed. Static string-literal element access may identify an already-supported member spelling. Ordinary object methods merely named `require`, `resolve`, or `getBuiltinModule` remain outside the authority model. -The `process.getBuiltinModule(...)` classification is intentionally narrow: it recognizes only the ambient `process` spelling and the explicit `globalThis.process` spelling documented by Node.js, plus syntax-only comma indirection that preserves one of those exact expressions as the right operand, and standard invocation/retention syntax when the receiver/target is one of those exact loader expressions. It does not resolve aliases, arbitrary receivers, or dynamic computed member names, and it does not infer that a similarly named method on another object has Node module authority. +The `process.getBuiltinModule(...)` classification is intentionally narrow: it recognizes only the ambient `process` spelling and the explicit `globalThis.process` / `global.process` spellings whose `process` member resolves to Node's ambient process object, plus syntax-only comma indirection that preserves one of those exact expressions as the right operand, and standard invocation/retention syntax when the receiver/target is one of those exact loader expressions. It does not resolve aliases, arbitrary receivers, or dynamic computed member names, and it does not infer that a similarly named method on another object has Node module authority. This is a release-evidence boundary, not a runtime loader. It adds no filesystem, network, credential, persistence, deployment, model, or host authority to Inkspan. @@ -16,7 +16,7 @@ This is a release-evidence boundary, not a runtime loader. It adds no filesystem Node.js documents `require()` as the CommonJS module loader, `require.main` as the entry-module reference for CommonJS entry points, and `require.resolve()` as using the internal `require()` resolution machinery without loading the resolved module. These semantics make both loading and resolution relevant executable authority in an artifact expected to be self-contained. -Node.js also documents `process.getBuiltinModule(id)` as a globally available synchronous way to load a Node built-in module, including the explicit `globalThis.process.getBuiltinModule(...)` form for environment-conditional access. Because the packed Markdown artifact is required to be self-contained and free of executable runtime module authority, leaving that authority in the artifact is a packaging failure even though the target is a Node built-in rather than an external package. +Node.js also documents `process.getBuiltinModule(id)` as a globally available synchronous way to load a Node built-in module. In supported Node runtimes, `process`, `globalThis.process`, and `global.process` refer to the same ambient process object; the branch regression executes the `global.process.getBuiltinModule(...)` spelling to keep that equivalence machine-checked rather than inferred from arbitrary object structure. Because the packed Markdown artifact is required to be self-contained and free of executable runtime module authority, leaving that authority in the artifact is a packaging failure even though the target is a Node built-in rather than an external package. ECMA-262 defines `Function.prototype.bind` as producing a bound function whose target and leading arguments are retained for subsequent calls. That retained callable is already executable loader/resolver authority before later invocation, so a packed artifact that stores a directly recognizable `require.bind(...)`, `require.resolve.bind(...)`, or ambient `process.getBuiltinModule.bind(...)` capability has not eliminated the authority merely because the call happens later. ECMA-262 also defines `Function.prototype.call` and `Function.prototype.apply` as invoking their callable receiver with explicit receiver/argument data, `Reflect.apply(target, thisArgument, argumentsList)` as invoking a callable target with an argument list, ordinary `new` expressions as constructor invocation, and `Reflect.construct(target, argumentsList, newTarget)` as construction through an explicitly supplied target. Therefore, a verifier that recognizes only direct loader calls can miss equivalent statically recognizable authority invoked through standard `call`/`apply`/`Reflect.apply` or retained/composed through standard `bind` forms. @@ -24,7 +24,7 @@ The verifier intentionally remains syntax-bounded. ECMAScript permits arbitrary ## Assurance implications -The regression suite must include positive cases for each supported syntax family, including retained bound CommonJS loader/resolver capabilities before later invocation and Node built-in loading through both documented ambient process spellings, direct and comma-indirected calls, exact-receiver `.call(...)` / `.apply(...)`, `Reflect.apply(...)`, retained `.bind(...)`, and immediate bound invocation, plus negative controls for ordinary objects with similarly named methods. Invalid emitted JavaScript fails closed. Any extension to the recognized syntax must be test-first and must preserve the no-execution/no-alias-evaluation invariant. +The regression suite must include positive cases for each supported syntax family, including retained bound CommonJS loader/resolver capabilities before later invocation and Node built-in loading through all three exact ambient process spellings (`process`, `globalThis.process`, and `global.process`), direct and comma-indirected calls, exact-receiver `.call(...)` / `.apply(...)`, `Reflect.apply(...)`, retained `.bind(...)`, and immediate bound invocation, plus negative controls for ordinary objects with similarly named methods. Invalid emitted JavaScript fails closed. Any extension to the recognized syntax must be test-first and must preserve the no-execution/no-alias-evaluation invariant. Release acceptance must use the exact packed artifact produced from the exact candidate head. Passing source tests on a predecessor, a different checkout SHA, or a status-only/model-only signal is not evidence that the packed artifact is free of runtime module authority.