bugproof 0.1.1
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/CHANGELOG.md +65 -0
- package/README.md +256 -0
- package/assets/icon-16x16.png +0 -0
- package/assets/icon-32x32.png +0 -0
- package/assets/icon-512x512.png +0 -0
- package/dist/capture/engine.d.ts +12 -0
- package/dist/capture/engine.d.ts.map +1 -0
- package/dist/capture/engine.js +129 -0
- package/dist/capture/engine.js.map +1 -0
- package/dist/capture/packager.d.ts +39 -0
- package/dist/capture/packager.d.ts.map +1 -0
- package/dist/capture/packager.js +145 -0
- package/dist/capture/packager.js.map +1 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +538 -0
- package/dist/cli.js.map +1 -0
- package/dist/diff/engine.d.ts +28 -0
- package/dist/diff/engine.d.ts.map +1 -0
- package/dist/diff/engine.js +88 -0
- package/dist/diff/engine.js.map +1 -0
- package/dist/replay/engine.d.ts +41 -0
- package/dist/replay/engine.d.ts.map +1 -0
- package/dist/replay/engine.js +69 -0
- package/dist/replay/engine.js.map +1 -0
- package/dist/replay/sandbox.d.ts +33 -0
- package/dist/replay/sandbox.d.ts.map +1 -0
- package/dist/replay/sandbox.js +167 -0
- package/dist/replay/sandbox.js.map +1 -0
- package/dist/replay/verdict.d.ts +8 -0
- package/dist/replay/verdict.d.ts.map +1 -0
- package/dist/replay/verdict.js +32 -0
- package/dist/replay/verdict.js.map +1 -0
- package/dist/sandbox/bugbox.d.ts +38 -0
- package/dist/sandbox/bugbox.d.ts.map +1 -0
- package/dist/sandbox/bugbox.js +122 -0
- package/dist/sandbox/bugbox.js.map +1 -0
- package/dist/sandbox/capabilities.d.ts +33 -0
- package/dist/sandbox/capabilities.d.ts.map +1 -0
- package/dist/sandbox/capabilities.js +53 -0
- package/dist/sandbox/capabilities.js.map +1 -0
- package/dist/sandbox/filesystem.d.ts +50 -0
- package/dist/sandbox/filesystem.d.ts.map +1 -0
- package/dist/sandbox/filesystem.js +134 -0
- package/dist/sandbox/filesystem.js.map +1 -0
- package/dist/sandbox/network.d.ts +68 -0
- package/dist/sandbox/network.d.ts.map +1 -0
- package/dist/sandbox/network.js +136 -0
- package/dist/sandbox/network.js.map +1 -0
- package/dist/sandbox/process.d.ts +17 -0
- package/dist/sandbox/process.d.ts.map +1 -0
- package/dist/sandbox/process.js +30 -0
- package/dist/sandbox/process.js.map +1 -0
- package/dist/sandbox/resources.d.ts +21 -0
- package/dist/sandbox/resources.d.ts.map +1 -0
- package/dist/sandbox/resources.js +60 -0
- package/dist/sandbox/resources.js.map +1 -0
- package/dist/types/artifact.d.ts +57 -0
- package/dist/types/artifact.d.ts.map +1 -0
- package/dist/types/artifact.js +2 -0
- package/dist/types/artifact.js.map +1 -0
- package/dist/types/failure.d.ts +12 -0
- package/dist/types/failure.d.ts.map +1 -0
- package/dist/types/failure.js +2 -0
- package/dist/types/failure.js.map +1 -0
- package/dist/utils/archive.d.ts +13 -0
- package/dist/utils/archive.d.ts.map +1 -0
- package/dist/utils/archive.js +39 -0
- package/dist/utils/archive.js.map +1 -0
- package/dist/utils/associations.d.ts +10 -0
- package/dist/utils/associations.d.ts.map +1 -0
- package/dist/utils/associations.js +46 -0
- package/dist/utils/associations.js.map +1 -0
- package/dist/utils/exclude.d.ts +12 -0
- package/dist/utils/exclude.d.ts.map +1 -0
- package/dist/utils/exclude.js +42 -0
- package/dist/utils/exclude.js.map +1 -0
- package/dist/utils/fingerprint.d.ts +16 -0
- package/dist/utils/fingerprint.d.ts.map +1 -0
- package/dist/utils/fingerprint.js +72 -0
- package/dist/utils/fingerprint.js.map +1 -0
- package/dist/utils/git.d.ts +13 -0
- package/dist/utils/git.d.ts.map +1 -0
- package/dist/utils/git.js +41 -0
- package/dist/utils/git.js.map +1 -0
- package/dist/utils/json-output.d.ts +36 -0
- package/dist/utils/json-output.d.ts.map +1 -0
- package/dist/utils/json-output.js +49 -0
- package/dist/utils/json-output.js.map +1 -0
- package/dist/utils/paths.d.ts +19 -0
- package/dist/utils/paths.d.ts.map +1 -0
- package/dist/utils/paths.js +43 -0
- package/dist/utils/paths.js.map +1 -0
- package/dist/utils/secrets.d.ts +13 -0
- package/dist/utils/secrets.d.ts.map +1 -0
- package/dist/utils/secrets.js +52 -0
- package/dist/utils/secrets.js.map +1 -0
- package/dist/utils/security.d.ts +27 -0
- package/dist/utils/security.d.ts.map +1 -0
- package/dist/utils/security.js +75 -0
- package/dist/utils/security.js.map +1 -0
- package/dist/utils/ui.d.ts +31 -0
- package/dist/utils/ui.d.ts.map +1 -0
- package/dist/utils/ui.js +54 -0
- package/dist/utils/ui.js.map +1 -0
- package/package.json +80 -0
- package/scripts/bugproof-file-association-linux.sh +80 -0
- package/scripts/bugproof-file-association-macos.sh +48 -0
- package/scripts/bugproof-file-association-windows.reg +44 -0
- package/scripts/postinstall.cjs +215 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bugbox.js","sourceRoot":"","sources":["../../src/sandbox/bugbox.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bug-Box Platform Capabilities Detection
|
|
3
|
+
*
|
|
4
|
+
* Probes the current OS for available isolation primitives.
|
|
5
|
+
* Each check is safe, unprivileged, and fast (<50ms per probe).
|
|
6
|
+
* Results are used by bugbox.ts to decide which isolation layers to apply.
|
|
7
|
+
*/
|
|
8
|
+
export interface PlatformCapabilities {
|
|
9
|
+
/** Current OS: 'linux', 'win32', or 'darwin' */
|
|
10
|
+
platform: NodeJS.Platform;
|
|
11
|
+
/** Linux: can we run `unshare` for PID/network namespace isolation? */
|
|
12
|
+
hasUnshare: boolean;
|
|
13
|
+
/** Linux: is cgroups v2 available for resource limits? */
|
|
14
|
+
hasCgroupsV2: boolean;
|
|
15
|
+
/** Windows: Job Objects are always available for process/resource limits */
|
|
16
|
+
hasJobObjects: boolean;
|
|
17
|
+
/** Windows: can we create firewall rules via `netsh`? */
|
|
18
|
+
hasNetsh: boolean;
|
|
19
|
+
/** macOS: can we use `sandbox-exec` for profile-based sandboxing? */
|
|
20
|
+
hasSandboxExec: boolean;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Checks whether a command is available on the system PATH.
|
|
24
|
+
* Uses `where` on Windows and `which` on Unix.
|
|
25
|
+
* Never throws — returns false for any error.
|
|
26
|
+
*/
|
|
27
|
+
export declare function commandExists(name: string): boolean;
|
|
28
|
+
/**
|
|
29
|
+
* Detects which isolation primitives are available on the current platform.
|
|
30
|
+
* Safe to call repeatedly — each call re-probes (no caching).
|
|
31
|
+
*/
|
|
32
|
+
export declare function detectCapabilities(): PlatformCapabilities;
|
|
33
|
+
//# sourceMappingURL=capabilities.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"capabilities.d.ts","sourceRoot":"","sources":["../../src/sandbox/capabilities.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAMH,MAAM,WAAW,oBAAoB;IACnC,gDAAgD;IAChD,QAAQ,EAAE,MAAM,CAAC,QAAQ,CAAC;IAC1B,uEAAuE;IACvE,UAAU,EAAE,OAAO,CAAC;IACpB,0DAA0D;IAC1D,YAAY,EAAE,OAAO,CAAC;IACtB,4EAA4E;IAC5E,aAAa,EAAE,OAAO,CAAC;IACvB,yDAAyD;IACzD,QAAQ,EAAE,OAAO,CAAC;IAClB,qEAAqE;IACrE,cAAc,EAAE,OAAO,CAAC;CACzB;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAcnD;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,IAAI,oBAAoB,CAuBzD"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bug-Box Platform Capabilities Detection
|
|
3
|
+
*
|
|
4
|
+
* Probes the current OS for available isolation primitives.
|
|
5
|
+
* Each check is safe, unprivileged, and fast (<50ms per probe).
|
|
6
|
+
* Results are used by bugbox.ts to decide which isolation layers to apply.
|
|
7
|
+
*/
|
|
8
|
+
import * as os from 'os';
|
|
9
|
+
import * as fs from 'fs';
|
|
10
|
+
import { spawnSync } from 'child_process';
|
|
11
|
+
/**
|
|
12
|
+
* Checks whether a command is available on the system PATH.
|
|
13
|
+
* Uses `where` on Windows and `which` on Unix.
|
|
14
|
+
* Never throws — returns false for any error.
|
|
15
|
+
*/
|
|
16
|
+
export function commandExists(name) {
|
|
17
|
+
if (!name || !name.trim())
|
|
18
|
+
return false;
|
|
19
|
+
try {
|
|
20
|
+
const cmd = os.platform() === 'win32' ? 'where' : 'which';
|
|
21
|
+
const result = spawnSync(cmd, [name], {
|
|
22
|
+
encoding: 'utf-8',
|
|
23
|
+
timeout: 3000,
|
|
24
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
25
|
+
});
|
|
26
|
+
return result.status === 0;
|
|
27
|
+
}
|
|
28
|
+
catch {
|
|
29
|
+
return false;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Detects which isolation primitives are available on the current platform.
|
|
34
|
+
* Safe to call repeatedly — each call re-probes (no caching).
|
|
35
|
+
*/
|
|
36
|
+
export function detectCapabilities() {
|
|
37
|
+
const platform = os.platform();
|
|
38
|
+
return {
|
|
39
|
+
platform,
|
|
40
|
+
// Linux: check for `unshare` (user namespace isolation, no root needed)
|
|
41
|
+
hasUnshare: platform === 'linux' && commandExists('unshare'),
|
|
42
|
+
// Linux: check for cgroups v2 unified hierarchy
|
|
43
|
+
hasCgroupsV2: platform === 'linux' &&
|
|
44
|
+
fs.existsSync('/sys/fs/cgroup/cgroup.controllers'),
|
|
45
|
+
// Windows: Job Objects are a built-in Win32 API, always available
|
|
46
|
+
hasJobObjects: platform === 'win32',
|
|
47
|
+
// Windows: netsh for firewall rules
|
|
48
|
+
hasNetsh: platform === 'win32' && commandExists('netsh'),
|
|
49
|
+
// macOS: sandbox-exec for profile-based sandboxing
|
|
50
|
+
hasSandboxExec: platform === 'darwin' && commandExists('sandbox-exec'),
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
//# sourceMappingURL=capabilities.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"capabilities.js","sourceRoot":"","sources":["../../src/sandbox/capabilities.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,MAAM,IAAI,CAAC;AACzB,OAAO,KAAK,EAAE,MAAM,IAAI,CAAC;AACzB,OAAO,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAiB1C;;;;GAIG;AACH,MAAM,UAAU,aAAa,CAAC,IAAY;IACxC,IAAI,CAAC,IAAI,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE;QAAE,OAAO,KAAK,CAAC;IAExC,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,EAAE,CAAC,QAAQ,EAAE,KAAK,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC;QAC1D,MAAM,MAAM,GAAG,SAAS,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,EAAE;YACpC,QAAQ,EAAE,OAAO;YACjB,OAAO,EAAE,IAAI;YACb,KAAK,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC;SAChC,CAAC,CAAC;QACH,OAAO,MAAM,CAAC,MAAM,KAAK,CAAC,CAAC;IAC7B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,kBAAkB;IAChC,MAAM,QAAQ,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC;IAE/B,OAAO;QACL,QAAQ;QAER,wEAAwE;QACxE,UAAU,EAAE,QAAQ,KAAK,OAAO,IAAI,aAAa,CAAC,SAAS,CAAC;QAE5D,gDAAgD;QAChD,YAAY,EACV,QAAQ,KAAK,OAAO;YACpB,EAAE,CAAC,UAAU,CAAC,mCAAmC,CAAC;QAEpD,kEAAkE;QAClE,aAAa,EAAE,QAAQ,KAAK,OAAO;QAEnC,oCAAoC;QACpC,QAAQ,EAAE,QAAQ,KAAK,OAAO,IAAI,aAAa,CAAC,OAAO,CAAC;QAExD,mDAAmD;QACnD,cAAc,EAAE,QAAQ,KAAK,QAAQ,IAAI,aAAa,CAAC,cAAc,CAAC;KACvE,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bug-Box Filesystem Isolation
|
|
3
|
+
*
|
|
4
|
+
* Creates a structured temp directory with restrictive permissions.
|
|
5
|
+
* Layout:
|
|
6
|
+
* bugbox-XXXXX/
|
|
7
|
+
* ├── files/ — Read-only source snapshot (locked after population)
|
|
8
|
+
* ├── workspace/ — Read-write CWD for the replayed process
|
|
9
|
+
* └── logs/ — Read-write stdout/stderr capture
|
|
10
|
+
*
|
|
11
|
+
* Permissions:
|
|
12
|
+
* Linux/macOS: chmod 0700 (owner-only)
|
|
13
|
+
* Windows: icacls inheritance removal + current-user-only grant
|
|
14
|
+
*/
|
|
15
|
+
export interface IsolatedDirResult {
|
|
16
|
+
/** Root of the entire Bug-Box sandbox */
|
|
17
|
+
rootDir: string;
|
|
18
|
+
/** Read-only directory for captured source files */
|
|
19
|
+
filesDir: string;
|
|
20
|
+
/** Read-write working directory (the replayed process CWD) */
|
|
21
|
+
workspaceDir: string;
|
|
22
|
+
/** Read-write directory for stdout/stderr log capture */
|
|
23
|
+
logsDir: string;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Creates a new isolated directory structure with restrictive permissions.
|
|
27
|
+
* Works on Linux, macOS, and Windows without requiring root/admin.
|
|
28
|
+
*/
|
|
29
|
+
export declare function createIsolatedDir(): IsolatedDirResult;
|
|
30
|
+
/**
|
|
31
|
+
* Locks a directory to read-only.
|
|
32
|
+
* Used after populating filesDir with the source snapshot,
|
|
33
|
+
* so the replayed process cannot modify captured files.
|
|
34
|
+
*
|
|
35
|
+
* Linux/macOS: removes write bit (chmod a-w recursively).
|
|
36
|
+
* Windows: icacls to deny write.
|
|
37
|
+
*/
|
|
38
|
+
export declare function lockDirReadOnly(dirPath: string): void;
|
|
39
|
+
/**
|
|
40
|
+
* Restores write permissions on a directory that was previously locked.
|
|
41
|
+
* Must be called before cleanup if lockDirReadOnly was used.
|
|
42
|
+
*/
|
|
43
|
+
export declare function unlockDir(dirPath: string): void;
|
|
44
|
+
/**
|
|
45
|
+
* Removes the entire isolated directory.
|
|
46
|
+
* Handles read-only files by unlocking first.
|
|
47
|
+
* Never throws — best-effort cleanup.
|
|
48
|
+
*/
|
|
49
|
+
export declare function cleanupIsolatedDir(result: IsolatedDirResult): void;
|
|
50
|
+
//# sourceMappingURL=filesystem.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"filesystem.d.ts","sourceRoot":"","sources":["../../src/sandbox/filesystem.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAOH,MAAM,WAAW,iBAAiB;IAChC,yCAAyC;IACzC,OAAO,EAAE,MAAM,CAAC;IAChB,oDAAoD;IACpD,QAAQ,EAAE,MAAM,CAAC;IACjB,8DAA8D;IAC9D,YAAY,EAAE,MAAM,CAAC;IACrB,yDAAyD;IACzD,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,IAAI,iBAAiB,CAgBrD;AAED;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAcrD;AAED;;;GAGG;AACH,wBAAgB,SAAS,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAc/C;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,iBAAiB,GAAG,IAAI,CAelE"}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bug-Box Filesystem Isolation
|
|
3
|
+
*
|
|
4
|
+
* Creates a structured temp directory with restrictive permissions.
|
|
5
|
+
* Layout:
|
|
6
|
+
* bugbox-XXXXX/
|
|
7
|
+
* ├── files/ — Read-only source snapshot (locked after population)
|
|
8
|
+
* ├── workspace/ — Read-write CWD for the replayed process
|
|
9
|
+
* └── logs/ — Read-write stdout/stderr capture
|
|
10
|
+
*
|
|
11
|
+
* Permissions:
|
|
12
|
+
* Linux/macOS: chmod 0700 (owner-only)
|
|
13
|
+
* Windows: icacls inheritance removal + current-user-only grant
|
|
14
|
+
*/
|
|
15
|
+
import * as fs from 'fs';
|
|
16
|
+
import * as path from 'path';
|
|
17
|
+
import * as os from 'os';
|
|
18
|
+
import { spawnSync } from 'child_process';
|
|
19
|
+
/**
|
|
20
|
+
* Creates a new isolated directory structure with restrictive permissions.
|
|
21
|
+
* Works on Linux, macOS, and Windows without requiring root/admin.
|
|
22
|
+
*/
|
|
23
|
+
export function createIsolatedDir() {
|
|
24
|
+
const rootDir = fs.mkdtempSync(path.join(os.tmpdir(), 'bugbox-'));
|
|
25
|
+
// Apply restrictive permissions to the root
|
|
26
|
+
applyRestrictivePermissions(rootDir);
|
|
27
|
+
// Create subdirectories
|
|
28
|
+
const filesDir = path.join(rootDir, 'files');
|
|
29
|
+
const workspaceDir = path.join(rootDir, 'workspace');
|
|
30
|
+
const logsDir = path.join(rootDir, 'logs');
|
|
31
|
+
fs.mkdirSync(filesDir, { recursive: true });
|
|
32
|
+
fs.mkdirSync(workspaceDir, { recursive: true });
|
|
33
|
+
fs.mkdirSync(logsDir, { recursive: true });
|
|
34
|
+
return { rootDir, filesDir, workspaceDir, logsDir };
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Locks a directory to read-only.
|
|
38
|
+
* Used after populating filesDir with the source snapshot,
|
|
39
|
+
* so the replayed process cannot modify captured files.
|
|
40
|
+
*
|
|
41
|
+
* Linux/macOS: removes write bit (chmod a-w recursively).
|
|
42
|
+
* Windows: icacls to deny write.
|
|
43
|
+
*/
|
|
44
|
+
export function lockDirReadOnly(dirPath) {
|
|
45
|
+
if (!fs.existsSync(dirPath))
|
|
46
|
+
return;
|
|
47
|
+
if (os.platform() === 'win32') {
|
|
48
|
+
// On Windows, use icacls to set read-only attribute on all files
|
|
49
|
+
spawnSync('icacls', [dirPath, '/deny', `${os.userInfo().username}:(W)`, '/T', '/C'], {
|
|
50
|
+
encoding: 'utf-8',
|
|
51
|
+
timeout: 10000,
|
|
52
|
+
stdio: 'pipe',
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
else {
|
|
56
|
+
// Unix: remove write bit recursively
|
|
57
|
+
setPermissionsRecursive(dirPath, 0o555, 0o444);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Restores write permissions on a directory that was previously locked.
|
|
62
|
+
* Must be called before cleanup if lockDirReadOnly was used.
|
|
63
|
+
*/
|
|
64
|
+
export function unlockDir(dirPath) {
|
|
65
|
+
if (!fs.existsSync(dirPath))
|
|
66
|
+
return;
|
|
67
|
+
if (os.platform() === 'win32') {
|
|
68
|
+
// Remove the deny rule
|
|
69
|
+
spawnSync('icacls', [dirPath, '/remove:d', os.userInfo().username, '/T', '/C'], {
|
|
70
|
+
encoding: 'utf-8',
|
|
71
|
+
timeout: 10000,
|
|
72
|
+
stdio: 'pipe',
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
else {
|
|
76
|
+
// Unix: restore write bit
|
|
77
|
+
setPermissionsRecursive(dirPath, 0o755, 0o644);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Removes the entire isolated directory.
|
|
82
|
+
* Handles read-only files by unlocking first.
|
|
83
|
+
* Never throws — best-effort cleanup.
|
|
84
|
+
*/
|
|
85
|
+
export function cleanupIsolatedDir(result) {
|
|
86
|
+
if (!fs.existsSync(result.rootDir))
|
|
87
|
+
return;
|
|
88
|
+
try {
|
|
89
|
+
// Unlock filesDir in case it was locked read-only
|
|
90
|
+
unlockDir(result.filesDir);
|
|
91
|
+
}
|
|
92
|
+
catch {
|
|
93
|
+
// Best effort
|
|
94
|
+
}
|
|
95
|
+
try {
|
|
96
|
+
fs.rmSync(result.rootDir, { recursive: true, force: true });
|
|
97
|
+
}
|
|
98
|
+
catch {
|
|
99
|
+
// Best effort — on Windows, locked handles may prevent immediate removal
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
// ── Internal helpers ──
|
|
103
|
+
/**
|
|
104
|
+
* Applies restrictive permissions to the root sandbox directory.
|
|
105
|
+
* Owner-only on Unix. Current-user-only ACL on Windows.
|
|
106
|
+
*/
|
|
107
|
+
function applyRestrictivePermissions(dirPath) {
|
|
108
|
+
if (os.platform() === 'win32') {
|
|
109
|
+
// Remove inherited ACLs, grant full control to current user only
|
|
110
|
+
spawnSync('icacls', [dirPath, '/inheritance:r', '/grant:r', `${os.userInfo().username}:(OI)(CI)F`], { encoding: 'utf-8', timeout: 5000, stdio: 'pipe' });
|
|
111
|
+
}
|
|
112
|
+
else {
|
|
113
|
+
// Unix: 0700 — owner can read/write/execute, nobody else
|
|
114
|
+
fs.chmodSync(dirPath, 0o700);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Recursively sets permissions on directories and files.
|
|
119
|
+
* dirMode applies to directories, fileMode applies to files.
|
|
120
|
+
*/
|
|
121
|
+
function setPermissionsRecursive(dirPath, dirMode, fileMode) {
|
|
122
|
+
fs.chmodSync(dirPath, dirMode);
|
|
123
|
+
const entries = fs.readdirSync(dirPath, { withFileTypes: true });
|
|
124
|
+
for (const entry of entries) {
|
|
125
|
+
const fullPath = path.join(dirPath, entry.name);
|
|
126
|
+
if (entry.isDirectory()) {
|
|
127
|
+
setPermissionsRecursive(fullPath, dirMode, fileMode);
|
|
128
|
+
}
|
|
129
|
+
else if (entry.isFile()) {
|
|
130
|
+
fs.chmodSync(fullPath, fileMode);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
//# sourceMappingURL=filesystem.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"filesystem.js","sourceRoot":"","sources":["../../src/sandbox/filesystem.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,KAAK,EAAE,MAAM,IAAI,CAAC;AACzB,OAAO,KAAK,IAAI,MAAM,MAAM,CAAC;AAC7B,OAAO,KAAK,EAAE,MAAM,IAAI,CAAC;AACzB,OAAO,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAa1C;;;GAGG;AACH,MAAM,UAAU,iBAAiB;IAC/B,MAAM,OAAO,GAAG,EAAE,CAAC,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,MAAM,EAAE,EAAE,SAAS,CAAC,CAAC,CAAC;IAElE,4CAA4C;IAC5C,2BAA2B,CAAC,OAAO,CAAC,CAAC;IAErC,wBAAwB;IACxB,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;IAC7C,MAAM,YAAY,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,WAAW,CAAC,CAAC;IACrD,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;IAE3C,EAAE,CAAC,SAAS,CAAC,QAAQ,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC5C,EAAE,CAAC,SAAS,CAAC,YAAY,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAChD,EAAE,CAAC,SAAS,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAE3C,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,OAAO,EAAE,CAAC;AACtD,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,eAAe,CAAC,OAAe;IAC7C,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,OAAO,CAAC;QAAE,OAAO;IAEpC,IAAI,EAAE,CAAC,QAAQ,EAAE,KAAK,OAAO,EAAE,CAAC;QAC9B,iEAAiE;QACjE,SAAS,CAAC,QAAQ,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,EAAE;YACnF,QAAQ,EAAE,OAAO;YACjB,OAAO,EAAE,KAAK;YACd,KAAK,EAAE,MAAM;SACd,CAAC,CAAC;IACL,CAAC;SAAM,CAAC;QACN,qCAAqC;QACrC,uBAAuB,CAAC,OAAO,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC;IACjD,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,SAAS,CAAC,OAAe;IACvC,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,OAAO,CAAC;QAAE,OAAO;IAEpC,IAAI,EAAE,CAAC,QAAQ,EAAE,KAAK,OAAO,EAAE,CAAC;QAC9B,uBAAuB;QACvB,SAAS,CAAC,QAAQ,EAAE,CAAC,OAAO,EAAE,WAAW,EAAE,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE,IAAI,EAAE,IAAI,CAAC,EAAE;YAC9E,QAAQ,EAAE,OAAO;YACjB,OAAO,EAAE,KAAK;YACd,KAAK,EAAE,MAAM;SACd,CAAC,CAAC;IACL,CAAC;SAAM,CAAC;QACN,0BAA0B;QAC1B,uBAAuB,CAAC,OAAO,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC;IACjD,CAAC;AACH,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,kBAAkB,CAAC,MAAyB;IAC1D,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,MAAM,CAAC,OAAO,CAAC;QAAE,OAAO;IAE3C,IAAI,CAAC;QACH,kDAAkD;QAClD,SAAS,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;IAC7B,CAAC;IAAC,MAAM,CAAC;QACP,cAAc;IAChB,CAAC;IAED,IAAI,CAAC;QACH,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IAC9D,CAAC;IAAC,MAAM,CAAC;QACP,yEAAyE;IAC3E,CAAC;AACH,CAAC;AAED,yBAAyB;AAEzB;;;GAGG;AACH,SAAS,2BAA2B,CAAC,OAAe;IAClD,IAAI,EAAE,CAAC,QAAQ,EAAE,KAAK,OAAO,EAAE,CAAC;QAC9B,iEAAiE;QACjE,SAAS,CACP,QAAQ,EACR,CAAC,OAAO,EAAE,gBAAgB,EAAE,UAAU,EAAE,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,YAAY,CAAC,EAC9E,EAAE,QAAQ,EAAE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,CACpD,CAAC;IACJ,CAAC;SAAM,CAAC;QACN,yDAAyD;QACzD,EAAE,CAAC,SAAS,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;IAC/B,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,SAAS,uBAAuB,CAC9B,OAAe,EACf,OAAe,EACf,QAAgB;IAEhB,EAAE,CAAC,SAAS,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;IAE/B,MAAM,OAAO,GAAG,EAAE,CAAC,WAAW,CAAC,OAAO,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC;IACjE,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;QAC5B,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;QAChD,IAAI,KAAK,CAAC,WAAW,EAAE,EAAE,CAAC;YACxB,uBAAuB,CAAC,QAAQ,EAAE,OAAO,EAAE,QAAQ,CAAC,CAAC;QACvD,CAAC;aAAM,IAAI,KAAK,CAAC,MAAM,EAAE,EAAE,CAAC;YAC1B,EAAE,CAAC,SAAS,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;QACnC,CAAC;IACH,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bug-Box Network Isolation
|
|
3
|
+
*
|
|
4
|
+
* Provides three strategies to prevent a replayed command from accessing the network:
|
|
5
|
+
*
|
|
6
|
+
* - unshare (Linux): wraps the command in `unshare --net`, creating a new
|
|
7
|
+
* network namespace with only a loopback interface.
|
|
8
|
+
* - sandbox-exec (macOS): wraps the command in `sandbox-exec -p '(deny network*)'`.
|
|
9
|
+
* - netsh (Windows): creates a temporary Windows Firewall rule before the
|
|
10
|
+
* process starts and removes it after the process exits.
|
|
11
|
+
* This is a pre/post approach, not a command wrapper.
|
|
12
|
+
* - none: no network isolation. Used when no primitive is available,
|
|
13
|
+
* or when the user explicitly opts out.
|
|
14
|
+
*
|
|
15
|
+
* Design:
|
|
16
|
+
* selectNetworkStrategy() — picks the best strategy for the current platform.
|
|
17
|
+
* buildNetworkIsolationArgs() — transforms the command array (wrapping if needed).
|
|
18
|
+
* createNetworkCleanup() — returns a cleanup function for post-exec teardown.
|
|
19
|
+
*/
|
|
20
|
+
import { PlatformCapabilities } from './capabilities';
|
|
21
|
+
/** The isolation approach selected for this platform. */
|
|
22
|
+
export type NetworkStrategy = 'unshare' | 'netsh' | 'sandbox-exec' | 'none';
|
|
23
|
+
/** Result of command transformation for network isolation. */
|
|
24
|
+
export interface NetworkIsolationResult {
|
|
25
|
+
/** The (possibly wrapped) command array to spawn. */
|
|
26
|
+
command: string[];
|
|
27
|
+
/** If true, the caller must run pre-exec setup (e.g. netsh rule) before spawn. */
|
|
28
|
+
needsPreExec: boolean;
|
|
29
|
+
/** The strategy that was applied. */
|
|
30
|
+
strategy: NetworkStrategy;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Selects the best available network isolation strategy for the given platform.
|
|
34
|
+
*/
|
|
35
|
+
export declare function selectNetworkStrategy(caps: PlatformCapabilities): NetworkStrategy;
|
|
36
|
+
/**
|
|
37
|
+
* Transforms a command array to include network isolation, if the strategy
|
|
38
|
+
* supports command wrapping (unshare, sandbox-exec).
|
|
39
|
+
*
|
|
40
|
+
* For `netsh`, the command is returned unchanged because netsh uses firewall
|
|
41
|
+
* rules applied before/after the process, not a wrapper.
|
|
42
|
+
*
|
|
43
|
+
* For `none`, the command is returned unchanged.
|
|
44
|
+
*/
|
|
45
|
+
export declare function buildNetworkIsolationArgs(strategy: NetworkStrategy, command: string[]): NetworkIsolationResult;
|
|
46
|
+
/**
|
|
47
|
+
* Creates a cleanup function that removes any network isolation artifacts
|
|
48
|
+
* after the replayed process exits.
|
|
49
|
+
*
|
|
50
|
+
* - unshare / sandbox-exec: the kernel automatically tears down the namespace
|
|
51
|
+
* or sandbox when the process exits. Cleanup is a no-op.
|
|
52
|
+
* - netsh: removes the temporary firewall rule by name.
|
|
53
|
+
* - none: no-op.
|
|
54
|
+
*
|
|
55
|
+
* @param strategy The isolation strategy that was applied.
|
|
56
|
+
* @param ruleName (netsh only) The firewall rule name to remove.
|
|
57
|
+
*/
|
|
58
|
+
export declare function createNetworkCleanup(strategy: NetworkStrategy, ruleName?: string): () => void;
|
|
59
|
+
/**
|
|
60
|
+
* Adds a Windows Firewall rule that blocks all outbound traffic for a process.
|
|
61
|
+
* Called as pre-exec for the `netsh` strategy.
|
|
62
|
+
*
|
|
63
|
+
* @param ruleName Unique rule name for later cleanup.
|
|
64
|
+
* @param exePath Path to the executable to block.
|
|
65
|
+
* @returns true if the rule was added, false on failure.
|
|
66
|
+
*/
|
|
67
|
+
export declare function addFirewallBlockRule(ruleName: string, exePath: string): boolean;
|
|
68
|
+
//# sourceMappingURL=network.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"network.d.ts","sourceRoot":"","sources":["../../src/sandbox/network.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;AAGtD,yDAAyD;AACzD,MAAM,MAAM,eAAe,GAAG,SAAS,GAAG,OAAO,GAAG,cAAc,GAAG,MAAM,CAAC;AAE5E,8DAA8D;AAC9D,MAAM,WAAW,sBAAsB;IACrC,qDAAqD;IACrD,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,kFAAkF;IAClF,YAAY,EAAE,OAAO,CAAC;IACtB,qCAAqC;IACrC,QAAQ,EAAE,eAAe,CAAC;CAC3B;AAED;;GAEG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,oBAAoB,GAAG,eAAe,CAWjF;AAED;;;;;;;;GAQG;AACH,wBAAgB,yBAAyB,CACvC,QAAQ,EAAE,eAAe,EACzB,OAAO,EAAE,MAAM,EAAE,GAChB,sBAAsB,CA4CxB;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,oBAAoB,CAClC,QAAQ,EAAE,eAAe,EACzB,QAAQ,CAAC,EAAE,MAAM,GAChB,MAAM,IAAI,CAiBZ;AAED;;;;;;;GAOG;AACH,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAiB/E"}
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bug-Box Network Isolation
|
|
3
|
+
*
|
|
4
|
+
* Provides three strategies to prevent a replayed command from accessing the network:
|
|
5
|
+
*
|
|
6
|
+
* - unshare (Linux): wraps the command in `unshare --net`, creating a new
|
|
7
|
+
* network namespace with only a loopback interface.
|
|
8
|
+
* - sandbox-exec (macOS): wraps the command in `sandbox-exec -p '(deny network*)'`.
|
|
9
|
+
* - netsh (Windows): creates a temporary Windows Firewall rule before the
|
|
10
|
+
* process starts and removes it after the process exits.
|
|
11
|
+
* This is a pre/post approach, not a command wrapper.
|
|
12
|
+
* - none: no network isolation. Used when no primitive is available,
|
|
13
|
+
* or when the user explicitly opts out.
|
|
14
|
+
*
|
|
15
|
+
* Design:
|
|
16
|
+
* selectNetworkStrategy() — picks the best strategy for the current platform.
|
|
17
|
+
* buildNetworkIsolationArgs() — transforms the command array (wrapping if needed).
|
|
18
|
+
* createNetworkCleanup() — returns a cleanup function for post-exec teardown.
|
|
19
|
+
*/
|
|
20
|
+
import { spawnSync } from 'child_process';
|
|
21
|
+
/**
|
|
22
|
+
* Selects the best available network isolation strategy for the given platform.
|
|
23
|
+
*/
|
|
24
|
+
export function selectNetworkStrategy(caps) {
|
|
25
|
+
switch (caps.platform) {
|
|
26
|
+
case 'linux':
|
|
27
|
+
return caps.hasUnshare ? 'unshare' : 'none';
|
|
28
|
+
case 'win32':
|
|
29
|
+
return caps.hasNetsh ? 'netsh' : 'none';
|
|
30
|
+
case 'darwin':
|
|
31
|
+
return caps.hasSandboxExec ? 'sandbox-exec' : 'none';
|
|
32
|
+
default:
|
|
33
|
+
return 'none';
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Transforms a command array to include network isolation, if the strategy
|
|
38
|
+
* supports command wrapping (unshare, sandbox-exec).
|
|
39
|
+
*
|
|
40
|
+
* For `netsh`, the command is returned unchanged because netsh uses firewall
|
|
41
|
+
* rules applied before/after the process, not a wrapper.
|
|
42
|
+
*
|
|
43
|
+
* For `none`, the command is returned unchanged.
|
|
44
|
+
*/
|
|
45
|
+
export function buildNetworkIsolationArgs(strategy, command) {
|
|
46
|
+
switch (strategy) {
|
|
47
|
+
case 'unshare':
|
|
48
|
+
return {
|
|
49
|
+
// unshare --net creates a new network namespace with only loopback.
|
|
50
|
+
// --map-root-user allows it to work without root (user namespace).
|
|
51
|
+
command: ['unshare', '--net', '--map-root-user', '--', ...command],
|
|
52
|
+
needsPreExec: false,
|
|
53
|
+
strategy,
|
|
54
|
+
};
|
|
55
|
+
case 'sandbox-exec':
|
|
56
|
+
return {
|
|
57
|
+
// sandbox-exec with a deny-network profile.
|
|
58
|
+
// The profile denies all network operations; the process can still
|
|
59
|
+
// use local filesystem and IPC.
|
|
60
|
+
command: [
|
|
61
|
+
'sandbox-exec',
|
|
62
|
+
'-p',
|
|
63
|
+
'(version 1)(allow default)(deny network*)',
|
|
64
|
+
'--',
|
|
65
|
+
...command,
|
|
66
|
+
],
|
|
67
|
+
needsPreExec: false,
|
|
68
|
+
strategy,
|
|
69
|
+
};
|
|
70
|
+
case 'netsh':
|
|
71
|
+
// netsh doesn't wrap the command. The caller must call pre-exec setup
|
|
72
|
+
// (addFirewallBlockRule) and post-exec cleanup (removeFirewallBlockRule).
|
|
73
|
+
return {
|
|
74
|
+
command,
|
|
75
|
+
needsPreExec: true,
|
|
76
|
+
strategy,
|
|
77
|
+
};
|
|
78
|
+
case 'none':
|
|
79
|
+
default:
|
|
80
|
+
return {
|
|
81
|
+
command,
|
|
82
|
+
needsPreExec: false,
|
|
83
|
+
strategy,
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Creates a cleanup function that removes any network isolation artifacts
|
|
89
|
+
* after the replayed process exits.
|
|
90
|
+
*
|
|
91
|
+
* - unshare / sandbox-exec: the kernel automatically tears down the namespace
|
|
92
|
+
* or sandbox when the process exits. Cleanup is a no-op.
|
|
93
|
+
* - netsh: removes the temporary firewall rule by name.
|
|
94
|
+
* - none: no-op.
|
|
95
|
+
*
|
|
96
|
+
* @param strategy The isolation strategy that was applied.
|
|
97
|
+
* @param ruleName (netsh only) The firewall rule name to remove.
|
|
98
|
+
*/
|
|
99
|
+
export function createNetworkCleanup(strategy, ruleName) {
|
|
100
|
+
if (strategy === 'netsh' && ruleName) {
|
|
101
|
+
return () => {
|
|
102
|
+
try {
|
|
103
|
+
spawnSync('netsh', ['advfirewall', 'firewall', 'delete', 'rule', `name=${ruleName}`], { encoding: 'utf-8', timeout: 5000, stdio: 'pipe' });
|
|
104
|
+
}
|
|
105
|
+
catch {
|
|
106
|
+
// Best effort — rule may not exist or we lack permissions
|
|
107
|
+
}
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
// All other strategies: kernel cleans up automatically, or nothing was applied
|
|
111
|
+
return () => { };
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Adds a Windows Firewall rule that blocks all outbound traffic for a process.
|
|
115
|
+
* Called as pre-exec for the `netsh` strategy.
|
|
116
|
+
*
|
|
117
|
+
* @param ruleName Unique rule name for later cleanup.
|
|
118
|
+
* @param exePath Path to the executable to block.
|
|
119
|
+
* @returns true if the rule was added, false on failure.
|
|
120
|
+
*/
|
|
121
|
+
export function addFirewallBlockRule(ruleName, exePath) {
|
|
122
|
+
try {
|
|
123
|
+
const result = spawnSync('netsh', [
|
|
124
|
+
'advfirewall', 'firewall', 'add', 'rule',
|
|
125
|
+
`name=${ruleName}`,
|
|
126
|
+
'dir=out',
|
|
127
|
+
'action=block',
|
|
128
|
+
`program=${exePath}`,
|
|
129
|
+
], { encoding: 'utf-8', timeout: 5000, stdio: 'pipe' });
|
|
130
|
+
return result.status === 0;
|
|
131
|
+
}
|
|
132
|
+
catch {
|
|
133
|
+
return false;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
//# sourceMappingURL=network.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"network.js","sourceRoot":"","sources":["../../src/sandbox/network.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAGH,OAAO,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAe1C;;GAEG;AACH,MAAM,UAAU,qBAAqB,CAAC,IAA0B;IAC9D,QAAQ,IAAI,CAAC,QAAQ,EAAE,CAAC;QACtB,KAAK,OAAO;YACV,OAAO,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC;QAC9C,KAAK,OAAO;YACV,OAAO,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC;QAC1C,KAAK,QAAQ;YACX,OAAO,IAAI,CAAC,cAAc,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC,CAAC,MAAM,CAAC;QACvD;YACE,OAAO,MAAM,CAAC;IAClB,CAAC;AACH,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,yBAAyB,CACvC,QAAyB,EACzB,OAAiB;IAEjB,QAAQ,QAAQ,EAAE,CAAC;QACjB,KAAK,SAAS;YACZ,OAAO;gBACL,oEAAoE;gBACpE,mEAAmE;gBACnE,OAAO,EAAE,CAAC,SAAS,EAAE,OAAO,EAAE,iBAAiB,EAAE,IAAI,EAAE,GAAG,OAAO,CAAC;gBAClE,YAAY,EAAE,KAAK;gBACnB,QAAQ;aACT,CAAC;QAEJ,KAAK,cAAc;YACjB,OAAO;gBACL,4CAA4C;gBAC5C,mEAAmE;gBACnE,gCAAgC;gBAChC,OAAO,EAAE;oBACP,cAAc;oBACd,IAAI;oBACJ,2CAA2C;oBAC3C,IAAI;oBACJ,GAAG,OAAO;iBACX;gBACD,YAAY,EAAE,KAAK;gBACnB,QAAQ;aACT,CAAC;QAEJ,KAAK,OAAO;YACV,sEAAsE;YACtE,0EAA0E;YAC1E,OAAO;gBACL,OAAO;gBACP,YAAY,EAAE,IAAI;gBAClB,QAAQ;aACT,CAAC;QAEJ,KAAK,MAAM,CAAC;QACZ;YACE,OAAO;gBACL,OAAO;gBACP,YAAY,EAAE,KAAK;gBACnB,QAAQ;aACT,CAAC;IACN,CAAC;AACH,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,oBAAoB,CAClC,QAAyB,EACzB,QAAiB;IAEjB,IAAI,QAAQ,KAAK,OAAO,IAAI,QAAQ,EAAE,CAAC;QACrC,OAAO,GAAG,EAAE;YACV,IAAI,CAAC;gBACH,SAAS,CACP,OAAO,EACP,CAAC,aAAa,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,EAAE,QAAQ,QAAQ,EAAE,CAAC,EACjE,EAAE,QAAQ,EAAE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,CACpD,CAAC;YACJ,CAAC;YAAC,MAAM,CAAC;gBACP,0DAA0D;YAC5D,CAAC;QACH,CAAC,CAAC;IACJ,CAAC;IAED,+EAA+E;IAC/E,OAAO,GAAG,EAAE,GAAE,CAAC,CAAC;AAClB,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,oBAAoB,CAAC,QAAgB,EAAE,OAAe;IACpE,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,SAAS,CACtB,OAAO,EACP;YACE,aAAa,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM;YACxC,QAAQ,QAAQ,EAAE;YAClB,SAAS;YACT,cAAc;YACd,WAAW,OAAO,EAAE;SACrB,EACD,EAAE,QAAQ,EAAE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,CACpD,CAAC;QACF,OAAO,MAAM,CAAC,MAAM,KAAK,CAAC,CAAC;IAC7B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bug-Box Process Isolation
|
|
3
|
+
*
|
|
4
|
+
* Provides OS-level process namespace isolation so the replayed command
|
|
5
|
+
* cannot see or signal host processes.
|
|
6
|
+
*/
|
|
7
|
+
import { PlatformCapabilities } from './capabilities';
|
|
8
|
+
export type ProcessStrategy = 'unshare' | 'none';
|
|
9
|
+
/**
|
|
10
|
+
* Selects the best available process isolation strategy based on capabilities.
|
|
11
|
+
*/
|
|
12
|
+
export declare function selectProcessStrategy(caps: PlatformCapabilities): ProcessStrategy;
|
|
13
|
+
/**
|
|
14
|
+
* Modifies the command array to run within a new process namespace.
|
|
15
|
+
*/
|
|
16
|
+
export declare function buildProcessIsolationArgs(strategy: ProcessStrategy, command: string[]): string[];
|
|
17
|
+
//# sourceMappingURL=process.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"process.d.ts","sourceRoot":"","sources":["../../src/sandbox/process.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;AAEtD,MAAM,MAAM,eAAe,GAAG,SAAS,GAAG,MAAM,CAAC;AAEjD;;GAEG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,oBAAoB,GAAG,eAAe,CAQjF;AAED;;GAEG;AACH,wBAAgB,yBAAyB,CAAC,QAAQ,EAAE,eAAe,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,MAAM,EAAE,CAShG"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bug-Box Process Isolation
|
|
3
|
+
*
|
|
4
|
+
* Provides OS-level process namespace isolation so the replayed command
|
|
5
|
+
* cannot see or signal host processes.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Selects the best available process isolation strategy based on capabilities.
|
|
9
|
+
*/
|
|
10
|
+
export function selectProcessStrategy(caps) {
|
|
11
|
+
if (caps.platform === 'linux' && caps.hasUnshare) {
|
|
12
|
+
return 'unshare';
|
|
13
|
+
}
|
|
14
|
+
// Windows Job Objects provide resource limits but not true PID namespace hiding.
|
|
15
|
+
// We handle Windows Job Objects in the resources layer instead.
|
|
16
|
+
return 'none';
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Modifies the command array to run within a new process namespace.
|
|
20
|
+
*/
|
|
21
|
+
export function buildProcessIsolationArgs(strategy, command) {
|
|
22
|
+
if (strategy === 'unshare') {
|
|
23
|
+
// --pid: create new PID namespace
|
|
24
|
+
// --fork: fork the new process (required for --pid)
|
|
25
|
+
// --mount-proc: mount a new /proc filesystem so tools like `ps` work correctly
|
|
26
|
+
return ['unshare', '--pid', '--fork', '--mount-proc', '--', ...command];
|
|
27
|
+
}
|
|
28
|
+
return command;
|
|
29
|
+
}
|
|
30
|
+
//# sourceMappingURL=process.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"process.js","sourceRoot":"","sources":["../../src/sandbox/process.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAMH;;GAEG;AACH,MAAM,UAAU,qBAAqB,CAAC,IAA0B;IAC9D,IAAI,IAAI,CAAC,QAAQ,KAAK,OAAO,IAAI,IAAI,CAAC,UAAU,EAAE,CAAC;QACjD,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,iFAAiF;IACjF,gEAAgE;IAChE,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,yBAAyB,CAAC,QAAyB,EAAE,OAAiB;IACpF,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;QAC3B,kCAAkC;QAClC,oDAAoD;QACpD,+EAA+E;QAC/E,OAAO,CAAC,SAAS,EAAE,OAAO,EAAE,QAAQ,EAAE,cAAc,EAAE,IAAI,EAAE,GAAG,OAAO,CAAC,CAAC;IAC1E,CAAC;IAED,OAAO,OAAO,CAAC;AACjB,CAAC"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bug-Box Resource Isolation
|
|
3
|
+
*
|
|
4
|
+
* Provides resource limits (memory, CPU) for the replayed process
|
|
5
|
+
* using cgroups v2 on Linux and Job Objects on Windows.
|
|
6
|
+
*/
|
|
7
|
+
import { PlatformCapabilities } from './capabilities';
|
|
8
|
+
export type ResourceStrategy = 'cgroups' | 'job-object' | 'none';
|
|
9
|
+
export interface ResourceLimits {
|
|
10
|
+
maxMemoryMB?: number;
|
|
11
|
+
maxCpuPercent?: number;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Selects the best available resource isolation strategy based on capabilities.
|
|
15
|
+
*/
|
|
16
|
+
export declare function selectResourceStrategy(caps: PlatformCapabilities): ResourceStrategy;
|
|
17
|
+
/**
|
|
18
|
+
* Modifies the command array to apply resource limits.
|
|
19
|
+
*/
|
|
20
|
+
export declare function buildResourceIsolationArgs(strategy: ResourceStrategy, command: string[], limits: ResourceLimits): string[];
|
|
21
|
+
//# sourceMappingURL=resources.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resources.d.ts","sourceRoot":"","sources":["../../src/sandbox/resources.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;AAEtD,MAAM,MAAM,gBAAgB,GAAG,SAAS,GAAG,YAAY,GAAG,MAAM,CAAC;AAEjE,MAAM,WAAW,cAAc;IAC7B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED;;GAEG;AACH,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,oBAAoB,GAAG,gBAAgB,CAUnF;AAED;;GAEG;AACH,wBAAgB,0BAA0B,CACxC,QAAQ,EAAE,gBAAgB,EAC1B,OAAO,EAAE,MAAM,EAAE,EACjB,MAAM,EAAE,cAAc,GACrB,MAAM,EAAE,CA8CV"}
|