@vibe-validate/extractors 0.17.0-rc2 → 0.17.0-rc4
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 +72 -0
- package/dist/extractor-registry.d.ts.map +1 -0
- package/dist/extractor-registry.js +243 -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 -13
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +27 -13
- package/dist/index.js.map +1 -1
- package/dist/maven-checkstyle-extractor.d.ts.map +1 -1
- package/dist/maven-checkstyle-extractor.js +2 -17
- package/dist/maven-checkstyle-extractor.js.map +1 -1
- 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-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 +51 -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 -186
- 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,395 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sandbox Module Tests
|
|
3
|
+
*
|
|
4
|
+
* Tests secure extractor execution using isolated-vm.
|
|
5
|
+
* These tests validate the sandbox can execute real extractor code safely.
|
|
6
|
+
*/
|
|
7
|
+
import { describe, it, expect } from 'vitest';
|
|
8
|
+
import { runInSandbox, createSandboxedCode, SandboxStatsCollector, } from './sandbox.js';
|
|
9
|
+
describe('Sandbox Module', () => {
|
|
10
|
+
describe('runInSandbox', () => {
|
|
11
|
+
it('should execute simple extractor code successfully', async () => {
|
|
12
|
+
const code = `
|
|
13
|
+
function extract(content) {
|
|
14
|
+
return [
|
|
15
|
+
{ message: 'Test error', severity: 'error' }
|
|
16
|
+
];
|
|
17
|
+
}
|
|
18
|
+
`;
|
|
19
|
+
const result = await runInSandbox({
|
|
20
|
+
code,
|
|
21
|
+
input: 'test input',
|
|
22
|
+
extractorName: 'test'
|
|
23
|
+
});
|
|
24
|
+
expect(result.success).toBe(true);
|
|
25
|
+
expect(result.errors).toHaveLength(1);
|
|
26
|
+
expect(result.errors?.[0].message).toBe('Test error');
|
|
27
|
+
expect(result.stats.durationMs).toBeGreaterThan(0);
|
|
28
|
+
});
|
|
29
|
+
it('should handle extractor that throws an error', async () => {
|
|
30
|
+
const code = `
|
|
31
|
+
function extract(content) {
|
|
32
|
+
throw new Error('Intentional failure');
|
|
33
|
+
}
|
|
34
|
+
`;
|
|
35
|
+
const result = await runInSandbox({
|
|
36
|
+
code,
|
|
37
|
+
input: 'test input',
|
|
38
|
+
extractorName: 'test'
|
|
39
|
+
});
|
|
40
|
+
expect(result.success).toBe(false);
|
|
41
|
+
expect(result.error).toContain('Intentional failure');
|
|
42
|
+
});
|
|
43
|
+
it('should enforce memory limits', async () => {
|
|
44
|
+
const code = `
|
|
45
|
+
function extract(content) {
|
|
46
|
+
// Try to allocate memory and fill it
|
|
47
|
+
const arrays = [];
|
|
48
|
+
try {
|
|
49
|
+
while (true) {
|
|
50
|
+
// Allocate 1MB chunks
|
|
51
|
+
arrays.push(new Array(256 * 1024).fill(1));
|
|
52
|
+
}
|
|
53
|
+
} catch (e) {
|
|
54
|
+
// Memory exhausted - this is expected
|
|
55
|
+
throw new Error('Out of memory');
|
|
56
|
+
}
|
|
57
|
+
return [];
|
|
58
|
+
}
|
|
59
|
+
`;
|
|
60
|
+
const result = await runInSandbox({
|
|
61
|
+
code,
|
|
62
|
+
input: 'test',
|
|
63
|
+
extractorName: 'memory-hog',
|
|
64
|
+
memoryLimitMB: 10, // Very low limit
|
|
65
|
+
timeoutMs: 1000 // Also set timeout to prevent hanging
|
|
66
|
+
});
|
|
67
|
+
// Should fail either due to memory limit or timeout
|
|
68
|
+
expect(result.success).toBe(false);
|
|
69
|
+
expect(result.error).toBeDefined();
|
|
70
|
+
});
|
|
71
|
+
it('should enforce timeout limits', async () => {
|
|
72
|
+
const code = `
|
|
73
|
+
function extract(content) {
|
|
74
|
+
// Infinite loop
|
|
75
|
+
while (true) {}
|
|
76
|
+
return [];
|
|
77
|
+
}
|
|
78
|
+
`;
|
|
79
|
+
const result = await runInSandbox({
|
|
80
|
+
code,
|
|
81
|
+
input: 'test',
|
|
82
|
+
extractorName: 'infinite-loop',
|
|
83
|
+
timeoutMs: 100 // Short timeout
|
|
84
|
+
});
|
|
85
|
+
expect(result.success).toBe(false);
|
|
86
|
+
expect(result.error).toContain('timed out');
|
|
87
|
+
});
|
|
88
|
+
it('should execute real extractor code (TypeScript-like)', async () => {
|
|
89
|
+
const code = String.raw `
|
|
90
|
+
function extract(content) {
|
|
91
|
+
const errors = [];
|
|
92
|
+
const pattern = /error TS(\d+):\s*(.+)/g;
|
|
93
|
+
|
|
94
|
+
let match;
|
|
95
|
+
while ((match = pattern.exec(content)) !== null) {
|
|
96
|
+
errors.push({
|
|
97
|
+
message: match[2].trim(),
|
|
98
|
+
code: 'TS' + match[1],
|
|
99
|
+
severity: 'error'
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
return errors;
|
|
104
|
+
}
|
|
105
|
+
`;
|
|
106
|
+
const input = `
|
|
107
|
+
src/test.ts:10:15 - error TS2322: Type 'string' is not assignable to type 'number'.
|
|
108
|
+
src/test.ts:20:5 - error TS2304: Cannot find name 'undefined'.
|
|
109
|
+
`;
|
|
110
|
+
const result = await runInSandbox({
|
|
111
|
+
code,
|
|
112
|
+
input,
|
|
113
|
+
extractorName: 'typescript'
|
|
114
|
+
});
|
|
115
|
+
expect(result.success).toBe(true);
|
|
116
|
+
expect(result.errors).toHaveLength(2);
|
|
117
|
+
expect(result.errors?.[0].code).toBe('TS2322');
|
|
118
|
+
expect(result.errors?.[1].code).toBe('TS2304');
|
|
119
|
+
});
|
|
120
|
+
it('should execute real extractor code (Maven-like)', async () => {
|
|
121
|
+
const code = String.raw `
|
|
122
|
+
function extract(content) {
|
|
123
|
+
const errors = [];
|
|
124
|
+
const pattern = /\[ERROR\]\s+([^:]+):\[(\d+),(\d+)\]\s+(.+)/g;
|
|
125
|
+
|
|
126
|
+
let match;
|
|
127
|
+
while ((match = pattern.exec(content)) !== null) {
|
|
128
|
+
errors.push({
|
|
129
|
+
file: match[1].trim(),
|
|
130
|
+
line: parseInt(match[2], 10),
|
|
131
|
+
column: parseInt(match[3], 10),
|
|
132
|
+
message: match[4].trim(),
|
|
133
|
+
severity: 'error'
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
return errors;
|
|
138
|
+
}
|
|
139
|
+
`;
|
|
140
|
+
const input = `
|
|
141
|
+
[ERROR] /src/main/java/App.java:[15,20] cannot find symbol
|
|
142
|
+
[ERROR] /src/main/java/Utils.java:[42,8] incompatible types
|
|
143
|
+
`;
|
|
144
|
+
const result = await runInSandbox({
|
|
145
|
+
code,
|
|
146
|
+
input,
|
|
147
|
+
extractorName: 'maven-compiler'
|
|
148
|
+
});
|
|
149
|
+
expect(result.success).toBe(true);
|
|
150
|
+
expect(result.errors).toHaveLength(2);
|
|
151
|
+
expect(result.errors?.[0].file).toContain('App.java');
|
|
152
|
+
expect(result.errors?.[0].line).toBe(15);
|
|
153
|
+
expect(result.errors?.[1].file).toContain('Utils.java');
|
|
154
|
+
expect(result.errors?.[1].line).toBe(42);
|
|
155
|
+
});
|
|
156
|
+
it('should provide accurate performance statistics', async () => {
|
|
157
|
+
const code = `
|
|
158
|
+
function extract(content) {
|
|
159
|
+
// Do some work
|
|
160
|
+
let result = [];
|
|
161
|
+
for (let i = 0; i < 1000; i++) {
|
|
162
|
+
result.push({ message: 'Error ' + i, severity: 'error' });
|
|
163
|
+
}
|
|
164
|
+
return result;
|
|
165
|
+
}
|
|
166
|
+
`;
|
|
167
|
+
const result = await runInSandbox({
|
|
168
|
+
code,
|
|
169
|
+
input: 'test',
|
|
170
|
+
extractorName: 'perf-test'
|
|
171
|
+
});
|
|
172
|
+
expect(result.success).toBe(true);
|
|
173
|
+
expect(result.stats.durationMs).toBeGreaterThan(0);
|
|
174
|
+
expect(result.stats.durationMs).toBeLessThan(1000); // Should be fast
|
|
175
|
+
expect(result.stats.memoryUsedMB).toBeGreaterThan(0);
|
|
176
|
+
});
|
|
177
|
+
it('should handle extractors that return empty arrays', async () => {
|
|
178
|
+
const code = `
|
|
179
|
+
function extract(content) {
|
|
180
|
+
return [];
|
|
181
|
+
}
|
|
182
|
+
`;
|
|
183
|
+
const result = await runInSandbox({
|
|
184
|
+
code,
|
|
185
|
+
input: 'no errors here',
|
|
186
|
+
extractorName: 'clean'
|
|
187
|
+
});
|
|
188
|
+
expect(result.success).toBe(true);
|
|
189
|
+
expect(result.errors).toHaveLength(0);
|
|
190
|
+
});
|
|
191
|
+
it('should handle extractors with complex logic', async () => {
|
|
192
|
+
const code = String.raw `
|
|
193
|
+
function extract(content) {
|
|
194
|
+
const errors = [];
|
|
195
|
+
const lines = content.split('\n');
|
|
196
|
+
|
|
197
|
+
for (let i = 0; i < lines.length; i++) {
|
|
198
|
+
const line = lines[i];
|
|
199
|
+
|
|
200
|
+
// Skip empty lines
|
|
201
|
+
if (!line.trim()) continue;
|
|
202
|
+
|
|
203
|
+
// Parse error format
|
|
204
|
+
if (line.includes('ERROR:')) {
|
|
205
|
+
const parts = line.split('ERROR:');
|
|
206
|
+
if (parts.length === 2) {
|
|
207
|
+
errors.push({
|
|
208
|
+
message: parts[1].trim(),
|
|
209
|
+
line: i + 1,
|
|
210
|
+
severity: 'error'
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
return errors;
|
|
217
|
+
}
|
|
218
|
+
`;
|
|
219
|
+
const input = `
|
|
220
|
+
Line 1: Some output
|
|
221
|
+
Line 2: ERROR: First error
|
|
222
|
+
Line 3: More output
|
|
223
|
+
Line 4: ERROR: Second error
|
|
224
|
+
`;
|
|
225
|
+
const result = await runInSandbox({
|
|
226
|
+
code,
|
|
227
|
+
input,
|
|
228
|
+
extractorName: 'complex'
|
|
229
|
+
});
|
|
230
|
+
expect(result.success).toBe(true);
|
|
231
|
+
expect(result.errors).toHaveLength(2);
|
|
232
|
+
expect(result.errors?.[0].message).toBe('First error');
|
|
233
|
+
expect(result.errors?.[1].message).toBe('Second error');
|
|
234
|
+
});
|
|
235
|
+
});
|
|
236
|
+
describe('createSandboxedCode', () => {
|
|
237
|
+
it('should handle named function declarations', () => {
|
|
238
|
+
function extract(content) {
|
|
239
|
+
return [{ message: content, severity: 'error' }];
|
|
240
|
+
}
|
|
241
|
+
const code = createSandboxedCode(extract);
|
|
242
|
+
expect(code).toContain('function extract(');
|
|
243
|
+
expect(code).toContain('return [');
|
|
244
|
+
});
|
|
245
|
+
it('should handle arrow functions', () => {
|
|
246
|
+
const extract = (content) => {
|
|
247
|
+
return [{ message: content, severity: 'error' }];
|
|
248
|
+
};
|
|
249
|
+
const code = createSandboxedCode(extract);
|
|
250
|
+
expect(code).toContain('function extract(content)');
|
|
251
|
+
});
|
|
252
|
+
});
|
|
253
|
+
describe('SandboxStatsCollector', () => {
|
|
254
|
+
it('should collect execution statistics', async () => {
|
|
255
|
+
const collector = new SandboxStatsCollector();
|
|
256
|
+
const code = `
|
|
257
|
+
function extract(content) {
|
|
258
|
+
return [{ message: 'Test', severity: 'error' }];
|
|
259
|
+
}
|
|
260
|
+
`;
|
|
261
|
+
// Execute multiple times
|
|
262
|
+
for (let i = 0; i < 3; i++) {
|
|
263
|
+
const result = await runInSandbox({
|
|
264
|
+
code,
|
|
265
|
+
input: 'test',
|
|
266
|
+
extractorName: 'stats-test'
|
|
267
|
+
});
|
|
268
|
+
collector.record(result);
|
|
269
|
+
}
|
|
270
|
+
const stats = collector.getStats();
|
|
271
|
+
expect(stats.totalExecutions).toBe(3);
|
|
272
|
+
expect(stats.successfulExecutions).toBe(3);
|
|
273
|
+
expect(stats.failedExecutions).toBe(0);
|
|
274
|
+
expect(stats.averageDurationMs).toBeGreaterThan(0);
|
|
275
|
+
expect(stats.averageMemoryUsedMB).toBeGreaterThan(0);
|
|
276
|
+
});
|
|
277
|
+
it('should track both successful and failed executions', async () => {
|
|
278
|
+
const collector = new SandboxStatsCollector();
|
|
279
|
+
// Successful execution
|
|
280
|
+
const successCode = `
|
|
281
|
+
function extract(content) {
|
|
282
|
+
return [];
|
|
283
|
+
}
|
|
284
|
+
`;
|
|
285
|
+
const successResult = await runInSandbox({
|
|
286
|
+
code: successCode,
|
|
287
|
+
input: 'test',
|
|
288
|
+
extractorName: 'success'
|
|
289
|
+
});
|
|
290
|
+
collector.record(successResult);
|
|
291
|
+
// Failed execution
|
|
292
|
+
const failCode = `
|
|
293
|
+
function extract(content) {
|
|
294
|
+
throw new Error('Fail');
|
|
295
|
+
}
|
|
296
|
+
`;
|
|
297
|
+
const failResult = await runInSandbox({
|
|
298
|
+
code: failCode,
|
|
299
|
+
input: 'test',
|
|
300
|
+
extractorName: 'fail'
|
|
301
|
+
});
|
|
302
|
+
collector.record(failResult);
|
|
303
|
+
const stats = collector.getStats();
|
|
304
|
+
expect(stats.totalExecutions).toBe(2);
|
|
305
|
+
expect(stats.successfulExecutions).toBe(1);
|
|
306
|
+
expect(stats.failedExecutions).toBe(1);
|
|
307
|
+
});
|
|
308
|
+
it('should reset statistics', async () => {
|
|
309
|
+
const collector = new SandboxStatsCollector();
|
|
310
|
+
const code = `
|
|
311
|
+
function extract(content) {
|
|
312
|
+
return [];
|
|
313
|
+
}
|
|
314
|
+
`;
|
|
315
|
+
const result = await runInSandbox({
|
|
316
|
+
code,
|
|
317
|
+
input: 'test',
|
|
318
|
+
extractorName: 'reset-test'
|
|
319
|
+
});
|
|
320
|
+
collector.record(result);
|
|
321
|
+
expect(collector.getStats().totalExecutions).toBe(1);
|
|
322
|
+
collector.reset();
|
|
323
|
+
expect(collector.getStats().totalExecutions).toBe(0);
|
|
324
|
+
expect(collector.getStats().successfulExecutions).toBe(0);
|
|
325
|
+
expect(collector.getStats().failedExecutions).toBe(0);
|
|
326
|
+
});
|
|
327
|
+
});
|
|
328
|
+
describe('Security Tests', () => {
|
|
329
|
+
it('should block access to Node.js process', async () => {
|
|
330
|
+
const code = `
|
|
331
|
+
function extract(content) {
|
|
332
|
+
// Try to access process
|
|
333
|
+
if (typeof process !== 'undefined') {
|
|
334
|
+
return [{ message: 'Process accessible!', severity: 'error' }];
|
|
335
|
+
}
|
|
336
|
+
return [];
|
|
337
|
+
}
|
|
338
|
+
`;
|
|
339
|
+
const result = await runInSandbox({
|
|
340
|
+
code,
|
|
341
|
+
input: 'test',
|
|
342
|
+
extractorName: 'process-test'
|
|
343
|
+
});
|
|
344
|
+
expect(result.success).toBe(true);
|
|
345
|
+
expect(result.errors).toHaveLength(0); // process should be undefined
|
|
346
|
+
});
|
|
347
|
+
it('should block access to require', async () => {
|
|
348
|
+
const code = `
|
|
349
|
+
function extract(content) {
|
|
350
|
+
// Try to use require
|
|
351
|
+
try {
|
|
352
|
+
require('fs');
|
|
353
|
+
return [{ message: 'require accessible!', severity: 'error' }];
|
|
354
|
+
} catch (e) {
|
|
355
|
+
return [];
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
`;
|
|
359
|
+
const result = await runInSandbox({
|
|
360
|
+
code,
|
|
361
|
+
input: 'test',
|
|
362
|
+
extractorName: 'require-test'
|
|
363
|
+
});
|
|
364
|
+
expect(result.success).toBe(true);
|
|
365
|
+
expect(result.errors).toHaveLength(0); // require should fail
|
|
366
|
+
});
|
|
367
|
+
it('should allow safe operations (String, Array, Object, JSON)', async () => {
|
|
368
|
+
const code = String.raw `
|
|
369
|
+
function extract(content) {
|
|
370
|
+
// Use safe APIs
|
|
371
|
+
const lines = content.split('\n');
|
|
372
|
+
const mapped = lines.map(l => l.trim());
|
|
373
|
+
const filtered = mapped.filter(l => l.length > 0);
|
|
374
|
+
const obj = { count: filtered.length };
|
|
375
|
+
const json = JSON.stringify(obj);
|
|
376
|
+
const parsed = JSON.parse(json);
|
|
377
|
+
|
|
378
|
+
return [{
|
|
379
|
+
message: 'Count: ' + parsed.count,
|
|
380
|
+
severity: 'info'
|
|
381
|
+
}];
|
|
382
|
+
}
|
|
383
|
+
`;
|
|
384
|
+
const result = await runInSandbox({
|
|
385
|
+
code,
|
|
386
|
+
input: 'line1\nline2\n\nline3',
|
|
387
|
+
extractorName: 'safe-api-test'
|
|
388
|
+
});
|
|
389
|
+
expect(result.success).toBe(true);
|
|
390
|
+
expect(result.errors).toHaveLength(1);
|
|
391
|
+
expect(result.errors?.[0].message).toBe('Count: 3');
|
|
392
|
+
});
|
|
393
|
+
});
|
|
394
|
+
});
|
|
395
|
+
//# sourceMappingURL=sandbox.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sandbox.test.js","sourceRoot":"","sources":["../src/sandbox.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sandboxed Extractor Wrapper
|
|
3
|
+
*
|
|
4
|
+
* Wraps extractor plugins to execute in a secure sandbox when configured with 'sandbox' trust level.
|
|
5
|
+
* For 'full' trust, extractors run directly without sandboxing for maximum performance.
|
|
6
|
+
*
|
|
7
|
+
* @package @vibe-validate/extractors
|
|
8
|
+
*/
|
|
9
|
+
import type { ExtractorPlugin, ErrorExtractorResult } from './types.js';
|
|
10
|
+
/**
|
|
11
|
+
* Trust level for extractor execution
|
|
12
|
+
* - 'full': Run with full Node.js access (trusted code, faster)
|
|
13
|
+
* - 'sandbox': Run in isolated V8 context (untrusted code, secure)
|
|
14
|
+
*/
|
|
15
|
+
export type ExtractorTrustLevel = 'full' | 'sandbox';
|
|
16
|
+
/**
|
|
17
|
+
* Options for sandboxed extractor creation
|
|
18
|
+
*/
|
|
19
|
+
export interface SandboxedExtractorOptions {
|
|
20
|
+
/** Trust level (default: 'sandbox') */
|
|
21
|
+
trust?: ExtractorTrustLevel;
|
|
22
|
+
/** Memory limit in MB for sandboxed execution (default: 128) */
|
|
23
|
+
memoryLimitMB?: number;
|
|
24
|
+
/** Timeout in milliseconds for sandboxed execution (default: 5000) */
|
|
25
|
+
timeoutMs?: number;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Wrap an extractor plugin to run in a sandbox based on trust level
|
|
29
|
+
*
|
|
30
|
+
* This function returns a wrapped extract function that:
|
|
31
|
+
* - If trust='full': Runs the original extract function directly (no sandbox)
|
|
32
|
+
* - If trust='sandbox': Runs the extract function in an isolated V8 context
|
|
33
|
+
*
|
|
34
|
+
* @param plugin - The extractor plugin to wrap
|
|
35
|
+
* @param options - Sandbox configuration options
|
|
36
|
+
* @returns Wrapped extract function that respects trust level
|
|
37
|
+
*
|
|
38
|
+
* @example Trusted execution (no sandbox)
|
|
39
|
+
* ```typescript
|
|
40
|
+
* const wrappedExtract = createSandboxedExtractor(plugin, { trust: 'full' });
|
|
41
|
+
* const result = wrappedExtract('error output');
|
|
42
|
+
* ```
|
|
43
|
+
*
|
|
44
|
+
* @example Sandboxed execution (secure)
|
|
45
|
+
* ```typescript
|
|
46
|
+
* const wrappedExtract = createSandboxedExtractor(plugin, { trust: 'sandbox' });
|
|
47
|
+
* const result = await wrappedExtract('error output');
|
|
48
|
+
* ```
|
|
49
|
+
*/
|
|
50
|
+
export declare function createSandboxedExtractor(plugin: ExtractorPlugin, options?: SandboxedExtractorOptions): (output: string, command?: string) => Promise<ErrorExtractorResult>;
|
|
51
|
+
//# sourceMappingURL=sandboxed-extractor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sandboxed-extractor.d.ts","sourceRoot":"","sources":["../src/sandboxed-extractor.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,KAAK,EAAE,eAAe,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAExE;;;;GAIG;AACH,MAAM,MAAM,mBAAmB,GAAG,MAAM,GAAG,SAAS,CAAC;AAErD;;GAEG;AACH,MAAM,WAAW,yBAAyB;IACxC,uCAAuC;IACvC,KAAK,CAAC,EAAE,mBAAmB,CAAC;IAE5B,gEAAgE;IAChE,aAAa,CAAC,EAAE,MAAM,CAAC;IAEvB,sEAAsE;IACtE,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,wBAAwB,CACtC,MAAM,EAAE,eAAe,EACvB,OAAO,GAAE,yBAA8B,GAEtC,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC,oBAAoB,CAAC,CAsJrE"}
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sandboxed Extractor Wrapper
|
|
3
|
+
*
|
|
4
|
+
* Wraps extractor plugins to execute in a secure sandbox when configured with 'sandbox' trust level.
|
|
5
|
+
* For 'full' trust, extractors run directly without sandboxing for maximum performance.
|
|
6
|
+
*
|
|
7
|
+
* @package @vibe-validate/extractors
|
|
8
|
+
*/
|
|
9
|
+
import { runInSandbox } from './sandbox.js';
|
|
10
|
+
/**
|
|
11
|
+
* Wrap an extractor plugin to run in a sandbox based on trust level
|
|
12
|
+
*
|
|
13
|
+
* This function returns a wrapped extract function that:
|
|
14
|
+
* - If trust='full': Runs the original extract function directly (no sandbox)
|
|
15
|
+
* - If trust='sandbox': Runs the extract function in an isolated V8 context
|
|
16
|
+
*
|
|
17
|
+
* @param plugin - The extractor plugin to wrap
|
|
18
|
+
* @param options - Sandbox configuration options
|
|
19
|
+
* @returns Wrapped extract function that respects trust level
|
|
20
|
+
*
|
|
21
|
+
* @example Trusted execution (no sandbox)
|
|
22
|
+
* ```typescript
|
|
23
|
+
* const wrappedExtract = createSandboxedExtractor(plugin, { trust: 'full' });
|
|
24
|
+
* const result = wrappedExtract('error output');
|
|
25
|
+
* ```
|
|
26
|
+
*
|
|
27
|
+
* @example Sandboxed execution (secure)
|
|
28
|
+
* ```typescript
|
|
29
|
+
* const wrappedExtract = createSandboxedExtractor(plugin, { trust: 'sandbox' });
|
|
30
|
+
* const result = await wrappedExtract('error output');
|
|
31
|
+
* ```
|
|
32
|
+
*/
|
|
33
|
+
export function createSandboxedExtractor(plugin, options = {}
|
|
34
|
+
// eslint-disable-next-line no-unused-vars
|
|
35
|
+
) {
|
|
36
|
+
const trust = options.trust ?? 'sandbox';
|
|
37
|
+
const memoryLimitMB = options.memoryLimitMB ?? 128;
|
|
38
|
+
const timeoutMs = options.timeoutMs ?? 5000;
|
|
39
|
+
// If trusted, return the original extract function (wrapped in Promise for consistency)
|
|
40
|
+
if (trust === 'full') {
|
|
41
|
+
return async (_output, _command) => {
|
|
42
|
+
return plugin.extract(_output, _command);
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
// Otherwise, wrap with sandbox execution
|
|
46
|
+
return async (output, command) => {
|
|
47
|
+
try {
|
|
48
|
+
// Serialize the extract function to string for sandbox execution
|
|
49
|
+
const extractFnCode = plugin.extract.toString();
|
|
50
|
+
// Create wrapped code that returns the full ErrorExtractorResult
|
|
51
|
+
// The sandbox's wrapper expects 'extract' to return 'errors', so we need to return the whole result
|
|
52
|
+
const sandboxCode = `
|
|
53
|
+
// Extract function from plugin
|
|
54
|
+
const extractFn = ${extractFnCode};
|
|
55
|
+
|
|
56
|
+
// Wrapper that calls the extract function and returns the full result
|
|
57
|
+
function extract(input) {
|
|
58
|
+
const command = ${command ? JSON.stringify(command) : 'undefined'};
|
|
59
|
+
const result = extractFn(input, command);
|
|
60
|
+
|
|
61
|
+
// If result is a Promise, throw error (sandbox doesn't support async)
|
|
62
|
+
if (result && typeof result.then === 'function') {
|
|
63
|
+
throw new Error('Async extractors cannot be sandboxed (use trust: "full")');
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Return the full ErrorExtractorResult object
|
|
67
|
+
// The sandbox wrapper will JSON.stringify this as { success: true, errors: <result> }
|
|
68
|
+
return result;
|
|
69
|
+
}
|
|
70
|
+
`;
|
|
71
|
+
// Execute in sandbox
|
|
72
|
+
const sandboxResult = await runInSandbox({
|
|
73
|
+
code: sandboxCode,
|
|
74
|
+
input: output,
|
|
75
|
+
extractorName: plugin.metadata.name,
|
|
76
|
+
memoryLimitMB,
|
|
77
|
+
timeoutMs,
|
|
78
|
+
});
|
|
79
|
+
// Check if sandbox execution succeeded
|
|
80
|
+
if (!sandboxResult.success) {
|
|
81
|
+
console.error(`[vibe-validate] Sandbox execution failed for extractor "${plugin.metadata.name}": ${sandboxResult.error}`);
|
|
82
|
+
// Return empty result with error metadata
|
|
83
|
+
return {
|
|
84
|
+
errors: [],
|
|
85
|
+
totalErrors: 0,
|
|
86
|
+
summary: `Sandbox execution failed: ${sandboxResult.error}`,
|
|
87
|
+
guidance: 'Check extractor code for syntax errors or unsafe operations',
|
|
88
|
+
metadata: {
|
|
89
|
+
detection: {
|
|
90
|
+
extractor: plugin.metadata.name,
|
|
91
|
+
confidence: 0,
|
|
92
|
+
patterns: [],
|
|
93
|
+
reason: `Sandbox execution error: ${sandboxResult.error}`,
|
|
94
|
+
},
|
|
95
|
+
confidence: 0,
|
|
96
|
+
completeness: 0,
|
|
97
|
+
issues: [`Sandbox execution failed: ${sandboxResult.error}`],
|
|
98
|
+
},
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
// Sandbox returns the full ErrorExtractorResult in the 'errors' field
|
|
102
|
+
// (The sandbox's wrapper puts the return value of extract() into the 'errors' field)
|
|
103
|
+
if (!sandboxResult.errors) {
|
|
104
|
+
return {
|
|
105
|
+
errors: [],
|
|
106
|
+
totalErrors: 0,
|
|
107
|
+
summary: 'Invalid sandbox result structure',
|
|
108
|
+
guidance: 'Extractor must return ErrorExtractorResult object',
|
|
109
|
+
metadata: {
|
|
110
|
+
detection: {
|
|
111
|
+
extractor: plugin.metadata.name,
|
|
112
|
+
confidence: 0,
|
|
113
|
+
patterns: [],
|
|
114
|
+
reason: 'Invalid result structure from sandbox',
|
|
115
|
+
},
|
|
116
|
+
confidence: 0,
|
|
117
|
+
completeness: 0,
|
|
118
|
+
issues: ['Sandbox returned invalid result structure'],
|
|
119
|
+
},
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
// Validate that sandboxResult.errors is a valid ErrorExtractorResult
|
|
123
|
+
const extractorResult = sandboxResult.errors;
|
|
124
|
+
// Check for required fields
|
|
125
|
+
if (!extractorResult.errors || !Array.isArray(extractorResult.errors)) {
|
|
126
|
+
return {
|
|
127
|
+
errors: [],
|
|
128
|
+
totalErrors: 0,
|
|
129
|
+
summary: 'Invalid extractor result: missing or invalid errors array',
|
|
130
|
+
guidance: 'Extractor must return ErrorExtractorResult with errors array',
|
|
131
|
+
metadata: {
|
|
132
|
+
detection: {
|
|
133
|
+
extractor: plugin.metadata.name,
|
|
134
|
+
confidence: 0,
|
|
135
|
+
patterns: [],
|
|
136
|
+
reason: 'Invalid ErrorExtractorResult structure',
|
|
137
|
+
},
|
|
138
|
+
confidence: 0,
|
|
139
|
+
completeness: 0,
|
|
140
|
+
issues: ['Extractor returned invalid ErrorExtractorResult (missing errors array)'],
|
|
141
|
+
},
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
// sandboxResult.errors is the full ErrorExtractorResult from the plugin
|
|
145
|
+
return extractorResult;
|
|
146
|
+
}
|
|
147
|
+
catch (error) {
|
|
148
|
+
// Handle unexpected errors
|
|
149
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
150
|
+
console.error(`[vibe-validate] Unexpected error wrapping extractor "${plugin.metadata.name}": ${errorMessage}`);
|
|
151
|
+
// Return empty result with error metadata
|
|
152
|
+
return {
|
|
153
|
+
errors: [],
|
|
154
|
+
totalErrors: 0,
|
|
155
|
+
summary: `Extractor wrapper error: ${errorMessage}`,
|
|
156
|
+
guidance: 'Check extractor code and sandbox configuration',
|
|
157
|
+
metadata: {
|
|
158
|
+
detection: {
|
|
159
|
+
extractor: plugin.metadata.name,
|
|
160
|
+
confidence: 0,
|
|
161
|
+
patterns: [],
|
|
162
|
+
reason: `Wrapper error: ${errorMessage}`,
|
|
163
|
+
},
|
|
164
|
+
confidence: 0,
|
|
165
|
+
completeness: 0,
|
|
166
|
+
issues: [`Wrapper error: ${errorMessage}`],
|
|
167
|
+
},
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
//# sourceMappingURL=sandboxed-extractor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sandboxed-extractor.js","sourceRoot":"","sources":["../src/sandboxed-extractor.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sandboxed-extractor.test.d.ts","sourceRoot":"","sources":["../src/sandboxed-extractor.test.ts"],"names":[],"mappings":"AAAA;;GAEG"}
|