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
package/CHANGELOG.md ADDED
@@ -0,0 +1,65 @@
1
+ # Changelog
2
+
3
+ All notable changes to BugProof will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [0.1.0] - 2026-05-06
9
+
10
+ ### Added
11
+
12
+ - **Capture command** — Record failing commands with full context (source code, environment, output)
13
+ - **Replay command** — Execute captured bug artifacts with sandbox isolation and fingerprint matching
14
+ - **Inspect command** — Examine artifact contents without running code
15
+ - **Diff command** — Compare two artifacts side-by-side for changes
16
+ - **Cross-platform support** — Capture on Windows, replay on Linux/macOS with exact fingerprint matching
17
+ - **Sandbox isolation** — Linux cgroups v2, Windows Job Objects, macOS sandbox-exec isolation
18
+ - **Secret redaction** — Automatic detection and masking of 20+ secret patterns (API keys, tokens, passwords)
19
+ - **File association** — Post-install registration of `.bug` file type (Windows/Linux/macOS)
20
+ - **Install-time checks** — Verify Node.js 18+, Git availability, optional language toolchains
21
+ - **JSON output mode** — Structured output for CI/CD integration
22
+ - **Environment override** — Custom environment variables during replay
23
+ - **Exclude patterns** — Skip files matching patterns during capture
24
+ - **Source snapshots** — Bundle git-tracked files with artifacts
25
+
26
+ ### Security
27
+
28
+ - Path traversal prevention via `isPathWithinBoundary()`
29
+ - Git ref injection prevention via `isValidGitRef()`
30
+ - Shell injection prevention (no shell: true in spawn calls)
31
+ - Environment variable hijacking prevention (LD_PRELOAD, NODE_OPTIONS blocklist)
32
+ - Symlink escape prevention in file operations
33
+
34
+ ### Testing
35
+
36
+ - 19 test suites with 131 passing tests
37
+ - 60%+ code coverage across all modules
38
+ - Cross-platform validation (Windows ↔ Linux)
39
+ - E2E sandbox isolation tests
40
+
41
+ ### Documentation
42
+
43
+ - Comprehensive README with quick start guide
44
+ - CLI help command for discoverability
45
+ - Command reference with examples
46
+ - File association setup instructions
47
+ - Troubleshooting and FAQ sections
48
+
49
+ ## [0.2.0] - Planned
50
+
51
+ - npm global install polish
52
+ - Docker sandbox fallback
53
+ - Web UI for artifact inspection
54
+ - Language-specific dependency detection
55
+
56
+ ## [0.3.0] - Planned
57
+
58
+ - Artifact cloud storage (push/pull with signing)
59
+ - GitHub issue integration
60
+ - Richer diff visualization
61
+
62
+ ## [0.4.0] - Planned
63
+
64
+ - Advanced CI/CD integration
65
+ - Enterprise features (audit logging, team sharing)
package/README.md ADDED
@@ -0,0 +1,256 @@
1
+ # BugProof
2
+
3
+ <div align="center">
4
+
5
+ ![BugProof Logo](assets/icon-512x512.png?raw=true&size=200)
6
+
7
+ **Executable bugs, not bug reports.**
8
+
9
+ Capture a backend or CLI failure into a portable `.bug` artifact that another machine can replay.
10
+
11
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
12
+ [![Node.js](https://img.shields.io/badge/Node.js-18%2B-blue)]()
13
+ [![Cross-Platform](https://img.shields.io/badge/Cross--Platform-Windows%20%7C%20Linux%20%7C%20macOS-blueviolet)]()
14
+
15
+ </div>
16
+
17
+ ## What BugProof Captures
18
+
19
+ A `.bug` artifact includes:
20
+ - Source snapshot (git-tracked files, optional untracked)
21
+ - Command, arguments, and working directory
22
+ - Environment schema (secret values redacted)
23
+ - Stdout/stderr and failure fingerprint
24
+ - Capture metadata (OS, architecture, commit, branch)
25
+
26
+ This makes replay deterministic and shareable.
27
+
28
+ ## Install (NPM Package)
29
+
30
+ ```bash
31
+ npm install -g bugproof
32
+ ```
33
+
34
+ ### Install-time checks (automatic)
35
+
36
+ During installation, BugProof now automatically:
37
+ - Verifies Node.js version (requires 18+)
38
+ - Checks that Git is available
39
+ - Detects optional language toolchains (python/java/gcc/g++/go/rustc)
40
+ - Attempts `.bug` file association and icon registration (best effort, user scope)
41
+
42
+ If association setup fails on your system, run manual scripts:
43
+ - Windows: `scripts/bugproof-file-association-windows.reg`
44
+ - Linux: `scripts/bugproof-file-association-linux.sh`
45
+ - macOS: `scripts/bugproof-file-association-macos.sh`
46
+
47
+ ## Requirements
48
+
49
+ Required:
50
+ - Node.js 18+
51
+ - Git
52
+
53
+ Optional (only needed if your captured command uses them):
54
+ - Python / Java / GCC / G++ / Go / Rust toolchains
55
+
56
+ ## Quick Start
57
+
58
+ ### 1) Capture a failure
59
+
60
+ ```bash
61
+ bugproof capture -- npm test
62
+ ```
63
+
64
+ ### 2) Replay it anywhere
65
+
66
+ ```bash
67
+ bugproof replay bug_1778049738215.bug
68
+ ```
69
+
70
+ ### 3) Inspect artifact contents
71
+
72
+ ```bash
73
+ bugproof inspect bug_1778049738215.bug
74
+ ```
75
+
76
+ ### 4) Diff two artifacts
77
+
78
+ ```bash
79
+ bugproof diff old.bug new.bug
80
+ ```
81
+
82
+ ## CLI Help (Lists Everything)
83
+
84
+ Show full command list and global options:
85
+
86
+ ```bash
87
+ bugproof --help
88
+ ```
89
+
90
+ Show command-specific help:
91
+
92
+ ```bash
93
+ bugproof help capture
94
+ bugproof help replay
95
+ bugproof help inspect
96
+ bugproof help diff
97
+ ```
98
+
99
+ You can also use:
100
+
101
+ ```bash
102
+ bugproof capture --help
103
+ bugproof replay --help
104
+ bugproof inspect --help
105
+ bugproof diff --help
106
+ ```
107
+
108
+ ## Commands Reference
109
+
110
+ ### `bugproof capture [command...]`
111
+
112
+ Capture a command execution as a `.bug` artifact.
113
+
114
+ Examples:
115
+
116
+ ```bash
117
+ bugproof capture -- npm test
118
+ bugproof capture -n auth-crash -d "Login fails on expired session" -- node server.js
119
+ bugproof capture --include-untracked -- python app.py
120
+ bugproof capture -e "*.log" -e "*.tmp" -- go test ./...
121
+ bugproof capture --timeout 600000 -- java -cp . Main
122
+ bugproof capture --json -- node script.js
123
+ ```
124
+
125
+ Options:
126
+ - `--include-untracked` Include untracked files (`git ls-files -o`)
127
+ - `--skip-secrets` Skip environment secret scan
128
+ - `--timeout <ms>` Command timeout in milliseconds (default: `300000`)
129
+ - `-n, --name <name>` Human-readable artifact name
130
+ - `-d, --description <desc>` Bug description
131
+ - `-e, --exclude <pattern>` Exclude files matching pattern (repeatable)
132
+ - `--json` Structured JSON output
133
+
134
+ ### `bugproof replay <artifact>`
135
+
136
+ Replay a `.bug` artifact and compare failure signature.
137
+
138
+ Examples:
139
+
140
+ ```bash
141
+ bugproof replay my-bug.bug
142
+ bugproof replay --version-match strict my-bug.bug
143
+ bugproof replay --version-match branch my-bug.bug
144
+ bugproof replay --sandbox isolated my-bug.bug
145
+ bugproof replay --env API_URL=https://staging.local --env DEBUG=true my-bug.bug
146
+ bugproof replay --json my-bug.bug
147
+ ```
148
+
149
+ Options:
150
+ - `--version-match <mode>` `strict | current | branch` (default: `current`)
151
+ - `--sandbox <level>` `workspace | isolated | full` (default: `workspace`)
152
+ - `--env <var=value>` Override environment variable (repeatable)
153
+ - `--json` Structured JSON output
154
+
155
+ ### `bugproof inspect <artifact>`
156
+
157
+ Inspect artifact metadata and failure details without replaying.
158
+
159
+ Examples:
160
+
161
+ ```bash
162
+ bugproof inspect my-bug.bug
163
+ bugproof inspect --json my-bug.bug
164
+ ```
165
+
166
+ Options:
167
+ - `--json` Structured JSON output
168
+
169
+ ### `bugproof diff <left> <right>`
170
+
171
+ Compare two artifacts side-by-side.
172
+
173
+ Examples:
174
+
175
+ ```bash
176
+ bugproof diff bug-before.bug bug-after.bug
177
+ bugproof diff --json bug-before.bug bug-after.bug
178
+ ```
179
+
180
+ Options:
181
+ - `--json` Structured JSON output
182
+
183
+ ## File Association and Icon Registration
184
+
185
+ ### Windows
186
+
187
+ BugProof installer registers `.bug` under:
188
+ - `HKCU\\Software\\Classes\\.bug`
189
+ - `HKCU\\Software\\Classes\\BugProof.Artifact`
190
+
191
+ with open command pointing to:
192
+ - `node <package>/dist/cli.js replay "%1"`
193
+
194
+ ### Linux
195
+
196
+ BugProof installer registers:
197
+ - MIME type `application/x-bugproof`
198
+ - `bugproof.desktop` handler
199
+ - User-level icon entry in `~/.local/share/icons/hicolor/...`
200
+
201
+ ### macOS
202
+
203
+ Installer attempts registration via bundled script. If Finder association does not apply, run:
204
+
205
+ ```bash
206
+ bash scripts/bugproof-file-association-macos.sh
207
+ ```
208
+
209
+ ## Cross-Platform Replay Matrix
210
+
211
+ | Capture \ Replay | Windows | Linux | macOS |
212
+ |---|---|---|---|
213
+ | Windows | Yes | Yes | Yes |
214
+ | Linux | Yes | Yes | Yes |
215
+ | macOS | Yes | Yes | Yes |
216
+
217
+ Notes:
218
+ - Exit codes may differ by OS for signals/crashes.
219
+ - Fingerprint/error-pattern matching is used for reproduction verdict.
220
+
221
+ ## Legacy Notes (Kept from Old README)
222
+
223
+ These project principles remain unchanged:
224
+ - Security-first default behavior
225
+ - Language-agnostic command capture
226
+ - Minimal runtime dependencies
227
+ - Reproducibility over screenshots/log snippets
228
+
229
+ ## Roadmap
230
+
231
+ - [x] v0.1: CLI core (capture/replay/inspect/diff)
232
+ - [x] v0.1: Cross-platform replay support
233
+ - [x] v0.1: Secret redaction and sandbox layers
234
+ - [ ] v0.2: npm global install polish and Docker sandbox fallback
235
+ - [ ] v0.2: Web UI for artifact inspection
236
+ - [ ] v0.3: Artifact push/pull and signing
237
+ - [ ] v0.3: Language-specific dependency detection
238
+ - [ ] v0.4: GitHub issue integration and richer diff visualization
239
+
240
+ ## Development
241
+
242
+ ```bash
243
+ npm install
244
+ npm run build
245
+ npm test
246
+ ```
247
+
248
+ ## CI and Releases
249
+
250
+ - `push` and `pull_request` CI runs only for core paths such as `src/`, `scripts/`, `tests/`, `package.json`, `tsconfig.json`, and `assets/`.
251
+ - Docs-only edits like `README.md` do not start the CI workflow.
252
+ - Publishing to npmjs.com and GitHub Packages runs only on version tags like `v0.1.0`.
253
+
254
+ ## License
255
+
256
+ MIT
Binary file
Binary file
Binary file
@@ -0,0 +1,12 @@
1
+ import { RunConfig } from '../types/artifact.js';
2
+ import { FailureRecord } from '../types/failure.js';
3
+ /**
4
+ * Spawns the command, captures its output, and produces a FailureRecord.
5
+ * Uses streaming to handle large output without memory issues.
6
+ */
7
+ export declare function executeAndCapture(config: RunConfig): Promise<{
8
+ failure: FailureRecord;
9
+ stdout: string;
10
+ stderr: string;
11
+ }>;
12
+ //# sourceMappingURL=engine.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"engine.d.ts","sourceRoot":"","sources":["../../src/capture/engine.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AACjD,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAGpD;;;GAGG;AACH,wBAAsB,iBAAiB,CAAC,MAAM,EAAE,SAAS,GAAG,OAAO,CAAC;IAAE,OAAO,EAAE,aAAa,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC,CAwI9H"}
@@ -0,0 +1,129 @@
1
+ import { spawn } from 'child_process';
2
+ import { generateExactFingerprint, extractErrorPatterns } from '../utils/fingerprint.js';
3
+ /**
4
+ * Spawns the command, captures its output, and produces a FailureRecord.
5
+ * Uses streaming to handle large output without memory issues.
6
+ */
7
+ export async function executeAndCapture(config) {
8
+ return new Promise((resolve) => {
9
+ const startTime = Date.now();
10
+ // Define resolveExecutable first, before using it
11
+ const resolveExecutable = (cmd) => {
12
+ const normalized = cmd.trim().toLowerCase();
13
+ if (normalized === 'node' || normalized === 'node.exe') {
14
+ return process.execPath;
15
+ }
16
+ return cmd;
17
+ };
18
+ const command = resolveExecutable(config.command[0]);
19
+ const args = config.command.slice(1);
20
+ // We keep strings in memory for v0.1 (in v0.2 this would stream directly to disk)
21
+ let stdoutBuffer = '';
22
+ let stderrBuffer = '';
23
+ let stdoutLines = 0;
24
+ let stderrLines = 0;
25
+ // Safety limit to avoid OOM: 1MB per stream
26
+ const MAX_BUFFER_SIZE = 1024 * 1024;
27
+ let isTimeout = false;
28
+ let resolved = false;
29
+ let proc;
30
+ const safeResolve = (value) => {
31
+ if (resolved)
32
+ return;
33
+ resolved = true;
34
+ resolve(value);
35
+ };
36
+ try {
37
+ proc = spawn(command, args, {
38
+ cwd: config.working_directory,
39
+ env: config.environment,
40
+ shell: false // Prevent shell injection
41
+ });
42
+ }
43
+ catch (err) {
44
+ // Command not found or spawn error
45
+ const errStr = String(err);
46
+ safeResolve({
47
+ failure: {
48
+ exit_code: 1,
49
+ signal: null,
50
+ stdout_lines: 0,
51
+ stderr_lines: 1,
52
+ stderr_snippet: errStr,
53
+ fingerprint: generateExactFingerprint(errStr),
54
+ error_patterns: extractErrorPatterns(errStr),
55
+ duration_ms: Date.now() - startTime,
56
+ timeout: false
57
+ },
58
+ stdout: '',
59
+ stderr: errStr
60
+ });
61
+ return;
62
+ }
63
+ // Set timeout
64
+ const timeoutHandle = setTimeout(() => {
65
+ isTimeout = true;
66
+ proc.kill('SIGKILL');
67
+ }, config.timeout_ms);
68
+ if (config.capture_output) {
69
+ proc.stdout?.on('data', (data) => {
70
+ const chunk = data.toString();
71
+ stdoutLines += (chunk.match(/\n/g) || []).length;
72
+ if (stdoutBuffer.length < MAX_BUFFER_SIZE) {
73
+ stdoutBuffer += chunk;
74
+ }
75
+ });
76
+ proc.stderr?.on('data', (data) => {
77
+ const chunk = data.toString();
78
+ stderrLines += (chunk.match(/\n/g) || []).length;
79
+ if (stderrBuffer.length < MAX_BUFFER_SIZE) {
80
+ stderrBuffer += chunk;
81
+ }
82
+ });
83
+ }
84
+ proc.on('close', (code, signal) => {
85
+ clearTimeout(timeoutHandle);
86
+ const duration = Date.now() - startTime;
87
+ // Determine snippet (last 5 lines of stderr)
88
+ const lines = stderrBuffer.trim().split('\n');
89
+ const snippet = lines.slice(Math.max(0, lines.length - 5)).join('\n');
90
+ const failure = {
91
+ exit_code: code ?? 1,
92
+ signal: signal,
93
+ stdout_lines: stdoutLines,
94
+ stderr_lines: stderrLines,
95
+ stderr_snippet: snippet,
96
+ fingerprint: generateExactFingerprint(stderrBuffer),
97
+ error_patterns: extractErrorPatterns(stderrBuffer),
98
+ duration_ms: duration,
99
+ timeout: isTimeout
100
+ };
101
+ safeResolve({
102
+ failure,
103
+ stdout: stdoutBuffer,
104
+ stderr: stderrBuffer
105
+ });
106
+ });
107
+ proc.on('error', (err) => {
108
+ clearTimeout(timeoutHandle);
109
+ const errStr = String(err);
110
+ stderrBuffer += errStr;
111
+ safeResolve({
112
+ failure: {
113
+ exit_code: 1,
114
+ signal: null,
115
+ stdout_lines: stdoutLines,
116
+ stderr_lines: stderrLines + 1,
117
+ stderr_snippet: errStr,
118
+ fingerprint: generateExactFingerprint(errStr),
119
+ error_patterns: extractErrorPatterns(errStr),
120
+ duration_ms: Date.now() - startTime,
121
+ timeout: false
122
+ },
123
+ stdout: stdoutBuffer,
124
+ stderr: stderrBuffer
125
+ });
126
+ });
127
+ });
128
+ }
129
+ //# sourceMappingURL=engine.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"engine.js","sourceRoot":"","sources":["../../src/capture/engine.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,39 @@
1
+ import { ArtifactManifest, EnvSchema, RunConfig, ArtifactMetadata } from '../types/artifact.js';
2
+ import { FailureRecord } from '../types/failure.js';
3
+ export interface PackageOptions {
4
+ manifest: ArtifactManifest;
5
+ envSchema: EnvSchema;
6
+ runConfig: RunConfig;
7
+ metadata: ArtifactMetadata;
8
+ failure: FailureRecord;
9
+ stdout: string;
10
+ stderr: string;
11
+ secretKeys: string[];
12
+ includeUntracked?: boolean;
13
+ excludePatterns?: string[];
14
+ }
15
+ export interface FileEntry {
16
+ path: string;
17
+ size: number;
18
+ sha256: string;
19
+ }
20
+ /**
21
+ * Packages the artifact into the .bug directory format specified in DESIGN.md.
22
+ *
23
+ * Directory layout:
24
+ * manifest.json
25
+ * env.schema.json
26
+ * metadata.json
27
+ * run.json (environment secrets stripped)
28
+ * failure.json
29
+ * logs/stdout.txt
30
+ * logs/stderr.txt
31
+ * logs/fingerprint.json
32
+ * files/... (git-tracked source snapshot)
33
+ */
34
+ export declare function packageArtifact(artifactPath: string, options: PackageOptions): Promise<{
35
+ filesCount: number;
36
+ totalSize: number;
37
+ fileEntries: FileEntry[];
38
+ }>;
39
+ //# sourceMappingURL=packager.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"packager.d.ts","sourceRoot":"","sources":["../../src/capture/packager.ts"],"names":[],"mappings":"AAQA,OAAO,EAAE,gBAAgB,EAAE,SAAS,EAAE,SAAS,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAChG,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAEpD,MAAM,WAAW,cAAc;IAC7B,QAAQ,EAAE,gBAAgB,CAAC;IAC3B,SAAS,EAAE,SAAS,CAAC;IACrB,SAAS,EAAE,SAAS,CAAC;IACrB,QAAQ,EAAE,gBAAgB,CAAC;IAC3B,OAAO,EAAE,aAAa,CAAC;IACvB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;CAC5B;AAED,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;CAChB;AAKD;;;;;;;;;;;;;GAaG;AACH,wBAAsB,eAAe,CACnC,YAAY,EAAE,MAAM,EACpB,OAAO,EAAE,cAAc,GACtB,OAAO,CAAC;IAAE,UAAU,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,SAAS,EAAE,CAAA;CAAE,CAAC,CA6E9E"}
@@ -0,0 +1,145 @@
1
+ import * as fs from 'fs';
2
+ import * as path from 'path';
3
+ import * as crypto from 'crypto';
4
+ import * as os from 'os';
5
+ import { spawnSync } from 'child_process';
6
+ import { zipDirectory } from '../utils/archive.js';
7
+ import { filterByExcludePatterns } from '../utils/exclude.js';
8
+ import { isPathWithinBoundary } from '../utils/security.js';
9
+ const MAX_ARTIFACT_SIZE = 50 * 1024 * 1024; // 50MB hard limit per DESIGN.md
10
+ const WARN_THRESHOLD = 10 * 1024 * 1024; // 10MB warning per DESIGN.md
11
+ /**
12
+ * Packages the artifact into the .bug directory format specified in DESIGN.md.
13
+ *
14
+ * Directory layout:
15
+ * manifest.json
16
+ * env.schema.json
17
+ * metadata.json
18
+ * run.json (environment secrets stripped)
19
+ * failure.json
20
+ * logs/stdout.txt
21
+ * logs/stderr.txt
22
+ * logs/fingerprint.json
23
+ * files/... (git-tracked source snapshot)
24
+ */
25
+ export async function packageArtifact(artifactPath, options) {
26
+ // 1. Create a temporary staging directory
27
+ const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'bugproof-pkg-'));
28
+ try {
29
+ // 2. Copy source files first so we can compute counts and checksums
30
+ const filesDir = path.join(tempDir, 'files');
31
+ fs.mkdirSync(filesDir, { recursive: true });
32
+ const fileEntries = copySourceFiles(filesDir, options.runConfig.working_directory, options.includeUntracked ?? false, options.excludePatterns ?? []);
33
+ const totalSize = fileEntries.reduce((sum, f) => sum + f.size, 0);
34
+ // 3. Create manifest with actual file stats (immutable: don't mutate input)
35
+ const manifestWithStats = {
36
+ ...options.manifest,
37
+ files_count: fileEntries.length,
38
+ files_size_bytes: totalSize,
39
+ };
40
+ // 4. Build a sanitized RunConfig (strip secret values from environment)
41
+ const sanitizedEnv = {};
42
+ for (const [key, val] of Object.entries(options.runConfig.environment)) {
43
+ if (options.secretKeys.includes(key)) {
44
+ sanitizedEnv[key] = '<REDACTED>';
45
+ }
46
+ else {
47
+ sanitizedEnv[key] = val;
48
+ }
49
+ }
50
+ const safeRunConfig = { ...options.runConfig, environment: sanitizedEnv };
51
+ // 5. Write JSON schema files
52
+ fs.writeFileSync(path.join(tempDir, 'manifest.json'), JSON.stringify(manifestWithStats, null, 2));
53
+ fs.writeFileSync(path.join(tempDir, 'env.schema.json'), JSON.stringify(options.envSchema, null, 2));
54
+ fs.writeFileSync(path.join(tempDir, 'metadata.json'), JSON.stringify(options.metadata, null, 2));
55
+ fs.writeFileSync(path.join(tempDir, 'run.json'), JSON.stringify(safeRunConfig, null, 2));
56
+ fs.writeFileSync(path.join(tempDir, 'failure.json'), JSON.stringify(options.failure, null, 2));
57
+ // 6. Write file manifest with checksums
58
+ fs.writeFileSync(path.join(tempDir, 'files.json'), JSON.stringify(fileEntries, null, 2));
59
+ // 7. Write logs
60
+ const logsDir = path.join(tempDir, 'logs');
61
+ fs.mkdirSync(logsDir, { recursive: true });
62
+ fs.writeFileSync(path.join(logsDir, 'stdout.txt'), options.stdout);
63
+ fs.writeFileSync(path.join(logsDir, 'stderr.txt'), options.stderr);
64
+ fs.writeFileSync(path.join(logsDir, 'fingerprint.json'), JSON.stringify({
65
+ fingerprint: options.failure.fingerprint,
66
+ error_patterns: options.failure.error_patterns,
67
+ }, null, 2));
68
+ // 8. Compress the temporary directory into the final .bug zip archive
69
+ await zipDirectory(tempDir, artifactPath);
70
+ return { filesCount: fileEntries.length, totalSize, fileEntries };
71
+ }
72
+ catch (err) {
73
+ // Cleanup incomplete artifact on failure
74
+ if (fs.existsSync(artifactPath)) {
75
+ fs.rmSync(artifactPath, { force: true });
76
+ }
77
+ throw err;
78
+ }
79
+ finally {
80
+ // Always clean up the temporary staging directory
81
+ fs.rmSync(tempDir, { recursive: true, force: true });
82
+ }
83
+ }
84
+ /**
85
+ * Copy git-tracked files into the artifact, computing SHA-256 checksums per file.
86
+ */
87
+ function copySourceFiles(filesDir, workingDir, includeUntracked, excludePatterns = []) {
88
+ const gitArgs = ['ls-files'];
89
+ if (includeUntracked) {
90
+ gitArgs.push('-o', '--exclude-standard');
91
+ }
92
+ const result = spawnSync('git', gitArgs, { cwd: workingDir, encoding: 'utf-8' });
93
+ if (result.status !== 0) {
94
+ throw new Error(`git ls-files failed: ${result.stderr}`);
95
+ }
96
+ let relativePaths = result.stdout
97
+ .split('\n')
98
+ .map((f) => f.trim())
99
+ .filter((f) => f.length > 0);
100
+ // Apply --exclude patterns
101
+ if (excludePatterns.length > 0) {
102
+ relativePaths = filterByExcludePatterns(relativePaths, excludePatterns);
103
+ }
104
+ const entries = [];
105
+ let runningSize = 0;
106
+ for (const relPath of relativePaths) {
107
+ const sourcePath = path.join(workingDir, relPath);
108
+ if (!fs.existsSync(sourcePath))
109
+ continue;
110
+ // Security: validate path stays within boundaries
111
+ if (!isPathWithinBoundary(sourcePath, workingDir)) {
112
+ process.stderr.write(` Skipping ${relPath}: path traversal detected\n`);
113
+ continue;
114
+ }
115
+ const stats = fs.statSync(sourcePath);
116
+ if (!stats.isFile())
117
+ continue;
118
+ runningSize += stats.size;
119
+ if (runningSize > MAX_ARTIFACT_SIZE) {
120
+ throw new Error(`Artifact would exceed the 50 MB limit (currently ${(runningSize / 1024 / 1024).toFixed(1)} MB). ` +
121
+ 'Add large files to .gitignore or use --exclude patterns.');
122
+ }
123
+ if (runningSize > WARN_THRESHOLD && entries.length > 0 && entries.length % 50 === 0) {
124
+ process.stderr.write(` Warning: artifact is ${(runningSize / 1024 / 1024).toFixed(1)} MB and growing...\n`);
125
+ }
126
+ const targetPath = path.join(filesDir, relPath);
127
+ // Security: validate target path stays within artifact boundary
128
+ if (!isPathWithinBoundary(targetPath, filesDir)) {
129
+ process.stderr.write(` Skipping ${relPath}: target path escapes artifact\n`);
130
+ continue;
131
+ }
132
+ fs.mkdirSync(path.dirname(targetPath), { recursive: true });
133
+ fs.copyFileSync(sourcePath, targetPath);
134
+ // Compute SHA-256 checksum
135
+ const fileBuffer = fs.readFileSync(sourcePath);
136
+ const hash = crypto.createHash('sha256').update(fileBuffer).digest('hex');
137
+ entries.push({
138
+ path: relPath.replace(/\\/g, '/'), // normalize to forward slashes
139
+ size: stats.size,
140
+ sha256: hash,
141
+ });
142
+ }
143
+ return entries;
144
+ }
145
+ //# sourceMappingURL=packager.js.map