@securecode-ai/mcp 0.5.4 → 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.
Files changed (188) hide show
  1. package/dist/api/types.d.ts +7 -0
  2. package/dist/approval/auditLog.d.ts +1 -1
  3. package/dist/approval/auditLog.js +11 -3
  4. package/dist/approval/auditLog.js.map +1 -1
  5. package/dist/approval/broker.d.ts +7 -2
  6. package/dist/approval/broker.js +239 -110
  7. package/dist/approval/broker.js.map +1 -1
  8. package/dist/approval/policy.d.ts +10 -0
  9. package/dist/approval/policy.js +104 -0
  10. package/dist/approval/policy.js.map +1 -0
  11. package/dist/approval/types.d.ts +11 -2
  12. package/dist/approval/types.js +10 -1
  13. package/dist/approval/types.js.map +1 -1
  14. package/dist/attack/agentScanExecutor.d.ts +35 -1
  15. package/dist/attack/agentScanExecutor.js +405 -76
  16. package/dist/attack/agentScanExecutor.js.map +1 -1
  17. package/dist/attack/agentScanLoop.d.ts +20 -0
  18. package/dist/attack/agentScanLoop.js +1264 -60
  19. package/dist/attack/agentScanLoop.js.map +1 -1
  20. package/dist/attack/agentScanProtocol.d.ts +382 -6
  21. package/dist/attack/agentScanProtocol.js +110 -4
  22. package/dist/attack/agentScanProtocol.js.map +1 -1
  23. package/dist/attack/agentTrace.d.ts +73 -0
  24. package/dist/attack/agentTrace.js +228 -0
  25. package/dist/attack/agentTrace.js.map +1 -0
  26. package/dist/attack/architectureScoutExecutor.d.ts +18 -0
  27. package/dist/attack/architectureScoutExecutor.js +211 -0
  28. package/dist/attack/architectureScoutExecutor.js.map +1 -0
  29. package/dist/attack/architectureScoutLoop.d.ts +36 -0
  30. package/dist/attack/architectureScoutLoop.js +403 -0
  31. package/dist/attack/architectureScoutLoop.js.map +1 -0
  32. package/dist/attack/architectureScoutProtocol.d.ts +209 -0
  33. package/dist/attack/architectureScoutProtocol.js +47 -0
  34. package/dist/attack/architectureScoutProtocol.js.map +1 -0
  35. package/dist/attack/candidateStore.d.ts +95 -0
  36. package/dist/attack/candidateStore.js +231 -0
  37. package/dist/attack/candidateStore.js.map +1 -0
  38. package/dist/attack/evidenceLedger.d.ts +67 -0
  39. package/dist/attack/evidenceLedger.js +192 -0
  40. package/dist/attack/evidenceLedger.js.map +1 -0
  41. package/dist/attack/finishGate.d.ts +62 -0
  42. package/dist/attack/finishGate.js +209 -0
  43. package/dist/attack/finishGate.js.map +1 -0
  44. package/dist/attack/fixCodeMerge.d.ts +27 -0
  45. package/dist/attack/fixCodeMerge.js +42 -0
  46. package/dist/attack/fixCodeMerge.js.map +1 -0
  47. package/dist/attack/fixVerifyLoop.d.ts +55 -0
  48. package/dist/attack/fixVerifyLoop.js +187 -0
  49. package/dist/attack/fixVerifyLoop.js.map +1 -0
  50. package/dist/attack/investigationProfiles.d.ts +36 -0
  51. package/dist/attack/investigationProfiles.js +144 -0
  52. package/dist/attack/investigationProfiles.js.map +1 -0
  53. package/dist/attack/investigationState.d.ts +227 -0
  54. package/dist/attack/investigationState.js +666 -0
  55. package/dist/attack/investigationState.js.map +1 -0
  56. package/dist/attack/mutationOperators.d.ts +22 -0
  57. package/dist/attack/mutationOperators.js +170 -0
  58. package/dist/attack/mutationOperators.js.map +1 -0
  59. package/dist/attack/mutationTest.d.ts +33 -0
  60. package/dist/attack/mutationTest.js +110 -0
  61. package/dist/attack/mutationTest.js.map +1 -0
  62. package/dist/attack/proofGate.d.ts +20 -0
  63. package/dist/attack/proofGate.js +104 -0
  64. package/dist/attack/proofGate.js.map +1 -0
  65. package/dist/attack/proofTypes.d.ts +57 -0
  66. package/dist/attack/proofTypes.js +32 -0
  67. package/dist/attack/proofTypes.js.map +1 -0
  68. package/dist/attack/protocolValidator.d.ts +50 -0
  69. package/dist/attack/protocolValidator.js +427 -0
  70. package/dist/attack/protocolValidator.js.map +1 -0
  71. package/dist/attack/qualityMetrics.d.ts +133 -0
  72. package/dist/attack/qualityMetrics.js +226 -0
  73. package/dist/attack/qualityMetrics.js.map +1 -0
  74. package/dist/attack/scanScheduler.d.ts +52 -0
  75. package/dist/attack/scanScheduler.js +265 -0
  76. package/dist/attack/scanScheduler.js.map +1 -0
  77. package/dist/attack/scanState.d.ts +58 -0
  78. package/dist/attack/scanState.js +102 -0
  79. package/dist/attack/scanState.js.map +1 -0
  80. package/dist/attack/searchIntent.d.ts +16 -0
  81. package/dist/attack/searchIntent.js +57 -0
  82. package/dist/attack/searchIntent.js.map +1 -0
  83. package/dist/attack/verifyLoop.d.ts +43 -0
  84. package/dist/attack/verifyLoop.js +329 -15
  85. package/dist/attack/verifyLoop.js.map +1 -1
  86. package/dist/attack/workItem.d.ts +57 -0
  87. package/dist/attack/workItem.js +225 -0
  88. package/dist/attack/workItem.js.map +1 -0
  89. package/dist/audit/findingReviewQueue.d.ts +109 -0
  90. package/dist/audit/findingReviewQueue.js +335 -0
  91. package/dist/audit/findingReviewQueue.js.map +1 -0
  92. package/dist/audit/scanAuditLog.d.ts +160 -0
  93. package/dist/audit/scanAuditLog.js +404 -0
  94. package/dist/audit/scanAuditLog.js.map +1 -0
  95. package/dist/dependency/dependencyChecker.js +35 -9
  96. package/dist/dependency/dependencyChecker.js.map +1 -1
  97. package/dist/dependency/exploitPriority.d.ts +33 -0
  98. package/dist/dependency/exploitPriority.js +78 -0
  99. package/dist/dependency/exploitPriority.js.map +1 -0
  100. package/dist/dependency/finding.d.ts +16 -0
  101. package/dist/dependency/osvClient.js +2 -0
  102. package/dist/dependency/osvClient.js.map +1 -1
  103. package/dist/dependency/types.d.ts +12 -1
  104. package/dist/mcp/server.js +13 -2
  105. package/dist/mcp/server.js.map +1 -1
  106. package/dist/mcp/tools.d.ts +1 -0
  107. package/dist/mcp/tools.js +127 -6
  108. package/dist/mcp/tools.js.map +1 -1
  109. package/dist/project-map/agentMemory.d.ts +83 -1
  110. package/dist/project-map/agentMemory.js +197 -7
  111. package/dist/project-map/agentMemory.js.map +1 -1
  112. package/dist/project-map/architectureContext.d.ts +202 -0
  113. package/dist/project-map/architectureContext.js +421 -0
  114. package/dist/project-map/architectureContext.js.map +1 -0
  115. package/dist/project-map/blastRadius.d.ts +54 -0
  116. package/dist/project-map/blastRadius.js +196 -0
  117. package/dist/project-map/blastRadius.js.map +1 -0
  118. package/dist/project-map/callGraphExtractor.d.ts +13 -0
  119. package/dist/project-map/callGraphExtractor.js +222 -0
  120. package/dist/project-map/callGraphExtractor.js.map +1 -0
  121. package/dist/project-map/capabilityRegistry.d.ts +46 -0
  122. package/dist/project-map/capabilityRegistry.js +91 -0
  123. package/dist/project-map/capabilityRegistry.js.map +1 -0
  124. package/dist/project-map/findTests.d.ts +29 -0
  125. package/dist/project-map/findTests.js +236 -0
  126. package/dist/project-map/findTests.js.map +1 -0
  127. package/dist/project-map/handlerInventory.d.ts +112 -0
  128. package/dist/project-map/handlerInventory.js +184 -0
  129. package/dist/project-map/handlerInventory.js.map +1 -0
  130. package/dist/project-map/implementationResolver.d.ts +43 -0
  131. package/dist/project-map/implementationResolver.js +217 -0
  132. package/dist/project-map/implementationResolver.js.map +1 -0
  133. package/dist/project-map/mapContext.d.ts +6 -1
  134. package/dist/project-map/mapContext.js +1 -0
  135. package/dist/project-map/mapContext.js.map +1 -1
  136. package/dist/project-map/scanCache.d.ts +57 -3
  137. package/dist/project-map/scanCache.js +61 -2
  138. package/dist/project-map/scanCache.js.map +1 -1
  139. package/dist/project-map/symbolIndex.d.ts +39 -0
  140. package/dist/project-map/symbolIndex.js +385 -0
  141. package/dist/project-map/symbolIndex.js.map +1 -0
  142. package/dist/tooling/agentEvalScoring.d.ts +77 -0
  143. package/dist/tooling/agentEvalScoring.js +140 -0
  144. package/dist/tooling/agentEvalScoring.js.map +1 -0
  145. package/dist/tooling/agentRegression.d.ts +53 -0
  146. package/dist/tooling/agentRegression.js +99 -0
  147. package/dist/tooling/agentRegression.js.map +1 -0
  148. package/dist/tools/agentScan.d.ts +69 -0
  149. package/dist/tools/agentScan.js +702 -43
  150. package/dist/tools/agentScan.js.map +1 -1
  151. package/dist/tools/findingReviewTools.d.ts +22 -0
  152. package/dist/tools/findingReviewTools.js +121 -0
  153. package/dist/tools/findingReviewTools.js.map +1 -0
  154. package/dist/tools/fix.js +1 -1
  155. package/dist/tools/fix.js.map +1 -1
  156. package/dist/tools/map.js +191 -1
  157. package/dist/tools/map.js.map +1 -1
  158. package/dist/tools/runTests.d.ts +2 -0
  159. package/dist/tools/runTests.js +29 -0
  160. package/dist/tools/runTests.js.map +1 -0
  161. package/dist/utils/effectMock.d.ts +1 -0
  162. package/dist/utils/effectMock.js +162 -0
  163. package/dist/utils/effectMock.js.map +1 -0
  164. package/dist/utils/gitContext.d.ts +26 -0
  165. package/dist/utils/gitContext.js +317 -0
  166. package/dist/utils/gitContext.js.map +1 -0
  167. package/dist/utils/localTestRunner.d.ts +57 -2
  168. package/dist/utils/localTestRunner.js +122 -101
  169. package/dist/utils/localTestRunner.js.map +1 -1
  170. package/dist/utils/runtimeDetect.d.ts +12 -2
  171. package/dist/utils/runtimeDetect.js +247 -10
  172. package/dist/utils/runtimeDetect.js.map +1 -1
  173. package/dist/utils/securityConfig.d.ts +17 -0
  174. package/dist/utils/securityConfig.js +192 -0
  175. package/dist/utils/securityConfig.js.map +1 -0
  176. package/dist/utils/testCommandPolicy.d.ts +36 -0
  177. package/dist/utils/testCommandPolicy.js +252 -0
  178. package/dist/utils/testCommandPolicy.js.map +1 -0
  179. package/dist/utils/testRunner.d.ts +56 -0
  180. package/dist/utils/testRunner.js +246 -0
  181. package/dist/utils/testRunner.js.map +1 -0
  182. package/dist/utils/testSafety.d.ts +40 -2
  183. package/dist/utils/testSafety.js +205 -26
  184. package/dist/utils/testSafety.js.map +1 -1
  185. package/dist/utils/verificationSandbox.d.ts +113 -0
  186. package/dist/utils/verificationSandbox.js +656 -0
  187. package/dist/utils/verificationSandbox.js.map +1 -0
  188. package/package.json +1 -1
@@ -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":";AAAA;;;;;;;;;;;;;;GAcG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAyHH,gDA6CC;AApKD,uCAAyB;AACzB,2CAA6B;AAC7B,mCAA+C;AAU/C,MAAM,eAAe,GAA8D;IAC/E,EAAE,IAAI,EAAE,cAAc,EAAE,IAAI,EAAE,KAAK,EAAE,WAAW,EAAE,mDAAmD,EAAE;IACvG,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,WAAW,EAAE,qDAAqD,EAAE;IACjG,EAAE,IAAI,EAAE,YAAY,EAAE,IAAI,EAAE,KAAK,EAAE,WAAW,EAAE,yCAAyC,EAAE;IAC3F,EAAE,IAAI,EAAE,iBAAiB,EAAE,IAAI,EAAE,KAAK,EAAE,WAAW,EAAE,oCAAoC,EAAE;IAC3F,EAAE,IAAI,EAAE,kBAAkB,EAAE,IAAI,EAAE,SAAS,EAAE,WAAW,EAAE,gCAAgC,EAAE;IAC5F,EAAE,IAAI,EAAE,kBAAkB,EAAE,IAAI,EAAE,SAAS,EAAE,WAAW,EAAE,gCAAgC,EAAE;IAC5F,EAAE,IAAI,EAAE,gBAAgB,EAAE,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,oBAAoB,EAAE;IAC3E,EAAE,IAAI,EAAE,gBAAgB,EAAE,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,oBAAoB,EAAE;IAC3E,EAAE,IAAI,EAAE,gBAAgB,EAAE,IAAI,EAAE,SAAS,EAAE,WAAW,EAAE,0CAA0C,EAAE;IACpG,EAAE,IAAI,EAAE,gBAAgB,EAAE,IAAI,EAAE,SAAS,EAAE,WAAW,EAAE,0CAA0C,EAAE;IACpG,EAAE,IAAI,EAAE,iBAAiB,EAAE,IAAI,EAAE,SAAS,EAAE,WAAW,EAAE,0CAA0C,EAAE;IACrG,EAAE,IAAI,EAAE,8BAA8B,EAAE,IAAI,EAAE,YAAY,EAAE,WAAW,EAAE,2BAA2B,EAAE;IACtG,EAAE,IAAI,EAAE,sBAAsB,EAAE,IAAI,EAAE,YAAY,EAAE,WAAW,EAAE,mBAAmB,EAAE;IACtF,EAAE,IAAI,EAAE,sBAAsB,EAAE,IAAI,EAAE,YAAY,EAAE,WAAW,EAAE,mBAAmB,EAAE;CACzF,CAAC;AAEF,SAAS,eAAe,CAAC,aAAqB,EAAE,IAAgB;IAC5D,MAAM,GAAG,GAAiB,EAAE,CAAC;IAE7B,KAAK,MAAM,OAAO,IAAI,eAAe,EAAE,CAAC;QACpC,IAAI,IAAI,KAAK,KAAK,IAAI,OAAO,CAAC,IAAI,KAAK,IAAI;YAAE,SAAS;QAEtD,MAAM,UAAU,GAAG;YACf,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,OAAO,CAAC,IAAI,CAAC;YACtC,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,QAAQ,EAAE,OAAO,CAAC,IAAI,CAAC;YAChD,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,KAAK,EAAE,OAAO,CAAC,IAAI,CAAC;SAChD,CAAC;QAEF,KAAK,MAAM,SAAS,IAAI,UAAU,EAAE,CAAC;YACjC,IAAI,EAAE,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;gBAC3B,MAAM,GAAG,GAAG,IAAI,CAAC,QAAQ,CAAC,aAAa,EAAE,SAAS,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;gBACxE,GAAG,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,GAAG,EAAE,IAAI,EAAE,OAAO,CAAC,IAAI,EAAE,WAAW,EAAE,OAAO,CAAC,WAAW,EAAE,CAAC,CAAC;gBAC9E,MAAM;YACV,CAAC;QACL,CAAC;IACL,CAAC;IAED,OAAO,GAAG,CAAC;AACf,CAAC;AAED,SAAS,eAAe,CAAC,OAAe;IACpC,OAAO,OAAO;SACT,KAAK,CAAC,IAAI,CAAC;SACX,GAAG,CAAC,IAAI,CAAC,EAAE;QACR,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;QAC5B,IAAI,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,OAAO,KAAK,EAAE;YAAE,OAAO,IAAI,CAAC;QAC3D,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QAChC,IAAI,KAAK,KAAK,CAAC,CAAC;YAAE,OAAO,IAAI,CAAC;QAC9B,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,IAAI,EAAE,CAAC;QACxC,MAAM,QAAQ,GAAG,+CAA+C,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAC3E,IAAI,QAAQ;YAAE,OAAO,GAAG,GAAG,WAAW,CAAC;QACvC,OAAO,GAAG,GAAG,MAAM,CAAC;IACxB,CAAC,CAAC;SACD,IAAI,CAAC,IAAI,CAAC,CAAC;AACpB,CAAC;AAED,SAAS,eAAe,CAAC,QAAgB,EAAE,aAAqB;IAC5D,MAAM,GAAG,GAAG,IAAA,4BAAoB,EAAC,aAAa,EAAE,QAAQ,CAAC,CAAC;IAC1D,MAAM,OAAO,GAAG,EAAE,CAAC,YAAY,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;IAC7C,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,WAAW,EAAE,CAAC;IAEjD,IAAI,QAAQ,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;QAC5B,OAAO,eAAe,CAAC,OAAO,CAAC,CAAC;IACpC,CAAC;IAED,IAAI,GAAG,KAAK,KAAK,IAAI,GAAG,KAAK,KAAK,IAAI,GAAG,KAAK,MAAM,IAAI,GAAG,KAAK,OAAO,EAAE,CAAC;QACtE,OAAO,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC;IACnC,CAAC;IAED,OAAO,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC;AACnC,CAAC;AAED,SAAS,kBAAkB,CAAC,aAAqB,EAAE,IAAgB;IAC/D,MAAM,GAAG,GAAsD,EAAE,CAAC;IAClE,MAAM,QAAQ,GAAyD;QACnE,EAAE,KAAK,EAAE,eAAe,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,eAAe,EAAE;QACnE,EAAE,KAAK,EAAE,aAAa,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,aAAa,EAAE;QAC5D,EAAE,KAAK,EAAE,kBAAkB,EAAE,IAAI,EAAE,YAAY,EAAE,KAAK,EAAE,kBAAkB,EAAE;QAC5E,EAAE,KAAK,EAAE,YAAY,EAAE,IAAI,EAAE,YAAY,EAAE,KAAK,EAAE,mBAAmB,EAAE;QACvE,EAAE,KAAK,EAAE,oBAAoB,EAAE,IAAI,EAAE,YAAY,EAAE,KAAK,EAAE,2BAA2B,EAAE;QACvF,EAAE,KAAK,EAAE,mDAAmD,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,YAAY,EAAE;QACpG,EAAE,KAAK,EAAE,gCAAgC,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,aAAa,EAAE;QAClF,EAAE,KAAK,EAAE,+BAA+B,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,iBAAiB,EAAE;QACrF,EAAE,KAAK,EAAE,4CAA4C,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,wBAAwB,EAAE;KAC5G,CAAC;IAEF,KAAK,MAAM,CAAC,IAAI,QAAQ,EAAE,CAAC;QACvB,IAAI,IAAI,KAAK,KAAK,IAAI,CAAC,CAAC,IAAI,KAAK,IAAI;YAAE,SAAS;QAChD,IAAI,CAAC;YACD,MAAM,EAAE,UAAU,EAAE,GAAG,OAAO,CAAC,qBAAqB,CAAC,CAAC;YACtD,MAAM,MAAM,GAAG,UAAU,CAAC,aAAa,EAAE,CAAC,CAAC,KAAK,EAAE,uBAAuB,CAAC,CAAC;YAC3E,IAAI,MAAM,IAAI,MAAM,CAAC,IAAI,EAAE,CAAC;gBACxB,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC;oBACxC,GAAG,CAAC,IAAI,CAAC;wBACL,IAAI,EAAE,GAAG,CAAC,IAAI;wBACd,OAAO,EAAE,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC;wBAC/B,IAAI,EAAE,GAAG,CAAC,IAAI;qBACjB,CAAC,CAAC;gBACP,CAAC;YACL,CAAC;QACL,CAAC;QAAC,MAAM,CAAC,CAAC,iBAAiB,CAAC,CAAC;IACjC,CAAC;IAED,OAAO,GAAG,CAAC;AACf,CAAC;AAEM,KAAK,UAAU,kBAAkB,CACpC,aAAqB,EACrB,OAAmB,KAAK;IAExB,MAAM,WAAW,GAAG,eAAe,CAAC,aAAa,EAAE,IAAI,CAAC,CAAC;IACzD,MAAM,aAAa,GAAG,kBAAkB,CAAC,aAAa,EAAE,IAAI,CAAC,CAAC;IAE9D,IAAI,WAAW,CAAC,MAAM,KAAK,CAAC,IAAI,aAAa,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACzD,OAAO,MAAM,IAAI,KAAK,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,GAAG,GAAG,qJAAqJ,CAAC;IACvM,CAAC;IAED,MAAM,KAAK,GAAa,CAAC,oBAAoB,IAAI,KAAK,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC;IAEhF,IAAI,WAAW,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACzB,KAAK,CAAC,IAAI,CAAC,EAAE,EAAE,eAAe,CAAC,CAAC;QAChC,KAAK,MAAM,EAAE,IAAI,WAAW,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,CAAC;YACxC,KAAK,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC,IAAI,MAAM,EAAE,CAAC,WAAW,EAAE,CAAC,CAAC;YAC/C,IAAI,CAAC;gBACD,MAAM,OAAO,GAAG,eAAe,CAAC,EAAE,CAAC,IAAI,EAAE,aAAa,CAAC,CAAC;gBACxD,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;gBACpB,KAAK,MAAM,IAAI,IAAI,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,CAAC;oBAClD,KAAK,CAAC,IAAI,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC;gBAC5B,CAAC;gBACD,IAAI,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,MAAM,GAAG,EAAE,EAAE,CAAC;oBAClC,KAAK,CAAC,IAAI,CAAC,mBAAmB,CAAC,CAAC;gBACpC,CAAC;gBACD,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;YACxB,CAAC;YAAC,OAAO,CAAM,EAAE,CAAC;gBACd,KAAK,CAAC,IAAI,CAAC,qBAAqB,CAAC,CAAC,OAAO,GAAG,CAAC,CAAC;YAClD,CAAC;QACL,CAAC;IACL,CAAC;IAED,IAAI,aAAa,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC3B,KAAK,CAAC,IAAI,CAAC,EAAE,EAAE,gCAAgC,CAAC,CAAC;QACjD,KAAK,MAAM,EAAE,IAAI,aAAa,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,CAAC;YAC1C,KAAK,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC,IAAI,IAAI,EAAE,CAAC,IAAI,EAAE,CAAC,CAAC;YACtC,KAAK,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,OAAO,EAAE,CAAC,CAAC;QACpC,CAAC;IACL,CAAC;IAED,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACf,KAAK,CAAC,IAAI,CAAC,0MAA0M,CAAC,CAAC;IAEvN,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC5B,CAAC"}
@@ -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;
@@ -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>;