repro-surgeon 0.1.0
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 +15 -0
- package/CODE_OF_CONDUCT.md +7 -0
- package/CONTRIBUTING.md +31 -0
- package/LICENSE +21 -0
- package/README.md +159 -0
- package/SECURITY.md +15 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.js +165 -0
- package/dist/cli.js.map +1 -0
- package/dist/config.d.ts +3 -0
- package/dist/config.js +177 -0
- package/dist/config.js.map +1 -0
- package/dist/dependencies.d.ts +37 -0
- package/dist/dependencies.js +237 -0
- package/dist/dependencies.js.map +1 -0
- package/dist/engine.d.ts +25 -0
- package/dist/engine.js +327 -0
- package/dist/engine.js.map +1 -0
- package/dist/export.d.ts +7 -0
- package/dist/export.js +520 -0
- package/dist/export.js.map +1 -0
- package/dist/graph.d.ts +3 -0
- package/dist/graph.js +247 -0
- package/dist/graph.js.map +1 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.js +7 -0
- package/dist/index.js.map +1 -0
- package/dist/oracle.d.ts +2 -0
- package/dist/oracle.js +81 -0
- package/dist/oracle.js.map +1 -0
- package/dist/protection.d.ts +2 -0
- package/dist/protection.js +6 -0
- package/dist/protection.js.map +1 -0
- package/dist/report.d.ts +5 -0
- package/dist/report.js +539 -0
- package/dist/report.js.map +1 -0
- package/dist/runner.d.ts +3 -0
- package/dist/runner.js +195 -0
- package/dist/runner.js.map +1 -0
- package/dist/snapshot.d.ts +11 -0
- package/dist/snapshot.js +220 -0
- package/dist/snapshot.js.map +1 -0
- package/dist/state.d.ts +9 -0
- package/dist/state.js +143 -0
- package/dist/state.js.map +1 -0
- package/dist/transforms.d.ts +3 -0
- package/dist/transforms.js +188 -0
- package/dist/transforms.js.map +1 -0
- package/dist/types.d.ts +207 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/docs/architecture.md +91 -0
- package/docs/assets/report.png +0 -0
- package/docs/configuration.md +73 -0
- package/docs/evidence/dependency-removal.json +109 -0
- package/docs/evidence/next-frameworks.json +271 -0
- package/docs/evidence/next-pages.json +152 -0
- package/docs/evidence/rounding.json +46 -0
- package/docs/report-surface.md +22 -0
- package/docs/troubleshooting.md +47 -0
- package/docs/validation.md +103 -0
- package/examples/next-container/README.md +7 -0
- package/examples/next-container/app/about/page.jsx +1 -0
- package/examples/next-container/app/case.css +5 -0
- package/examples/next-container/app/layout.jsx +3 -0
- package/examples/next-container/app/page.jsx +4 -0
- package/examples/next-container/lib/catalog.js +2 -0
- package/examples/next-container/next.config.mjs +1 -0
- package/examples/next-container/package-lock.json +952 -0
- package/examples/next-container/package.json +13 -0
- package/examples/next-container/repro-surgeon.json +37 -0
- package/examples/next-css-bom/README.md +7 -0
- package/examples/next-css-bom/app/about/page.jsx +1 -0
- package/examples/next-css-bom/app/case.css +3 -0
- package/examples/next-css-bom/app/layout.jsx +3 -0
- package/examples/next-css-bom/app/page.jsx +4 -0
- package/examples/next-css-bom/lib/catalog.js +2 -0
- package/examples/next-css-bom/next.config.mjs +1 -0
- package/examples/next-css-bom/package-lock.json +952 -0
- package/examples/next-css-bom/package.json +13 -0
- package/examples/next-css-bom/repro-surgeon.json +36 -0
- package/examples/next-pages/README.md +14 -0
- package/examples/next-pages/lib/unused-catalog.js +5 -0
- package/examples/next-pages/next.config.mjs +1 -0
- package/examples/next-pages/package-lock.json +952 -0
- package/examples/next-pages/package.json +11 -0
- package/examples/next-pages/pages/health.jsx +3 -0
- package/examples/next-pages/pages/index.jsx +9 -0
- package/examples/next-pages/repro-surgeon.json +18 -0
- package/examples/next-root-param/README.md +7 -0
- package/examples/next-root-param/app/[lang-country]/about/page.jsx +1 -0
- package/examples/next-root-param/app/[lang-country]/layout.tsx +4 -0
- package/examples/next-root-param/app/[lang-country]/page.tsx +3 -0
- package/examples/next-root-param/lib/catalog.js +2 -0
- package/examples/next-root-param/next-env.d.ts +2 -0
- package/examples/next-root-param/next.config.mjs +1 -0
- package/examples/next-root-param/package-lock.json +1016 -0
- package/examples/next-root-param/package.json +19 -0
- package/examples/next-root-param/repro-surgeon.json +37 -0
- package/examples/next-root-param/tsconfig.json +35 -0
- package/examples/rounding/README.md +16 -0
- package/examples/rounding/check.mjs +6 -0
- package/examples/rounding/config/store.json +7 -0
- package/examples/rounding/fixtures/invoice.json +11 -0
- package/examples/rounding/package-lock.json +7 -0
- package/examples/rounding/package.json +7 -0
- package/examples/rounding/repro-surgeon.json +12 -0
- package/examples/rounding/src/catalog.mjs +9 -0
- package/examples/rounding/src/shipping.mjs +7 -0
- package/examples/rounding/src/totals.mjs +11 -0
- package/package.json +29 -0
package/dist/runner.js
ADDED
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
import { spawn } from 'node:child_process';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
import { performance } from 'node:perf_hooks';
|
|
4
|
+
const ESSENTIAL_ENVIRONMENT = [
|
|
5
|
+
'PATH',
|
|
6
|
+
'TMPDIR',
|
|
7
|
+
'TMP',
|
|
8
|
+
'TEMP',
|
|
9
|
+
'LANG',
|
|
10
|
+
'LC_ALL',
|
|
11
|
+
'SystemRoot',
|
|
12
|
+
'ComSpec',
|
|
13
|
+
'PATHEXT',
|
|
14
|
+
];
|
|
15
|
+
const PROTECTED_ENVIRONMENT = new Set([
|
|
16
|
+
'HOME',
|
|
17
|
+
'USERPROFILE',
|
|
18
|
+
'XDG_CONFIG_HOME',
|
|
19
|
+
'XDG_CACHE_HOME',
|
|
20
|
+
'XDG_DATA_HOME',
|
|
21
|
+
'NPM_CONFIG_USERCONFIG',
|
|
22
|
+
'NPM_CONFIG_CACHE',
|
|
23
|
+
]);
|
|
24
|
+
export function cleanEnvironment(home, names) {
|
|
25
|
+
const environment = {};
|
|
26
|
+
for (const name of [...ESSENTIAL_ENVIRONMENT, ...names]) {
|
|
27
|
+
if (PROTECTED_ENVIRONMENT.has(name))
|
|
28
|
+
continue;
|
|
29
|
+
const value = process.env[name];
|
|
30
|
+
if (value !== undefined)
|
|
31
|
+
environment[name] = value;
|
|
32
|
+
}
|
|
33
|
+
environment.HOME = home;
|
|
34
|
+
environment.USERPROFILE = home;
|
|
35
|
+
environment.XDG_CONFIG_HOME = join(home, '.config');
|
|
36
|
+
environment.XDG_CACHE_HOME = join(home, '.cache');
|
|
37
|
+
environment.XDG_DATA_HOME = join(home, '.local', 'share');
|
|
38
|
+
environment.NPM_CONFIG_USERCONFIG = join(home, '.npmrc');
|
|
39
|
+
environment.NPM_CONFIG_CACHE = join(home, '.npm-cache');
|
|
40
|
+
return environment;
|
|
41
|
+
}
|
|
42
|
+
function emptyResult(startedAt, flags = {}) {
|
|
43
|
+
return {
|
|
44
|
+
exitCode: null,
|
|
45
|
+
signal: null,
|
|
46
|
+
timedOut: false,
|
|
47
|
+
aborted: false,
|
|
48
|
+
outputLimitExceeded: false,
|
|
49
|
+
stdout: '',
|
|
50
|
+
stderr: '',
|
|
51
|
+
durationMs: Math.max(0, performance.now() - startedAt),
|
|
52
|
+
...flags,
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
function groupExists(pid) {
|
|
56
|
+
try {
|
|
57
|
+
process.kill(-pid, 0);
|
|
58
|
+
return true;
|
|
59
|
+
}
|
|
60
|
+
catch {
|
|
61
|
+
return false;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
function signalProcessTree(child, signal) {
|
|
65
|
+
if (child.pid === undefined)
|
|
66
|
+
return;
|
|
67
|
+
try {
|
|
68
|
+
if (process.platform === 'win32')
|
|
69
|
+
child.kill(signal);
|
|
70
|
+
else
|
|
71
|
+
process.kill(-child.pid, signal);
|
|
72
|
+
}
|
|
73
|
+
catch {
|
|
74
|
+
// The process tree may have already exited.
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
async function cleanRemainingProcessGroup(child) {
|
|
78
|
+
if (child.pid === undefined || process.platform === 'win32' || !groupExists(child.pid))
|
|
79
|
+
return;
|
|
80
|
+
signalProcessTree(child, 'SIGTERM');
|
|
81
|
+
await new Promise((resolve) => setTimeout(resolve, 50));
|
|
82
|
+
if (groupExists(child.pid))
|
|
83
|
+
signalProcessTree(child, 'SIGKILL');
|
|
84
|
+
}
|
|
85
|
+
export async function runCommand(command, options) {
|
|
86
|
+
const startedAt = performance.now();
|
|
87
|
+
if (options.signal?.aborted === true) {
|
|
88
|
+
return emptyResult(startedAt, { aborted: true, error: 'Command was aborted before launch' });
|
|
89
|
+
}
|
|
90
|
+
if (command.length === 0
|
|
91
|
+
|| command[0]?.trim().length === 0
|
|
92
|
+
|| command.some((part) => typeof part !== 'string' || part.includes('\0'))) {
|
|
93
|
+
return emptyResult(startedAt, { error: 'Command must contain a non-empty executable and argv strings without NUL bytes' });
|
|
94
|
+
}
|
|
95
|
+
return await new Promise((resolve) => {
|
|
96
|
+
const stdout = [];
|
|
97
|
+
const stderr = [];
|
|
98
|
+
let retainedBytes = 0;
|
|
99
|
+
let timedOut = false;
|
|
100
|
+
let aborted = false;
|
|
101
|
+
let outputLimitExceeded = false;
|
|
102
|
+
let commandError;
|
|
103
|
+
let settled = false;
|
|
104
|
+
let forceKillTimer;
|
|
105
|
+
let drainTimer;
|
|
106
|
+
let cleanup;
|
|
107
|
+
let termination;
|
|
108
|
+
const executable = command[0];
|
|
109
|
+
const child = spawn(executable, command.slice(1), {
|
|
110
|
+
cwd: options.cwd,
|
|
111
|
+
env: options.env,
|
|
112
|
+
shell: false,
|
|
113
|
+
detached: process.platform !== 'win32',
|
|
114
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
115
|
+
});
|
|
116
|
+
const requestStop = () => {
|
|
117
|
+
signalProcessTree(child, 'SIGTERM');
|
|
118
|
+
forceKillTimer ??= setTimeout(() => signalProcessTree(child, 'SIGKILL'), 100);
|
|
119
|
+
};
|
|
120
|
+
const retain = (destination, chunk) => {
|
|
121
|
+
const data = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
|
|
122
|
+
const remaining = Math.max(0, options.maxOutputBytes - retainedBytes);
|
|
123
|
+
if (remaining > 0) {
|
|
124
|
+
const saved = data.length <= remaining ? data : data.subarray(0, remaining);
|
|
125
|
+
destination.push(saved);
|
|
126
|
+
retainedBytes += saved.length;
|
|
127
|
+
}
|
|
128
|
+
if (data.length > remaining && !outputLimitExceeded) {
|
|
129
|
+
outputLimitExceeded = true;
|
|
130
|
+
requestStop();
|
|
131
|
+
}
|
|
132
|
+
};
|
|
133
|
+
child.stdout?.on('data', (chunk) => retain(stdout, chunk));
|
|
134
|
+
child.stderr?.on('data', (chunk) => retain(stderr, chunk));
|
|
135
|
+
const timeout = setTimeout(() => {
|
|
136
|
+
timedOut = true;
|
|
137
|
+
requestStop();
|
|
138
|
+
}, options.timeoutMs);
|
|
139
|
+
const onAbort = () => {
|
|
140
|
+
aborted = true;
|
|
141
|
+
requestStop();
|
|
142
|
+
};
|
|
143
|
+
options.signal?.addEventListener('abort', onAbort, { once: true });
|
|
144
|
+
const beginCleanup = () => {
|
|
145
|
+
clearTimeout(timeout);
|
|
146
|
+
options.signal?.removeEventListener('abort', onAbort);
|
|
147
|
+
cleanup ??= cleanRemainingProcessGroup(child);
|
|
148
|
+
return cleanup;
|
|
149
|
+
};
|
|
150
|
+
const finish = async (exitCode, signal) => {
|
|
151
|
+
if (settled)
|
|
152
|
+
return;
|
|
153
|
+
settled = true;
|
|
154
|
+
if (drainTimer !== undefined)
|
|
155
|
+
clearTimeout(drainTimer);
|
|
156
|
+
await beginCleanup();
|
|
157
|
+
if (forceKillTimer !== undefined)
|
|
158
|
+
clearTimeout(forceKillTimer);
|
|
159
|
+
const invalidated = timedOut || aborted || outputLimitExceeded || commandError !== undefined;
|
|
160
|
+
const result = {
|
|
161
|
+
exitCode: invalidated ? null : (termination?.exitCode ?? exitCode),
|
|
162
|
+
signal: termination?.signal ?? signal,
|
|
163
|
+
timedOut,
|
|
164
|
+
aborted,
|
|
165
|
+
outputLimitExceeded,
|
|
166
|
+
stdout: Buffer.concat(stdout).toString('utf8'),
|
|
167
|
+
stderr: Buffer.concat(stderr).toString('utf8'),
|
|
168
|
+
durationMs: Math.max(0, performance.now() - startedAt),
|
|
169
|
+
};
|
|
170
|
+
if (commandError !== undefined)
|
|
171
|
+
result.error = commandError;
|
|
172
|
+
resolve(result);
|
|
173
|
+
};
|
|
174
|
+
child.once('error', (error) => {
|
|
175
|
+
commandError = error.message;
|
|
176
|
+
setImmediate(() => void finish(null, null));
|
|
177
|
+
});
|
|
178
|
+
child.once('exit', (exitCode, signal) => {
|
|
179
|
+
termination = { exitCode, signal };
|
|
180
|
+
// Descendants may retain the output pipes after the direct child exits.
|
|
181
|
+
// Execution is over; stop its timer and clean the group before close.
|
|
182
|
+
void beginCleanup().then(() => {
|
|
183
|
+
if (settled)
|
|
184
|
+
return;
|
|
185
|
+
drainTimer = setTimeout(() => {
|
|
186
|
+
commandError ??= 'Command output pipes did not close after process cleanup';
|
|
187
|
+
child.stdout?.destroy();
|
|
188
|
+
child.stderr?.destroy();
|
|
189
|
+
}, 250);
|
|
190
|
+
});
|
|
191
|
+
});
|
|
192
|
+
child.once('close', (exitCode, signal) => void finish(exitCode, signal));
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
//# sourceMappingURL=runner.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runner.js","sourceRoot":"","sources":["../src/runner.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { Config, Inventory, Metrics, ReviewFinding, Snapshot, Transformation } from './types.ts';
|
|
2
|
+
export declare function safeRelativePath(value: string): void;
|
|
3
|
+
export declare function matchesPath(name: string, patterns: string[]): boolean;
|
|
4
|
+
export declare function assertDisjointPaths(source: string, destination: string): Promise<void>;
|
|
5
|
+
export declare function inventoryProject(root: string, config: Pick<Config, 'include' | 'exclude'>, maxBytes?: number): Promise<Inventory>;
|
|
6
|
+
export declare function writeSnapshot(snapshot: Snapshot, root: string): Promise<void>;
|
|
7
|
+
export declare function snapshotHash(snapshot: Snapshot): string;
|
|
8
|
+
export declare function hashValue(value: unknown): string;
|
|
9
|
+
export declare function metrics(snapshot: Snapshot): Metrics;
|
|
10
|
+
export declare function applyTransformation(snapshot: Snapshot, transformation: Transformation): Snapshot;
|
|
11
|
+
export declare function scanForReview(snapshot: Snapshot): ReviewFinding[];
|
package/dist/snapshot.js
ADDED
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
import { lstat, mkdir, readdir, readFile, realpath, writeFile, chmod } from 'node:fs/promises';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import ignore from 'ignore';
|
|
5
|
+
import picomatch from 'picomatch';
|
|
6
|
+
const generated = new Set(['.git', 'node_modules', '.next', '.nuxt', '.turbo', '.cache', '.vercel', '.repro', '.repro-surgeon', '.local', '.superpowers', 'dist', 'out', 'build', 'coverage']);
|
|
7
|
+
const credentials = /^(?:\.env(?:\..*)?|\.npmrc|\.yarnrc(?:\.yml)?|\.netrc|\.ssh|\.aws|\.azure|\.config|id_rsa.*|id_ed25519.*|credentials(?:\.json)?|.*\.(?:pem|key|p12|pfx))$/i;
|
|
8
|
+
const locks = new Set(['package-lock.json', 'npm-shrinkwrap.json']);
|
|
9
|
+
export function safeRelativePath(value) {
|
|
10
|
+
if (!value || value.includes('\0') || value.includes('\\') || path.posix.isAbsolute(value) || /^[A-Za-z]:/.test(value) || value.split('/').some(part => !part || part === '.' || part === '..')) {
|
|
11
|
+
throw new Error(`Unsafe relative path: ${JSON.stringify(value)}`);
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
export function matchesPath(name, patterns) {
|
|
15
|
+
return patterns.some(pattern => name === pattern || name.startsWith(pattern.replace(/\/$/, '') + '/') || picomatch(pattern, { dot: true })(name));
|
|
16
|
+
}
|
|
17
|
+
async function canonicalFuturePath(input) {
|
|
18
|
+
const absolute = path.resolve(input);
|
|
19
|
+
try {
|
|
20
|
+
return await realpath(absolute);
|
|
21
|
+
}
|
|
22
|
+
catch (error) {
|
|
23
|
+
if (error.code !== 'ENOENT')
|
|
24
|
+
throw error;
|
|
25
|
+
const parent = path.dirname(absolute);
|
|
26
|
+
if (parent === absolute)
|
|
27
|
+
throw error;
|
|
28
|
+
return path.join(await canonicalFuturePath(parent), path.basename(absolute));
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
export async function assertDisjointPaths(source, destination) {
|
|
32
|
+
const [a, b] = await Promise.all([canonicalFuturePath(source), canonicalFuturePath(destination)]);
|
|
33
|
+
if (a === b || a.startsWith(b + path.sep) || b.startsWith(a + path.sep)) {
|
|
34
|
+
throw new Error('Source and output directories overlap. Choose an output beside the project, outside its tree.');
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
function addIgnoreRules(matcher, relative, contents) {
|
|
38
|
+
if (!relative) {
|
|
39
|
+
matcher.add(contents);
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
const prefix = relative.replace(/[\\*?\[\]]/g, '\\$&') + '/';
|
|
43
|
+
for (const line of contents.replace(/^\uFEFF/, '').split(/\r?\n/)) {
|
|
44
|
+
if (/^ *$/.test(line) || line.startsWith('#'))
|
|
45
|
+
continue;
|
|
46
|
+
const negated = line.startsWith('!');
|
|
47
|
+
const pattern = negated ? line.slice(1) : line;
|
|
48
|
+
if (/^ *$/.test(pattern) || pattern === '/')
|
|
49
|
+
continue;
|
|
50
|
+
// A slash before the optional directory suffix anchors a pattern to its
|
|
51
|
+
// .gitignore directory. Basename patterns also apply in its descendants.
|
|
52
|
+
const anchored = pattern.replace(/ +$/, '').replace(/\/$/, '').includes('/');
|
|
53
|
+
const scoped = pattern.startsWith('/') ? pattern.slice(1) : pattern;
|
|
54
|
+
matcher.add(`${negated ? '!' : ''}/${prefix}${anchored ? '' : '**/'}${scoped}`);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
export async function inventoryProject(root, config, maxBytes = 128 * 1024 * 1024) {
|
|
58
|
+
const absolute = await realpath(root);
|
|
59
|
+
const result = { snapshot: new Map(), excluded: [], warnings: [] };
|
|
60
|
+
let bytes = 0;
|
|
61
|
+
const visit = async (relative, inherited) => {
|
|
62
|
+
const full = path.join(absolute, relative);
|
|
63
|
+
const rules = ignore().add(inherited);
|
|
64
|
+
try {
|
|
65
|
+
const ignorePath = path.join(full, '.gitignore');
|
|
66
|
+
const info = await lstat(ignorePath);
|
|
67
|
+
if (info.isFile())
|
|
68
|
+
addIgnoreRules(rules, relative, await readFile(ignorePath, 'utf8'));
|
|
69
|
+
}
|
|
70
|
+
catch (error) {
|
|
71
|
+
if (error.code !== 'ENOENT')
|
|
72
|
+
throw error;
|
|
73
|
+
}
|
|
74
|
+
const entries = (await readdir(full, { withFileTypes: true })).sort((a, b) => a.name.localeCompare(b.name, 'en'));
|
|
75
|
+
for (const entry of entries) {
|
|
76
|
+
const name = relative ? `${relative}/${entry.name}` : entry.name;
|
|
77
|
+
safeRelativePath(name);
|
|
78
|
+
let reason;
|
|
79
|
+
if (entry.isSymbolicLink())
|
|
80
|
+
reason = 'Symbolic link excluded; target was not read';
|
|
81
|
+
else if (generated.has(entry.name))
|
|
82
|
+
reason = 'Generated or internal directory';
|
|
83
|
+
else if (credentials.test(entry.name))
|
|
84
|
+
reason = 'Credential or environment file';
|
|
85
|
+
else if (entry.name === '.DS_Store')
|
|
86
|
+
reason = 'Operating-system metadata';
|
|
87
|
+
else if (matchesPath(name, config.exclude))
|
|
88
|
+
reason = 'Explicit exclude rule';
|
|
89
|
+
else if (!matchesPath(name, config.include)) {
|
|
90
|
+
if (rules.ignores(name + (entry.isDirectory() ? '/' : '')))
|
|
91
|
+
reason = 'Git ignore rule';
|
|
92
|
+
}
|
|
93
|
+
if (reason) {
|
|
94
|
+
result.excluded.push({ path: name, reason });
|
|
95
|
+
// Explicit inclusion can reach a file inside an ignored directory, but never a hard exclusion.
|
|
96
|
+
if (entry.isDirectory() && reason === 'Git ignore rule' && config.include.some(p => p.startsWith(name + '/') || p.includes('*')))
|
|
97
|
+
await visit(name, rules);
|
|
98
|
+
continue;
|
|
99
|
+
}
|
|
100
|
+
if (entry.isDirectory())
|
|
101
|
+
await visit(name, rules);
|
|
102
|
+
else if (entry.isFile()) {
|
|
103
|
+
const filePath = path.join(absolute, name);
|
|
104
|
+
const before = await lstat(filePath);
|
|
105
|
+
if (!before.isFile())
|
|
106
|
+
throw new Error(`Source changed while scanning: ${name}`);
|
|
107
|
+
if (bytes + before.size > maxBytes)
|
|
108
|
+
throw new Error(`Source snapshot exceeds ${maxBytes} bytes. Exclude large fixtures or generated files.`);
|
|
109
|
+
const content = await readFile(filePath);
|
|
110
|
+
const after = await lstat(filePath);
|
|
111
|
+
if (!after.isFile() || before.ino !== after.ino || before.mtimeMs !== after.mtimeMs || before.size !== content.length)
|
|
112
|
+
throw new Error(`Source changed while scanning: ${name}`);
|
|
113
|
+
bytes += content.length;
|
|
114
|
+
result.snapshot.set(name, { content, mode: after.mode & 0o777 });
|
|
115
|
+
}
|
|
116
|
+
else
|
|
117
|
+
result.excluded.push({ path: name, reason: 'Non-regular file' });
|
|
118
|
+
}
|
|
119
|
+
};
|
|
120
|
+
await visit('', ignore());
|
|
121
|
+
if (!result.snapshot.has('package.json'))
|
|
122
|
+
throw new Error('No package.json was included. Choose the root of a single npm package.');
|
|
123
|
+
if (result.excluded.some(x => /Symbolic/.test(x.reason)))
|
|
124
|
+
result.warnings.push('Symbolic links were excluded. A failure depending on them may not reproduce.');
|
|
125
|
+
return result;
|
|
126
|
+
}
|
|
127
|
+
async function ensureRealDirectory(directory) {
|
|
128
|
+
try {
|
|
129
|
+
const info = await lstat(directory);
|
|
130
|
+
if (info.isSymbolicLink() || !info.isDirectory())
|
|
131
|
+
throw new Error(`Refusing a symbolic link or non-directory destination: ${directory}`);
|
|
132
|
+
}
|
|
133
|
+
catch (error) {
|
|
134
|
+
if (error.code !== 'ENOENT')
|
|
135
|
+
throw error;
|
|
136
|
+
const parent = path.dirname(directory);
|
|
137
|
+
if (parent !== directory)
|
|
138
|
+
await ensureRealDirectory(parent);
|
|
139
|
+
await mkdir(directory);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
export async function writeSnapshot(snapshot, root) {
|
|
143
|
+
const absolute = path.resolve(root);
|
|
144
|
+
await ensureRealDirectory(absolute);
|
|
145
|
+
for (const [name, file] of [...snapshot].sort(([a], [b]) => a.localeCompare(b, 'en'))) {
|
|
146
|
+
safeRelativePath(name);
|
|
147
|
+
const parts = name.split('/');
|
|
148
|
+
let directory = absolute;
|
|
149
|
+
for (const segment of parts.slice(0, -1)) {
|
|
150
|
+
directory = path.join(directory, segment);
|
|
151
|
+
await ensureRealDirectory(directory);
|
|
152
|
+
}
|
|
153
|
+
const destination = path.join(absolute, name);
|
|
154
|
+
try {
|
|
155
|
+
const info = await lstat(destination);
|
|
156
|
+
if (!info.isFile() || info.isSymbolicLink())
|
|
157
|
+
throw new Error(`Refusing a symbolic link or non-file destination: ${name}`);
|
|
158
|
+
}
|
|
159
|
+
catch (error) {
|
|
160
|
+
if (error.code !== 'ENOENT')
|
|
161
|
+
throw error;
|
|
162
|
+
}
|
|
163
|
+
await writeFile(destination, file.content, { flag: 'w', mode: file.mode & 0o777 });
|
|
164
|
+
await chmod(destination, file.mode & 0o777);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
export function snapshotHash(snapshot) {
|
|
168
|
+
const hash = createHash('sha256');
|
|
169
|
+
for (const [name, file] of [...snapshot].sort(([a], [b]) => a.localeCompare(b, 'en'))) {
|
|
170
|
+
hash.update(JSON.stringify([name, file.mode, file.content.length]) + '\n');
|
|
171
|
+
hash.update(file.content);
|
|
172
|
+
}
|
|
173
|
+
return hash.digest('hex');
|
|
174
|
+
}
|
|
175
|
+
export function hashValue(value) {
|
|
176
|
+
return createHash('sha256').update(JSON.stringify(value)).digest('hex');
|
|
177
|
+
}
|
|
178
|
+
export function metrics(snapshot) {
|
|
179
|
+
let bytes = 0;
|
|
180
|
+
let sourceBytes = 0;
|
|
181
|
+
for (const [name, file] of snapshot) {
|
|
182
|
+
bytes += file.content.length;
|
|
183
|
+
if (!locks.has(name) && name !== 'package.json')
|
|
184
|
+
sourceBytes += file.content.length;
|
|
185
|
+
}
|
|
186
|
+
let dependencies = 0;
|
|
187
|
+
try {
|
|
188
|
+
const pkg = JSON.parse(snapshot.get('package.json')?.content.toString() ?? '{}');
|
|
189
|
+
dependencies = new Set([...Object.keys(pkg.dependencies ?? {}), ...Object.keys(pkg.devDependencies ?? {}), ...Object.keys(pkg.optionalDependencies ?? {})]).size;
|
|
190
|
+
}
|
|
191
|
+
catch { /* Invalid manifests are diagnosed before execution. */ }
|
|
192
|
+
return { files: snapshot.size, bytes, sourceBytes, dependencies };
|
|
193
|
+
}
|
|
194
|
+
export function applyTransformation(snapshot, transformation) {
|
|
195
|
+
const next = new Map(snapshot);
|
|
196
|
+
for (const edit of transformation.edits) {
|
|
197
|
+
safeRelativePath(edit.path);
|
|
198
|
+
if (edit.content === null)
|
|
199
|
+
next.delete(edit.path);
|
|
200
|
+
else
|
|
201
|
+
next.set(edit.path, { content: Buffer.from(edit.content), mode: snapshot.get(edit.path)?.mode ?? 0o644 });
|
|
202
|
+
}
|
|
203
|
+
return next;
|
|
204
|
+
}
|
|
205
|
+
export function scanForReview(snapshot) {
|
|
206
|
+
const findings = [];
|
|
207
|
+
for (const [name, entry] of snapshot) {
|
|
208
|
+
if (entry.content.includes(0))
|
|
209
|
+
continue;
|
|
210
|
+
const text = entry.content.toString('utf8');
|
|
211
|
+
if (/-----BEGIN (?:RSA |EC |OPENSSH )?PRIVATE KEY-----|\b(?:gh[pousr]_[A-Za-z0-9]{30,}|github_pat_[A-Za-z0-9_]{30,}|AKIA[A-Z0-9]{16}|sk_live_[A-Za-z0-9]{20,})/.test(text))
|
|
212
|
+
findings.push({ path: name, kind: 'secret', message: 'Possible credential found. Inspect this file before sharing.' });
|
|
213
|
+
if (/(?:\/Users\/[^/\s"']+\/|\/home\/[^/\s"']+\/|[A-Za-z]:\\Users\\)/.test(text))
|
|
214
|
+
findings.push({ path: name, kind: 'local-path', message: 'An absolute personal-directory path remains.' });
|
|
215
|
+
if (/https?:\/\/[^\s/:]+:[^\s/@]+@/.test(text))
|
|
216
|
+
findings.push({ path: name, kind: 'secret', message: 'A URL may contain embedded credentials.' });
|
|
217
|
+
}
|
|
218
|
+
return findings;
|
|
219
|
+
}
|
|
220
|
+
//# sourceMappingURL=snapshot.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"snapshot.js","sourceRoot":"","sources":["../src/snapshot.ts"],"names":[],"mappings":"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"}
|
package/dist/state.d.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { RunState, RuntimeInfo, Snapshot } from './types.ts';
|
|
2
|
+
export declare function atomicWrite(file: string, content: string | Buffer): Promise<void>;
|
|
3
|
+
export declare function saveCheckpoint(root: string, state: RunState, snapshot: Snapshot): Promise<void>;
|
|
4
|
+
export declare function loadCheckpoint(root: string): Promise<{
|
|
5
|
+
state: RunState;
|
|
6
|
+
snapshot: Snapshot;
|
|
7
|
+
}>;
|
|
8
|
+
export declare function acquireRunLock(root: string): Promise<() => Promise<void>>;
|
|
9
|
+
export declare function assertCompatibleRuntime(recorded: RuntimeInfo, current: RuntimeInfo): void;
|
package/dist/state.js
ADDED
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import { createHash, randomUUID } from 'node:crypto';
|
|
2
|
+
import { mkdir, open, readFile, rename, rm } from 'node:fs/promises';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { hashValue, safeRelativePath, snapshotHash } from "./snapshot.js";
|
|
5
|
+
export async function atomicWrite(file, content) {
|
|
6
|
+
await mkdir(path.dirname(file), { recursive: true, mode: 0o700 });
|
|
7
|
+
const temporary = file + '.' + randomUUID() + '.tmp';
|
|
8
|
+
const handle = await open(temporary, 'wx', 0o600);
|
|
9
|
+
try {
|
|
10
|
+
await handle.writeFile(content);
|
|
11
|
+
await handle.sync();
|
|
12
|
+
}
|
|
13
|
+
finally {
|
|
14
|
+
await handle.close();
|
|
15
|
+
}
|
|
16
|
+
try {
|
|
17
|
+
await rename(temporary, file);
|
|
18
|
+
}
|
|
19
|
+
catch (error) {
|
|
20
|
+
await rm(temporary, { force: true });
|
|
21
|
+
throw error;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
export async function saveCheckpoint(root, state, snapshot) {
|
|
25
|
+
if (snapshotHash(snapshot) !== state.bestHash)
|
|
26
|
+
throw new Error('Checkpoint snapshot hash differs from the accepted state.');
|
|
27
|
+
await mkdir(path.join(root, 'blobs'), { recursive: true, mode: 0o700 });
|
|
28
|
+
await mkdir(path.join(root, 'snapshots'), { recursive: true, mode: 0o700 });
|
|
29
|
+
const manifest = [];
|
|
30
|
+
for (const [name, file] of [...snapshot].sort(([a], [b]) => a.localeCompare(b, 'en'))) {
|
|
31
|
+
safeRelativePath(name);
|
|
32
|
+
const blob = createHash('sha256').update(file.content).digest('hex');
|
|
33
|
+
const blobPath = path.join(root, 'blobs', blob);
|
|
34
|
+
try {
|
|
35
|
+
const handle = await open(blobPath, 'wx', 0o600);
|
|
36
|
+
try {
|
|
37
|
+
await handle.writeFile(file.content);
|
|
38
|
+
await handle.sync();
|
|
39
|
+
}
|
|
40
|
+
finally {
|
|
41
|
+
await handle.close();
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
catch (error) {
|
|
45
|
+
if (error.code !== 'EEXIST')
|
|
46
|
+
throw error;
|
|
47
|
+
const existing = await readFile(blobPath);
|
|
48
|
+
if (createHash('sha256').update(existing).digest('hex') !== blob)
|
|
49
|
+
throw new Error('Corrupt source blob in checkpoint.');
|
|
50
|
+
}
|
|
51
|
+
manifest.push({ path: name, mode: file.mode, blob });
|
|
52
|
+
}
|
|
53
|
+
await atomicWrite(path.join(root, 'snapshots', state.bestHash + '.json'), JSON.stringify(manifest));
|
|
54
|
+
await atomicWrite(path.join(root, 'state.json'), JSON.stringify({ checksum: hashValue(state), state }, null, 2) + '\n');
|
|
55
|
+
}
|
|
56
|
+
export async function loadCheckpoint(root) {
|
|
57
|
+
let envelope;
|
|
58
|
+
try {
|
|
59
|
+
envelope = JSON.parse(await readFile(path.join(root, 'state.json'), 'utf8'));
|
|
60
|
+
}
|
|
61
|
+
catch {
|
|
62
|
+
throw new Error('Could not read the checkpoint. It is missing or corrupt.');
|
|
63
|
+
}
|
|
64
|
+
if (!envelope.state || envelope.state.version !== 1 || hashValue(envelope.state) !== envelope.checksum || !/^[a-f0-9]{64}$/.test(envelope.state.bestHash))
|
|
65
|
+
throw new Error('Checkpoint checksum is corrupt or its version is unsupported.');
|
|
66
|
+
const { state } = envelope;
|
|
67
|
+
const manifest = JSON.parse(await readFile(path.join(root, 'snapshots', state.bestHash + '.json'), 'utf8'));
|
|
68
|
+
if (!Array.isArray(manifest))
|
|
69
|
+
throw new Error('Corrupt snapshot manifest.');
|
|
70
|
+
const snapshot = new Map();
|
|
71
|
+
for (const row of manifest) {
|
|
72
|
+
if (!row || typeof row.path !== 'string' || !Number.isInteger(row.mode) || row.mode < 0 || row.mode > 0o777 || typeof row.blob !== 'string' || !/^[a-f0-9]{64}$/.test(row.blob))
|
|
73
|
+
throw new Error('Corrupt snapshot entry.');
|
|
74
|
+
safeRelativePath(row.path);
|
|
75
|
+
if (snapshot.has(row.path))
|
|
76
|
+
throw new Error('Corrupt snapshot has duplicate files.');
|
|
77
|
+
const content = await readFile(path.join(root, 'blobs', row.blob));
|
|
78
|
+
if (createHash('sha256').update(content).digest('hex') !== row.blob)
|
|
79
|
+
throw new Error('Source blob checksum is corrupt.');
|
|
80
|
+
snapshot.set(row.path, { content, mode: row.mode });
|
|
81
|
+
}
|
|
82
|
+
if (snapshotHash(snapshot) !== state.bestHash)
|
|
83
|
+
throw new Error('Snapshot checksum is corrupt.');
|
|
84
|
+
return { state, snapshot };
|
|
85
|
+
}
|
|
86
|
+
export async function acquireRunLock(root) {
|
|
87
|
+
await mkdir(root, { recursive: true, mode: 0o700 });
|
|
88
|
+
const lockPath = path.resolve(root, 'run.lock');
|
|
89
|
+
const nonce = randomUUID();
|
|
90
|
+
try {
|
|
91
|
+
const handle = await open(lockPath, 'wx', 0o600);
|
|
92
|
+
try {
|
|
93
|
+
await handle.writeFile(JSON.stringify({ pid: process.pid, nonce }));
|
|
94
|
+
}
|
|
95
|
+
finally {
|
|
96
|
+
await handle.close();
|
|
97
|
+
}
|
|
98
|
+
return async () => {
|
|
99
|
+
try {
|
|
100
|
+
const current = JSON.parse(await readFile(lockPath, 'utf8'));
|
|
101
|
+
if (current.nonce === nonce)
|
|
102
|
+
await rm(lockPath);
|
|
103
|
+
}
|
|
104
|
+
catch (error) {
|
|
105
|
+
if (error.code !== 'ENOENT')
|
|
106
|
+
throw error;
|
|
107
|
+
}
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
catch (error) {
|
|
111
|
+
if (error.code !== 'EEXIST')
|
|
112
|
+
throw error;
|
|
113
|
+
const recovery = `Stop all processes using this run directory and confirm it has no active owner, then manually remove ${JSON.stringify(lockPath)} and retry.`;
|
|
114
|
+
let stale = false;
|
|
115
|
+
try {
|
|
116
|
+
const lock = JSON.parse(await readFile(lockPath, 'utf8'));
|
|
117
|
+
if (!Number.isInteger(lock.pid) || lock.pid <= 0)
|
|
118
|
+
throw new Error('invalid lock');
|
|
119
|
+
try {
|
|
120
|
+
process.kill(lock.pid, 0);
|
|
121
|
+
}
|
|
122
|
+
catch (err) {
|
|
123
|
+
if (err.code === 'ESRCH')
|
|
124
|
+
stale = true;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
catch {
|
|
128
|
+
throw new Error(`Run is locked and its owner cannot be verified. ${recovery}`);
|
|
129
|
+
}
|
|
130
|
+
if (!stale)
|
|
131
|
+
throw new Error('This run has an active process and is locked. Wait for it to finish.');
|
|
132
|
+
// A stale pathname can be replaced by another contender after any read.
|
|
133
|
+
// Refusing recovery avoids unlinking a newly acquired live lock.
|
|
134
|
+
throw new Error(`This run has a stale lock. Automatic recovery is disabled. ${recovery}`);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
export function assertCompatibleRuntime(recorded, current) {
|
|
138
|
+
for (const field of ['node', 'npm', 'platform', 'arch', 'tool']) {
|
|
139
|
+
if (recorded[field] !== current[field])
|
|
140
|
+
throw new Error(`Runtime environment changed (${field}: ${recorded[field]} to ${current[field]}). Start a new run with this environment.`);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
//# sourceMappingURL=state.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"state.js","sourceRoot":"","sources":["../src/state.ts"],"names":[],"mappings":"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"}
|