@securecode-ai/mcp 0.5.5 → 0.5.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/api/types.d.ts +7 -0
- package/dist/approval/auditLog.d.ts +1 -1
- package/dist/approval/auditLog.js +11 -3
- package/dist/approval/auditLog.js.map +1 -1
- package/dist/approval/broker.d.ts +7 -2
- package/dist/approval/broker.js +239 -110
- package/dist/approval/broker.js.map +1 -1
- package/dist/approval/policy.d.ts +10 -0
- package/dist/approval/policy.js +104 -0
- package/dist/approval/policy.js.map +1 -0
- package/dist/approval/types.d.ts +11 -2
- package/dist/approval/types.js +10 -1
- package/dist/approval/types.js.map +1 -1
- package/dist/attack/agentScanExecutor.d.ts +35 -1
- package/dist/attack/agentScanExecutor.js +405 -76
- package/dist/attack/agentScanExecutor.js.map +1 -1
- package/dist/attack/agentScanLoop.d.ts +20 -0
- package/dist/attack/agentScanLoop.js +1264 -60
- package/dist/attack/agentScanLoop.js.map +1 -1
- package/dist/attack/agentScanProtocol.d.ts +382 -6
- package/dist/attack/agentScanProtocol.js +110 -4
- package/dist/attack/agentScanProtocol.js.map +1 -1
- package/dist/attack/agentTrace.d.ts +73 -0
- package/dist/attack/agentTrace.js +228 -0
- package/dist/attack/agentTrace.js.map +1 -0
- package/dist/attack/architectureScoutExecutor.d.ts +18 -0
- package/dist/attack/architectureScoutExecutor.js +211 -0
- package/dist/attack/architectureScoutExecutor.js.map +1 -0
- package/dist/attack/architectureScoutLoop.d.ts +36 -0
- package/dist/attack/architectureScoutLoop.js +403 -0
- package/dist/attack/architectureScoutLoop.js.map +1 -0
- package/dist/attack/architectureScoutProtocol.d.ts +209 -0
- package/dist/attack/architectureScoutProtocol.js +47 -0
- package/dist/attack/architectureScoutProtocol.js.map +1 -0
- package/dist/attack/candidateStore.d.ts +95 -0
- package/dist/attack/candidateStore.js +231 -0
- package/dist/attack/candidateStore.js.map +1 -0
- package/dist/attack/evidenceLedger.d.ts +67 -0
- package/dist/attack/evidenceLedger.js +192 -0
- package/dist/attack/evidenceLedger.js.map +1 -0
- package/dist/attack/finishGate.d.ts +62 -0
- package/dist/attack/finishGate.js +209 -0
- package/dist/attack/finishGate.js.map +1 -0
- package/dist/attack/fixCodeMerge.d.ts +27 -0
- package/dist/attack/fixCodeMerge.js +42 -0
- package/dist/attack/fixCodeMerge.js.map +1 -0
- package/dist/attack/fixVerifyLoop.d.ts +55 -0
- package/dist/attack/fixVerifyLoop.js +187 -0
- package/dist/attack/fixVerifyLoop.js.map +1 -0
- package/dist/attack/investigationProfiles.d.ts +36 -0
- package/dist/attack/investigationProfiles.js +144 -0
- package/dist/attack/investigationProfiles.js.map +1 -0
- package/dist/attack/investigationState.d.ts +227 -0
- package/dist/attack/investigationState.js +666 -0
- package/dist/attack/investigationState.js.map +1 -0
- package/dist/attack/mutationOperators.d.ts +22 -0
- package/dist/attack/mutationOperators.js +170 -0
- package/dist/attack/mutationOperators.js.map +1 -0
- package/dist/attack/mutationTest.d.ts +33 -0
- package/dist/attack/mutationTest.js +110 -0
- package/dist/attack/mutationTest.js.map +1 -0
- package/dist/attack/proofGate.d.ts +20 -0
- package/dist/attack/proofGate.js +104 -0
- package/dist/attack/proofGate.js.map +1 -0
- package/dist/attack/proofTypes.d.ts +57 -0
- package/dist/attack/proofTypes.js +32 -0
- package/dist/attack/proofTypes.js.map +1 -0
- package/dist/attack/protocolValidator.d.ts +50 -0
- package/dist/attack/protocolValidator.js +427 -0
- package/dist/attack/protocolValidator.js.map +1 -0
- package/dist/attack/qualityMetrics.d.ts +133 -0
- package/dist/attack/qualityMetrics.js +226 -0
- package/dist/attack/qualityMetrics.js.map +1 -0
- package/dist/attack/scanScheduler.d.ts +52 -0
- package/dist/attack/scanScheduler.js +265 -0
- package/dist/attack/scanScheduler.js.map +1 -0
- package/dist/attack/scanState.d.ts +58 -0
- package/dist/attack/scanState.js +102 -0
- package/dist/attack/scanState.js.map +1 -0
- package/dist/attack/searchIntent.d.ts +16 -0
- package/dist/attack/searchIntent.js +57 -0
- package/dist/attack/searchIntent.js.map +1 -0
- package/dist/attack/verifyLoop.d.ts +43 -0
- package/dist/attack/verifyLoop.js +314 -14
- package/dist/attack/verifyLoop.js.map +1 -1
- package/dist/attack/workItem.d.ts +57 -0
- package/dist/attack/workItem.js +225 -0
- package/dist/attack/workItem.js.map +1 -0
- package/dist/audit/findingReviewQueue.d.ts +109 -0
- package/dist/audit/findingReviewQueue.js +335 -0
- package/dist/audit/findingReviewQueue.js.map +1 -0
- package/dist/audit/scanAuditLog.d.ts +160 -0
- package/dist/audit/scanAuditLog.js +404 -0
- package/dist/audit/scanAuditLog.js.map +1 -0
- package/dist/dependency/dependencyChecker.js +35 -9
- package/dist/dependency/dependencyChecker.js.map +1 -1
- package/dist/dependency/exploitPriority.d.ts +33 -0
- package/dist/dependency/exploitPriority.js +78 -0
- package/dist/dependency/exploitPriority.js.map +1 -0
- package/dist/dependency/finding.d.ts +16 -0
- package/dist/dependency/osvClient.js +2 -0
- package/dist/dependency/osvClient.js.map +1 -1
- package/dist/dependency/types.d.ts +12 -1
- package/dist/mcp/server.js +13 -2
- package/dist/mcp/server.js.map +1 -1
- package/dist/mcp/tools.d.ts +1 -0
- package/dist/mcp/tools.js +127 -6
- package/dist/mcp/tools.js.map +1 -1
- package/dist/project-map/agentMemory.d.ts +83 -1
- package/dist/project-map/agentMemory.js +197 -7
- package/dist/project-map/agentMemory.js.map +1 -1
- package/dist/project-map/architectureContext.d.ts +202 -0
- package/dist/project-map/architectureContext.js +421 -0
- package/dist/project-map/architectureContext.js.map +1 -0
- package/dist/project-map/blastRadius.d.ts +54 -0
- package/dist/project-map/blastRadius.js +196 -0
- package/dist/project-map/blastRadius.js.map +1 -0
- package/dist/project-map/callGraphExtractor.d.ts +13 -0
- package/dist/project-map/callGraphExtractor.js +222 -0
- package/dist/project-map/callGraphExtractor.js.map +1 -0
- package/dist/project-map/capabilityRegistry.d.ts +46 -0
- package/dist/project-map/capabilityRegistry.js +91 -0
- package/dist/project-map/capabilityRegistry.js.map +1 -0
- package/dist/project-map/findTests.d.ts +29 -0
- package/dist/project-map/findTests.js +236 -0
- package/dist/project-map/findTests.js.map +1 -0
- package/dist/project-map/handlerInventory.d.ts +112 -0
- package/dist/project-map/handlerInventory.js +184 -0
- package/dist/project-map/handlerInventory.js.map +1 -0
- package/dist/project-map/implementationResolver.d.ts +43 -0
- package/dist/project-map/implementationResolver.js +217 -0
- package/dist/project-map/implementationResolver.js.map +1 -0
- package/dist/project-map/mapContext.d.ts +6 -1
- package/dist/project-map/mapContext.js +1 -0
- package/dist/project-map/mapContext.js.map +1 -1
- package/dist/project-map/scanCache.d.ts +57 -3
- package/dist/project-map/scanCache.js +61 -2
- package/dist/project-map/scanCache.js.map +1 -1
- package/dist/project-map/symbolIndex.d.ts +39 -0
- package/dist/project-map/symbolIndex.js +385 -0
- package/dist/project-map/symbolIndex.js.map +1 -0
- package/dist/tooling/agentEvalScoring.d.ts +77 -0
- package/dist/tooling/agentEvalScoring.js +140 -0
- package/dist/tooling/agentEvalScoring.js.map +1 -0
- package/dist/tooling/agentRegression.d.ts +53 -0
- package/dist/tooling/agentRegression.js +99 -0
- package/dist/tooling/agentRegression.js.map +1 -0
- package/dist/tools/agentScan.d.ts +69 -0
- package/dist/tools/agentScan.js +702 -43
- package/dist/tools/agentScan.js.map +1 -1
- package/dist/tools/findingReviewTools.d.ts +22 -0
- package/dist/tools/findingReviewTools.js +121 -0
- package/dist/tools/findingReviewTools.js.map +1 -0
- package/dist/tools/fix.js +1 -1
- package/dist/tools/fix.js.map +1 -1
- package/dist/tools/map.js +191 -1
- package/dist/tools/map.js.map +1 -1
- package/dist/tools/runTests.d.ts +2 -0
- package/dist/tools/runTests.js +29 -0
- package/dist/tools/runTests.js.map +1 -0
- package/dist/utils/effectMock.d.ts +1 -0
- package/dist/utils/effectMock.js +162 -0
- package/dist/utils/effectMock.js.map +1 -0
- package/dist/utils/gitContext.d.ts +26 -0
- package/dist/utils/gitContext.js +317 -0
- package/dist/utils/gitContext.js.map +1 -0
- package/dist/utils/localTestRunner.d.ts +57 -2
- package/dist/utils/localTestRunner.js +122 -115
- package/dist/utils/localTestRunner.js.map +1 -1
- package/dist/utils/securityConfig.d.ts +17 -0
- package/dist/utils/securityConfig.js +192 -0
- package/dist/utils/securityConfig.js.map +1 -0
- package/dist/utils/testCommandPolicy.d.ts +36 -0
- package/dist/utils/testCommandPolicy.js +252 -0
- package/dist/utils/testCommandPolicy.js.map +1 -0
- package/dist/utils/testRunner.d.ts +56 -0
- package/dist/utils/testRunner.js +246 -0
- package/dist/utils/testRunner.js.map +1 -0
- package/dist/utils/testSafety.d.ts +40 -2
- package/dist/utils/testSafety.js +205 -26
- package/dist/utils/testSafety.js.map +1 -1
- package/dist/utils/verificationSandbox.d.ts +113 -0
- package/dist/utils/verificationSandbox.js +656 -0
- package/dist/utils/verificationSandbox.js.map +1 -0
- package/package.json +1 -1
|
@@ -1,131 +1,138 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
return ar;
|
|
24
|
-
};
|
|
25
|
-
return ownKeys(o);
|
|
26
|
-
};
|
|
27
|
-
return function (mod) {
|
|
28
|
-
if (mod && mod.__esModule) return mod;
|
|
29
|
-
var result = {};
|
|
30
|
-
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
-
__setModuleDefault(result, mod);
|
|
32
|
-
return result;
|
|
33
|
-
};
|
|
34
|
-
})();
|
|
2
|
+
/**
|
|
3
|
+
* Local test runner — drives execution of LLM-generated verification tests
|
|
4
|
+
* through the verification sandbox.
|
|
5
|
+
*
|
|
6
|
+
* SECURITY MODEL (post-rewrite):
|
|
7
|
+
* 1. `checkTestSafety()` runs first as defense-in-depth. If it blocks,
|
|
8
|
+
* we return early without spawning anything.
|
|
9
|
+
* 2. `detectSandbox()` returns the best available real isolation backend
|
|
10
|
+
* (Docker preferred, Deno as a JS/TS-only fallback). If neither is
|
|
11
|
+
* available, this runner returns `sandbox-unavailable` — it does NOT
|
|
12
|
+
* fall back to executing the script with host privileges.
|
|
13
|
+
* 3. The sandbox backend executes the script with no network, a
|
|
14
|
+
* read-only workspace, a tmpfs /tmp, capped CPU/memory/pids, and a
|
|
15
|
+
* scrubbed env (only NODE_ENV and PATH). The verdict is parsed from
|
|
16
|
+
* stdout/stderr against `/PASS:/` and `/FAIL:/` markers.
|
|
17
|
+
*
|
|
18
|
+
* The previous implementation spread the full `process.env` into the
|
|
19
|
+
* child and ran via `execFileSync` with no isolation. That allowed
|
|
20
|
+
* prompt-injected content in scanned source to ride into the
|
|
21
|
+
* verify-generate prompt and produce a test that exfiltrated secrets.
|
|
22
|
+
*/
|
|
35
23
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
24
|
+
exports.SANDBOX_UNAVAILABLE_MESSAGE = void 0;
|
|
36
25
|
exports.runLocalTest = runLocalTest;
|
|
37
|
-
|
|
38
|
-
const path = __importStar(require("path"));
|
|
39
|
-
const child_process_1 = require("child_process");
|
|
26
|
+
exports.runTestCommand = runTestCommand;
|
|
40
27
|
const testSafety_1 = require("./testSafety");
|
|
41
|
-
const
|
|
42
|
-
const
|
|
43
|
-
const
|
|
44
|
-
|
|
28
|
+
const effectMock_1 = require("./effectMock");
|
|
29
|
+
const verificationSandbox_1 = require("./verificationSandbox");
|
|
30
|
+
const DEFAULT_TIMEOUT_MS = 30_000;
|
|
31
|
+
/**
|
|
32
|
+
* The message shown when no isolation backend is available. Exported so the
|
|
33
|
+
* agent-scan tool can reuse it in its top-level `verifyHint` (instead of
|
|
34
|
+
* every finding carrying the full text in its `provenReason`).
|
|
35
|
+
*
|
|
36
|
+
* Why this matters: without a sandbox, the verify loop returns INCONCLUSIVE
|
|
37
|
+
* for every high/medium finding. The headline differentiator of Pipeline 2
|
|
38
|
+
* ("exploit-verified, not just LLM-believed") is offline. The user needs to
|
|
39
|
+
* know — in one place, with a copy-pasteable install path — what to do.
|
|
40
|
+
*/
|
|
41
|
+
exports.SANDBOX_UNAVAILABLE_MESSAGE = 'No verification sandbox backend (Docker or Deno) was detected locally. ' +
|
|
42
|
+
'Exploit verification was skipped — the finding is reported as INCONCLUSIVE ' +
|
|
43
|
+
'with confidence capped at 75%. To enable exploit-verified findings:\n' +
|
|
44
|
+
' • Docker (preferred, supports any language): https://docs.docker.com/get-docker\n' +
|
|
45
|
+
' • Deno (JS/TS only, lighter install): https://deno.com/#installation\n' +
|
|
46
|
+
'If neither is available, the API-side sandbox (on the server) is used as an ' +
|
|
47
|
+
'automatic fallback — no local install required, but slower (network round-trip per finding).';
|
|
48
|
+
async function runLocalTest(script, runner, workspaceRoot, options) {
|
|
49
|
+
// 1. Defense-in-depth static check.
|
|
45
50
|
const safety = (0, testSafety_1.checkTestSafety)(script, workspaceRoot);
|
|
46
51
|
if (!safety.allowed) {
|
|
47
|
-
return { verdict: '
|
|
52
|
+
return { verdict: 'blocked', output: `Test script blocked: ${safety.reason}`, exitCode: -1 };
|
|
48
53
|
}
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
const isTsRunner = runner === 'tsx' || runner === 'ts' || runner === 'bun' || runner === 'deno' || runner === 'pnpm-tsx' || runner === 'yarn-tsx';
|
|
54
|
-
const ext = isPython ? '.py' : (isTsRunner ? '.test.ts' : '.test.js');
|
|
55
|
-
const testFile = path.join(testDir, `verify-test-${Date.now()}-${Math.random().toString(36).slice(2, 8)}${ext}`);
|
|
56
|
-
try {
|
|
57
|
-
fs.writeFileSync(testFile, script, 'utf8');
|
|
58
|
-
let stdout = '';
|
|
59
|
-
let stderr = '';
|
|
60
|
-
let exitCode = 0;
|
|
61
|
-
let timedOut = false;
|
|
62
|
-
try {
|
|
63
|
-
const isWindows = process.platform === 'win32';
|
|
64
|
-
let runnerBin;
|
|
65
|
-
let runnerArgs;
|
|
66
|
-
if (runner === 'tsx') {
|
|
67
|
-
runnerBin = 'npx';
|
|
68
|
-
runnerArgs = ['tsx', testFile];
|
|
69
|
-
}
|
|
70
|
-
else if (runner === 'pnpm-tsx') {
|
|
71
|
-
runnerBin = 'pnpm';
|
|
72
|
-
runnerArgs = ['exec', 'tsx', testFile];
|
|
73
|
-
}
|
|
74
|
-
else if (runner === 'yarn-tsx') {
|
|
75
|
-
runnerBin = 'yarn';
|
|
76
|
-
runnerArgs = ['tsx', testFile];
|
|
77
|
-
}
|
|
78
|
-
else if (runner === 'deno') {
|
|
79
|
-
runnerBin = 'deno';
|
|
80
|
-
runnerArgs = ['run', '--allow-read', '--allow-env', testFile];
|
|
81
|
-
}
|
|
82
|
-
else if (runner === 'bun' && isWindows) {
|
|
83
|
-
runnerBin = 'npx';
|
|
84
|
-
runnerArgs = ['bun', testFile];
|
|
85
|
-
}
|
|
86
|
-
else {
|
|
87
|
-
runnerBin = runner;
|
|
88
|
-
runnerArgs = [testFile];
|
|
89
|
-
}
|
|
90
|
-
stdout = (0, child_process_1.execFileSync)(runnerBin, runnerArgs, {
|
|
91
|
-
cwd: workspaceRoot,
|
|
92
|
-
timeout: TIMEOUT_MS,
|
|
93
|
-
encoding: 'utf8',
|
|
94
|
-
stdio: ['pipe', 'pipe', 'pipe'],
|
|
95
|
-
env: { ...process.env, NODE_ENV: 'test' },
|
|
96
|
-
shell: isWindows,
|
|
97
|
-
});
|
|
98
|
-
}
|
|
99
|
-
catch (err) {
|
|
100
|
-
stdout = err.stdout || '';
|
|
101
|
-
stderr = err.stderr || '';
|
|
102
|
-
exitCode = err.status ?? 1;
|
|
103
|
-
if (err.signal === 'SIGTERM')
|
|
104
|
-
timedOut = true;
|
|
105
|
-
}
|
|
106
|
-
const output = (stdout + '\n' + stderr).trim();
|
|
107
|
-
if (timedOut) {
|
|
108
|
-
return { verdict: 'timeout', output: 'Test timed out after 30s', exitCode: -1 };
|
|
109
|
-
}
|
|
110
|
-
if (PASS_PATTERN.test(output)) {
|
|
111
|
-
return { verdict: 'pass', output, exitCode };
|
|
112
|
-
}
|
|
113
|
-
if (FAIL_PATTERN.test(output)) {
|
|
114
|
-
return { verdict: 'fail', output, exitCode };
|
|
115
|
-
}
|
|
116
|
-
if (exitCode === 0) {
|
|
117
|
-
return { verdict: 'error', output: output + '\nTest exited 0 but did not print PASS: or FAIL:', exitCode };
|
|
54
|
+
if (options?.setupScript) {
|
|
55
|
+
const setupSafety = (0, testSafety_1.checkTestSafety)(options.setupScript, workspaceRoot);
|
|
56
|
+
if (!setupSafety.allowed) {
|
|
57
|
+
return { verdict: 'blocked', output: `Setup script blocked: ${setupSafety.reason}`, exitCode: -1 };
|
|
118
58
|
}
|
|
119
|
-
return { verdict: 'error', output, exitCode };
|
|
120
59
|
}
|
|
121
|
-
|
|
122
|
-
|
|
60
|
+
// 2. Resolve the sandbox backend. Production: detectSandbox(). Tests: inject.
|
|
61
|
+
const backend = options?.sandboxBackend ?? (0, verificationSandbox_1.detectSandbox)();
|
|
62
|
+
if (!backend) {
|
|
63
|
+
return {
|
|
64
|
+
verdict: 'sandbox-unavailable',
|
|
65
|
+
output: exports.SANDBOX_UNAVAILABLE_MESSAGE,
|
|
66
|
+
exitCode: -1,
|
|
67
|
+
};
|
|
123
68
|
}
|
|
124
|
-
|
|
69
|
+
// 2b. Auto-create effect mock for JS/TS projects (harmless if unused).
|
|
70
|
+
// Must happen before the sandbox mounts the workspace.
|
|
71
|
+
if (runner !== 'python' && runner !== 'python3') {
|
|
125
72
|
try {
|
|
126
|
-
|
|
73
|
+
(0, effectMock_1.createEffectMock)(workspaceRoot);
|
|
127
74
|
}
|
|
128
75
|
catch { }
|
|
129
76
|
}
|
|
77
|
+
// 3. Execute inside the sandbox.
|
|
78
|
+
const execOpts = {
|
|
79
|
+
script,
|
|
80
|
+
runner,
|
|
81
|
+
workspaceRoot,
|
|
82
|
+
setupScript: options?.setupScript,
|
|
83
|
+
timeoutMs: options?.timeoutMs ?? DEFAULT_TIMEOUT_MS,
|
|
84
|
+
signal: options?.signal,
|
|
85
|
+
};
|
|
86
|
+
const result = await backend.execute(execOpts);
|
|
87
|
+
// Preserve the "exited 0 but no PASS/FAIL marker" message — it's a
|
|
88
|
+
// useful signal that the test ran but didn't follow the protocol.
|
|
89
|
+
if (result.verdict === 'error' && result.exitCode === 0) {
|
|
90
|
+
return {
|
|
91
|
+
verdict: 'error',
|
|
92
|
+
output: `${result.output}\nTest exited 0 but did not print PASS: or FAIL:`,
|
|
93
|
+
exitCode: result.exitCode,
|
|
94
|
+
backend: result.backend,
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
return {
|
|
98
|
+
verdict: result.verdict,
|
|
99
|
+
output: result.output,
|
|
100
|
+
exitCode: result.exitCode,
|
|
101
|
+
backend: result.backend,
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Run a validated test command (npm test, pytest, etc.) inside the sandbox.
|
|
106
|
+
* Uses command mode — no script file is written; the executable + args are
|
|
107
|
+
* passed directly to the sandbox backend. The sandbox enforces network=none,
|
|
108
|
+
* read-only workspace, and resource limits.
|
|
109
|
+
*
|
|
110
|
+
* Returns the same LocalTestResult shape as runLocalTest, with exit-code-based
|
|
111
|
+
* verdicts (0 = pass, nonzero = fail).
|
|
112
|
+
*/
|
|
113
|
+
async function runTestCommand(executable, args, workspaceRoot, options) {
|
|
114
|
+
const backend = options?.sandboxBackend ?? (0, verificationSandbox_1.detectSandbox)();
|
|
115
|
+
if (!backend) {
|
|
116
|
+
return {
|
|
117
|
+
verdict: 'sandbox-unavailable',
|
|
118
|
+
output: exports.SANDBOX_UNAVAILABLE_MESSAGE,
|
|
119
|
+
exitCode: -1,
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
const execOpts = {
|
|
123
|
+
mode: 'command',
|
|
124
|
+
executable,
|
|
125
|
+
args,
|
|
126
|
+
workspaceRoot,
|
|
127
|
+
timeoutMs: options?.timeoutMs ?? DEFAULT_TIMEOUT_MS,
|
|
128
|
+
signal: options?.signal,
|
|
129
|
+
};
|
|
130
|
+
const result = await backend.execute(execOpts);
|
|
131
|
+
return {
|
|
132
|
+
verdict: result.verdict,
|
|
133
|
+
output: result.output,
|
|
134
|
+
exitCode: result.exitCode,
|
|
135
|
+
backend: result.backend,
|
|
136
|
+
};
|
|
130
137
|
}
|
|
131
138
|
//# sourceMappingURL=localTestRunner.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"localTestRunner.js","sourceRoot":"","sources":["../../src/utils/localTestRunner.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"localTestRunner.js","sourceRoot":"","sources":["../../src/utils/localTestRunner.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Security config discovery — finds and parses security-relevant config
|
|
3
|
+
* files in the workspace so the agent can reason about ACTUAL config
|
|
4
|
+
* instead of guessing from prompt rules.
|
|
5
|
+
*
|
|
6
|
+
* Replaces the system prompt's hardcoded FP-suppression rules ("config-gated
|
|
7
|
+
* rate limits are not vulnerabilities") with a tool that lets the agent
|
|
8
|
+
* check the config and reason about what it finds.
|
|
9
|
+
*
|
|
10
|
+
* Security rules:
|
|
11
|
+
* - For .env files, return key NAMES and set/unset status only — never values
|
|
12
|
+
* - Prefer .env.example over real .env
|
|
13
|
+
* - All paths workspace-confined via resolveWorkspacePath
|
|
14
|
+
* - Output truncated to 16KB
|
|
15
|
+
*/
|
|
16
|
+
export type ConfigKind = 'auth' | 'cors' | 'rate_limit' | 'headers' | 'env' | 'all';
|
|
17
|
+
export declare function readSecurityConfig(workspaceRoot: string, kind?: ConfigKind): Promise<string>;
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Security config discovery — finds and parses security-relevant config
|
|
4
|
+
* files in the workspace so the agent can reason about ACTUAL config
|
|
5
|
+
* instead of guessing from prompt rules.
|
|
6
|
+
*
|
|
7
|
+
* Replaces the system prompt's hardcoded FP-suppression rules ("config-gated
|
|
8
|
+
* rate limits are not vulnerabilities") with a tool that lets the agent
|
|
9
|
+
* check the config and reason about what it finds.
|
|
10
|
+
*
|
|
11
|
+
* Security rules:
|
|
12
|
+
* - For .env files, return key NAMES and set/unset status only — never values
|
|
13
|
+
* - Prefer .env.example over real .env
|
|
14
|
+
* - All paths workspace-confined via resolveWorkspacePath
|
|
15
|
+
* - Output truncated to 16KB
|
|
16
|
+
*/
|
|
17
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
18
|
+
if (k2 === undefined) k2 = k;
|
|
19
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
20
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
21
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
22
|
+
}
|
|
23
|
+
Object.defineProperty(o, k2, desc);
|
|
24
|
+
}) : (function(o, m, k, k2) {
|
|
25
|
+
if (k2 === undefined) k2 = k;
|
|
26
|
+
o[k2] = m[k];
|
|
27
|
+
}));
|
|
28
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
29
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
30
|
+
}) : function(o, v) {
|
|
31
|
+
o["default"] = v;
|
|
32
|
+
});
|
|
33
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
34
|
+
var ownKeys = function(o) {
|
|
35
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
36
|
+
var ar = [];
|
|
37
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
38
|
+
return ar;
|
|
39
|
+
};
|
|
40
|
+
return ownKeys(o);
|
|
41
|
+
};
|
|
42
|
+
return function (mod) {
|
|
43
|
+
if (mod && mod.__esModule) return mod;
|
|
44
|
+
var result = {};
|
|
45
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
46
|
+
__setModuleDefault(result, mod);
|
|
47
|
+
return result;
|
|
48
|
+
};
|
|
49
|
+
})();
|
|
50
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
51
|
+
exports.readSecurityConfig = readSecurityConfig;
|
|
52
|
+
const fs = __importStar(require("fs"));
|
|
53
|
+
const path = __importStar(require("path"));
|
|
54
|
+
const files_1 = require("./files");
|
|
55
|
+
const CONFIG_PATTERNS = [
|
|
56
|
+
{ file: '.env.example', kind: 'env', description: 'Environment variable template (safe to show keys)' },
|
|
57
|
+
{ file: '.env', kind: 'env', description: 'Environment variables (KEYS ONLY — values redacted)' },
|
|
58
|
+
{ file: '.env.local', kind: 'env', description: 'Local environment overrides (KEYS ONLY)' },
|
|
59
|
+
{ file: '.env.production', kind: 'env', description: 'Production environment (KEYS ONLY)' },
|
|
60
|
+
{ file: 'helmet.config.js', kind: 'headers', description: 'Helmet security headers config' },
|
|
61
|
+
{ file: 'helmet.config.ts', kind: 'headers', description: 'Helmet security headers config' },
|
|
62
|
+
{ file: 'cors.config.js', kind: 'cors', description: 'CORS configuration' },
|
|
63
|
+
{ file: 'cors.config.ts', kind: 'cors', description: 'CORS configuration' },
|
|
64
|
+
{ file: 'next.config.js', kind: 'headers', description: 'Next.js config (may contain headers/CSP)' },
|
|
65
|
+
{ file: 'next.config.ts', kind: 'headers', description: 'Next.js config (may contain headers/CSP)' },
|
|
66
|
+
{ file: 'next.config.mjs', kind: 'headers', description: 'Next.js config (may contain headers/CSP)' },
|
|
67
|
+
{ file: 'express-rate-limit.config.js', kind: 'rate_limit', description: 'Express rate limit config' },
|
|
68
|
+
{ file: 'rate-limit.config.js', kind: 'rate_limit', description: 'Rate limit config' },
|
|
69
|
+
{ file: 'rate-limit.config.ts', kind: 'rate_limit', description: 'Rate limit config' },
|
|
70
|
+
];
|
|
71
|
+
function findConfigFiles(workspaceRoot, kind) {
|
|
72
|
+
const out = [];
|
|
73
|
+
for (const pattern of CONFIG_PATTERNS) {
|
|
74
|
+
if (kind !== 'all' && pattern.kind !== kind)
|
|
75
|
+
continue;
|
|
76
|
+
const candidates = [
|
|
77
|
+
path.join(workspaceRoot, pattern.file),
|
|
78
|
+
path.join(workspaceRoot, 'config', pattern.file),
|
|
79
|
+
path.join(workspaceRoot, 'src', pattern.file),
|
|
80
|
+
];
|
|
81
|
+
for (const candidate of candidates) {
|
|
82
|
+
if (fs.existsSync(candidate)) {
|
|
83
|
+
const rel = path.relative(workspaceRoot, candidate).replace(/\\/g, '/');
|
|
84
|
+
out.push({ path: rel, kind: pattern.kind, description: pattern.description });
|
|
85
|
+
break;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
return out;
|
|
90
|
+
}
|
|
91
|
+
function redactEnvValues(content) {
|
|
92
|
+
return content
|
|
93
|
+
.split('\n')
|
|
94
|
+
.map(line => {
|
|
95
|
+
const trimmed = line.trim();
|
|
96
|
+
if (trimmed.startsWith('#') || trimmed === '')
|
|
97
|
+
return line;
|
|
98
|
+
const eqIdx = line.indexOf('=');
|
|
99
|
+
if (eqIdx === -1)
|
|
100
|
+
return line;
|
|
101
|
+
const key = line.slice(0, eqIdx).trim();
|
|
102
|
+
const isSecret = /SECRET|KEY|TOKEN|PASSWORD|PRIVATE|CREDENTIAL/i.test(key);
|
|
103
|
+
if (isSecret)
|
|
104
|
+
return `${key}=REDACTED`;
|
|
105
|
+
return `${key}=set`;
|
|
106
|
+
})
|
|
107
|
+
.join('\n');
|
|
108
|
+
}
|
|
109
|
+
function parseConfigFile(filePath, workspaceRoot) {
|
|
110
|
+
const abs = (0, files_1.resolveWorkspacePath)(workspaceRoot, filePath);
|
|
111
|
+
const content = fs.readFileSync(abs, 'utf8');
|
|
112
|
+
const ext = path.extname(filePath).toLowerCase();
|
|
113
|
+
if (filePath.includes('.env')) {
|
|
114
|
+
return redactEnvValues(content);
|
|
115
|
+
}
|
|
116
|
+
if (ext === '.js' || ext === '.ts' || ext === '.mjs' || ext === '.json') {
|
|
117
|
+
return content.slice(0, 12000);
|
|
118
|
+
}
|
|
119
|
+
return content.slice(0, 12000);
|
|
120
|
+
}
|
|
121
|
+
function searchInlineConfig(workspaceRoot, kind) {
|
|
122
|
+
const out = [];
|
|
123
|
+
const patterns = [
|
|
124
|
+
{ regex: 'helmet\\s*\\(', kind: 'headers', label: 'helmet() call' },
|
|
125
|
+
{ regex: 'cors\\s*\\(', kind: 'cors', label: 'cors() call' },
|
|
126
|
+
{ regex: 'rateLimit\\s*\\(', kind: 'rate_limit', label: 'rateLimit() call' },
|
|
127
|
+
{ regex: 'rate-limit', kind: 'rate_limit', label: 'rate-limit import' },
|
|
128
|
+
{ regex: 'express-rate-limit', kind: 'rate_limit', label: 'express-rate-limit import' },
|
|
129
|
+
{ regex: 'Content-Security-Policy|contentSecurityPolicy|CSP', kind: 'headers', label: 'CSP header' },
|
|
130
|
+
{ regex: 'HSTS|strict-Transport-Security', kind: 'headers', label: 'HSTS header' },
|
|
131
|
+
{ regex: 'X-Frame-Options|xFrameOptions', kind: 'headers', label: 'X-Frame-Options' },
|
|
132
|
+
{ regex: 'X-Content-Type-Options|xContentTypeOptions', kind: 'headers', label: 'X-Content-Type-Options' },
|
|
133
|
+
];
|
|
134
|
+
for (const p of patterns) {
|
|
135
|
+
if (kind !== 'all' && p.kind !== kind)
|
|
136
|
+
continue;
|
|
137
|
+
try {
|
|
138
|
+
const { searchCode } = require('../utils/searchCode');
|
|
139
|
+
const result = searchCode(workspaceRoot, p.regex, '*.{js,ts,mjs,jsx,tsx}');
|
|
140
|
+
if (result && result.hits) {
|
|
141
|
+
for (const hit of result.hits.slice(0, 3)) {
|
|
142
|
+
out.push({
|
|
143
|
+
file: hit.path,
|
|
144
|
+
snippet: hit.text.slice(0, 200),
|
|
145
|
+
line: hit.line,
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
catch { /* best-effort */ }
|
|
151
|
+
}
|
|
152
|
+
return out;
|
|
153
|
+
}
|
|
154
|
+
async function readSecurityConfig(workspaceRoot, kind = 'all') {
|
|
155
|
+
const configFiles = findConfigFiles(workspaceRoot, kind);
|
|
156
|
+
const inlineConfigs = searchInlineConfig(workspaceRoot, kind);
|
|
157
|
+
if (configFiles.length === 0 && inlineConfigs.length === 0) {
|
|
158
|
+
return `No ${kind === 'all' ? '' : kind + ' '}security config files found. The project may not have explicit security config — check if it relies on framework defaults or environment variables.`;
|
|
159
|
+
}
|
|
160
|
+
const lines = [`Security config (${kind === 'all' ? 'all' : kind}):`];
|
|
161
|
+
if (configFiles.length > 0) {
|
|
162
|
+
lines.push('', 'Config files:');
|
|
163
|
+
for (const cf of configFiles.slice(0, 10)) {
|
|
164
|
+
lines.push(` ${cf.path} — ${cf.description}`);
|
|
165
|
+
try {
|
|
166
|
+
const content = parseConfigFile(cf.path, workspaceRoot);
|
|
167
|
+
lines.push(' ```');
|
|
168
|
+
for (const line of content.split('\n').slice(0, 40)) {
|
|
169
|
+
lines.push(` ${line}`);
|
|
170
|
+
}
|
|
171
|
+
if (content.split('\n').length > 40) {
|
|
172
|
+
lines.push(' ... (truncated)');
|
|
173
|
+
}
|
|
174
|
+
lines.push(' ```');
|
|
175
|
+
}
|
|
176
|
+
catch (e) {
|
|
177
|
+
lines.push(` (error reading: ${e.message})`);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
if (inlineConfigs.length > 0) {
|
|
182
|
+
lines.push('', 'Inline config found in source:');
|
|
183
|
+
for (const ic of inlineConfigs.slice(0, 10)) {
|
|
184
|
+
lines.push(` ${ic.file}:${ic.line}`);
|
|
185
|
+
lines.push(` ${ic.snippet}`);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
lines.push('');
|
|
189
|
+
lines.push('Use this evidence to determine if a security control is actually configured before reporting it as a vulnerability. A missing config file does not mean the control is absent — check inline config too.');
|
|
190
|
+
return lines.join('\n');
|
|
191
|
+
}
|
|
192
|
+
//# sourceMappingURL=securityConfig.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"securityConfig.js","sourceRoot":"","sources":["../../src/utils/securityConfig.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Test command policy — strict validation for existing-test mode of run_tests.
|
|
3
|
+
*
|
|
4
|
+
* Allows only known package-manager test commands (npm/pnpm/yarn/bun test,
|
|
5
|
+
* pytest) with optional file/pattern arguments. Rejects shell operators,
|
|
6
|
+
* arbitrary executables, lifecycle commands other than test, absolute paths
|
|
7
|
+
* outside the workspace, and path traversal.
|
|
8
|
+
*
|
|
9
|
+
* Security rules:
|
|
10
|
+
* - No shell operators (&&, ||, ;, |, >, <, $(), backticks)
|
|
11
|
+
* - Only the `test` lifecycle command (no build/install/publish/run/exec)
|
|
12
|
+
* - No --shell/--script-shell escape flags
|
|
13
|
+
* - All file paths workspace-confined via resolveWorkspacePath
|
|
14
|
+
* - Output is a validated executable + args array (never a shell string)
|
|
15
|
+
*/
|
|
16
|
+
export type TestPackageManager = 'npm' | 'pnpm' | 'yarn' | 'bun' | 'pytest';
|
|
17
|
+
export interface ValidatedTestCommand {
|
|
18
|
+
packageManager: TestPackageManager;
|
|
19
|
+
executable: string;
|
|
20
|
+
args: string[];
|
|
21
|
+
cwd: string;
|
|
22
|
+
selectedFiles: string[];
|
|
23
|
+
timeoutMs: number;
|
|
24
|
+
}
|
|
25
|
+
export interface CommandPolicyResult {
|
|
26
|
+
ok: boolean;
|
|
27
|
+
command?: ValidatedTestCommand;
|
|
28
|
+
error?: string;
|
|
29
|
+
}
|
|
30
|
+
export declare function validateTestCommand(input: {
|
|
31
|
+
packageManager?: string;
|
|
32
|
+
testFiles?: string[];
|
|
33
|
+
testPattern?: string;
|
|
34
|
+
timeoutMs?: number;
|
|
35
|
+
}, workspaceRoot: string): CommandPolicyResult;
|
|
36
|
+
export declare function formatValidatedCommand(cmd: ValidatedTestCommand): string;
|