@securecode-ai/mcp 0.5.5 → 0.5.7
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/README.md +52 -12
- 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
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Test command policy — strict validation for existing-test mode of run_tests.
|
|
4
|
+
*
|
|
5
|
+
* Allows only known package-manager test commands (npm/pnpm/yarn/bun test,
|
|
6
|
+
* pytest) with optional file/pattern arguments. Rejects shell operators,
|
|
7
|
+
* arbitrary executables, lifecycle commands other than test, absolute paths
|
|
8
|
+
* outside the workspace, and path traversal.
|
|
9
|
+
*
|
|
10
|
+
* Security rules:
|
|
11
|
+
* - No shell operators (&&, ||, ;, |, >, <, $(), backticks)
|
|
12
|
+
* - Only the `test` lifecycle command (no build/install/publish/run/exec)
|
|
13
|
+
* - No --shell/--script-shell escape flags
|
|
14
|
+
* - All file paths workspace-confined via resolveWorkspacePath
|
|
15
|
+
* - Output is a validated executable + args array (never a shell string)
|
|
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.validateTestCommand = validateTestCommand;
|
|
52
|
+
exports.formatValidatedCommand = formatValidatedCommand;
|
|
53
|
+
const path = __importStar(require("path"));
|
|
54
|
+
const files_1 = require("./files");
|
|
55
|
+
const SUPPORTED_PMS = ['npm', 'pnpm', 'yarn', 'bun', 'pytest'];
|
|
56
|
+
const SHELL_OPERATORS = ['&&', '||', ';', '|', '>', '<', '$(', '`', '>&', '<&'];
|
|
57
|
+
const BLOCKED_FLAGS = new Set([
|
|
58
|
+
'--shell', '--script-shell', '--foreground-scripts', '--no-shell',
|
|
59
|
+
'-S', '--bash', '--sh', '--cmd', '--exec', '--host',
|
|
60
|
+
]);
|
|
61
|
+
const NPM_TEST_SUBCOMMANDS = new Set(['test', 't', 'tst']);
|
|
62
|
+
const NPM_FORBIDDEN_SUBCOMMANDS = new Set([
|
|
63
|
+
'run', 'run-script', 'exec', 'x', 'install', 'i', 'add', 'isntall',
|
|
64
|
+
'uninstall', 'r', 'rm', 'publish', 'unpublish', 'build', 'rebuild',
|
|
65
|
+
'start', 'stop', 'restart', 'pack', 'ci', 'fund', 'link', 'unlink',
|
|
66
|
+
'create', 'init', 'hook', 'org', 'team', 'token', 'owner', 'access',
|
|
67
|
+
'dist-tag', 'version', 'deprecate', 'profile', 'config', 'cache',
|
|
68
|
+
'clean-install', 'ci-install', 'force-clean-install',
|
|
69
|
+
]);
|
|
70
|
+
const MAX_PATTERN_LENGTH = 200;
|
|
71
|
+
const MAX_FILES = 50;
|
|
72
|
+
const DEFAULT_TIMEOUT_MS = 60_000;
|
|
73
|
+
const MAX_TIMEOUT_MS = 300_000;
|
|
74
|
+
function isWorkspaceRelative(p) {
|
|
75
|
+
if (!p)
|
|
76
|
+
return false;
|
|
77
|
+
if (path.isAbsolute(p))
|
|
78
|
+
return false;
|
|
79
|
+
if (p.includes('..'))
|
|
80
|
+
return false;
|
|
81
|
+
if (p.startsWith('~'))
|
|
82
|
+
return false;
|
|
83
|
+
return true;
|
|
84
|
+
}
|
|
85
|
+
function isSafeFilePath(p, workspaceRoot) {
|
|
86
|
+
if (!isWorkspaceRelative(p))
|
|
87
|
+
return false;
|
|
88
|
+
try {
|
|
89
|
+
(0, files_1.resolveWorkspacePath)(workspaceRoot, p);
|
|
90
|
+
return true;
|
|
91
|
+
}
|
|
92
|
+
catch {
|
|
93
|
+
return false;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
function containsShellOperators(s) {
|
|
97
|
+
return SHELL_OPERATORS.some(op => s.includes(op));
|
|
98
|
+
}
|
|
99
|
+
function normalizePackageManager(pm) {
|
|
100
|
+
const lower = (pm || '').toLowerCase();
|
|
101
|
+
return SUPPORTED_PMS.includes(lower) ? lower : null;
|
|
102
|
+
}
|
|
103
|
+
function validateTimeoutMs(timeoutMs) {
|
|
104
|
+
if (timeoutMs === undefined || timeoutMs === null)
|
|
105
|
+
return DEFAULT_TIMEOUT_MS;
|
|
106
|
+
const n = Number(timeoutMs);
|
|
107
|
+
if (!Number.isFinite(n) || n <= 0)
|
|
108
|
+
return DEFAULT_TIMEOUT_MS;
|
|
109
|
+
return Math.min(Math.floor(n), MAX_TIMEOUT_MS);
|
|
110
|
+
}
|
|
111
|
+
function buildNpmArgs(testFiles, testPattern) {
|
|
112
|
+
const args = ['test'];
|
|
113
|
+
if (testFiles.length > 0 || testPattern) {
|
|
114
|
+
args.push('--');
|
|
115
|
+
if (testPattern)
|
|
116
|
+
args.push(testPattern);
|
|
117
|
+
for (const f of testFiles)
|
|
118
|
+
args.push(f);
|
|
119
|
+
}
|
|
120
|
+
return args;
|
|
121
|
+
}
|
|
122
|
+
function buildPnpmArgs(testFiles, testPattern) {
|
|
123
|
+
const args = ['test'];
|
|
124
|
+
if (testFiles.length > 0 || testPattern) {
|
|
125
|
+
args.push('--');
|
|
126
|
+
if (testPattern)
|
|
127
|
+
args.push(testPattern);
|
|
128
|
+
for (const f of testFiles)
|
|
129
|
+
args.push(f);
|
|
130
|
+
}
|
|
131
|
+
return args;
|
|
132
|
+
}
|
|
133
|
+
function buildYarnArgs(testFiles, testPattern) {
|
|
134
|
+
const args = ['test'];
|
|
135
|
+
if (testPattern)
|
|
136
|
+
args.push(testPattern);
|
|
137
|
+
for (const f of testFiles)
|
|
138
|
+
args.push(f);
|
|
139
|
+
return args;
|
|
140
|
+
}
|
|
141
|
+
function buildBunArgs(testFiles, testPattern) {
|
|
142
|
+
const args = ['test'];
|
|
143
|
+
if (testPattern)
|
|
144
|
+
args.push(testPattern);
|
|
145
|
+
for (const f of testFiles)
|
|
146
|
+
args.push(f);
|
|
147
|
+
return args;
|
|
148
|
+
}
|
|
149
|
+
function buildPytestArgs(testFiles, testPattern) {
|
|
150
|
+
const args = [];
|
|
151
|
+
if (testPattern)
|
|
152
|
+
args.push('-k', testPattern);
|
|
153
|
+
for (const f of testFiles)
|
|
154
|
+
args.push(f);
|
|
155
|
+
return args;
|
|
156
|
+
}
|
|
157
|
+
function detectSubcommand(arg) {
|
|
158
|
+
if (NPM_TEST_SUBCOMMANDS.has(arg))
|
|
159
|
+
return 'test';
|
|
160
|
+
if (NPM_FORBIDDEN_SUBCOMMANDS.has(arg))
|
|
161
|
+
return arg;
|
|
162
|
+
return null;
|
|
163
|
+
}
|
|
164
|
+
function validateTestCommand(input, workspaceRoot) {
|
|
165
|
+
const pm = input.packageManager
|
|
166
|
+
? normalizePackageManager(input.packageManager)
|
|
167
|
+
: null;
|
|
168
|
+
if (input.packageManager && !pm) {
|
|
169
|
+
return { ok: false, error: `Unsupported package manager: ${input.packageManager}. Supported: ${SUPPORTED_PMS.join(', ')}` };
|
|
170
|
+
}
|
|
171
|
+
const testFilesRaw = Array.isArray(input.testFiles) ? input.testFiles : [];
|
|
172
|
+
if (testFilesRaw.length > MAX_FILES) {
|
|
173
|
+
return { ok: false, error: `Too many test files (max ${MAX_FILES})` };
|
|
174
|
+
}
|
|
175
|
+
const testPattern = input.testPattern;
|
|
176
|
+
if (testPattern !== undefined && testPattern !== null) {
|
|
177
|
+
if (typeof testPattern !== 'string') {
|
|
178
|
+
return { ok: false, error: 'testPattern must be a string' };
|
|
179
|
+
}
|
|
180
|
+
if (testPattern.length > MAX_PATTERN_LENGTH) {
|
|
181
|
+
return { ok: false, error: `testPattern too long (max ${MAX_PATTERN_LENGTH} chars)` };
|
|
182
|
+
}
|
|
183
|
+
if (containsShellOperators(testPattern)) {
|
|
184
|
+
return { ok: false, error: `testPattern contains forbidden shell operators` };
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
const testFiles = [];
|
|
188
|
+
for (const f of testFilesRaw) {
|
|
189
|
+
if (typeof f !== 'string' || f.length === 0) {
|
|
190
|
+
return { ok: false, error: 'testFiles must be non-empty strings' };
|
|
191
|
+
}
|
|
192
|
+
if (containsShellOperators(f)) {
|
|
193
|
+
return { ok: false, error: `test file path contains forbidden shell operators: ${f}` };
|
|
194
|
+
}
|
|
195
|
+
if (!isSafeFilePath(f, workspaceRoot)) {
|
|
196
|
+
return { ok: false, error: `test file path is outside workspace or unsafe: ${f}` };
|
|
197
|
+
}
|
|
198
|
+
testFiles.push(f.replace(/\\/g, '/'));
|
|
199
|
+
}
|
|
200
|
+
const timeoutMs = validateTimeoutMs(input.timeoutMs);
|
|
201
|
+
const packageManager = pm || 'npm';
|
|
202
|
+
let executable;
|
|
203
|
+
let args;
|
|
204
|
+
switch (packageManager) {
|
|
205
|
+
case 'npm':
|
|
206
|
+
executable = 'npm';
|
|
207
|
+
args = buildNpmArgs(testFiles, testPattern);
|
|
208
|
+
break;
|
|
209
|
+
case 'pnpm':
|
|
210
|
+
executable = 'pnpm';
|
|
211
|
+
args = buildPnpmArgs(testFiles, testPattern);
|
|
212
|
+
break;
|
|
213
|
+
case 'yarn':
|
|
214
|
+
executable = 'yarn';
|
|
215
|
+
args = buildYarnArgs(testFiles, testPattern);
|
|
216
|
+
break;
|
|
217
|
+
case 'bun':
|
|
218
|
+
executable = 'bun';
|
|
219
|
+
args = buildBunArgs(testFiles, testPattern);
|
|
220
|
+
break;
|
|
221
|
+
case 'pytest':
|
|
222
|
+
executable = 'pytest';
|
|
223
|
+
args = buildPytestArgs(testFiles, testPattern);
|
|
224
|
+
break;
|
|
225
|
+
default:
|
|
226
|
+
return { ok: false, error: `Unhandled package manager: ${packageManager}` };
|
|
227
|
+
}
|
|
228
|
+
for (const a of args) {
|
|
229
|
+
if (BLOCKED_FLAGS.has(a)) {
|
|
230
|
+
return { ok: false, error: `Forbidden flag in test command: ${a}` };
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
const sub = detectSubcommand(args[0] || '');
|
|
234
|
+
if (sub && sub !== 'test') {
|
|
235
|
+
return { ok: false, error: `Forbidden npm subcommand: ${sub}. Only "test" is allowed.` };
|
|
236
|
+
}
|
|
237
|
+
return {
|
|
238
|
+
ok: true,
|
|
239
|
+
command: {
|
|
240
|
+
packageManager,
|
|
241
|
+
executable,
|
|
242
|
+
args,
|
|
243
|
+
cwd: workspaceRoot,
|
|
244
|
+
selectedFiles: testFiles,
|
|
245
|
+
timeoutMs,
|
|
246
|
+
},
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
function formatValidatedCommand(cmd) {
|
|
250
|
+
return `${cmd.executable} ${cmd.args.join(' ')}`;
|
|
251
|
+
}
|
|
252
|
+
//# sourceMappingURL=testCommandPolicy.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"testCommandPolicy.js","sourceRoot":"","sources":["../../src/utils/testCommandPolicy.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared test runner — the core service for run_tests (both public MCP tool
|
|
3
|
+
* and agent action). Handles:
|
|
4
|
+
* - Request validation
|
|
5
|
+
* - Human approval (every execution, no exceptions)
|
|
6
|
+
* - Sandbox execution via localTestRunner (generated) or runTestCommand (existing)
|
|
7
|
+
* - Bounded, redacted output
|
|
8
|
+
*
|
|
9
|
+
* Security model:
|
|
10
|
+
* - Approval is required before every execution — no batch grants, no timed grants.
|
|
11
|
+
* - The approval hash binds to mode, command/script hash, runner, timeout, workspace.
|
|
12
|
+
* - No execution occurs if approval is denied or times out.
|
|
13
|
+
* - No host fallback when no sandbox is available.
|
|
14
|
+
*/
|
|
15
|
+
import { ApprovalBroker } from '../approval/broker';
|
|
16
|
+
import { type ValidatedTestCommand } from './testCommandPolicy';
|
|
17
|
+
export type RunTestsMode = 'existing' | 'generated';
|
|
18
|
+
export interface RunTestsRequest {
|
|
19
|
+
mode: RunTestsMode;
|
|
20
|
+
testFiles?: string[];
|
|
21
|
+
testPattern?: string;
|
|
22
|
+
packageManager?: string;
|
|
23
|
+
script?: string;
|
|
24
|
+
runner?: string;
|
|
25
|
+
setupScript?: string;
|
|
26
|
+
timeoutMs?: number;
|
|
27
|
+
}
|
|
28
|
+
export type RunTestsStatus = 'passed' | 'failed' | 'error' | 'timeout' | 'blocked' | 'sandbox-unavailable' | 'denied';
|
|
29
|
+
export interface RunTestsResult {
|
|
30
|
+
approved: boolean;
|
|
31
|
+
requestId?: string;
|
|
32
|
+
mode: RunTestsMode;
|
|
33
|
+
status: RunTestsStatus;
|
|
34
|
+
exitCode: number;
|
|
35
|
+
output: string;
|
|
36
|
+
backend?: string;
|
|
37
|
+
command?: {
|
|
38
|
+
executable: string;
|
|
39
|
+
args: string[];
|
|
40
|
+
};
|
|
41
|
+
durationMs: number;
|
|
42
|
+
}
|
|
43
|
+
export interface ValidationFailure {
|
|
44
|
+
ok: false;
|
|
45
|
+
error: string;
|
|
46
|
+
}
|
|
47
|
+
export declare function validateRunTestsRequest(req: RunTestsRequest, workspaceRoot: string): {
|
|
48
|
+
ok: true;
|
|
49
|
+
command?: ValidatedTestCommand;
|
|
50
|
+
} | ValidationFailure;
|
|
51
|
+
export declare function runTests(req: RunTestsRequest, workspaceRoot: string, options?: {
|
|
52
|
+
signal?: AbortSignal;
|
|
53
|
+
sandboxBackend?: any;
|
|
54
|
+
/** Skip approval — for agent executor internal use only when the broker is managed externally. */
|
|
55
|
+
broker?: ApprovalBroker;
|
|
56
|
+
}): Promise<RunTestsResult>;
|
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Shared test runner — the core service for run_tests (both public MCP tool
|
|
4
|
+
* and agent action). Handles:
|
|
5
|
+
* - Request validation
|
|
6
|
+
* - Human approval (every execution, no exceptions)
|
|
7
|
+
* - Sandbox execution via localTestRunner (generated) or runTestCommand (existing)
|
|
8
|
+
* - Bounded, redacted output
|
|
9
|
+
*
|
|
10
|
+
* Security model:
|
|
11
|
+
* - Approval is required before every execution — no batch grants, no timed grants.
|
|
12
|
+
* - The approval hash binds to mode, command/script hash, runner, timeout, workspace.
|
|
13
|
+
* - No execution occurs if approval is denied or times out.
|
|
14
|
+
* - No host fallback when no sandbox is available.
|
|
15
|
+
*/
|
|
16
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
17
|
+
if (k2 === undefined) k2 = k;
|
|
18
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
19
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
20
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
21
|
+
}
|
|
22
|
+
Object.defineProperty(o, k2, desc);
|
|
23
|
+
}) : (function(o, m, k, k2) {
|
|
24
|
+
if (k2 === undefined) k2 = k;
|
|
25
|
+
o[k2] = m[k];
|
|
26
|
+
}));
|
|
27
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
28
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
29
|
+
}) : function(o, v) {
|
|
30
|
+
o["default"] = v;
|
|
31
|
+
});
|
|
32
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
33
|
+
var ownKeys = function(o) {
|
|
34
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
35
|
+
var ar = [];
|
|
36
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
37
|
+
return ar;
|
|
38
|
+
};
|
|
39
|
+
return ownKeys(o);
|
|
40
|
+
};
|
|
41
|
+
return function (mod) {
|
|
42
|
+
if (mod && mod.__esModule) return mod;
|
|
43
|
+
var result = {};
|
|
44
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
45
|
+
__setModuleDefault(result, mod);
|
|
46
|
+
return result;
|
|
47
|
+
};
|
|
48
|
+
})();
|
|
49
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
50
|
+
exports.validateRunTestsRequest = validateRunTestsRequest;
|
|
51
|
+
exports.runTests = runTests;
|
|
52
|
+
const crypto = __importStar(require("crypto"));
|
|
53
|
+
const fs = __importStar(require("fs"));
|
|
54
|
+
const path = __importStar(require("path"));
|
|
55
|
+
const broker_1 = require("../approval/broker");
|
|
56
|
+
const localTestRunner_1 = require("./localTestRunner");
|
|
57
|
+
const testCommandPolicy_1 = require("./testCommandPolicy");
|
|
58
|
+
const testSafety_1 = require("./testSafety");
|
|
59
|
+
const MAX_SCRIPT_SIZE = 64 * 1024;
|
|
60
|
+
const MAX_SETUP_SCRIPT_SIZE = 32 * 1024;
|
|
61
|
+
const MAX_OUTPUT = 16 * 1024;
|
|
62
|
+
const DEFAULT_TIMEOUT_MS = 60_000;
|
|
63
|
+
const SAFE_RUNNERS = new Set(['node', 'tsx', 'bun', 'deno', 'python', 'python3']);
|
|
64
|
+
function sha256Prefix(s) {
|
|
65
|
+
return crypto.createHash('sha256').update(s).digest('hex').substring(0, 16);
|
|
66
|
+
}
|
|
67
|
+
function hashWorkspaceFile(workspaceRoot, relPath) {
|
|
68
|
+
try {
|
|
69
|
+
const p = path.join(workspaceRoot, relPath);
|
|
70
|
+
if (!fs.existsSync(p))
|
|
71
|
+
return null;
|
|
72
|
+
const content = fs.readFileSync(p, 'utf8').slice(0, 65536);
|
|
73
|
+
return sha256Prefix(content);
|
|
74
|
+
}
|
|
75
|
+
catch {
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
function workspaceContentHash(workspaceRoot) {
|
|
80
|
+
const pkg = hashWorkspaceFile(workspaceRoot, 'package.json');
|
|
81
|
+
const lock = hashWorkspaceFile(workspaceRoot, 'package-lock.json')
|
|
82
|
+
|| hashWorkspaceFile(workspaceRoot, 'pnpm-lock.yaml')
|
|
83
|
+
|| hashWorkspaceFile(workspaceRoot, 'yarn.lock')
|
|
84
|
+
|| hashWorkspaceFile(workspaceRoot, 'bun.lock')
|
|
85
|
+
|| hashWorkspaceFile(workspaceRoot, 'requirements.txt')
|
|
86
|
+
|| hashWorkspaceFile(workspaceRoot, 'pyproject.toml');
|
|
87
|
+
if (!pkg && !lock)
|
|
88
|
+
return null;
|
|
89
|
+
return `${pkg || 'none'}:${lock || 'none'}`;
|
|
90
|
+
}
|
|
91
|
+
function truncateOutput(s) {
|
|
92
|
+
if (s.length <= MAX_OUTPUT)
|
|
93
|
+
return s;
|
|
94
|
+
return s.slice(0, MAX_OUTPUT) + '\n… [truncated]';
|
|
95
|
+
}
|
|
96
|
+
function mapVerdict(verdict) {
|
|
97
|
+
switch (verdict) {
|
|
98
|
+
case 'pass': return 'passed';
|
|
99
|
+
case 'fail': return 'failed';
|
|
100
|
+
case 'error': return 'error';
|
|
101
|
+
case 'timeout': return 'timeout';
|
|
102
|
+
case 'blocked': return 'blocked';
|
|
103
|
+
case 'sandbox-unavailable': return 'sandbox-unavailable';
|
|
104
|
+
default: return 'error';
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
function validateRunTestsRequest(req, workspaceRoot) {
|
|
108
|
+
if (!req.mode || (req.mode !== 'existing' && req.mode !== 'generated')) {
|
|
109
|
+
return { ok: false, error: 'mode must be "existing" or "generated"' };
|
|
110
|
+
}
|
|
111
|
+
if (req.mode === 'existing') {
|
|
112
|
+
const result = (0, testCommandPolicy_1.validateTestCommand)({
|
|
113
|
+
packageManager: req.packageManager,
|
|
114
|
+
testFiles: req.testFiles,
|
|
115
|
+
testPattern: req.testPattern,
|
|
116
|
+
timeoutMs: req.timeoutMs,
|
|
117
|
+
}, workspaceRoot);
|
|
118
|
+
if (!result.ok)
|
|
119
|
+
return { ok: false, error: result.error };
|
|
120
|
+
return { ok: true, command: result.command };
|
|
121
|
+
}
|
|
122
|
+
// Generated mode
|
|
123
|
+
if (!req.script || req.script.trim().length === 0) {
|
|
124
|
+
return { ok: false, error: 'generated mode requires "script" (non-empty string)' };
|
|
125
|
+
}
|
|
126
|
+
if (req.script.length > MAX_SCRIPT_SIZE) {
|
|
127
|
+
return { ok: false, error: `script too large (max ${MAX_SCRIPT_SIZE} bytes, got ${req.script.length})` };
|
|
128
|
+
}
|
|
129
|
+
if (!req.runner || !SAFE_RUNNERS.has(req.runner)) {
|
|
130
|
+
return { ok: false, error: `runner must be one of: ${[...SAFE_RUNNERS].join(', ')}. Got: ${req.runner || '(none)'}` };
|
|
131
|
+
}
|
|
132
|
+
if (req.setupScript && req.setupScript.length > MAX_SETUP_SCRIPT_SIZE) {
|
|
133
|
+
return { ok: false, error: `setupScript too large (max ${MAX_SETUP_SCRIPT_SIZE} bytes)` };
|
|
134
|
+
}
|
|
135
|
+
const safety = (0, testSafety_1.checkTestSafety)(req.script, workspaceRoot);
|
|
136
|
+
if (!safety.allowed) {
|
|
137
|
+
return { ok: false, error: `script blocked by safety check: ${safety.reason}` };
|
|
138
|
+
}
|
|
139
|
+
if (req.setupScript) {
|
|
140
|
+
const setupSafety = (0, testSafety_1.checkTestSafety)(req.setupScript, workspaceRoot);
|
|
141
|
+
if (!setupSafety.allowed) {
|
|
142
|
+
return { ok: false, error: `setupScript blocked by safety check: ${setupSafety.reason}` };
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
return { ok: true };
|
|
146
|
+
}
|
|
147
|
+
function buildApprovalSummary(req, command) {
|
|
148
|
+
if (req.mode === 'existing' && command) {
|
|
149
|
+
const files = command.selectedFiles.length > 0
|
|
150
|
+
? `\nFiles: ${command.selectedFiles.join(', ')}`
|
|
151
|
+
: '';
|
|
152
|
+
const pattern = req.testPattern ? `\nPattern: ${req.testPattern}` : '';
|
|
153
|
+
return `Run existing tests in sandbox\nCommand: ${(0, testCommandPolicy_1.formatValidatedCommand)(command)}\nTimeout: ${command.timeoutMs}ms${files}${pattern}`;
|
|
154
|
+
}
|
|
155
|
+
const setupInfo = req.setupScript ? `\nSetup script: ${req.setupScript.length} bytes` : '';
|
|
156
|
+
return `Run generated test script in sandbox\nRunner: ${req.runner}\nScript: ${req.script.length} bytes (hash ${sha256Prefix(req.script)})${setupInfo}\nTimeout: ${req.timeoutMs || DEFAULT_TIMEOUT_MS}ms`;
|
|
157
|
+
}
|
|
158
|
+
function buildOperationParts(req, command, workspaceRoot) {
|
|
159
|
+
const wsHash = workspaceContentHash(workspaceRoot);
|
|
160
|
+
if (req.mode === 'existing' && command) {
|
|
161
|
+
return [
|
|
162
|
+
req.mode,
|
|
163
|
+
command.executable,
|
|
164
|
+
command.args,
|
|
165
|
+
command.selectedFiles,
|
|
166
|
+
req.testPattern,
|
|
167
|
+
command.timeoutMs,
|
|
168
|
+
wsHash,
|
|
169
|
+
];
|
|
170
|
+
}
|
|
171
|
+
return [
|
|
172
|
+
req.mode,
|
|
173
|
+
req.runner,
|
|
174
|
+
req.script ? sha256Prefix(req.script) : null,
|
|
175
|
+
req.setupScript ? sha256Prefix(req.setupScript) : null,
|
|
176
|
+
req.timeoutMs || DEFAULT_TIMEOUT_MS,
|
|
177
|
+
wsHash,
|
|
178
|
+
];
|
|
179
|
+
}
|
|
180
|
+
async function runTests(req, workspaceRoot, options) {
|
|
181
|
+
const startTime = Date.now();
|
|
182
|
+
const validation = validateRunTestsRequest(req, workspaceRoot);
|
|
183
|
+
if (!validation.ok) {
|
|
184
|
+
return {
|
|
185
|
+
approved: false,
|
|
186
|
+
mode: req.mode,
|
|
187
|
+
status: 'denied',
|
|
188
|
+
exitCode: -1,
|
|
189
|
+
output: `Validation error: ${validation.error}`,
|
|
190
|
+
durationMs: Date.now() - startTime,
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
const command = validation.command;
|
|
194
|
+
const summary = buildApprovalSummary(req, command);
|
|
195
|
+
const operationParts = buildOperationParts(req, command, workspaceRoot);
|
|
196
|
+
const ownsBroker = !options?.broker;
|
|
197
|
+
const broker = options?.broker ?? new broker_1.ApprovalBroker();
|
|
198
|
+
if (ownsBroker)
|
|
199
|
+
await broker.start();
|
|
200
|
+
try {
|
|
201
|
+
const approval = await broker.requestApproval('securecode.run-tests', summary, operationParts, 60_000, 'paid-generation', workspaceRoot);
|
|
202
|
+
if (!approval.approved) {
|
|
203
|
+
return {
|
|
204
|
+
approved: false,
|
|
205
|
+
requestId: approval.requestId,
|
|
206
|
+
mode: req.mode,
|
|
207
|
+
status: 'denied',
|
|
208
|
+
exitCode: -1,
|
|
209
|
+
output: `Test execution ${approval.reason}.`,
|
|
210
|
+
durationMs: Date.now() - startTime,
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
let testResult;
|
|
214
|
+
if (req.mode === 'existing' && command) {
|
|
215
|
+
testResult = await (0, localTestRunner_1.runTestCommand)(command.executable, command.args, workspaceRoot, {
|
|
216
|
+
timeoutMs: command.timeoutMs,
|
|
217
|
+
signal: options?.signal,
|
|
218
|
+
sandboxBackend: options?.sandboxBackend,
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
else {
|
|
222
|
+
testResult = await (0, localTestRunner_1.runLocalTest)(req.script, req.runner, workspaceRoot, {
|
|
223
|
+
setupScript: req.setupScript || null,
|
|
224
|
+
timeoutMs: req.timeoutMs || DEFAULT_TIMEOUT_MS,
|
|
225
|
+
signal: options?.signal,
|
|
226
|
+
sandboxBackend: options?.sandboxBackend,
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
return {
|
|
230
|
+
approved: true,
|
|
231
|
+
requestId: approval.requestId,
|
|
232
|
+
mode: req.mode,
|
|
233
|
+
status: mapVerdict(testResult.verdict),
|
|
234
|
+
exitCode: testResult.exitCode,
|
|
235
|
+
output: truncateOutput(testResult.output),
|
|
236
|
+
backend: testResult.backend,
|
|
237
|
+
command: command ? { executable: command.executable, args: command.args } : undefined,
|
|
238
|
+
durationMs: Date.now() - startTime,
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
finally {
|
|
242
|
+
if (ownsBroker)
|
|
243
|
+
await broker.stop();
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
//# sourceMappingURL=testRunner.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"testRunner.js","sourceRoot":"","sources":["../../src/utils/testRunner.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,4 +1,42 @@
|
|
|
1
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Static test safety analyzer — defense-in-depth, NOT a security boundary.
|
|
3
|
+
*
|
|
4
|
+
* The real boundary is the verification sandbox (`verificationSandbox.ts`):
|
|
5
|
+
* Docker with `--network=none --read-only`, or Deno with `--allow-none`.
|
|
6
|
+
* This module exists to reject obviously dangerous scripts *before* we
|
|
7
|
+
* pay for a sandbox spawn, and as a second layer in case a sandbox backend
|
|
8
|
+
* is somehow misconfigured.
|
|
9
|
+
*
|
|
10
|
+
* Policy: deny-by-default. Only specific known-safe imports and globals
|
|
11
|
+
* are permitted. Anything else is blocked. The previous implementation
|
|
12
|
+
* was allow-by-default with a 4-module blocklist — it let `http`, `https`,
|
|
13
|
+
* `fs`, `dns`, `os`, `tls`, `vm`, `dgram`, `fetch`, `eval`, `new Function`,
|
|
14
|
+
* dynamic `import()`, and `process.env` access all pass. That gap is the
|
|
15
|
+
* reason this module was rewritten.
|
|
16
|
+
*
|
|
17
|
+
* What this analyzer checks (in priority order):
|
|
18
|
+
* 1. Static import / require specifiers — against an allowlist.
|
|
19
|
+
* 2. Dynamic require/import — blocked outright (cannot be statically
|
|
20
|
+
* analyzed).
|
|
21
|
+
* 3. Global function calls that execute strings as code — `eval`,
|
|
22
|
+
* `new Function`, `setTimeout(string)`, `setInterval(string)`,
|
|
23
|
+
* `setImmediate(string)`.
|
|
24
|
+
* 4. Network-related globals and Node built-ins (covered by #1 for
|
|
25
|
+
* imports; here for member-style access like `globalThis.fetch`).
|
|
26
|
+
* 5. Process/env access — `process.env`, `process.argv`, `process.cwd`,
|
|
27
|
+
* `process.execPath`, `process.cwd()`.
|
|
28
|
+
* 6. Reflection tricks that resolve to dangerous globals —
|
|
29
|
+
* `globalThis['eval']`, `globalThis['Function']`, `this['eval']`,
|
|
30
|
+
* bracket access with string literals that match dangerous names.
|
|
31
|
+
*
|
|
32
|
+
* Obfuscation handling: we catch literal bracket access like
|
|
33
|
+
* `globalThis["eval"]` and `process["env"]`. We do NOT attempt to defeat
|
|
34
|
+
* arbitrary runtime-computed string construction (e.g., `String.fromCharCode`
|
|
35
|
+
* to build a name) — that is the sandbox's job. This analyzer only catches
|
|
36
|
+
* what a defender can see statically.
|
|
37
|
+
*/
|
|
38
|
+
export interface TestSafetyResult {
|
|
2
39
|
allowed: boolean;
|
|
3
40
|
reason: string;
|
|
4
|
-
}
|
|
41
|
+
}
|
|
42
|
+
export declare function checkTestSafety(script: string, _workspaceRoot: string): TestSafetyResult;
|