diff --git a/package.json b/package.json index 4b46c6d..73ee621 100644 --- a/package.json +++ b/package.json @@ -52,7 +52,7 @@ "agent-access": "./plugins/agent-access/skills/agent-access/scripts/agent-access.mjs" }, "scripts": { - "test": "node --check plugins/agent-access/skills/agent-access/scripts/agent-access.mjs && plugins/agent-access/skills/agent-access/scripts/agent-access.mjs --help >/dev/null && node plugins/agent-access/skills/agent-access/scripts/agent-access.mjs audit-public . && node scripts/validate-codex-plugin.mjs && node scripts/privacy-probes.mjs" + "test": "node --check plugins/agent-access/skills/agent-access/scripts/agent-access.mjs && node scripts/smoke-cli-help.mjs && node plugins/agent-access/skills/agent-access/scripts/agent-access.mjs audit-public . && node scripts/validate-codex-plugin.mjs && node scripts/privacy-probes.mjs" }, "engines": { "node": ">=20" diff --git a/plugins/agent-access/skills/agent-access/scripts/agent-access.mjs b/plugins/agent-access/skills/agent-access/scripts/agent-access.mjs index 5738aa5..5f454da 100755 --- a/plugins/agent-access/skills/agent-access/scripts/agent-access.mjs +++ b/plugins/agent-access/skills/agent-access/scripts/agent-access.mjs @@ -143,8 +143,18 @@ function executableCandidates(entry) { function which(command) { if (!command) return null; + const text = String(command); const dirs = String(process.env.PATH || '').split(path.delimiter).filter(Boolean); - const candidates = command.includes(path.sep) ? [command] : dirs.map((dir) => path.join(dir, command)); + const hasPathSeparator = text.includes(path.sep) || (path.sep === '\\' && text.includes('/')); + const bases = hasPathSeparator ? [text] : dirs.map((dir) => path.join(dir, text)); + const extensions = process.platform === 'win32' && !path.extname(text) + ? String(process.env.PATHEXT || '.COM;.EXE;.BAT;.CMD') + .split(';') + .filter(Boolean) + : []; + const candidates = extensions.length + ? bases.flatMap((candidate) => [candidate, ...extensions.map((ext) => `${candidate}${ext.toLowerCase()}`), ...extensions.map((ext) => `${candidate}${ext.toUpperCase()}`)]) + : bases; for (const candidate of candidates) { try { fs.accessSync(candidate, fs.constants.X_OK); @@ -245,7 +255,7 @@ function redactAuthObject(value) { return [key, (typeof item === 'boolean' || typeof item === 'number' || item == null) ? item : '[REDACTED]']; } if (/path|file|dir|folder|database|db|profile/i.test(key)) { - return [key, (typeof item === 'boolean' || typeof item === 'number' || item == null) ? item : redactedPath(String(item))]; + return [key, (typeof item === 'boolean' || typeof item === 'number' || item == null) ? item : redactString(redactedPath(String(item)))]; } if (/password|secret|cookie|token|session|authorization|verification|qrcode|qr|payload|otp|pin/i.test(key) || /^code$/i.test(key)) { return [key, (typeof item === 'boolean' || typeof item === 'number' || item == null) ? item : '[REDACTED]']; diff --git a/scripts/privacy-probes.mjs b/scripts/privacy-probes.mjs index 3c4b438..d95c3d0 100755 --- a/scripts/privacy-probes.mjs +++ b/scripts/privacy-probes.mjs @@ -54,7 +54,7 @@ const registry = { name: 'stub', kind: 'test', targets: ['stub.local'], - command: 'node', + command: process.execPath, description: 'privacy probe', doctor: [], auth: { methods: [], broker: 'none' }, @@ -66,21 +66,27 @@ const registry = { ], }; registry.entries[0].doctor = [ - 'node', + process.execPath, '-e', `console.log(JSON.stringify({token:${JSON.stringify(privateToken)},user_id:${JSON.stringify(privateUserId)},path:${JSON.stringify(privatePath)}}))`, ]; -const doctor = run(process.execPath, [cli, 'doctor', 'stub', '--run'], { - input: JSON.stringify(registry), - env: { - ...process.env, - AGENT_ACCESS_REGISTRY: '/dev/stdin', - AGENT_ACCESS_STATE_DIR: path.join(os.tmpdir(), 'agent-access-audit-nonexistent'), - }, -}); -assert(doctor.status === 0, 'delegated doctor probe failed', { stdout: doctor.stdout, stderr: doctor.stderr }); -assertNoLeak(doctor.stdout + doctor.stderr, 'delegated doctor output'); +const registryDir = fs.mkdtempSync(path.join(os.tmpdir(), 'agent-access-registry-')); +try { + const registryPath = path.join(registryDir, 'registry.json'); + fs.writeFileSync(registryPath, JSON.stringify(registry, null, 2)); + const doctor = run(process.execPath, [cli, 'doctor', 'stub', '--run'], { + env: { + ...process.env, + AGENT_ACCESS_REGISTRY: registryPath, + AGENT_ACCESS_STATE_DIR: path.join(os.tmpdir(), 'agent-access-audit-nonexistent'), + }, + }); + assert(doctor.status === 0, 'delegated doctor probe failed', { stdout: doctor.stdout, stderr: doctor.stderr }); + assertNoLeak(doctor.stdout + doctor.stderr, 'delegated doctor output'); +} finally { + fs.rmSync(registryDir, { recursive: true, force: true }); +} const stateDir = fs.mkdtempSync(path.join(os.tmpdir(), 'agent-access-privacy-')); try { diff --git a/scripts/smoke-cli-help.mjs b/scripts/smoke-cli-help.mjs new file mode 100644 index 0000000..23b08cb --- /dev/null +++ b/scripts/smoke-cli-help.mjs @@ -0,0 +1,28 @@ +#!/usr/bin/env node + +import path from 'node:path'; +import { spawnSync } from 'node:child_process'; +import { fileURLToPath } from 'node:url'; + +const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..'); +const cli = path.join(root, 'plugins/agent-access/skills/agent-access/scripts/agent-access.mjs'); + +const result = spawnSync(process.execPath, [cli, '--help'], { + cwd: root, + encoding: 'utf8', + stdio: ['ignore', 'ignore', 'pipe'], + timeout: 30000, +}); + +if (result.status !== 0) { + console.error(JSON.stringify({ + ok: false, + command: 'smoke-cli-help', + status: result.status, + signal: result.signal, + stderr: result.stderr || '', + }, null, 2)); + process.exit(1); +} + +console.log(JSON.stringify({ ok: true, command: 'smoke-cli-help' }, null, 2));