From 34b2d7fa18b18553d721d0a49951f10efa9247d6 Mon Sep 17 00:00:00 2001 From: FilipBl4gojevic <84703395+FilipBl4gojevic@users.noreply.github.com> Date: Tue, 28 Jul 2026 11:13:15 +0200 Subject: [PATCH] test(signer): make the no-network boundary test able to actually fail Two holes: the shared-dependency assertion was a tautology that held for any manifest, and ambient globals (fetch, WebSocket) need no import so the import scan was blind to them. Both now enforced, and both verified to fail when a violation is injected. --- packages/signer/test/boundary.test.ts | 53 ++++++++++++++++++++++++--- 1 file changed, 47 insertions(+), 6 deletions(-) diff --git a/packages/signer/test/boundary.test.ts b/packages/signer/test/boundary.test.ts index df55f4f..13b4300 100644 --- a/packages/signer/test/boundary.test.ts +++ b/packages/signer/test/boundary.test.ts @@ -21,6 +21,27 @@ const FORBIDDEN_IMPORTS = [ 'node:tls', ]; +const SHARED_PKG = '@apexfusion/vector-mcp-shared'; +// /tx and /types are pure. /provider is network-capable. /config is deliberately +// excluded too: it carries network endpoint URLs the signer has no use for and +// should not be reaching into. A bare root import (no subpath at all) is excluded +// as well. This is an allowlist, not a denylist — anything not named here fails, +// including forms nobody has thought to forbid yet. +const ALLOWED_SHARED_SUBPATHS = [`${SHARED_PKG}/tx`, `${SHARED_PKG}/types`]; + +// fetch, WebSocket, EventSource, XMLHttpRequest, and navigator.sendBeacon are +// globals in Node 22 — no import statement exists for them, so the import-graph +// scan above is structurally blind to their use. These patterns are matched with +// word boundaries so they don't false-positive on legitimate identifiers like a +// variable named `prefetchCount`. +const AMBIENT_NETWORK_GLOBALS: { label: string; pattern: RegExp }[] = [ + { label: 'fetch(...)', pattern: /\bfetch\s*\(/ }, + { label: 'new WebSocket', pattern: /\bnew\s+WebSocket\b/ }, + { label: 'new EventSource', pattern: /\bnew\s+EventSource\b/ }, + { label: 'new XMLHttpRequest', pattern: /\bnew\s+XMLHttpRequest\b/ }, + { label: 'navigator.sendBeacon', pattern: /\bnavigator\s*\.\s*sendBeacon\b/ }, +]; + function sourceFiles(dir: string): string[] { return readdirSync(dir, { withFileTypes: true }).flatMap((e) => e.isDirectory() ? sourceFiles(join(dir, e.name)) : e.name.endsWith('.ts') ? [join(dir, e.name)] : [] @@ -48,11 +69,31 @@ describe('signer has no network capability', () => { assert.deepStrictEqual(bad, [], `network-capable dependencies declared: ${bad.join(', ')}`); }); - test('does not depend on the shared provider subpath', () => { - const pkg = JSON.parse(readFileSync(join(PKG_DIR, 'package.json'), 'utf8')); - const shared = { ...pkg.dependencies, ...pkg.devDependencies }['@apexfusion/vector-mcp-shared']; - // Depending on the package is fine — /tx is pure. Reaching /provider is not, - // and the shared package's export map has no root export to smuggle it through. - assert.ok(shared === undefined || typeof shared === 'string'); + test('only imports the shared package via an allowed pure subpath', () => { + const specifierPattern = new RegExp(`['"](${SHARED_PKG}(?:/[^'"]*)?)['"]`, 'g'); + const offenders: string[] = []; + for (const file of sourceFiles(SRC_DIR)) { + const text = readFileSync(file, 'utf8'); + for (const match of text.matchAll(specifierPattern)) { + const specifier = match[1]; + if (!ALLOWED_SHARED_SUBPATHS.includes(specifier)) { + offenders.push(`${file} imports '${specifier}'`); + } + } + } + assert.deepStrictEqual(offenders, [], `disallowed shared-package specifier found:\n ${offenders.join('\n ')}`); + }); + + test('no source file uses an ambient network global', () => { + const offenders: string[] = []; + for (const file of sourceFiles(SRC_DIR)) { + const text = readFileSync(file, 'utf8'); + for (const { label, pattern } of AMBIENT_NETWORK_GLOBALS) { + if (pattern.test(text)) { + offenders.push(`${file} uses ${label}`); + } + } + } + assert.deepStrictEqual(offenders, [], `ambient network globals found:\n ${offenders.join('\n ')}`); }); });