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.
Files changed (110) hide show
  1. package/CHANGELOG.md +65 -0
  2. package/README.md +256 -0
  3. package/assets/icon-16x16.png +0 -0
  4. package/assets/icon-32x32.png +0 -0
  5. package/assets/icon-512x512.png +0 -0
  6. package/dist/capture/engine.d.ts +12 -0
  7. package/dist/capture/engine.d.ts.map +1 -0
  8. package/dist/capture/engine.js +129 -0
  9. package/dist/capture/engine.js.map +1 -0
  10. package/dist/capture/packager.d.ts +39 -0
  11. package/dist/capture/packager.d.ts.map +1 -0
  12. package/dist/capture/packager.js +145 -0
  13. package/dist/capture/packager.js.map +1 -0
  14. package/dist/cli.d.ts +3 -0
  15. package/dist/cli.d.ts.map +1 -0
  16. package/dist/cli.js +538 -0
  17. package/dist/cli.js.map +1 -0
  18. package/dist/diff/engine.d.ts +28 -0
  19. package/dist/diff/engine.d.ts.map +1 -0
  20. package/dist/diff/engine.js +88 -0
  21. package/dist/diff/engine.js.map +1 -0
  22. package/dist/replay/engine.d.ts +41 -0
  23. package/dist/replay/engine.d.ts.map +1 -0
  24. package/dist/replay/engine.js +69 -0
  25. package/dist/replay/engine.js.map +1 -0
  26. package/dist/replay/sandbox.d.ts +33 -0
  27. package/dist/replay/sandbox.d.ts.map +1 -0
  28. package/dist/replay/sandbox.js +167 -0
  29. package/dist/replay/sandbox.js.map +1 -0
  30. package/dist/replay/verdict.d.ts +8 -0
  31. package/dist/replay/verdict.d.ts.map +1 -0
  32. package/dist/replay/verdict.js +32 -0
  33. package/dist/replay/verdict.js.map +1 -0
  34. package/dist/sandbox/bugbox.d.ts +38 -0
  35. package/dist/sandbox/bugbox.d.ts.map +1 -0
  36. package/dist/sandbox/bugbox.js +122 -0
  37. package/dist/sandbox/bugbox.js.map +1 -0
  38. package/dist/sandbox/capabilities.d.ts +33 -0
  39. package/dist/sandbox/capabilities.d.ts.map +1 -0
  40. package/dist/sandbox/capabilities.js +53 -0
  41. package/dist/sandbox/capabilities.js.map +1 -0
  42. package/dist/sandbox/filesystem.d.ts +50 -0
  43. package/dist/sandbox/filesystem.d.ts.map +1 -0
  44. package/dist/sandbox/filesystem.js +134 -0
  45. package/dist/sandbox/filesystem.js.map +1 -0
  46. package/dist/sandbox/network.d.ts +68 -0
  47. package/dist/sandbox/network.d.ts.map +1 -0
  48. package/dist/sandbox/network.js +136 -0
  49. package/dist/sandbox/network.js.map +1 -0
  50. package/dist/sandbox/process.d.ts +17 -0
  51. package/dist/sandbox/process.d.ts.map +1 -0
  52. package/dist/sandbox/process.js +30 -0
  53. package/dist/sandbox/process.js.map +1 -0
  54. package/dist/sandbox/resources.d.ts +21 -0
  55. package/dist/sandbox/resources.d.ts.map +1 -0
  56. package/dist/sandbox/resources.js +60 -0
  57. package/dist/sandbox/resources.js.map +1 -0
  58. package/dist/types/artifact.d.ts +57 -0
  59. package/dist/types/artifact.d.ts.map +1 -0
  60. package/dist/types/artifact.js +2 -0
  61. package/dist/types/artifact.js.map +1 -0
  62. package/dist/types/failure.d.ts +12 -0
  63. package/dist/types/failure.d.ts.map +1 -0
  64. package/dist/types/failure.js +2 -0
  65. package/dist/types/failure.js.map +1 -0
  66. package/dist/utils/archive.d.ts +13 -0
  67. package/dist/utils/archive.d.ts.map +1 -0
  68. package/dist/utils/archive.js +39 -0
  69. package/dist/utils/archive.js.map +1 -0
  70. package/dist/utils/associations.d.ts +10 -0
  71. package/dist/utils/associations.d.ts.map +1 -0
  72. package/dist/utils/associations.js +46 -0
  73. package/dist/utils/associations.js.map +1 -0
  74. package/dist/utils/exclude.d.ts +12 -0
  75. package/dist/utils/exclude.d.ts.map +1 -0
  76. package/dist/utils/exclude.js +42 -0
  77. package/dist/utils/exclude.js.map +1 -0
  78. package/dist/utils/fingerprint.d.ts +16 -0
  79. package/dist/utils/fingerprint.d.ts.map +1 -0
  80. package/dist/utils/fingerprint.js +72 -0
  81. package/dist/utils/fingerprint.js.map +1 -0
  82. package/dist/utils/git.d.ts +13 -0
  83. package/dist/utils/git.d.ts.map +1 -0
  84. package/dist/utils/git.js +41 -0
  85. package/dist/utils/git.js.map +1 -0
  86. package/dist/utils/json-output.d.ts +36 -0
  87. package/dist/utils/json-output.d.ts.map +1 -0
  88. package/dist/utils/json-output.js +49 -0
  89. package/dist/utils/json-output.js.map +1 -0
  90. package/dist/utils/paths.d.ts +19 -0
  91. package/dist/utils/paths.d.ts.map +1 -0
  92. package/dist/utils/paths.js +43 -0
  93. package/dist/utils/paths.js.map +1 -0
  94. package/dist/utils/secrets.d.ts +13 -0
  95. package/dist/utils/secrets.d.ts.map +1 -0
  96. package/dist/utils/secrets.js +52 -0
  97. package/dist/utils/secrets.js.map +1 -0
  98. package/dist/utils/security.d.ts +27 -0
  99. package/dist/utils/security.d.ts.map +1 -0
  100. package/dist/utils/security.js +75 -0
  101. package/dist/utils/security.js.map +1 -0
  102. package/dist/utils/ui.d.ts +31 -0
  103. package/dist/utils/ui.d.ts.map +1 -0
  104. package/dist/utils/ui.js +54 -0
  105. package/dist/utils/ui.js.map +1 -0
  106. package/package.json +80 -0
  107. package/scripts/bugproof-file-association-linux.sh +80 -0
  108. package/scripts/bugproof-file-association-macos.sh +48 -0
  109. package/scripts/bugproof-file-association-windows.reg +44 -0
  110. package/scripts/postinstall.cjs +215 -0
@@ -0,0 +1,88 @@
1
+ /**
2
+ * Diff engine: compares two .bug artifacts and produces a structured diff report.
3
+ */
4
+ export function diffArtifacts(left, right) {
5
+ const changes = [];
6
+ // Compare exit code
7
+ if (left.failure.exit_code !== right.failure.exit_code) {
8
+ changes.push({ field: 'exit_code', left: left.failure.exit_code, right: right.failure.exit_code });
9
+ }
10
+ // Compare fingerprint
11
+ if (left.failure.fingerprint !== right.failure.fingerprint) {
12
+ changes.push({ field: 'fingerprint', left: left.failure.fingerprint, right: right.failure.fingerprint });
13
+ }
14
+ // Compare command
15
+ const leftCmd = left.manifest.command.join(' ');
16
+ const rightCmd = right.manifest.command.join(' ');
17
+ if (leftCmd !== rightCmd) {
18
+ changes.push({ field: 'command', left: leftCmd, right: rightCmd });
19
+ }
20
+ // Compare OS
21
+ if (left.manifest.captured_on.os !== right.manifest.captured_on.os) {
22
+ changes.push({ field: 'os', left: left.manifest.captured_on.os, right: right.manifest.captured_on.os });
23
+ }
24
+ // Compare architecture
25
+ if (left.manifest.captured_on.arch !== right.manifest.captured_on.arch) {
26
+ changes.push({ field: 'arch', left: left.manifest.captured_on.arch, right: right.manifest.captured_on.arch });
27
+ }
28
+ // Compare Node version
29
+ if (left.manifest.captured_on.node_version !== right.manifest.captured_on.node_version) {
30
+ changes.push({
31
+ field: 'node_version',
32
+ left: left.manifest.captured_on.node_version,
33
+ right: right.manifest.captured_on.node_version,
34
+ });
35
+ }
36
+ // Compare error patterns
37
+ const leftPatterns = JSON.stringify(left.failure.error_patterns);
38
+ const rightPatterns = JSON.stringify(right.failure.error_patterns);
39
+ if (leftPatterns !== rightPatterns) {
40
+ changes.push({
41
+ field: 'error_patterns',
42
+ left: left.failure.error_patterns,
43
+ right: right.failure.error_patterns,
44
+ });
45
+ }
46
+ // Compare duration (only if significantly different, >20%)
47
+ const durationDiff = Math.abs(left.failure.duration_ms - right.failure.duration_ms);
48
+ const avgDuration = (left.failure.duration_ms + right.failure.duration_ms) / 2;
49
+ if (avgDuration > 0 && durationDiff / avgDuration > 0.2) {
50
+ changes.push({ field: 'duration_ms', left: left.failure.duration_ms, right: right.failure.duration_ms });
51
+ }
52
+ // Compare file lists
53
+ const fileChanges = diffFileEntries(left.files, right.files);
54
+ const hasFileChanges = fileChanges.added.length > 0 || fileChanges.removed.length > 0 || fileChanges.modified.length > 0;
55
+ return {
56
+ identical: changes.length === 0 && !hasFileChanges,
57
+ changes,
58
+ fileChanges: hasFileChanges ? fileChanges : { added: [], removed: [], modified: [] },
59
+ };
60
+ }
61
+ function diffFileEntries(leftFiles, rightFiles) {
62
+ const leftMap = new Map(leftFiles.map(f => [f.path, f]));
63
+ const rightMap = new Map(rightFiles.map(f => [f.path, f]));
64
+ const added = [];
65
+ const removed = [];
66
+ const modified = [];
67
+ // Files in right but not in left => added
68
+ for (const [path] of rightMap) {
69
+ if (!leftMap.has(path)) {
70
+ added.push(path);
71
+ }
72
+ }
73
+ // Files in left but not in right => removed
74
+ for (const [path] of leftMap) {
75
+ if (!rightMap.has(path)) {
76
+ removed.push(path);
77
+ }
78
+ }
79
+ // Files in both but with different hashes => modified
80
+ for (const [path, leftEntry] of leftMap) {
81
+ const rightEntry = rightMap.get(path);
82
+ if (rightEntry && leftEntry.sha256 !== rightEntry.sha256) {
83
+ modified.push(path);
84
+ }
85
+ }
86
+ return { added, removed, modified };
87
+ }
88
+ //# sourceMappingURL=engine.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"engine.js","sourceRoot":"","sources":["../../src/diff/engine.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,41 @@
1
+ import { RunConfig } from '../types/artifact.js';
2
+ import { FailureRecord } from '../types/failure.js';
3
+ export interface ReplayOptions {
4
+ artifactPath: string;
5
+ versionMatch: 'strict' | 'current' | 'branch';
6
+ envOverrides: Record<string, string>;
7
+ /** Git commit from the artifact manifest (used for strict mode) */
8
+ gitCommit?: string;
9
+ /** Git branch from the artifact manifest (used for branch mode) */
10
+ gitBranch?: string;
11
+ sandboxLevel?: 'workspace' | 'isolated' | 'full';
12
+ }
13
+ export interface ReplayResult {
14
+ actualFailure: FailureRecord;
15
+ expectedFailure: FailureRecord;
16
+ actualStdout: string;
17
+ actualStderr: string;
18
+ /** The directory where replay actually ran */
19
+ replayDirectory: string;
20
+ /** Whether the sandbox fell back to artifact file snapshots */
21
+ usedFallback?: boolean;
22
+ /** Sandbox architecture layers applied */
23
+ bugBox?: {
24
+ level: string;
25
+ appliedLayers: string[];
26
+ skippedLayers: string[];
27
+ platform: string;
28
+ };
29
+ }
30
+ /**
31
+ * Replays a captured artifact in an isolated sandbox.
32
+ *
33
+ * Three modes:
34
+ * - current: runs in cwd (no sandbox, fast)
35
+ * - strict: creates temp dir at the exact git commit, falls back to artifact files/
36
+ * - branch: creates temp dir at the branch tip, falls back to current
37
+ *
38
+ * The sandbox is always cleaned up after the command finishes.
39
+ */
40
+ export declare function replayArtifact(runConfig: RunConfig, expectedFailure: FailureRecord, options: ReplayOptions): Promise<ReplayResult>;
41
+ //# sourceMappingURL=engine.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"engine.d.ts","sourceRoot":"","sources":["../../src/replay/engine.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AACjD,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAKpD,MAAM,WAAW,aAAa;IAC5B,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,QAAQ,GAAG,SAAS,GAAG,QAAQ,CAAC;IAC9C,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACrC,mEAAmE;IACnE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,mEAAmE;IACnE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,YAAY,CAAC,EAAE,WAAW,GAAG,UAAU,GAAG,MAAM,CAAC;CAClD;AAED,MAAM,WAAW,YAAY;IAC3B,aAAa,EAAE,aAAa,CAAC;IAC7B,eAAe,EAAE,aAAa,CAAC;IAC/B,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;IACrB,8CAA8C;IAC9C,eAAe,EAAE,MAAM,CAAC;IACxB,+DAA+D;IAC/D,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,0CAA0C;IAC1C,MAAM,CAAC,EAAE;QACP,KAAK,EAAE,MAAM,CAAC;QACd,aAAa,EAAE,MAAM,EAAE,CAAC;QACxB,aAAa,EAAE,MAAM,EAAE,CAAC;QACxB,QAAQ,EAAE,MAAM,CAAC;KAClB,CAAC;CACH;AAED;;;;;;;;;GASG;AACH,wBAAsB,cAAc,CAClC,SAAS,EAAE,SAAS,EACpB,eAAe,EAAE,aAAa,EAC9B,OAAO,EAAE,aAAa,GACrB,OAAO,CAAC,YAAY,CAAC,CA0DvB"}
@@ -0,0 +1,69 @@
1
+ import { executeAndCapture } from '../capture/engine.js';
2
+ import { createBugBox } from '../sandbox/bugbox.js';
3
+ import { sanitizeArtifactEnvironment } from '../utils/security.js';
4
+ /**
5
+ * Replays a captured artifact in an isolated sandbox.
6
+ *
7
+ * Three modes:
8
+ * - current: runs in cwd (no sandbox, fast)
9
+ * - strict: creates temp dir at the exact git commit, falls back to artifact files/
10
+ * - branch: creates temp dir at the branch tip, falls back to current
11
+ *
12
+ * The sandbox is always cleaned up after the command finishes.
13
+ */
14
+ export async function replayArtifact(runConfig, expectedFailure, options) {
15
+ // 1. Create the sandbox workspace via Bug-Box orchestrator
16
+ const bugbox = await createBugBox({
17
+ level: options.sandboxLevel || 'workspace',
18
+ command: runConfig.command,
19
+ sandboxOptions: {
20
+ mode: options.versionMatch,
21
+ originalWorkingDir: runConfig.working_directory,
22
+ artifactPath: options.artifactPath,
23
+ gitCommit: options.gitCommit,
24
+ gitBranch: options.gitBranch,
25
+ },
26
+ });
27
+ try {
28
+ // 2. Merge environments — sanitize artifact env to block dangerous overrides
29
+ const safeArtifactEnv = sanitizeArtifactEnvironment(runConfig.environment);
30
+ const replayEnv = {
31
+ ...process.env,
32
+ ...safeArtifactEnv,
33
+ ...options.envOverrides,
34
+ };
35
+ const hostPath = process.env.PATH || process.env.Path || process.env.path;
36
+ if (hostPath) {
37
+ replayEnv.PATH = hostPath;
38
+ if (process.platform === 'win32') {
39
+ replayEnv.Path = hostPath;
40
+ }
41
+ }
42
+ // 3. Re-run the command in the sandbox directory
43
+ const replayConfig = {
44
+ ...runConfig,
45
+ ...bugbox.runConfigOverrides,
46
+ environment: replayEnv,
47
+ };
48
+ const result = await executeAndCapture(replayConfig);
49
+ return {
50
+ actualFailure: result.failure,
51
+ expectedFailure,
52
+ actualStdout: result.stdout,
53
+ actualStderr: result.stderr,
54
+ replayDirectory: bugbox.sandboxResult.workingDirectory,
55
+ usedFallback: bugbox.sandboxResult.usedFallback,
56
+ bugBox: {
57
+ level: options.sandboxLevel || 'workspace',
58
+ appliedLayers: bugbox.appliedLayers,
59
+ skippedLayers: bugbox.skippedLayers,
60
+ platform: bugbox.capabilities.platform,
61
+ },
62
+ };
63
+ }
64
+ finally {
65
+ // 4. Always clean up the sandbox
66
+ bugbox.cleanupFn();
67
+ }
68
+ }
69
+ //# sourceMappingURL=engine.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"engine.js","sourceRoot":"","sources":["../../src/replay/engine.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AACzD,OAAO,EAAE,YAAY,EAAgB,MAAM,sBAAsB,CAAC;AAClE,OAAO,EAAE,2BAA2B,EAAE,MAAM,sBAAsB,CAAC;AA+BnE;;;;;;;;;GASG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAClC,SAAoB,EACpB,eAA8B,EAC9B,OAAsB;IAEtB,2DAA2D;IAC3D,MAAM,MAAM,GAAiB,MAAM,YAAY,CAAC;QAC9C,KAAK,EAAE,OAAO,CAAC,YAAY,IAAI,WAAW;QAC1C,OAAO,EAAE,SAAS,CAAC,OAAO;QAC1B,cAAc,EAAE;YACd,IAAI,EAAE,OAAO,CAAC,YAAY;YAC1B,kBAAkB,EAAE,SAAS,CAAC,iBAAiB;YAC/C,YAAY,EAAE,OAAO,CAAC,YAAY;YAClC,SAAS,EAAE,OAAO,CAAC,SAAS;YAC5B,SAAS,EAAE,OAAO,CAAC,SAAS;SAC7B;KACF,CAAC,CAAC;IAEH,IAAI,CAAC;QACH,6EAA6E;QAC7E,MAAM,eAAe,GAAG,2BAA2B,CAAC,SAAS,CAAC,WAAW,CAAC,CAAC;QAC3E,MAAM,SAAS,GAAG;YAChB,GAAG,OAAO,CAAC,GAAG;YACd,GAAG,eAAe;YAClB,GAAG,OAAO,CAAC,YAAY;SACE,CAAC;QAE5B,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,IAAI,IAAI,OAAO,CAAC,GAAG,CAAC,IAAI,IAAI,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC;QAC1E,IAAI,QAAQ,EAAE,CAAC;YACb,SAAS,CAAC,IAAI,GAAG,QAAQ,CAAC;YAC1B,IAAI,OAAO,CAAC,QAAQ,KAAK,OAAO,EAAE,CAAC;gBACjC,SAAS,CAAC,IAAI,GAAG,QAAQ,CAAC;YAC5B,CAAC;QACH,CAAC;QAED,iDAAiD;QACjD,MAAM,YAAY,GAAc;YAC9B,GAAG,SAAS;YACZ,GAAG,MAAM,CAAC,kBAAkB;YAC5B,WAAW,EAAE,SAAS;SACvB,CAAC;QAEF,MAAM,MAAM,GAAG,MAAM,iBAAiB,CAAC,YAAY,CAAC,CAAC;QAErD,OAAO;YACL,aAAa,EAAE,MAAM,CAAC,OAAO;YAC7B,eAAe;YACf,YAAY,EAAE,MAAM,CAAC,MAAM;YAC3B,YAAY,EAAE,MAAM,CAAC,MAAM;YAC3B,eAAe,EAAE,MAAM,CAAC,aAAa,CAAC,gBAAgB;YACtD,YAAY,EAAE,MAAM,CAAC,aAAa,CAAC,YAAY;YAC/C,MAAM,EAAE;gBACN,KAAK,EAAE,OAAO,CAAC,YAAY,IAAI,WAAW;gBAC1C,aAAa,EAAE,MAAM,CAAC,aAAa;gBACnC,aAAa,EAAE,MAAM,CAAC,aAAa;gBACnC,QAAQ,EAAE,MAAM,CAAC,YAAY,CAAC,QAAQ;aACvC;SACF,CAAC;IACJ,CAAC;YAAS,CAAC;QACT,iCAAiC;QACjC,MAAM,CAAC,SAAS,EAAE,CAAC;IACrB,CAAC;AACH,CAAC"}
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Replay Sandbox: creates an isolated workspace for deterministic replay.
3
+ *
4
+ * Three modes:
5
+ * - current: run in cwd (no isolation, fast)
6
+ * - strict: git worktree at exact commit, falls back to artifact files/
7
+ * - branch: git worktree at branch tip, falls back to current
8
+ */
9
+ export interface SandboxOptions {
10
+ mode: 'current' | 'strict' | 'branch';
11
+ originalWorkingDir: string;
12
+ artifactPath: string;
13
+ gitCommit?: string;
14
+ gitBranch?: string;
15
+ /** Optional pre-created directory to place the sandbox into */
16
+ targetDir?: string;
17
+ }
18
+ export interface SandboxResult {
19
+ workingDirectory: string;
20
+ tempDir?: string;
21
+ needsCleanup: boolean;
22
+ /** True when git checkout failed and we fell back to the artifact's files/ snapshot */
23
+ usedFallback?: boolean;
24
+ }
25
+ /**
26
+ * Creates a sandbox workspace for replay.
27
+ */
28
+ export declare function createSandbox(options: SandboxOptions): Promise<SandboxResult>;
29
+ /**
30
+ * Removes the sandbox temp directory.
31
+ */
32
+ export declare function cleanupSandbox(result: SandboxResult): void;
33
+ //# sourceMappingURL=sandbox.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sandbox.d.ts","sourceRoot":"","sources":["../../src/replay/sandbox.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAQH,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,SAAS,GAAG,QAAQ,GAAG,QAAQ,CAAC;IACtC,kBAAkB,EAAE,MAAM,CAAC;IAC3B,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,+DAA+D;IAC/D,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,aAAa;IAC5B,gBAAgB,EAAE,MAAM,CAAC;IACzB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,OAAO,CAAC;IACtB,uFAAuF;IACvF,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB;AAED;;GAEG;AACH,wBAAsB,aAAa,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,aAAa,CAAC,CAsFnF;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,aAAa,GAAG,IAAI,CA0B1D"}
@@ -0,0 +1,167 @@
1
+ /**
2
+ * Replay Sandbox: creates an isolated workspace for deterministic replay.
3
+ *
4
+ * Three modes:
5
+ * - current: run in cwd (no isolation, fast)
6
+ * - strict: git worktree at exact commit, falls back to artifact files/
7
+ * - branch: git worktree at branch tip, falls back to current
8
+ */
9
+ import * as fs from 'fs';
10
+ import * as path from 'path';
11
+ import * as os from 'os';
12
+ import { spawnSync } from 'child_process';
13
+ import { isValidGitRef } from '../utils/security.js';
14
+ /**
15
+ * Creates a sandbox workspace for replay.
16
+ */
17
+ export async function createSandbox(options) {
18
+ // ── current mode: no isolation ──
19
+ if (options.mode === 'current') {
20
+ const tempDir = options.targetDir || fs.mkdtempSync(path.join(os.tmpdir(), 'bugproof-replay-'));
21
+ const artifactFilesDir = path.join(options.artifactPath, 'files');
22
+ if (fs.existsSync(artifactFilesDir)) {
23
+ copyDirRecursive(artifactFilesDir, tempDir);
24
+ return {
25
+ workingDirectory: tempDir,
26
+ tempDir,
27
+ needsCleanup: true,
28
+ usedFallback: true,
29
+ };
30
+ }
31
+ return {
32
+ workingDirectory: options.originalWorkingDir,
33
+ needsCleanup: false,
34
+ };
35
+ }
36
+ // ── branch mode without a branch: fall back to current ──
37
+ if (options.mode === 'branch' && !options.gitBranch) {
38
+ return {
39
+ workingDirectory: options.originalWorkingDir,
40
+ needsCleanup: false,
41
+ };
42
+ }
43
+ // ── strict or branch: try git worktree ──
44
+ const tempDir = options.targetDir || fs.mkdtempSync(path.join(os.tmpdir(), 'bugproof-replay-'));
45
+ // Determine the ref to checkout
46
+ const ref = options.mode === 'strict' ? options.gitCommit : options.gitBranch;
47
+ if (ref) {
48
+ // Security: validate ref before passing to git
49
+ if (!isValidGitRef(ref)) {
50
+ // Invalid ref, skip directly to fallback
51
+ }
52
+ else {
53
+ const worktreeResult = tryGitWorktree(options.originalWorkingDir, tempDir, ref);
54
+ if (worktreeResult.success) {
55
+ return {
56
+ workingDirectory: tempDir,
57
+ tempDir,
58
+ needsCleanup: true,
59
+ };
60
+ }
61
+ // Worktree failed. For strict mode, try a detached checkout clone.
62
+ if (options.mode === 'strict') {
63
+ const cloneResult = tryGitCloneAndCheckout(options.originalWorkingDir, tempDir, ref);
64
+ if (cloneResult.success) {
65
+ return {
66
+ workingDirectory: tempDir,
67
+ tempDir,
68
+ needsCleanup: true,
69
+ };
70
+ }
71
+ }
72
+ }
73
+ }
74
+ // ── Fallback: copy artifact's files/ snapshot into the temp dir ──
75
+ const artifactFilesDir = path.join(options.artifactPath, 'files');
76
+ if (fs.existsSync(artifactFilesDir)) {
77
+ copyDirRecursive(artifactFilesDir, tempDir);
78
+ return {
79
+ workingDirectory: tempDir,
80
+ tempDir,
81
+ needsCleanup: true,
82
+ usedFallback: true,
83
+ };
84
+ }
85
+ // Nothing worked, clean up and fall back to cwd
86
+ if (!options.targetDir) {
87
+ fs.rmSync(tempDir, { recursive: true, force: true });
88
+ }
89
+ return {
90
+ workingDirectory: options.originalWorkingDir,
91
+ needsCleanup: false,
92
+ usedFallback: true,
93
+ };
94
+ }
95
+ /**
96
+ * Removes the sandbox temp directory.
97
+ */
98
+ export function cleanupSandbox(result) {
99
+ if (!result.needsCleanup || !result.tempDir)
100
+ return;
101
+ try {
102
+ // If it was a worktree, remove it properly first
103
+ const gitDir = path.join(result.tempDir, '.git');
104
+ if (fs.existsSync(gitDir)) {
105
+ const gitContent = fs.readFileSync(gitDir, 'utf-8').trim();
106
+ if (gitContent.startsWith('gitdir:')) {
107
+ // This is a worktree, find the parent repo and remove the worktree
108
+ spawnSync('git', ['worktree', 'remove', '--force', result.tempDir], {
109
+ encoding: 'utf-8',
110
+ timeout: 10000,
111
+ });
112
+ return;
113
+ }
114
+ }
115
+ }
116
+ catch {
117
+ // Fall through to force delete
118
+ }
119
+ try {
120
+ fs.rmSync(result.tempDir, { recursive: true, force: true });
121
+ }
122
+ catch {
123
+ // Best effort cleanup
124
+ }
125
+ }
126
+ // ── Internal helpers ──
127
+ function tryGitWorktree(repoDir, targetDir, ref) {
128
+ // First verify the ref exists in this repo
129
+ const verify = spawnSync('git', ['rev-parse', '--verify', ref], {
130
+ cwd: repoDir,
131
+ encoding: 'utf-8',
132
+ timeout: 5000,
133
+ });
134
+ if (verify.status !== 0) {
135
+ return { success: false };
136
+ }
137
+ // Create a detached worktree
138
+ const result = spawnSync('git', ['worktree', 'add', '--detach', targetDir, '--', ref], { cwd: repoDir, encoding: 'utf-8', timeout: 30000 });
139
+ return { success: result.status === 0 };
140
+ }
141
+ function tryGitCloneAndCheckout(repoDir, targetDir, commitSha) {
142
+ // Local clone (no network, shares objects via hardlinks)
143
+ const clone = spawnSync('git', ['clone', '--no-checkout', '--shared', repoDir, targetDir], { encoding: 'utf-8', timeout: 30000 });
144
+ if (clone.status !== 0) {
145
+ return { success: false };
146
+ }
147
+ const checkout = spawnSync('git', ['checkout', commitSha], { cwd: targetDir, encoding: 'utf-8', timeout: 10000 });
148
+ return { success: checkout.status === 0 };
149
+ }
150
+ function copyDirRecursive(src, dest) {
151
+ fs.mkdirSync(dest, { recursive: true });
152
+ const entries = fs.readdirSync(src, { withFileTypes: true });
153
+ for (const entry of entries) {
154
+ // Security: skip symlinks to prevent escape attacks
155
+ if (entry.isSymbolicLink())
156
+ continue;
157
+ const srcPath = path.join(src, entry.name);
158
+ const destPath = path.join(dest, entry.name);
159
+ if (entry.isDirectory()) {
160
+ copyDirRecursive(srcPath, destPath);
161
+ }
162
+ else if (entry.isFile()) {
163
+ fs.copyFileSync(srcPath, destPath);
164
+ }
165
+ }
166
+ }
167
+ //# sourceMappingURL=sandbox.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sandbox.js","sourceRoot":"","sources":["../../src/replay/sandbox.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ import { ReplayResult } from './engine.js';
2
+ export type VerdictStatus = 'confirmed' | 'not_confirmed' | 'blocked_by_env';
3
+ export interface Verdict {
4
+ status: VerdictStatus;
5
+ message: string;
6
+ }
7
+ export declare function generateVerdict(result: ReplayResult): Verdict;
8
+ //# sourceMappingURL=verdict.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"verdict.d.ts","sourceRoot":"","sources":["../../src/replay/verdict.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE3C,MAAM,MAAM,aAAa,GAAG,WAAW,GAAG,eAAe,GAAG,gBAAgB,CAAC;AAE7E,MAAM,WAAW,OAAO;IACtB,MAAM,EAAE,aAAa,CAAC;IACtB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,wBAAgB,eAAe,CAAC,MAAM,EAAE,YAAY,GAAG,OAAO,CAqC7D"}
@@ -0,0 +1,32 @@
1
+ export function generateVerdict(result) {
2
+ const { expectedFailure, actualFailure } = result;
3
+ // 1. Exact Fingerprint Match
4
+ if (expectedFailure.fingerprint === actualFailure.fingerprint) {
5
+ return {
6
+ status: 'confirmed',
7
+ message: 'Reproduction confirmed (exact fingerprint match)'
8
+ };
9
+ }
10
+ // 2. Fuzzy Pattern Match
11
+ // Check if actual failure shares any of the same error patterns
12
+ const sharedPatterns = actualFailure.error_patterns.filter(p => expectedFailure.error_patterns.includes(p));
13
+ if (sharedPatterns.length > 0) {
14
+ return {
15
+ status: 'confirmed',
16
+ message: `Reproduction confirmed (fuzzy match on patterns: ${sharedPatterns.join(', ')})`
17
+ };
18
+ }
19
+ // 3. Different exit code, but failed
20
+ if (actualFailure.exit_code !== 0) {
21
+ return {
22
+ status: 'not_confirmed',
23
+ message: `Failed, but with a different error. Expected pattern: ${expectedFailure.error_patterns[0] || 'Unknown'} / Actual pattern: ${actualFailure.error_patterns[0] || 'Unknown'}`
24
+ };
25
+ }
26
+ // 4. Succeeded
27
+ return {
28
+ status: 'not_confirmed',
29
+ message: 'Command succeeded on replay. The bug did not reproduce.'
30
+ };
31
+ }
32
+ //# sourceMappingURL=verdict.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"verdict.js","sourceRoot":"","sources":["../../src/replay/verdict.ts"],"names":[],"mappings":"AASA,MAAM,UAAU,eAAe,CAAC,MAAoB;IAClD,MAAM,EAAE,eAAe,EAAE,aAAa,EAAE,GAAG,MAAM,CAAC;IAElD,6BAA6B;IAC7B,IAAI,eAAe,CAAC,WAAW,KAAK,aAAa,CAAC,WAAW,EAAE,CAAC;QAC9D,OAAO;YACL,MAAM,EAAE,WAAW;YACnB,OAAO,EAAE,kDAAkD;SAC5D,CAAC;IACJ,CAAC;IAED,yBAAyB;IACzB,gEAAgE;IAChE,MAAM,cAAc,GAAG,aAAa,CAAC,cAAc,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAC7D,eAAe,CAAC,cAAc,CAAC,QAAQ,CAAC,CAAC,CAAC,CAC3C,CAAC;IAEF,IAAI,cAAc,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC9B,OAAO;YACL,MAAM,EAAE,WAAW;YACnB,OAAO,EAAE,oDAAoD,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG;SAC1F,CAAC;IACJ,CAAC;IAED,qCAAqC;IACrC,IAAI,aAAa,CAAC,SAAS,KAAK,CAAC,EAAE,CAAC;QAClC,OAAO;YACL,MAAM,EAAE,eAAe;YACvB,OAAO,EAAE,yDAAyD,eAAe,CAAC,cAAc,CAAC,CAAC,CAAC,IAAI,SAAS,sBAAsB,aAAa,CAAC,cAAc,CAAC,CAAC,CAAC,IAAI,SAAS,EAAE;SACrL,CAAC;IACJ,CAAC;IAED,eAAe;IACf,OAAO;QACL,MAAM,EAAE,eAAe;QACvB,OAAO,EAAE,yDAAyD;KACnE,CAAC;AACJ,CAAC"}
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Bug-Box Orchestrator
3
+ *
4
+ * Ties together capabilities, filesystem permissions, network isolation,
5
+ * and the existing replay sandbox into a single easy-to-use interface.
6
+ */
7
+ import { SandboxResult, SandboxOptions } from '../replay/sandbox.js';
8
+ import { PlatformCapabilities } from './capabilities.js';
9
+ import { IsolatedDirResult } from './filesystem.js';
10
+ import { NetworkStrategy } from './network.js';
11
+ import { ProcessStrategy } from './process.js';
12
+ import { ResourceStrategy, ResourceLimits } from './resources.js';
13
+ import { RunConfig } from '../types/artifact.js';
14
+ export interface BugBoxOptions {
15
+ level: 'workspace' | 'isolated' | 'full';
16
+ sandboxOptions: SandboxOptions;
17
+ command: string[];
18
+ resourceLimits?: ResourceLimits;
19
+ }
20
+ export interface BugBoxResult {
21
+ sandboxResult: SandboxResult;
22
+ capabilities: PlatformCapabilities;
23
+ appliedLayers: string[];
24
+ skippedLayers: string[];
25
+ networkStrategy: NetworkStrategy;
26
+ processStrategy: ProcessStrategy;
27
+ resourceStrategy: ResourceStrategy;
28
+ isolatedDir?: IsolatedDirResult;
29
+ runConfigOverrides: Partial<RunConfig>;
30
+ cleanupFn: () => void;
31
+ }
32
+ /**
33
+ * Creates a Bug-Box environment for replaying an artifact.
34
+ *
35
+ * @param options Level of isolation and underlying sandbox options
36
+ */
37
+ export declare function createBugBox(options: BugBoxOptions): Promise<BugBoxResult>;
38
+ //# sourceMappingURL=bugbox.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bugbox.d.ts","sourceRoot":"","sources":["../../src/sandbox/bugbox.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,aAAa,EAAE,cAAc,EAAiC,MAAM,sBAAsB,CAAC;AACpG,OAAO,EAAsB,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AAC7E,OAAO,EAIL,iBAAiB,EAClB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAKL,eAAe,EAChB,MAAM,cAAc,CAAC;AACtB,OAAO,EAGL,eAAe,EAChB,MAAM,cAAc,CAAC;AACtB,OAAO,EAGL,gBAAgB,EAChB,cAAc,EACf,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AAEjD,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,WAAW,GAAG,UAAU,GAAG,MAAM,CAAC;IACzC,cAAc,EAAE,cAAc,CAAC;IAC/B,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,cAAc,CAAC,EAAE,cAAc,CAAC;CACjC;AAED,MAAM,WAAW,YAAY;IAC3B,aAAa,EAAE,aAAa,CAAC;IAC7B,YAAY,EAAE,oBAAoB,CAAC;IACnC,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,eAAe,EAAE,eAAe,CAAC;IACjC,eAAe,EAAE,eAAe,CAAC;IACjC,gBAAgB,EAAE,gBAAgB,CAAC;IACnC,WAAW,CAAC,EAAE,iBAAiB,CAAC;IAChC,kBAAkB,EAAE,OAAO,CAAC,SAAS,CAAC,CAAC;IACvC,SAAS,EAAE,MAAM,IAAI,CAAC;CACvB;AAED;;;;GAIG;AACH,wBAAsB,YAAY,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,YAAY,CAAC,CA2HhF"}
@@ -0,0 +1,122 @@
1
+ /**
2
+ * Bug-Box Orchestrator
3
+ *
4
+ * Ties together capabilities, filesystem permissions, network isolation,
5
+ * and the existing replay sandbox into a single easy-to-use interface.
6
+ */
7
+ import { createSandbox, cleanupSandbox } from '../replay/sandbox.js';
8
+ import { detectCapabilities } from './capabilities.js';
9
+ import { createIsolatedDir, lockDirReadOnly, cleanupIsolatedDir, } from './filesystem.js';
10
+ import { selectNetworkStrategy, buildNetworkIsolationArgs, createNetworkCleanup, addFirewallBlockRule, } from './network.js';
11
+ import { selectProcessStrategy, buildProcessIsolationArgs, } from './process.js';
12
+ import { selectResourceStrategy, buildResourceIsolationArgs, } from './resources.js';
13
+ /**
14
+ * Creates a Bug-Box environment for replaying an artifact.
15
+ *
16
+ * @param options Level of isolation and underlying sandbox options
17
+ */
18
+ export async function createBugBox(options) {
19
+ const caps = detectCapabilities();
20
+ const appliedLayers = [];
21
+ const skippedLayers = [];
22
+ // 1. Fast path: 'workspace' mode (no OS-level isolation, just git worktree)
23
+ if (options.level === 'workspace') {
24
+ const sandboxResult = await createSandbox(options.sandboxOptions);
25
+ return {
26
+ sandboxResult,
27
+ capabilities: caps,
28
+ appliedLayers,
29
+ skippedLayers,
30
+ networkStrategy: 'none',
31
+ processStrategy: 'none',
32
+ resourceStrategy: 'none',
33
+ runConfigOverrides: {
34
+ working_directory: sandboxResult.workingDirectory,
35
+ },
36
+ cleanupFn: () => cleanupSandbox(sandboxResult),
37
+ };
38
+ }
39
+ // 2. 'isolated' or 'full' mode: start by creating the restricted filesystem structure
40
+ const isolatedDir = createIsolatedDir();
41
+ appliedLayers.push('filesystem');
42
+ // 3. Populate the workspace with source files (via git worktree or fallback)
43
+ const sandboxResult = await createSandbox({
44
+ ...options.sandboxOptions,
45
+ targetDir: isolatedDir.workspaceDir, // Force sandbox to use our restricted dir
46
+ });
47
+ // If the sandbox fell back to copying the artifact's files/ snapshot,
48
+ // lock the filesDir read-only to prevent the replayed process from
49
+ // modifying the captured source snapshot.
50
+ if (sandboxResult.usedFallback) {
51
+ lockDirReadOnly(isolatedDir.filesDir);
52
+ }
53
+ const runConfigOverrides = {
54
+ working_directory: sandboxResult.workingDirectory,
55
+ };
56
+ // 4. Network Isolation
57
+ let netStrategy = 'none';
58
+ let netCleanup = () => { };
59
+ const ruleName = `bugbox-net-${Date.now()}`;
60
+ if (options.level === 'isolated' || options.level === 'full') {
61
+ netStrategy = selectNetworkStrategy(caps);
62
+ if (netStrategy === 'none') {
63
+ skippedLayers.push('network: primitive not available on this OS');
64
+ }
65
+ else {
66
+ appliedLayers.push('network');
67
+ const netResult = buildNetworkIsolationArgs(netStrategy, options.command);
68
+ runConfigOverrides.command = netResult.command;
69
+ if (netResult.needsPreExec) {
70
+ const exePath = netResult.command[0];
71
+ // If this fails, we just continue (best effort isolation)
72
+ addFirewallBlockRule(ruleName, exePath);
73
+ }
74
+ netCleanup = createNetworkCleanup(netStrategy, ruleName);
75
+ }
76
+ }
77
+ // 5. Process & Resource Isolation (Only in 'full' mode)
78
+ let procStrategy = 'none';
79
+ let resStrategy = 'none';
80
+ if (options.level === 'full') {
81
+ // Process Isolation
82
+ procStrategy = selectProcessStrategy(caps);
83
+ if (procStrategy === 'none') {
84
+ skippedLayers.push('process: primitive not available on this OS');
85
+ }
86
+ else {
87
+ appliedLayers.push('process');
88
+ runConfigOverrides.command = buildProcessIsolationArgs(procStrategy, runConfigOverrides.command || options.command);
89
+ }
90
+ // Resource Limits
91
+ resStrategy = selectResourceStrategy(caps);
92
+ if (resStrategy === 'none') {
93
+ skippedLayers.push('resources: primitive not available on this OS');
94
+ }
95
+ else if (options.resourceLimits && (options.resourceLimits.maxMemoryMB || options.resourceLimits.maxCpuPercent)) {
96
+ appliedLayers.push('resources');
97
+ runConfigOverrides.command = buildResourceIsolationArgs(resStrategy, runConfigOverrides.command || options.command, options.resourceLimits);
98
+ }
99
+ }
100
+ // 6. Build combined cleanup
101
+ const cleanupFn = () => {
102
+ // 1. Tear down network rules
103
+ netCleanup();
104
+ // 2. Remove git worktree
105
+ cleanupSandbox(sandboxResult);
106
+ // 3. Remove restricted temp directory
107
+ cleanupIsolatedDir(isolatedDir);
108
+ };
109
+ return {
110
+ sandboxResult,
111
+ capabilities: caps,
112
+ appliedLayers,
113
+ skippedLayers,
114
+ networkStrategy: netStrategy,
115
+ processStrategy: procStrategy,
116
+ resourceStrategy: resStrategy,
117
+ isolatedDir,
118
+ runConfigOverrides,
119
+ cleanupFn,
120
+ };
121
+ }
122
+ //# sourceMappingURL=bugbox.js.map