@vibe-validate/extractors 0.16.1 → 0.17.0-rc.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/extractor-registry.d.ts +104 -0
- package/dist/extractor-registry.d.ts.map +1 -0
- package/dist/extractor-registry.js +278 -0
- package/dist/extractor-registry.js.map +1 -0
- package/dist/extractors/ava/index.d.ts +23 -0
- package/dist/extractors/ava/index.d.ts.map +1 -0
- package/dist/extractors/ava/index.js +507 -0
- package/dist/extractors/ava/index.js.map +1 -0
- package/dist/extractors/ava/index.test.d.ts +7 -0
- package/dist/extractors/ava/index.test.d.ts.map +1 -0
- package/dist/extractors/ava/index.test.js +408 -0
- package/dist/extractors/ava/index.test.js.map +1 -0
- package/dist/extractors/eslint/index.d.ts +18 -0
- package/dist/extractors/eslint/index.d.ts.map +1 -0
- package/dist/extractors/eslint/index.js +206 -0
- package/dist/extractors/eslint/index.js.map +1 -0
- package/dist/extractors/eslint/index.test.d.ts +9 -0
- package/dist/extractors/eslint/index.test.d.ts.map +1 -0
- package/dist/extractors/eslint/index.test.js +191 -0
- package/dist/extractors/eslint/index.test.js.map +1 -0
- package/dist/extractors/generic/index.d.ts +30 -0
- package/dist/extractors/generic/index.d.ts.map +1 -0
- package/dist/extractors/generic/index.js +140 -0
- package/dist/extractors/generic/index.js.map +1 -0
- package/dist/extractors/generic/index.test.d.ts +7 -0
- package/dist/extractors/generic/index.test.d.ts.map +1 -0
- package/dist/extractors/generic/index.test.js +61 -0
- package/dist/extractors/generic/index.test.js.map +1 -0
- package/dist/extractors/jasmine/index.d.ts +17 -0
- package/dist/extractors/jasmine/index.d.ts.map +1 -0
- package/dist/extractors/jasmine/index.js +242 -0
- package/dist/extractors/jasmine/index.js.map +1 -0
- package/dist/extractors/jasmine/index.test.d.ts +7 -0
- package/dist/extractors/jasmine/index.test.d.ts.map +1 -0
- package/dist/extractors/jasmine/index.test.js +318 -0
- package/dist/extractors/jasmine/index.test.js.map +1 -0
- package/dist/extractors/jest/index.d.ts +17 -0
- package/dist/extractors/jest/index.d.ts.map +1 -0
- package/dist/extractors/jest/index.js +273 -0
- package/dist/extractors/jest/index.js.map +1 -0
- package/dist/extractors/jest/index.test.d.ts +9 -0
- package/dist/extractors/jest/index.test.d.ts.map +1 -0
- package/dist/extractors/jest/index.test.js +338 -0
- package/dist/extractors/jest/index.test.js.map +1 -0
- package/dist/extractors/junit/index.d.ts +18 -0
- package/dist/extractors/junit/index.d.ts.map +1 -0
- package/dist/extractors/junit/index.js +259 -0
- package/dist/extractors/junit/index.js.map +1 -0
- package/dist/extractors/junit/index.test.d.ts +7 -0
- package/dist/extractors/junit/index.test.d.ts.map +1 -0
- package/dist/extractors/junit/index.test.js +341 -0
- package/dist/extractors/junit/index.test.js.map +1 -0
- package/dist/extractors/maven-checkstyle/index.d.ts +23 -0
- package/dist/extractors/maven-checkstyle/index.d.ts.map +1 -0
- package/dist/extractors/maven-checkstyle/index.js +263 -0
- package/dist/extractors/maven-checkstyle/index.js.map +1 -0
- package/dist/extractors/maven-checkstyle/index.test.d.ts +2 -0
- package/dist/extractors/maven-checkstyle/index.test.d.ts.map +1 -0
- package/dist/extractors/maven-checkstyle/index.test.js +197 -0
- package/dist/extractors/maven-checkstyle/index.test.js.map +1 -0
- package/dist/extractors/maven-compiler/index.d.ts +23 -0
- package/dist/extractors/maven-compiler/index.d.ts.map +1 -0
- package/dist/extractors/maven-compiler/index.js +271 -0
- package/dist/extractors/maven-compiler/index.js.map +1 -0
- package/dist/extractors/maven-compiler/index.test.d.ts +2 -0
- package/dist/extractors/maven-compiler/index.test.d.ts.map +1 -0
- package/dist/extractors/maven-compiler/index.test.js +189 -0
- package/dist/extractors/maven-compiler/index.test.js.map +1 -0
- package/dist/extractors/maven-surefire/index.d.ts +23 -0
- package/dist/extractors/maven-surefire/index.d.ts.map +1 -0
- package/dist/extractors/maven-surefire/index.js +292 -0
- package/dist/extractors/maven-surefire/index.js.map +1 -0
- package/dist/extractors/maven-surefire/index.test.d.ts +2 -0
- package/dist/extractors/maven-surefire/index.test.d.ts.map +1 -0
- package/dist/extractors/maven-surefire/index.test.js +163 -0
- package/dist/extractors/maven-surefire/index.test.js.map +1 -0
- package/dist/extractors/mocha/index.d.ts +17 -0
- package/dist/extractors/mocha/index.d.ts.map +1 -0
- package/dist/extractors/mocha/index.js +241 -0
- package/dist/extractors/mocha/index.js.map +1 -0
- package/dist/extractors/mocha/index.test.d.ts +7 -0
- package/dist/extractors/mocha/index.test.d.ts.map +1 -0
- package/dist/extractors/mocha/index.test.js +300 -0
- package/dist/extractors/mocha/index.test.js.map +1 -0
- package/dist/extractors/playwright/index.d.ts +17 -0
- package/dist/extractors/playwright/index.d.ts.map +1 -0
- package/dist/extractors/playwright/index.js +320 -0
- package/dist/extractors/playwright/index.js.map +1 -0
- package/dist/extractors/playwright/index.test.d.ts +7 -0
- package/dist/extractors/playwright/index.test.d.ts.map +1 -0
- package/dist/extractors/playwright/index.test.js +274 -0
- package/dist/extractors/playwright/index.test.js.map +1 -0
- package/dist/extractors/tap/index.d.ts +23 -0
- package/dist/extractors/tap/index.d.ts.map +1 -0
- package/dist/extractors/tap/index.js +352 -0
- package/dist/extractors/tap/index.js.map +1 -0
- package/dist/extractors/tap/index.test.d.ts +7 -0
- package/dist/extractors/tap/index.test.d.ts.map +1 -0
- package/dist/extractors/tap/index.test.js +100 -0
- package/dist/extractors/tap/index.test.js.map +1 -0
- package/dist/extractors/typescript/index.d.ts +17 -0
- package/dist/extractors/typescript/index.d.ts.map +1 -0
- package/dist/extractors/typescript/index.js +150 -0
- package/dist/extractors/typescript/index.js.map +1 -0
- package/dist/extractors/typescript/index.test.d.ts +9 -0
- package/dist/extractors/typescript/index.test.d.ts.map +1 -0
- package/dist/extractors/typescript/index.test.js +177 -0
- package/dist/extractors/typescript/index.test.js.map +1 -0
- package/dist/extractors/vitest/index.d.ts +17 -0
- package/dist/extractors/vitest/index.d.ts.map +1 -0
- package/dist/extractors/vitest/index.js +564 -0
- package/dist/extractors/vitest/index.js.map +1 -0
- package/dist/extractors/vitest/index.test.d.ts +9 -0
- package/dist/extractors/vitest/index.test.d.ts.map +1 -0
- package/dist/extractors/vitest/index.test.js +373 -0
- package/dist/extractors/vitest/index.test.js.map +1 -0
- package/dist/index.d.ts +27 -11
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +27 -11
- package/dist/index.js.map +1 -1
- package/dist/maven-checkstyle-extractor.d.ts +20 -0
- package/dist/maven-checkstyle-extractor.d.ts.map +1 -0
- package/dist/maven-checkstyle-extractor.js +193 -0
- package/dist/maven-checkstyle-extractor.js.map +1 -0
- package/dist/maven-compiler-extractor.d.ts +20 -0
- package/dist/maven-compiler-extractor.d.ts.map +1 -0
- package/dist/maven-compiler-extractor.js +218 -0
- package/dist/maven-compiler-extractor.js.map +1 -0
- package/dist/maven-surefire-extractor.d.ts +20 -0
- package/dist/maven-surefire-extractor.d.ts.map +1 -0
- package/dist/maven-surefire-extractor.js +228 -0
- package/dist/maven-surefire-extractor.js.map +1 -0
- package/dist/maven-utils.d.ts +24 -0
- package/dist/maven-utils.d.ts.map +1 -0
- package/dist/maven-utils.js +36 -0
- package/dist/maven-utils.js.map +1 -0
- package/dist/plugin-loader.d.ts +82 -0
- package/dist/plugin-loader.d.ts.map +1 -0
- package/dist/plugin-loader.js +200 -0
- package/dist/plugin-loader.js.map +1 -0
- package/dist/sandbox.d.ts +161 -0
- package/dist/sandbox.d.ts.map +1 -0
- package/dist/sandbox.js +254 -0
- package/dist/sandbox.js.map +1 -0
- package/dist/sandbox.test.d.ts +8 -0
- package/dist/sandbox.test.d.ts.map +1 -0
- package/dist/sandbox.test.js +395 -0
- package/dist/sandbox.test.js.map +1 -0
- package/dist/sandboxed-extractor.d.ts +46 -0
- package/dist/sandboxed-extractor.d.ts.map +1 -0
- package/dist/sandboxed-extractor.js +172 -0
- package/dist/sandboxed-extractor.js.map +1 -0
- package/dist/sandboxed-extractor.test.d.ts +5 -0
- package/dist/sandboxed-extractor.test.d.ts.map +1 -0
- package/dist/sandboxed-extractor.test.js +346 -0
- package/dist/sandboxed-extractor.test.js.map +1 -0
- package/dist/smart-extractor.d.ts +22 -10
- package/dist/smart-extractor.d.ts.map +1 -1
- package/dist/smart-extractor.js +116 -163
- package/dist/smart-extractor.js.map +1 -1
- package/dist/types.d.ts +94 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +2 -1
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sandbox Module for Secure Extractor Execution
|
|
3
|
+
*
|
|
4
|
+
* Uses isolated-vm to run extractor plugins in a secure V8 Isolate.
|
|
5
|
+
* This provides true security isolation by preventing access to Node.js APIs
|
|
6
|
+
* and limiting memory/CPU usage.
|
|
7
|
+
*
|
|
8
|
+
* @see docs/sandbox-research.md for detailed architecture and security model
|
|
9
|
+
*/
|
|
10
|
+
import type { FormattedError } from './types.js';
|
|
11
|
+
export interface SandboxOptions {
|
|
12
|
+
/**
|
|
13
|
+
* Memory limit in MB (default: 128)
|
|
14
|
+
* Prevents memory exhaustion attacks
|
|
15
|
+
*/
|
|
16
|
+
memoryLimitMB?: number;
|
|
17
|
+
/**
|
|
18
|
+
* Execution timeout in milliseconds (default: 5000)
|
|
19
|
+
* Prevents infinite loop attacks
|
|
20
|
+
*/
|
|
21
|
+
timeoutMs?: number;
|
|
22
|
+
/**
|
|
23
|
+
* Extractor function code to execute
|
|
24
|
+
* Must define a function named 'extract' that takes string input
|
|
25
|
+
*/
|
|
26
|
+
code: string;
|
|
27
|
+
/**
|
|
28
|
+
* Input data to pass to the extractor
|
|
29
|
+
* This is the command output to parse for errors
|
|
30
|
+
*/
|
|
31
|
+
input: string;
|
|
32
|
+
/**
|
|
33
|
+
* Extractor name for debugging/logging
|
|
34
|
+
*/
|
|
35
|
+
extractorName: string;
|
|
36
|
+
}
|
|
37
|
+
export interface SandboxResult {
|
|
38
|
+
/**
|
|
39
|
+
* Whether execution succeeded without errors
|
|
40
|
+
*/
|
|
41
|
+
success: boolean;
|
|
42
|
+
/**
|
|
43
|
+
* Extracted errors if successful
|
|
44
|
+
*/
|
|
45
|
+
errors?: FormattedError[];
|
|
46
|
+
/**
|
|
47
|
+
* Error message if execution failed
|
|
48
|
+
*/
|
|
49
|
+
error?: string;
|
|
50
|
+
/**
|
|
51
|
+
* Execution statistics
|
|
52
|
+
*/
|
|
53
|
+
stats: {
|
|
54
|
+
/**
|
|
55
|
+
* Execution duration in milliseconds
|
|
56
|
+
*/
|
|
57
|
+
durationMs: number;
|
|
58
|
+
/**
|
|
59
|
+
* Peak memory usage in MB
|
|
60
|
+
*/
|
|
61
|
+
memoryUsedMB: number;
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Error thrown when sandbox execution fails
|
|
66
|
+
*/
|
|
67
|
+
export declare class SandboxExecutionError extends Error {
|
|
68
|
+
readonly extractorName: string;
|
|
69
|
+
readonly _cause?: unknown | undefined;
|
|
70
|
+
constructor(message: string, extractorName: string, _cause?: unknown | undefined);
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Run extractor code in a secure V8 Isolate
|
|
74
|
+
*
|
|
75
|
+
* Security features:
|
|
76
|
+
* - Isolated V8 heap (no access to Node.js globals)
|
|
77
|
+
* - Memory limit enforcement
|
|
78
|
+
* - Execution timeout protection
|
|
79
|
+
* - No filesystem, network, or process access
|
|
80
|
+
*
|
|
81
|
+
* @param options - Sandbox configuration
|
|
82
|
+
* @returns Result containing extracted errors or error details
|
|
83
|
+
*
|
|
84
|
+
* @example
|
|
85
|
+
* ```typescript
|
|
86
|
+
* const result = await runInSandbox({
|
|
87
|
+
* code: 'function extract(input) { return []; }',
|
|
88
|
+
* input: 'error output',
|
|
89
|
+
* extractorName: 'test-extractor'
|
|
90
|
+
* });
|
|
91
|
+
*
|
|
92
|
+
* if (result.success) {
|
|
93
|
+
* console.log('Extracted errors:', result.errors);
|
|
94
|
+
* } else {
|
|
95
|
+
* console.error('Execution failed:', result.error);
|
|
96
|
+
* }
|
|
97
|
+
* ```
|
|
98
|
+
*/
|
|
99
|
+
export declare function runInSandbox(options: SandboxOptions): Promise<SandboxResult>;
|
|
100
|
+
/**
|
|
101
|
+
* Create sandboxed code from an extractor function
|
|
102
|
+
*
|
|
103
|
+
* Converts a regular function to a string that can be executed in the sandbox.
|
|
104
|
+
* The function must:
|
|
105
|
+
* - Be named 'extract'
|
|
106
|
+
* - Take a single string parameter
|
|
107
|
+
* - Return ExtractedError[]
|
|
108
|
+
*
|
|
109
|
+
* @param extractFn - The extractor function to sandbox
|
|
110
|
+
* @returns Sandboxed code string
|
|
111
|
+
*
|
|
112
|
+
* @example
|
|
113
|
+
* ```typescript
|
|
114
|
+
* function extract(content: string): ExtractedError[] {
|
|
115
|
+
* const errors = [];
|
|
116
|
+
* const pattern = /ERROR: (.+)/g;
|
|
117
|
+
* let match;
|
|
118
|
+
* while ((match = pattern.exec(content)) !== null) {
|
|
119
|
+
* errors.push({ message: match[1], severity: 'error' });
|
|
120
|
+
* }
|
|
121
|
+
* return errors;
|
|
122
|
+
* }
|
|
123
|
+
*
|
|
124
|
+
* const code = createSandboxedCode(extract);
|
|
125
|
+
* // code is now a string that can be passed to runInSandbox
|
|
126
|
+
* ```
|
|
127
|
+
*/
|
|
128
|
+
export declare function createSandboxedCode(extractFn: (_content: string) => FormattedError[]): string;
|
|
129
|
+
/**
|
|
130
|
+
* Performance statistics for sandbox operations
|
|
131
|
+
* Used for monitoring and optimization
|
|
132
|
+
*/
|
|
133
|
+
export interface SandboxStats {
|
|
134
|
+
totalExecutions: number;
|
|
135
|
+
successfulExecutions: number;
|
|
136
|
+
failedExecutions: number;
|
|
137
|
+
averageDurationMs: number;
|
|
138
|
+
averageMemoryUsedMB: number;
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Sandbox performance tracker
|
|
142
|
+
* Collects statistics across multiple executions
|
|
143
|
+
*/
|
|
144
|
+
export declare class SandboxStatsCollector {
|
|
145
|
+
private stats;
|
|
146
|
+
private totalDurationMs;
|
|
147
|
+
private totalMemoryUsedMB;
|
|
148
|
+
/**
|
|
149
|
+
* Record a sandbox execution result
|
|
150
|
+
*/
|
|
151
|
+
record(result: SandboxResult): void;
|
|
152
|
+
/**
|
|
153
|
+
* Get current statistics
|
|
154
|
+
*/
|
|
155
|
+
getStats(): Readonly<SandboxStats>;
|
|
156
|
+
/**
|
|
157
|
+
* Reset statistics
|
|
158
|
+
*/
|
|
159
|
+
reset(): void;
|
|
160
|
+
}
|
|
161
|
+
//# sourceMappingURL=sandbox.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sandbox.d.ts","sourceRoot":"","sources":["../src/sandbox.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAsBjD,MAAM,WAAW,cAAc;IAC7B;;;OAGG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;IAEvB;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB;;;OAGG;IACH,IAAI,EAAE,MAAM,CAAC;IAEb;;;OAGG;IACH,KAAK,EAAE,MAAM,CAAC;IAEd;;OAEG;IACH,aAAa,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,aAAa;IAC5B;;OAEG;IACH,OAAO,EAAE,OAAO,CAAC;IAEjB;;OAEG;IACH,MAAM,CAAC,EAAE,cAAc,EAAE,CAAC;IAE1B;;OAEG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IAEf;;OAEG;IACH,KAAK,EAAE;QACL;;WAEG;QACH,UAAU,EAAE,MAAM,CAAC;QAEnB;;WAEG;QACH,YAAY,EAAE,MAAM,CAAC;KACtB,CAAC;CACH;AAED;;GAEG;AACH,qBAAa,qBAAsB,SAAQ,KAAK;aAG5B,aAAa,EAAE,MAAM;aACrB,MAAM,CAAC,EAAE,OAAO;gBAFhC,OAAO,EAAE,MAAM,EACC,aAAa,EAAE,MAAM,EACrB,MAAM,CAAC,EAAE,OAAO,YAAA;CAKnC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,wBAAsB,YAAY,CAChC,OAAO,EAAE,cAAc,GACtB,OAAO,CAAC,aAAa,CAAC,CAmGxB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,wBAAgB,mBAAmB,CACjC,SAAS,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,cAAc,EAAE,GAChD,MAAM,CAuBR;AAED;;;GAGG;AACH,MAAM,WAAW,YAAY;IAC3B,eAAe,EAAE,MAAM,CAAC;IACxB,oBAAoB,EAAE,MAAM,CAAC;IAC7B,gBAAgB,EAAE,MAAM,CAAC;IACzB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,mBAAmB,EAAE,MAAM,CAAC;CAC7B;AAED;;;GAGG;AACH,qBAAa,qBAAqB;IAChC,OAAO,CAAC,KAAK,CAMX;IAEF,OAAO,CAAC,eAAe,CAAK;IAC5B,OAAO,CAAC,iBAAiB,CAAK;IAE9B;;OAEG;IACH,MAAM,CAAC,MAAM,EAAE,aAAa,GAAG,IAAI;IAmBnC;;OAEG;IACH,QAAQ,IAAI,QAAQ,CAAC,YAAY,CAAC;IAIlC;;OAEG;IACH,KAAK,IAAI,IAAI;CAWd"}
|
package/dist/sandbox.js
ADDED
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sandbox Module for Secure Extractor Execution
|
|
3
|
+
*
|
|
4
|
+
* Uses isolated-vm to run extractor plugins in a secure V8 Isolate.
|
|
5
|
+
* This provides true security isolation by preventing access to Node.js APIs
|
|
6
|
+
* and limiting memory/CPU usage.
|
|
7
|
+
*
|
|
8
|
+
* @see docs/sandbox-research.md for detailed architecture and security model
|
|
9
|
+
*/
|
|
10
|
+
// Lazy-load isolated-vm (CommonJS module)
|
|
11
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- isolated-vm has no types
|
|
12
|
+
let ivm;
|
|
13
|
+
async function getIVM() {
|
|
14
|
+
if (!ivm) {
|
|
15
|
+
try {
|
|
16
|
+
// isolated-vm is CommonJS, use dynamic import and createRequire
|
|
17
|
+
const { createRequire } = await import('node:module');
|
|
18
|
+
const require = createRequire(import.meta.url);
|
|
19
|
+
ivm = require('isolated-vm');
|
|
20
|
+
}
|
|
21
|
+
catch {
|
|
22
|
+
// If isolated-vm is not installed, throw helpful error
|
|
23
|
+
throw new Error('isolated-vm is required for sandbox execution. Install with: pnpm add isolated-vm');
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
return ivm;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Error thrown when sandbox execution fails
|
|
30
|
+
*/
|
|
31
|
+
export class SandboxExecutionError extends Error {
|
|
32
|
+
extractorName;
|
|
33
|
+
_cause;
|
|
34
|
+
constructor(message, extractorName, _cause) {
|
|
35
|
+
super(`Sandbox execution failed for ${extractorName}: ${message}`);
|
|
36
|
+
this.extractorName = extractorName;
|
|
37
|
+
this._cause = _cause;
|
|
38
|
+
this.name = 'SandboxExecutionError';
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Run extractor code in a secure V8 Isolate
|
|
43
|
+
*
|
|
44
|
+
* Security features:
|
|
45
|
+
* - Isolated V8 heap (no access to Node.js globals)
|
|
46
|
+
* - Memory limit enforcement
|
|
47
|
+
* - Execution timeout protection
|
|
48
|
+
* - No filesystem, network, or process access
|
|
49
|
+
*
|
|
50
|
+
* @param options - Sandbox configuration
|
|
51
|
+
* @returns Result containing extracted errors or error details
|
|
52
|
+
*
|
|
53
|
+
* @example
|
|
54
|
+
* ```typescript
|
|
55
|
+
* const result = await runInSandbox({
|
|
56
|
+
* code: 'function extract(input) { return []; }',
|
|
57
|
+
* input: 'error output',
|
|
58
|
+
* extractorName: 'test-extractor'
|
|
59
|
+
* });
|
|
60
|
+
*
|
|
61
|
+
* if (result.success) {
|
|
62
|
+
* console.log('Extracted errors:', result.errors);
|
|
63
|
+
* } else {
|
|
64
|
+
* console.error('Execution failed:', result.error);
|
|
65
|
+
* }
|
|
66
|
+
* ```
|
|
67
|
+
*/
|
|
68
|
+
export async function runInSandbox(options) {
|
|
69
|
+
const startTime = performance.now();
|
|
70
|
+
const ivm = await getIVM();
|
|
71
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- isolated-vm has no types
|
|
72
|
+
let isolate;
|
|
73
|
+
try {
|
|
74
|
+
// Create V8 Isolate with memory limit
|
|
75
|
+
isolate = new ivm.Isolate({
|
|
76
|
+
memoryLimit: options.memoryLimitMB ?? 128
|
|
77
|
+
});
|
|
78
|
+
const context = await isolate.createContext();
|
|
79
|
+
// Wrap extractor code to return JSON-serializable result
|
|
80
|
+
const wrappedCode = `
|
|
81
|
+
${options.code}
|
|
82
|
+
|
|
83
|
+
function __executeExtractor(input) {
|
|
84
|
+
try {
|
|
85
|
+
const errors = extract(input);
|
|
86
|
+
return JSON.stringify({ success: true, errors });
|
|
87
|
+
} catch (error) {
|
|
88
|
+
return JSON.stringify({
|
|
89
|
+
success: false,
|
|
90
|
+
error: error instanceof Error ? error.message : String(error)
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
`;
|
|
95
|
+
// Compile code (cached by V8)
|
|
96
|
+
const script = await isolate.compileScript(wrappedCode);
|
|
97
|
+
await script.run(context);
|
|
98
|
+
// Transfer input to isolate (copy, don't share reference)
|
|
99
|
+
const inputCopy = new ivm.ExternalCopy(options.input);
|
|
100
|
+
await context.global.set('input', inputCopy.copyInto());
|
|
101
|
+
// Execute with timeout
|
|
102
|
+
const resultJson = await context.eval('__executeExtractor(input)', {
|
|
103
|
+
timeout: options.timeoutMs ?? 5000,
|
|
104
|
+
promise: true
|
|
105
|
+
});
|
|
106
|
+
const result = JSON.parse(resultJson);
|
|
107
|
+
// Get memory stats before disposal
|
|
108
|
+
const heapStats = isolate.getHeapStatisticsSync();
|
|
109
|
+
const memoryUsedMB = heapStats.used_heap_size / 1024 / 1024;
|
|
110
|
+
isolate.dispose();
|
|
111
|
+
const durationMs = performance.now() - startTime;
|
|
112
|
+
if (!result.success) {
|
|
113
|
+
return {
|
|
114
|
+
success: false,
|
|
115
|
+
error: result.error,
|
|
116
|
+
stats: {
|
|
117
|
+
durationMs,
|
|
118
|
+
memoryUsedMB
|
|
119
|
+
}
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
return {
|
|
123
|
+
success: true,
|
|
124
|
+
errors: result.errors ?? [],
|
|
125
|
+
stats: {
|
|
126
|
+
durationMs,
|
|
127
|
+
memoryUsedMB
|
|
128
|
+
}
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
catch (error) {
|
|
132
|
+
// Clean up isolate on error
|
|
133
|
+
if (isolate) {
|
|
134
|
+
try {
|
|
135
|
+
isolate.dispose();
|
|
136
|
+
}
|
|
137
|
+
catch {
|
|
138
|
+
// Ignore disposal errors
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
const durationMs = performance.now() - startTime;
|
|
142
|
+
return {
|
|
143
|
+
success: false,
|
|
144
|
+
error: error instanceof Error ? error.message : String(error),
|
|
145
|
+
stats: {
|
|
146
|
+
durationMs,
|
|
147
|
+
memoryUsedMB: 0
|
|
148
|
+
}
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Create sandboxed code from an extractor function
|
|
154
|
+
*
|
|
155
|
+
* Converts a regular function to a string that can be executed in the sandbox.
|
|
156
|
+
* The function must:
|
|
157
|
+
* - Be named 'extract'
|
|
158
|
+
* - Take a single string parameter
|
|
159
|
+
* - Return ExtractedError[]
|
|
160
|
+
*
|
|
161
|
+
* @param extractFn - The extractor function to sandbox
|
|
162
|
+
* @returns Sandboxed code string
|
|
163
|
+
*
|
|
164
|
+
* @example
|
|
165
|
+
* ```typescript
|
|
166
|
+
* function extract(content: string): ExtractedError[] {
|
|
167
|
+
* const errors = [];
|
|
168
|
+
* const pattern = /ERROR: (.+)/g;
|
|
169
|
+
* let match;
|
|
170
|
+
* while ((match = pattern.exec(content)) !== null) {
|
|
171
|
+
* errors.push({ message: match[1], severity: 'error' });
|
|
172
|
+
* }
|
|
173
|
+
* return errors;
|
|
174
|
+
* }
|
|
175
|
+
*
|
|
176
|
+
* const code = createSandboxedCode(extract);
|
|
177
|
+
* // code is now a string that can be passed to runInSandbox
|
|
178
|
+
* ```
|
|
179
|
+
*/
|
|
180
|
+
export function createSandboxedCode(extractFn) {
|
|
181
|
+
// Convert function to string and rename to 'extract' if needed
|
|
182
|
+
const fnString = extractFn.toString();
|
|
183
|
+
// Check if function is already named 'extract'
|
|
184
|
+
if (fnString.startsWith('function extract(')) {
|
|
185
|
+
return fnString;
|
|
186
|
+
}
|
|
187
|
+
// Anonymous or arrow function - wrap it
|
|
188
|
+
if (fnString.startsWith('(') || fnString.includes('=>')) {
|
|
189
|
+
return `function extract(content) { return (${fnString})(content); }`;
|
|
190
|
+
}
|
|
191
|
+
// Named function - extract body and create 'extract' wrapper
|
|
192
|
+
const functionPattern = /function\s+\w+\s*\([^)]*\)\s*{([\s\S]*)}$/;
|
|
193
|
+
const bodyMatch = functionPattern.exec(fnString);
|
|
194
|
+
if (bodyMatch) {
|
|
195
|
+
return `function extract(content) { ${bodyMatch[1]} }`;
|
|
196
|
+
}
|
|
197
|
+
// Fallback - assume it's valid code
|
|
198
|
+
return fnString;
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* Sandbox performance tracker
|
|
202
|
+
* Collects statistics across multiple executions
|
|
203
|
+
*/
|
|
204
|
+
export class SandboxStatsCollector {
|
|
205
|
+
stats = {
|
|
206
|
+
totalExecutions: 0,
|
|
207
|
+
successfulExecutions: 0,
|
|
208
|
+
failedExecutions: 0,
|
|
209
|
+
averageDurationMs: 0,
|
|
210
|
+
averageMemoryUsedMB: 0
|
|
211
|
+
};
|
|
212
|
+
totalDurationMs = 0;
|
|
213
|
+
totalMemoryUsedMB = 0;
|
|
214
|
+
/**
|
|
215
|
+
* Record a sandbox execution result
|
|
216
|
+
*/
|
|
217
|
+
record(result) {
|
|
218
|
+
this.stats.totalExecutions++;
|
|
219
|
+
if (result.success) {
|
|
220
|
+
this.stats.successfulExecutions++;
|
|
221
|
+
}
|
|
222
|
+
else {
|
|
223
|
+
this.stats.failedExecutions++;
|
|
224
|
+
}
|
|
225
|
+
this.totalDurationMs += result.stats.durationMs;
|
|
226
|
+
this.totalMemoryUsedMB += result.stats.memoryUsedMB;
|
|
227
|
+
// Update averages
|
|
228
|
+
this.stats.averageDurationMs =
|
|
229
|
+
this.totalDurationMs / this.stats.totalExecutions;
|
|
230
|
+
this.stats.averageMemoryUsedMB =
|
|
231
|
+
this.totalMemoryUsedMB / this.stats.totalExecutions;
|
|
232
|
+
}
|
|
233
|
+
/**
|
|
234
|
+
* Get current statistics
|
|
235
|
+
*/
|
|
236
|
+
getStats() {
|
|
237
|
+
return { ...this.stats };
|
|
238
|
+
}
|
|
239
|
+
/**
|
|
240
|
+
* Reset statistics
|
|
241
|
+
*/
|
|
242
|
+
reset() {
|
|
243
|
+
this.stats = {
|
|
244
|
+
totalExecutions: 0,
|
|
245
|
+
successfulExecutions: 0,
|
|
246
|
+
failedExecutions: 0,
|
|
247
|
+
averageDurationMs: 0,
|
|
248
|
+
averageMemoryUsedMB: 0
|
|
249
|
+
};
|
|
250
|
+
this.totalDurationMs = 0;
|
|
251
|
+
this.totalMemoryUsedMB = 0;
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
//# sourceMappingURL=sandbox.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sandbox.js","sourceRoot":"","sources":["../src/sandbox.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sandbox.test.d.ts","sourceRoot":"","sources":["../src/sandbox.test.ts"],"names":[],"mappings":"AAAA;;;;;GAKG"}
|