@expressots/studio-agent 4.0.0-preview.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +143 -0
- package/dist/agent.d.ts +127 -0
- package/dist/agent.d.ts.map +1 -0
- package/dist/agent.js +1031 -0
- package/dist/agent.js.map +1 -0
- package/dist/discovery/index.d.ts +2 -0
- package/dist/discovery/index.d.ts.map +1 -0
- package/dist/discovery/index.js +2 -0
- package/dist/discovery/index.js.map +1 -0
- package/dist/discovery/route-scanner.d.ts +35 -0
- package/dist/discovery/route-scanner.d.ts.map +1 -0
- package/dist/discovery/route-scanner.js +385 -0
- package/dist/discovery/route-scanner.js.map +1 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +15 -0
- package/dist/index.js.map +1 -0
- package/dist/instrumentation/index.d.ts +2 -0
- package/dist/instrumentation/index.d.ts.map +1 -0
- package/dist/instrumentation/index.js +2 -0
- package/dist/instrumentation/index.js.map +1 -0
- package/dist/instrumentation/tracer.d.ts +40 -0
- package/dist/instrumentation/tracer.d.ts.map +1 -0
- package/dist/instrumentation/tracer.js +190 -0
- package/dist/instrumentation/tracer.js.map +1 -0
- package/dist/introspection/container-introspector.d.ts +81 -0
- package/dist/introspection/container-introspector.d.ts.map +1 -0
- package/dist/introspection/container-introspector.js +251 -0
- package/dist/introspection/container-introspector.js.map +1 -0
- package/dist/logging/log-capture.d.ts +58 -0
- package/dist/logging/log-capture.d.ts.map +1 -0
- package/dist/logging/log-capture.js +184 -0
- package/dist/logging/log-capture.js.map +1 -0
- package/dist/recording/index.d.ts +2 -0
- package/dist/recording/index.d.ts.map +1 -0
- package/dist/recording/index.js +2 -0
- package/dist/recording/index.js.map +1 -0
- package/dist/recording/request-recorder.d.ts +43 -0
- package/dist/recording/request-recorder.d.ts.map +1 -0
- package/dist/recording/request-recorder.js +373 -0
- package/dist/recording/request-recorder.js.map +1 -0
- package/dist/security/fix-resolver.d.ts +40 -0
- package/dist/security/fix-resolver.d.ts.map +1 -0
- package/dist/security/fix-resolver.js +283 -0
- package/dist/security/fix-resolver.js.map +1 -0
- package/dist/security/fix-runner.d.ts +60 -0
- package/dist/security/fix-runner.d.ts.map +1 -0
- package/dist/security/fix-runner.js +188 -0
- package/dist/security/fix-runner.js.map +1 -0
- package/dist/security/index.d.ts +140 -0
- package/dist/security/index.d.ts.map +1 -0
- package/dist/security/index.js +460 -0
- package/dist/security/index.js.map +1 -0
- package/dist/security/lockfile-graph.d.ts +69 -0
- package/dist/security/lockfile-graph.d.ts.map +1 -0
- package/dist/security/lockfile-graph.js +245 -0
- package/dist/security/lockfile-graph.js.map +1 -0
- package/dist/security/npm-audit.d.ts +67 -0
- package/dist/security/npm-audit.d.ts.map +1 -0
- package/dist/security/npm-audit.js +320 -0
- package/dist/security/npm-audit.js.map +1 -0
- package/dist/security/osv-cache.d.ts +51 -0
- package/dist/security/osv-cache.d.ts.map +1 -0
- package/dist/security/osv-cache.js +99 -0
- package/dist/security/osv-cache.js.map +1 -0
- package/dist/security/osv-client.d.ts +47 -0
- package/dist/security/osv-client.d.ts.map +1 -0
- package/dist/security/osv-client.js +247 -0
- package/dist/security/osv-client.js.map +1 -0
- package/dist/security/posture-analyzer.d.ts +44 -0
- package/dist/security/posture-analyzer.d.ts.map +1 -0
- package/dist/security/posture-analyzer.js +397 -0
- package/dist/security/posture-analyzer.js.map +1 -0
- package/dist/security/reachability.d.ts +59 -0
- package/dist/security/reachability.d.ts.map +1 -0
- package/dist/security/reachability.js +302 -0
- package/dist/security/reachability.js.map +1 -0
- package/dist/security/score.d.ts +36 -0
- package/dist/security/score.d.ts.map +1 -0
- package/dist/security/score.js +94 -0
- package/dist/security/score.js.map +1 -0
- package/dist/types/index.d.ts +587 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +14 -0
- package/dist/types/index.js.map +1 -0
- package/package.json +75 -0
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Compute concrete remediation steps for supply-chain findings.
|
|
3
|
+
*
|
|
4
|
+
* For each finding we want to answer two questions:
|
|
5
|
+
*
|
|
6
|
+
* 1. **What's the root cause?** For transitive vulns the user can't
|
|
7
|
+
* `npm install <vulnerable-pkg>@<fixedVersion>` — they have to
|
|
8
|
+
* bump whichever direct dep brought the package in. We resolve
|
|
9
|
+
* this from the lockfile graph.
|
|
10
|
+
*
|
|
11
|
+
* 2. **What's the fix command?** Either a one-liner the user can
|
|
12
|
+
* paste (`npm install pkg@^X`), `npm audit fix` for the
|
|
13
|
+
* automatic case, or `--force` for semver-major upgrades. When
|
|
14
|
+
* no fix exists we return a `'none'` spec so the UI can render a
|
|
15
|
+
* consistent disabled button.
|
|
16
|
+
*
|
|
17
|
+
* Pure functions — no I/O, no spawning. The engine wires the spec into
|
|
18
|
+
* the fix-runner when the user clicks "Apply fix".
|
|
19
|
+
*/
|
|
20
|
+
import type { DependencyFinding, FixGroup } from '../types/index.js';
|
|
21
|
+
import type { AuditFixAvailability } from './npm-audit.js';
|
|
22
|
+
import type { LockfileGraph } from './lockfile-graph.js';
|
|
23
|
+
/**
|
|
24
|
+
* Enrich every finding in-place with `fix` and `rootCause`. The
|
|
25
|
+
* lockfile graph is optional — when absent (no `package-lock.json`)
|
|
26
|
+
* we still produce a best-effort `FixSpec` based on the finding's
|
|
27
|
+
* own `fixedVersion`.
|
|
28
|
+
*/
|
|
29
|
+
export declare function enrichFindings(findings: DependencyFinding[], fixAvailability: Map<string, AuditFixAvailability>, lockfile: LockfileGraph | null): DependencyFinding[];
|
|
30
|
+
/**
|
|
31
|
+
* Bucket findings that share a fix command together so the UI can show
|
|
32
|
+
* "Upgrade lodash — fixes 4 advisories" rather than four separate rows.
|
|
33
|
+
*
|
|
34
|
+
* Grouping key:
|
|
35
|
+
* - install commands → group by exact command string
|
|
36
|
+
* - audit-fix / audit-fix-force → one bucket each across the whole report
|
|
37
|
+
* - override / none → not grouped (each finding stays individual)
|
|
38
|
+
*/
|
|
39
|
+
export declare function buildFixGroups(findings: DependencyFinding[]): FixGroup[];
|
|
40
|
+
//# sourceMappingURL=fix-resolver.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fix-resolver.d.ts","sourceRoot":"","sources":["../../src/security/fix-resolver.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,KAAK,EACV,iBAAiB,EACjB,QAAQ,EAIT,MAAM,mBAAmB,CAAC;AAC3B,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;AAC3D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAWzD;;;;;GAKG;AACH,wBAAgB,cAAc,CAC5B,QAAQ,EAAE,iBAAiB,EAAE,EAC7B,eAAe,EAAE,GAAG,CAAC,MAAM,EAAE,oBAAoB,CAAC,EAClD,QAAQ,EAAE,aAAa,GAAG,IAAI,GAC7B,iBAAiB,EAAE,CAYrB;AAkGD;;;;;;;;GAQG;AACH,wBAAgB,cAAc,CAAC,QAAQ,EAAE,iBAAiB,EAAE,GAAG,QAAQ,EAAE,CAsDxE"}
|
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Compute concrete remediation steps for supply-chain findings.
|
|
3
|
+
*
|
|
4
|
+
* For each finding we want to answer two questions:
|
|
5
|
+
*
|
|
6
|
+
* 1. **What's the root cause?** For transitive vulns the user can't
|
|
7
|
+
* `npm install <vulnerable-pkg>@<fixedVersion>` — they have to
|
|
8
|
+
* bump whichever direct dep brought the package in. We resolve
|
|
9
|
+
* this from the lockfile graph.
|
|
10
|
+
*
|
|
11
|
+
* 2. **What's the fix command?** Either a one-liner the user can
|
|
12
|
+
* paste (`npm install pkg@^X`), `npm audit fix` for the
|
|
13
|
+
* automatic case, or `--force` for semver-major upgrades. When
|
|
14
|
+
* no fix exists we return a `'none'` spec so the UI can render a
|
|
15
|
+
* consistent disabled button.
|
|
16
|
+
*
|
|
17
|
+
* Pure functions — no I/O, no spawning. The engine wires the spec into
|
|
18
|
+
* the fix-runner when the user clicks "Apply fix".
|
|
19
|
+
*/
|
|
20
|
+
/** Severity ordering used to pick the "worst" severity per fix group. */
|
|
21
|
+
const SEVERITY_ORDER = {
|
|
22
|
+
CRITICAL: 4,
|
|
23
|
+
HIGH: 3,
|
|
24
|
+
MEDIUM: 2,
|
|
25
|
+
LOW: 1,
|
|
26
|
+
INFO: 0,
|
|
27
|
+
};
|
|
28
|
+
/**
|
|
29
|
+
* Enrich every finding in-place with `fix` and `rootCause`. The
|
|
30
|
+
* lockfile graph is optional — when absent (no `package-lock.json`)
|
|
31
|
+
* we still produce a best-effort `FixSpec` based on the finding's
|
|
32
|
+
* own `fixedVersion`.
|
|
33
|
+
*/
|
|
34
|
+
export function enrichFindings(findings, fixAvailability, lockfile) {
|
|
35
|
+
return findings.map((finding) => {
|
|
36
|
+
const rootCause = lockfile
|
|
37
|
+
? lockfile.findRootCause(finding.package) ?? undefined
|
|
38
|
+
: undefined;
|
|
39
|
+
const fix = resolveFix(finding, fixAvailability, rootCause, lockfile);
|
|
40
|
+
return {
|
|
41
|
+
...finding,
|
|
42
|
+
fix,
|
|
43
|
+
rootCause,
|
|
44
|
+
};
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Pick the right command for `finding`. Order of preference:
|
|
49
|
+
*
|
|
50
|
+
* 1. If the package is a direct dep and we know a `fixedVersion`,
|
|
51
|
+
* generate a precise `npm install <pkg>@<ver>` command.
|
|
52
|
+
* 2. If npm-audit says it can fix without going semver-major:
|
|
53
|
+
* `npm audit fix`.
|
|
54
|
+
* 3. If npm-audit can only fix with `--force`: `npm audit fix --force`,
|
|
55
|
+
* flagged as breaking.
|
|
56
|
+
* 4. Otherwise — no upstream fix yet. UI renders an advisory link
|
|
57
|
+
* and a disabled "Apply fix" button.
|
|
58
|
+
*/
|
|
59
|
+
function resolveFix(finding, availability, rootCause, lockfile) {
|
|
60
|
+
const fa = availability.get(finding.package) ?? { kind: 'none' };
|
|
61
|
+
const isDirect = rootCause?.isDirect ?? lockfile?.isDirect(finding.package) ?? false;
|
|
62
|
+
// Direct dep with a known fix version → exact install command.
|
|
63
|
+
if (isDirect && finding.fixedVersion) {
|
|
64
|
+
const breaking = isSemVerMajor(finding.installedVersion, finding.fixedVersion);
|
|
65
|
+
return {
|
|
66
|
+
kind: 'install',
|
|
67
|
+
command: `npm install ${finding.package}@${quoteVersionRange(finding.fixedVersion)}`,
|
|
68
|
+
breaking,
|
|
69
|
+
label: `Upgrade ${finding.package} ${finding.installedVersion} → ${finding.fixedVersion}`,
|
|
70
|
+
note: breaking ? 'Semver-major upgrade — may include breaking changes.' : undefined,
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
// npm-audit reports a specific upgrade target (typically points at
|
|
74
|
+
// the root package the user owns, even for transitive vulns).
|
|
75
|
+
if (fa.kind === 'specific') {
|
|
76
|
+
if (!fa.isSemVerMajor) {
|
|
77
|
+
return {
|
|
78
|
+
kind: 'audit-fix',
|
|
79
|
+
command: 'npm audit fix',
|
|
80
|
+
breaking: false,
|
|
81
|
+
label: rootCause && !rootCause.isDirect
|
|
82
|
+
? `Upgrade ${rootCause.rootPackage} via npm audit fix`
|
|
83
|
+
: `Auto-fix ${fa.name ?? finding.package}`,
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
return {
|
|
87
|
+
kind: 'audit-fix-force',
|
|
88
|
+
command: 'npm audit fix --force',
|
|
89
|
+
breaking: true,
|
|
90
|
+
label: rootCause && !rootCause.isDirect
|
|
91
|
+
? `Force-upgrade ${rootCause.rootPackage} (semver-major)`
|
|
92
|
+
: `Force-fix ${fa.name ?? finding.package}`,
|
|
93
|
+
note: '`--force` may install semver-major upgrades that break your build.',
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
// npm-audit said it can auto-fix but didn't pin a version (the most
|
|
97
|
+
// common "fixAvailable: true" case). We still recommend running
|
|
98
|
+
// `npm audit fix` — npm will pick the right targets.
|
|
99
|
+
if (fa.kind === 'auto') {
|
|
100
|
+
return {
|
|
101
|
+
kind: 'audit-fix',
|
|
102
|
+
command: 'npm audit fix',
|
|
103
|
+
breaking: false,
|
|
104
|
+
label: rootCause && !rootCause.isDirect
|
|
105
|
+
? `Upgrade ${rootCause.rootPackage} via npm audit fix`
|
|
106
|
+
: `Auto-fix ${finding.package} via npm audit fix`,
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
// No fixAvailable target. If the vulnerable package isn't a direct
|
|
110
|
+
// dep but we know a fixed version, the user can pin it via
|
|
111
|
+
// `package.json` overrides — surface that as a manual step.
|
|
112
|
+
if (!isDirect && rootCause && finding.fixedVersion) {
|
|
113
|
+
return {
|
|
114
|
+
kind: 'override',
|
|
115
|
+
command: '',
|
|
116
|
+
breaking: false,
|
|
117
|
+
label: `Add a ${finding.package} override`,
|
|
118
|
+
note: `Upstream (${rootCause.rootPackage}) hasn't shipped a fixed version. ` +
|
|
119
|
+
`Add an "overrides" entry to package.json pinning ${finding.package}@${finding.fixedVersion}.`,
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
return {
|
|
123
|
+
kind: 'none',
|
|
124
|
+
command: '',
|
|
125
|
+
breaking: false,
|
|
126
|
+
label: 'No upstream fix yet',
|
|
127
|
+
note: isDirect ? 'Watch the advisory link for a patched release.' : undefined,
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Bucket findings that share a fix command together so the UI can show
|
|
132
|
+
* "Upgrade lodash — fixes 4 advisories" rather than four separate rows.
|
|
133
|
+
*
|
|
134
|
+
* Grouping key:
|
|
135
|
+
* - install commands → group by exact command string
|
|
136
|
+
* - audit-fix / audit-fix-force → one bucket each across the whole report
|
|
137
|
+
* - override / none → not grouped (each finding stays individual)
|
|
138
|
+
*/
|
|
139
|
+
export function buildFixGroups(findings) {
|
|
140
|
+
const buckets = new Map();
|
|
141
|
+
for (const f of findings) {
|
|
142
|
+
if (!f.fix)
|
|
143
|
+
continue;
|
|
144
|
+
if (f.fix.kind === 'none' || f.fix.kind === 'override')
|
|
145
|
+
continue;
|
|
146
|
+
const key = `${f.fix.kind}::${f.fix.command}`;
|
|
147
|
+
const list = buckets.get(key) ?? [];
|
|
148
|
+
list.push(f);
|
|
149
|
+
buckets.set(key, list);
|
|
150
|
+
}
|
|
151
|
+
const out = [];
|
|
152
|
+
for (const [key, list] of buckets) {
|
|
153
|
+
if (list.length === 0)
|
|
154
|
+
continue;
|
|
155
|
+
const first = list[0];
|
|
156
|
+
const fix = first.fix;
|
|
157
|
+
const groupPkg = first.rootCause?.rootPackage ?? first.package;
|
|
158
|
+
const fromVersion = first.rootCause?.rootInstalledVersion ?? first.installedVersion;
|
|
159
|
+
const toVersion = inferTargetVersion(fix, first);
|
|
160
|
+
const severity = list.reduce((acc, f) => SEVERITY_ORDER[f.severity] > SEVERITY_ORDER[acc] ? f.severity : acc, 'INFO');
|
|
161
|
+
const reachability = pickStrongestReachability(list);
|
|
162
|
+
out.push({
|
|
163
|
+
id: `fg-${hashKey(key)}-${list.length}`,
|
|
164
|
+
package: groupPkg,
|
|
165
|
+
fromVersion,
|
|
166
|
+
toVersion,
|
|
167
|
+
breaking: fix.breaking,
|
|
168
|
+
severity,
|
|
169
|
+
findingIds: list.map((f) => f.id),
|
|
170
|
+
fix,
|
|
171
|
+
reachability,
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
// Sort highest-impact groups first: more findings, then higher severity,
|
|
175
|
+
// then reachability (confirmed > likely > unknown > unreachable).
|
|
176
|
+
out.sort((a, b) => {
|
|
177
|
+
if (b.findingIds.length !== a.findingIds.length) {
|
|
178
|
+
return b.findingIds.length - a.findingIds.length;
|
|
179
|
+
}
|
|
180
|
+
if (SEVERITY_ORDER[b.severity] !== SEVERITY_ORDER[a.severity]) {
|
|
181
|
+
return SEVERITY_ORDER[b.severity] - SEVERITY_ORDER[a.severity];
|
|
182
|
+
}
|
|
183
|
+
return reachabilityRank(b.reachability) - reachabilityRank(a.reachability);
|
|
184
|
+
});
|
|
185
|
+
return out;
|
|
186
|
+
}
|
|
187
|
+
// ────────────────────────────────────────────────────────────────────────
|
|
188
|
+
// Helpers
|
|
189
|
+
// ────────────────────────────────────────────────────────────────────────
|
|
190
|
+
/**
|
|
191
|
+
* Return the target version string we want to show on a fix-group card.
|
|
192
|
+
* For exact `install` commands we extract it from the command itself;
|
|
193
|
+
* for `audit-fix` we don't actually know the target until npm runs, so
|
|
194
|
+
* we fall back to the finding's `fixedVersion` (best signal we have).
|
|
195
|
+
*/
|
|
196
|
+
function inferTargetVersion(fix, finding) {
|
|
197
|
+
if (fix.kind === 'install') {
|
|
198
|
+
const m = fix.command.match(/@([^@]+)$/);
|
|
199
|
+
if (m)
|
|
200
|
+
return m[1].replace(/^['"]/, '').replace(/['"]$/, '');
|
|
201
|
+
}
|
|
202
|
+
return finding.fixedVersion ?? 'latest';
|
|
203
|
+
}
|
|
204
|
+
function pickStrongestReachability(findings) {
|
|
205
|
+
let best = -1;
|
|
206
|
+
let label;
|
|
207
|
+
for (const f of findings) {
|
|
208
|
+
const lvl = f.reachability?.level;
|
|
209
|
+
if (!lvl)
|
|
210
|
+
continue;
|
|
211
|
+
const rank = reachabilityRank(lvl);
|
|
212
|
+
if (rank > best) {
|
|
213
|
+
best = rank;
|
|
214
|
+
label = lvl;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
return label;
|
|
218
|
+
}
|
|
219
|
+
function reachabilityRank(r) {
|
|
220
|
+
switch (r) {
|
|
221
|
+
case 'confirmed':
|
|
222
|
+
return 3;
|
|
223
|
+
case 'likely':
|
|
224
|
+
return 2;
|
|
225
|
+
case 'unknown':
|
|
226
|
+
return 1;
|
|
227
|
+
case 'unreachable':
|
|
228
|
+
return 0;
|
|
229
|
+
default:
|
|
230
|
+
return -1;
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
/**
|
|
234
|
+
* Quote the version range for the shell when it contains characters
|
|
235
|
+
* that have special meaning in PowerShell / zsh. Caret (`^`) and tilde
|
|
236
|
+
* (`~`) are safe in bash but break in PowerShell — wrap in single
|
|
237
|
+
* quotes whenever we see them.
|
|
238
|
+
*/
|
|
239
|
+
function quoteVersionRange(version) {
|
|
240
|
+
if (/[\s<>=^~|*"'\\]/.test(version)) {
|
|
241
|
+
// Use single quotes; npm install on every platform accepts the
|
|
242
|
+
// shell-quoted form. Escape any embedded single quotes too.
|
|
243
|
+
return `'${version.replace(/'/g, "\\'")}'`;
|
|
244
|
+
}
|
|
245
|
+
return version;
|
|
246
|
+
}
|
|
247
|
+
/**
|
|
248
|
+
* Cheap, non-cryptographic hash for fix-group ids. Stable across
|
|
249
|
+
* scans so the UI can keep its expanded/collapsed state across
|
|
250
|
+
* incoming reports.
|
|
251
|
+
*/
|
|
252
|
+
function hashKey(input) {
|
|
253
|
+
let h = 2166136261;
|
|
254
|
+
for (let i = 0; i < input.length; i++) {
|
|
255
|
+
h ^= input.charCodeAt(i);
|
|
256
|
+
h = Math.imul(h, 16777619);
|
|
257
|
+
}
|
|
258
|
+
return (h >>> 0).toString(36);
|
|
259
|
+
}
|
|
260
|
+
/**
|
|
261
|
+
* Best-effort semver-major detection used when npm-audit doesn't tell
|
|
262
|
+
* us whether the upgrade is breaking. We strip non-digit prefixes and
|
|
263
|
+
* compare the leading number — good enough for the standard "X.Y.Z"
|
|
264
|
+
* shape; conservatively returns `false` on anything weirder so we
|
|
265
|
+
* don't scare users about benign patch bumps.
|
|
266
|
+
*/
|
|
267
|
+
function isSemVerMajor(from, to) {
|
|
268
|
+
const fromMajor = leadingMajor(from);
|
|
269
|
+
const toMajor = leadingMajor(to);
|
|
270
|
+
if (fromMajor === null || toMajor === null)
|
|
271
|
+
return false;
|
|
272
|
+
return toMajor > fromMajor;
|
|
273
|
+
}
|
|
274
|
+
function leadingMajor(version) {
|
|
275
|
+
// npm-audit `range` strings can be like ">=1.2.3 <2.0.0" — take the
|
|
276
|
+
// first numeric component.
|
|
277
|
+
const m = version.match(/(?:^|[^\d])(\d+)/);
|
|
278
|
+
if (!m)
|
|
279
|
+
return null;
|
|
280
|
+
const n = parseInt(m[1], 10);
|
|
281
|
+
return Number.isFinite(n) ? n : null;
|
|
282
|
+
}
|
|
283
|
+
//# sourceMappingURL=fix-resolver.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fix-resolver.js","sourceRoot":"","sources":["../../src/security/fix-resolver.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Spawn a fix command (`npm install pkg@ver`, `npm audit fix`, or
|
|
3
|
+
* `npm audit fix --force`) in the host project's cwd and stream its
|
|
4
|
+
* output back to the engine line-by-line.
|
|
5
|
+
*
|
|
6
|
+
* The runner is intentionally policy-free: it neither decides what
|
|
7
|
+
* command to run (that's the fix-resolver's job) nor what to do with
|
|
8
|
+
* the result (the engine triggers a rescan). It just owns spawning,
|
|
9
|
+
* timeouts, buffer caps, and line splitting.
|
|
10
|
+
*
|
|
11
|
+
* **Safety constraints**
|
|
12
|
+
* - Only commands matching a small allow-list of fix kinds run. The
|
|
13
|
+
* command string from the WS message is never forwarded to the
|
|
14
|
+
* shell verbatim; the runner re-builds the argv from a vetted
|
|
15
|
+
* `kind` enum + optional `pkg@version` tuple.
|
|
16
|
+
* - 10 min hard timeout. `npm install` over a slow network can easily
|
|
17
|
+
* take a few minutes; anything beyond that is a hung process.
|
|
18
|
+
* - 4 MB stdout / 256 KB stderr caps so a runaway can't OOM the agent.
|
|
19
|
+
*/
|
|
20
|
+
/** Lifecycle states surfaced to the UI for the "fix in progress" banner. */
|
|
21
|
+
export type FixState = 'running' | 'success' | 'error';
|
|
22
|
+
/** Caller-supplied identifier echoed in progress/result frames. */
|
|
23
|
+
export type FixTargetId = string;
|
|
24
|
+
/** The discrete kinds of fix invocations the runner will accept. */
|
|
25
|
+
export type FixCommandKind = 'install' | 'audit-fix' | 'audit-fix-force';
|
|
26
|
+
export interface FixRunInput {
|
|
27
|
+
cwd: string;
|
|
28
|
+
kind: FixCommandKind;
|
|
29
|
+
/** Required for `kind: 'install'`. */
|
|
30
|
+
package?: string;
|
|
31
|
+
/** Required for `kind: 'install'`. */
|
|
32
|
+
version?: string;
|
|
33
|
+
/** Identifier echoed back via progress/result frames. */
|
|
34
|
+
targetId: FixTargetId;
|
|
35
|
+
}
|
|
36
|
+
export interface FixRunResult {
|
|
37
|
+
state: FixState;
|
|
38
|
+
exitCode: number | null;
|
|
39
|
+
command: string;
|
|
40
|
+
durationMs: number;
|
|
41
|
+
stdoutTail: string;
|
|
42
|
+
stderrTail: string;
|
|
43
|
+
}
|
|
44
|
+
export type FixProgressHandler = (line: string, stream: 'stdout' | 'stderr') => void;
|
|
45
|
+
/**
|
|
46
|
+
* Build the argv for `kind`. Returns `null` for an invalid input so
|
|
47
|
+
* callers can fail fast without spawning anything.
|
|
48
|
+
*/
|
|
49
|
+
export declare function buildFixArgs(input: FixRunInput): {
|
|
50
|
+
cmd: string;
|
|
51
|
+
args: string[];
|
|
52
|
+
pretty: string;
|
|
53
|
+
} | null;
|
|
54
|
+
/**
|
|
55
|
+
* Spawn the fix command, stream line-by-line progress through
|
|
56
|
+
* `onProgress`, and resolve with the captured tails once the child
|
|
57
|
+
* exits or the timeout fires.
|
|
58
|
+
*/
|
|
59
|
+
export declare function runFix(input: FixRunInput, onProgress: FixProgressHandler): Promise<FixRunResult>;
|
|
60
|
+
//# sourceMappingURL=fix-runner.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fix-runner.d.ts","sourceRoot":"","sources":["../../src/security/fix-runner.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAIH,4EAA4E;AAC5E,MAAM,MAAM,QAAQ,GAAG,SAAS,GAAG,SAAS,GAAG,OAAO,CAAC;AAEvD,mEAAmE;AACnE,MAAM,MAAM,WAAW,GAAG,MAAM,CAAC;AAEjC,oEAAoE;AACpE,MAAM,MAAM,cAAc,GACtB,SAAS,GACT,WAAW,GACX,iBAAiB,CAAC;AAEtB,MAAM,WAAW,WAAW;IAC1B,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,cAAc,CAAC;IACrB,sCAAsC;IACtC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,sCAAsC;IACtC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,yDAAyD;IACzD,QAAQ,EAAE,WAAW,CAAC;CACvB;AAED,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,QAAQ,CAAC;IAChB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,MAAM,kBAAkB,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,GAAG,QAAQ,KAAK,IAAI,CAAC;AAOrF;;;GAGG;AACH,wBAAgB,YAAY,CAC1B,KAAK,EAAE,WAAW,GACjB;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,EAAE,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAwBxD;AAED;;;;GAIG;AACH,wBAAgB,MAAM,CACpB,KAAK,EAAE,WAAW,EAClB,UAAU,EAAE,kBAAkB,GAC7B,OAAO,CAAC,YAAY,CAAC,CA8GvB"}
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Spawn a fix command (`npm install pkg@ver`, `npm audit fix`, or
|
|
3
|
+
* `npm audit fix --force`) in the host project's cwd and stream its
|
|
4
|
+
* output back to the engine line-by-line.
|
|
5
|
+
*
|
|
6
|
+
* The runner is intentionally policy-free: it neither decides what
|
|
7
|
+
* command to run (that's the fix-resolver's job) nor what to do with
|
|
8
|
+
* the result (the engine triggers a rescan). It just owns spawning,
|
|
9
|
+
* timeouts, buffer caps, and line splitting.
|
|
10
|
+
*
|
|
11
|
+
* **Safety constraints**
|
|
12
|
+
* - Only commands matching a small allow-list of fix kinds run. The
|
|
13
|
+
* command string from the WS message is never forwarded to the
|
|
14
|
+
* shell verbatim; the runner re-builds the argv from a vetted
|
|
15
|
+
* `kind` enum + optional `pkg@version` tuple.
|
|
16
|
+
* - 10 min hard timeout. `npm install` over a slow network can easily
|
|
17
|
+
* take a few minutes; anything beyond that is a hung process.
|
|
18
|
+
* - 4 MB stdout / 256 KB stderr caps so a runaway can't OOM the agent.
|
|
19
|
+
*/
|
|
20
|
+
import { spawn } from 'node:child_process';
|
|
21
|
+
/** Hard cap for an in-flight fix command (10 min). */
|
|
22
|
+
const FIX_TIMEOUT_MS = 10 * 60 * 1000;
|
|
23
|
+
const MAX_STDOUT_BYTES = 4 * 1024 * 1024;
|
|
24
|
+
const MAX_STDERR_BYTES = 256 * 1024;
|
|
25
|
+
/**
|
|
26
|
+
* Build the argv for `kind`. Returns `null` for an invalid input so
|
|
27
|
+
* callers can fail fast without spawning anything.
|
|
28
|
+
*/
|
|
29
|
+
export function buildFixArgs(input) {
|
|
30
|
+
switch (input.kind) {
|
|
31
|
+
case 'install': {
|
|
32
|
+
if (!input.package || !input.version)
|
|
33
|
+
return null;
|
|
34
|
+
// Keep the *exact* spec the resolver emitted — quoting is not
|
|
35
|
+
// needed for spawn() because we bypass the shell.
|
|
36
|
+
const spec = `${input.package}@${input.version}`;
|
|
37
|
+
return {
|
|
38
|
+
cmd: 'npm',
|
|
39
|
+
args: ['install', spec],
|
|
40
|
+
pretty: `npm install ${spec}`,
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
case 'audit-fix':
|
|
44
|
+
return { cmd: 'npm', args: ['audit', 'fix'], pretty: 'npm audit fix' };
|
|
45
|
+
case 'audit-fix-force':
|
|
46
|
+
return {
|
|
47
|
+
cmd: 'npm',
|
|
48
|
+
args: ['audit', 'fix', '--force'],
|
|
49
|
+
pretty: 'npm audit fix --force',
|
|
50
|
+
};
|
|
51
|
+
default:
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Spawn the fix command, stream line-by-line progress through
|
|
57
|
+
* `onProgress`, and resolve with the captured tails once the child
|
|
58
|
+
* exits or the timeout fires.
|
|
59
|
+
*/
|
|
60
|
+
export function runFix(input, onProgress) {
|
|
61
|
+
return new Promise((resolve) => {
|
|
62
|
+
const argv = buildFixArgs(input);
|
|
63
|
+
if (!argv) {
|
|
64
|
+
resolve({
|
|
65
|
+
state: 'error',
|
|
66
|
+
exitCode: null,
|
|
67
|
+
command: '',
|
|
68
|
+
durationMs: 0,
|
|
69
|
+
stdoutTail: '',
|
|
70
|
+
stderrTail: 'Invalid fix command (missing package/version for install).',
|
|
71
|
+
});
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
const started = Date.now();
|
|
75
|
+
let child;
|
|
76
|
+
try {
|
|
77
|
+
child = spawn(argv.cmd, argv.args, {
|
|
78
|
+
cwd: input.cwd,
|
|
79
|
+
// npm on Windows is `npm.cmd`; the shell flag lets the OS resolve it.
|
|
80
|
+
shell: process.platform === 'win32',
|
|
81
|
+
env: process.env,
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
catch (err) {
|
|
85
|
+
resolve({
|
|
86
|
+
state: 'error',
|
|
87
|
+
exitCode: null,
|
|
88
|
+
command: argv.pretty,
|
|
89
|
+
durationMs: 0,
|
|
90
|
+
stdoutTail: '',
|
|
91
|
+
stderrTail: err.message || 'failed to spawn npm',
|
|
92
|
+
});
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
let stdoutBytes = 0;
|
|
96
|
+
let stderrBytes = 0;
|
|
97
|
+
let stdoutTail = '';
|
|
98
|
+
let stderrTail = '';
|
|
99
|
+
// Line buffer: hold partial lines across chunk boundaries so the UI
|
|
100
|
+
// never sees a half-formed log entry. Flush on newline.
|
|
101
|
+
let pendingOut = '';
|
|
102
|
+
let pendingErr = '';
|
|
103
|
+
child.stdout?.on('data', (chunk) => {
|
|
104
|
+
stdoutBytes += chunk.length;
|
|
105
|
+
const text = chunk.toString('utf-8');
|
|
106
|
+
if (stdoutBytes <= MAX_STDOUT_BYTES) {
|
|
107
|
+
stdoutTail += text;
|
|
108
|
+
if (stdoutTail.length > MAX_STDOUT_BYTES) {
|
|
109
|
+
stdoutTail = stdoutTail.slice(-MAX_STDOUT_BYTES);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
pendingOut += text;
|
|
113
|
+
pendingOut = flushLines(pendingOut, (line) => onProgress(line, 'stdout'));
|
|
114
|
+
});
|
|
115
|
+
child.stderr?.on('data', (chunk) => {
|
|
116
|
+
stderrBytes += chunk.length;
|
|
117
|
+
const text = chunk.toString('utf-8');
|
|
118
|
+
if (stderrBytes <= MAX_STDERR_BYTES) {
|
|
119
|
+
stderrTail += text;
|
|
120
|
+
if (stderrTail.length > MAX_STDERR_BYTES) {
|
|
121
|
+
stderrTail = stderrTail.slice(-MAX_STDERR_BYTES);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
pendingErr += text;
|
|
125
|
+
pendingErr = flushLines(pendingErr, (line) => onProgress(line, 'stderr'));
|
|
126
|
+
});
|
|
127
|
+
let settled = false;
|
|
128
|
+
const settle = (state, code) => {
|
|
129
|
+
if (settled)
|
|
130
|
+
return;
|
|
131
|
+
settled = true;
|
|
132
|
+
clearTimeout(timer);
|
|
133
|
+
// Flush any trailing partial line.
|
|
134
|
+
if (pendingOut.trim().length > 0)
|
|
135
|
+
onProgress(pendingOut.trim(), 'stdout');
|
|
136
|
+
if (pendingErr.trim().length > 0)
|
|
137
|
+
onProgress(pendingErr.trim(), 'stderr');
|
|
138
|
+
resolve({
|
|
139
|
+
state,
|
|
140
|
+
exitCode: code,
|
|
141
|
+
command: argv.pretty,
|
|
142
|
+
durationMs: Date.now() - started,
|
|
143
|
+
stdoutTail,
|
|
144
|
+
stderrTail,
|
|
145
|
+
});
|
|
146
|
+
};
|
|
147
|
+
const timer = setTimeout(() => {
|
|
148
|
+
try {
|
|
149
|
+
child.kill('SIGTERM');
|
|
150
|
+
}
|
|
151
|
+
catch {
|
|
152
|
+
// Process already exited — ignore.
|
|
153
|
+
}
|
|
154
|
+
// Give it a beat to clean up before we resolve as an error.
|
|
155
|
+
setTimeout(() => settle('error', null), 250);
|
|
156
|
+
}, FIX_TIMEOUT_MS);
|
|
157
|
+
child.on('error', () => settle('error', null));
|
|
158
|
+
child.on('close', (code) => {
|
|
159
|
+
// npm install exits 0 on success. npm audit fix exits 0 on full
|
|
160
|
+
// remediation; non-zero when issues remain (which from the
|
|
161
|
+
// user's POV may still be progress). We treat any non-zero exit
|
|
162
|
+
// as `error` so the UI surfaces it, but the rescan that follows
|
|
163
|
+
// will accurately show the new state.
|
|
164
|
+
settle(code === 0 ? 'success' : 'error', code ?? null);
|
|
165
|
+
});
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* Split `buffer` on newlines, hand each complete line to `onLine`, and
|
|
170
|
+
* return whatever partial line remains for next time.
|
|
171
|
+
*/
|
|
172
|
+
function flushLines(buffer, onLine) {
|
|
173
|
+
let start = 0;
|
|
174
|
+
for (let i = 0; i < buffer.length; i++) {
|
|
175
|
+
const ch = buffer.charCodeAt(i);
|
|
176
|
+
if (ch !== 10 && ch !== 13)
|
|
177
|
+
continue; // \n or \r
|
|
178
|
+
const line = buffer.slice(start, i);
|
|
179
|
+
if (line.length > 0)
|
|
180
|
+
onLine(line);
|
|
181
|
+
// Skip CRLF as a unit.
|
|
182
|
+
if (ch === 13 && buffer.charCodeAt(i + 1) === 10)
|
|
183
|
+
i++;
|
|
184
|
+
start = i + 1;
|
|
185
|
+
}
|
|
186
|
+
return buffer.slice(start);
|
|
187
|
+
}
|
|
188
|
+
//# sourceMappingURL=fix-runner.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fix-runner.js","sourceRoot":"","sources":["../../src/security/fix-runner.ts"],"names":[],"mappings":"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"}
|