@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,140 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Security engine — orchestrates supply-chain scanning (npm audit +
|
|
3
|
+
* OSV) and runtime posture analysis, packages the result into a single
|
|
4
|
+
* `SecurityReport`, and offers a debounced re-run hook the agent can
|
|
5
|
+
* call on each new exchange/log without breaking the host event loop.
|
|
6
|
+
*
|
|
7
|
+
* The engine is intentionally framework-free: it takes plain snapshot
|
|
8
|
+
* functions from the agent and emits reports to a listener. That makes
|
|
9
|
+
* it trivially testable and keeps the security policy (debounce,
|
|
10
|
+
* change detection, gating on connected clients) firmly in the agent
|
|
11
|
+
* itself — the engine just provides the data.
|
|
12
|
+
*/
|
|
13
|
+
import type { AppStructure, FixProgressMessage, FixResultMessage, RecordedExchange, RouteInfo, SecurityReport } from '../types/index.js';
|
|
14
|
+
import type { LogEntry } from '../logging/log-capture.js';
|
|
15
|
+
/**
|
|
16
|
+
* Snapshot accessors the engine reads on each pass. Functions, not
|
|
17
|
+
* snapshot objects, so we always see the freshest state without the
|
|
18
|
+
* agent having to thread updates through.
|
|
19
|
+
*/
|
|
20
|
+
export interface SecurityEngineDeps {
|
|
21
|
+
cwd: string;
|
|
22
|
+
dbPath: string;
|
|
23
|
+
getRoutes: () => RouteInfo[];
|
|
24
|
+
getStructure: () => AppStructure | null;
|
|
25
|
+
getExchanges: () => RecordedExchange[];
|
|
26
|
+
getLogs: () => LogEntry[];
|
|
27
|
+
}
|
|
28
|
+
export type SecurityReportListener = (report: SecurityReport) => void;
|
|
29
|
+
/**
|
|
30
|
+
* Hook the engine calls during an "Apply fix" run so the agent can
|
|
31
|
+
* stream output back to the UI. Kept as a callback (not a listener
|
|
32
|
+
* registered via `onReport`) because progress is per-call, not a
|
|
33
|
+
* global subscription.
|
|
34
|
+
*/
|
|
35
|
+
export type FixProgressListener = (msg: FixProgressMessage) => void;
|
|
36
|
+
/**
|
|
37
|
+
* Input to `applyFix` — references either a single finding (`findingId`)
|
|
38
|
+
* or a fix group (`fixGroupId`). When the request can't be resolved
|
|
39
|
+
* (stale id, no matching fix), the engine resolves with `success: false`
|
|
40
|
+
* and an explanatory `summary` rather than throwing.
|
|
41
|
+
*/
|
|
42
|
+
export interface ApplyFixInput {
|
|
43
|
+
targetKind: 'finding' | 'fix-group';
|
|
44
|
+
/** ID of the target — either `FixGroup.id` or `DependencyFinding.id`. */
|
|
45
|
+
targetId: string;
|
|
46
|
+
/** Set to true to allow semver-major upgrades (`--force`). */
|
|
47
|
+
allowMajor?: boolean;
|
|
48
|
+
}
|
|
49
|
+
export declare class SecurityEngine {
|
|
50
|
+
private readonly deps;
|
|
51
|
+
private readonly osvClient;
|
|
52
|
+
private readonly osvCache;
|
|
53
|
+
private lastDependencies;
|
|
54
|
+
private lastFixGroups;
|
|
55
|
+
private lastFixAvailability;
|
|
56
|
+
private lastLockfile;
|
|
57
|
+
private lastReachability;
|
|
58
|
+
private lastReport;
|
|
59
|
+
private lastHash;
|
|
60
|
+
private listener;
|
|
61
|
+
private postureTimer;
|
|
62
|
+
/**
|
|
63
|
+
* Start in `running` so a freshly-connected UI doesn't briefly show
|
|
64
|
+
* "no vulnerabilities" before the first scan completes. The engine
|
|
65
|
+
* flips this to `idle` / `error` when `runFullScan` finishes.
|
|
66
|
+
*/
|
|
67
|
+
private auditState;
|
|
68
|
+
private auditError;
|
|
69
|
+
private missingLockfile;
|
|
70
|
+
private auditInFlight;
|
|
71
|
+
/** State of an in-flight Apply-fix job (one at a time). */
|
|
72
|
+
private fixState;
|
|
73
|
+
private fixInFlight;
|
|
74
|
+
constructor(deps: SecurityEngineDeps);
|
|
75
|
+
/** Subscribe to security report transitions. Only one listener supported. */
|
|
76
|
+
onReport(listener: SecurityReportListener): void;
|
|
77
|
+
/** Latest known report (always safe — initialised to an empty one). */
|
|
78
|
+
getReport(): SecurityReport;
|
|
79
|
+
/**
|
|
80
|
+
* Run a full scan: `npm audit` + OSV.dev + posture analysis. Safe to
|
|
81
|
+
* call repeatedly — concurrent calls coalesce onto the in-flight
|
|
82
|
+
* promise so we never spawn two `npm audit` children at once.
|
|
83
|
+
*/
|
|
84
|
+
runFullScan(): Promise<void>;
|
|
85
|
+
/**
|
|
86
|
+
* Apply an enrichment pipeline against the latest snapshots. Split
|
|
87
|
+
* out so `scheduleRefresh` can re-run reachability against fresh
|
|
88
|
+
* exchanges without re-fetching OSV / re-running npm audit.
|
|
89
|
+
*/
|
|
90
|
+
private enrichDependencies;
|
|
91
|
+
/**
|
|
92
|
+
* Notify the engine that some upstream state likely changed (a new
|
|
93
|
+
* exchange, log line, etc.). Debounced — multiple calls inside the
|
|
94
|
+
* debounce window collapse to a single posture pass.
|
|
95
|
+
*/
|
|
96
|
+
scheduleRefresh(): void;
|
|
97
|
+
/** Cancel any pending refresh — called from `StudioAgent.stop()`. */
|
|
98
|
+
stop(): void;
|
|
99
|
+
/**
|
|
100
|
+
* Rebuild the report from current state and emit if its finding-id
|
|
101
|
+
* hash changed (or if the scan state changed). This is the single
|
|
102
|
+
* point where we decide whether to disturb the WS stream.
|
|
103
|
+
*
|
|
104
|
+
* On a posture-only refresh we also re-run reachability against the
|
|
105
|
+
* latest exchange buffer so the chips ("confirmed: 5 hits") update
|
|
106
|
+
* without a full audit.
|
|
107
|
+
*/
|
|
108
|
+
private rebuildAndEmit;
|
|
109
|
+
/** Emit a transient "scan running / error" frame even when findings haven't changed. */
|
|
110
|
+
private emitProgress;
|
|
111
|
+
/**
|
|
112
|
+
* Apply a remediation. The caller (agent) wires the per-line progress
|
|
113
|
+
* callback into a `fix_progress` WS broadcast. Returns the final
|
|
114
|
+
* `FixResultMessage` once the spawned command exits.
|
|
115
|
+
*
|
|
116
|
+
* The method enforces two invariants:
|
|
117
|
+
* 1. Only one fix runs at a time (`fixInFlight` is awaited).
|
|
118
|
+
* 2. After every fix attempt — success or failure — we trigger a
|
|
119
|
+
* full rescan. The post-scan `security` frame is what tells the
|
|
120
|
+
* UI whether the change actually cleared the advisory.
|
|
121
|
+
*/
|
|
122
|
+
applyFix(input: ApplyFixInput, onProgress: FixProgressListener): Promise<FixResultMessage>;
|
|
123
|
+
/**
|
|
124
|
+
* Look up the FixSpec corresponding to the user's request and convert
|
|
125
|
+
* it into the argv tuple `runFix` expects. Returns `null` for unknown
|
|
126
|
+
* ids / nothing-to-do specs.
|
|
127
|
+
*/
|
|
128
|
+
private resolveFixTarget;
|
|
129
|
+
private snapshotScanState;
|
|
130
|
+
}
|
|
131
|
+
export { OsvCache } from './osv-cache.js';
|
|
132
|
+
export { OsvClient } from './osv-client.js';
|
|
133
|
+
export { runNpmAudit } from './npm-audit.js';
|
|
134
|
+
export { analyzePosture } from './posture-analyzer.js';
|
|
135
|
+
export { buildSecurityReport, hashFindingIds, emptyReport } from './score.js';
|
|
136
|
+
export { LockfileGraph } from './lockfile-graph.js';
|
|
137
|
+
export { enrichFindings as enrichFindingsWithFixes, buildFixGroups, } from './fix-resolver.js';
|
|
138
|
+
export { buildReachabilitySnapshot, enrichWithReachability, } from './reachability.js';
|
|
139
|
+
export { runFix, buildFixArgs } from './fix-runner.js';
|
|
140
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/security/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,460 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Security engine — orchestrates supply-chain scanning (npm audit +
|
|
3
|
+
* OSV) and runtime posture analysis, packages the result into a single
|
|
4
|
+
* `SecurityReport`, and offers a debounced re-run hook the agent can
|
|
5
|
+
* call on each new exchange/log without breaking the host event loop.
|
|
6
|
+
*
|
|
7
|
+
* The engine is intentionally framework-free: it takes plain snapshot
|
|
8
|
+
* functions from the agent and emits reports to a listener. That makes
|
|
9
|
+
* it trivially testable and keeps the security policy (debounce,
|
|
10
|
+
* change detection, gating on connected clients) firmly in the agent
|
|
11
|
+
* itself — the engine just provides the data.
|
|
12
|
+
*/
|
|
13
|
+
import * as fs from 'node:fs';
|
|
14
|
+
import * as path from 'node:path';
|
|
15
|
+
import { runNpmAudit, } from './npm-audit.js';
|
|
16
|
+
import { OsvClient } from './osv-client.js';
|
|
17
|
+
import { OsvCache } from './osv-cache.js';
|
|
18
|
+
import { analyzePosture } from './posture-analyzer.js';
|
|
19
|
+
import { buildSecurityReport, emptyReport, hashFindingIds, } from './score.js';
|
|
20
|
+
import { LockfileGraph } from './lockfile-graph.js';
|
|
21
|
+
import { buildFixGroups, enrichFindings, } from './fix-resolver.js';
|
|
22
|
+
import { buildReachabilitySnapshot, enrichWithReachability, } from './reachability.js';
|
|
23
|
+
import { buildFixArgs, runFix, } from './fix-runner.js';
|
|
24
|
+
/** How often the engine will re-run posture analysis on event-driven triggers. */
|
|
25
|
+
const POSTURE_DEBOUNCE_MS = 2000;
|
|
26
|
+
export class SecurityEngine {
|
|
27
|
+
deps;
|
|
28
|
+
osvClient;
|
|
29
|
+
osvCache;
|
|
30
|
+
lastDependencies = [];
|
|
31
|
+
lastFixGroups = [];
|
|
32
|
+
lastFixAvailability = new Map();
|
|
33
|
+
lastLockfile = null;
|
|
34
|
+
lastReachability = null;
|
|
35
|
+
lastReport;
|
|
36
|
+
lastHash = '';
|
|
37
|
+
listener = null;
|
|
38
|
+
postureTimer = null;
|
|
39
|
+
/**
|
|
40
|
+
* Start in `running` so a freshly-connected UI doesn't briefly show
|
|
41
|
+
* "no vulnerabilities" before the first scan completes. The engine
|
|
42
|
+
* flips this to `idle` / `error` when `runFullScan` finishes.
|
|
43
|
+
*/
|
|
44
|
+
auditState = 'running';
|
|
45
|
+
auditError;
|
|
46
|
+
missingLockfile = false;
|
|
47
|
+
auditInFlight = null;
|
|
48
|
+
/** State of an in-flight Apply-fix job (one at a time). */
|
|
49
|
+
fixState = undefined;
|
|
50
|
+
fixInFlight = null;
|
|
51
|
+
constructor(deps) {
|
|
52
|
+
this.deps = deps;
|
|
53
|
+
this.osvCache = new OsvCache(deps.dbPath);
|
|
54
|
+
this.osvClient = new OsvClient(this.osvCache);
|
|
55
|
+
this.lastReport = emptyReport(this.snapshotScanState(0));
|
|
56
|
+
}
|
|
57
|
+
/** Subscribe to security report transitions. Only one listener supported. */
|
|
58
|
+
onReport(listener) {
|
|
59
|
+
this.listener = listener;
|
|
60
|
+
}
|
|
61
|
+
/** Latest known report (always safe — initialised to an empty one). */
|
|
62
|
+
getReport() {
|
|
63
|
+
return this.lastReport;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Run a full scan: `npm audit` + OSV.dev + posture analysis. Safe to
|
|
67
|
+
* call repeatedly — concurrent calls coalesce onto the in-flight
|
|
68
|
+
* promise so we never spawn two `npm audit` children at once.
|
|
69
|
+
*/
|
|
70
|
+
async runFullScan() {
|
|
71
|
+
if (this.auditInFlight)
|
|
72
|
+
return this.auditInFlight;
|
|
73
|
+
this.auditInFlight = (async () => {
|
|
74
|
+
this.auditState = 'running';
|
|
75
|
+
this.auditError = undefined;
|
|
76
|
+
this.emitProgress();
|
|
77
|
+
const audit = await runNpmAudit(this.deps.cwd);
|
|
78
|
+
this.missingLockfile = audit.state === 'missing-lockfile';
|
|
79
|
+
this.lastFixAvailability = audit.fixAvailability;
|
|
80
|
+
if (audit.state === 'error') {
|
|
81
|
+
this.auditState = 'error';
|
|
82
|
+
this.auditError = audit.error;
|
|
83
|
+
}
|
|
84
|
+
else {
|
|
85
|
+
this.auditState = 'idle';
|
|
86
|
+
}
|
|
87
|
+
let osvFindings = [];
|
|
88
|
+
if (audit.state === 'ok') {
|
|
89
|
+
try {
|
|
90
|
+
const packages = readInstalledPackages(this.deps.cwd);
|
|
91
|
+
osvFindings = await this.osvClient.lookup(packages);
|
|
92
|
+
}
|
|
93
|
+
catch {
|
|
94
|
+
// OSV failures degrade gracefully — we still ship npm audit findings.
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
// Reconcile npm + OSV first; then enrich with lockfile root-cause
|
|
98
|
+
// analysis; then with reachability. The enrichment passes are pure
|
|
99
|
+
// functions of (findings, snapshot) so order is deterministic.
|
|
100
|
+
const reconciled = reconcileDependencyFindings(audit.findings, osvFindings);
|
|
101
|
+
this.lastLockfile = LockfileGraph.load(this.deps.cwd);
|
|
102
|
+
this.lastReachability = await buildReachabilitySnapshot(this.deps.cwd, this.deps.getStructure());
|
|
103
|
+
this.lastDependencies = this.enrichDependencies(reconciled);
|
|
104
|
+
this.lastFixGroups = buildFixGroups(this.lastDependencies);
|
|
105
|
+
this.rebuildAndEmit();
|
|
106
|
+
})();
|
|
107
|
+
try {
|
|
108
|
+
await this.auditInFlight;
|
|
109
|
+
}
|
|
110
|
+
finally {
|
|
111
|
+
this.auditInFlight = null;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Apply an enrichment pipeline against the latest snapshots. Split
|
|
116
|
+
* out so `scheduleRefresh` can re-run reachability against fresh
|
|
117
|
+
* exchanges without re-fetching OSV / re-running npm audit.
|
|
118
|
+
*/
|
|
119
|
+
enrichDependencies(findings) {
|
|
120
|
+
const withFix = enrichFindings(findings, this.lastFixAvailability, this.lastLockfile);
|
|
121
|
+
if (!this.lastReachability)
|
|
122
|
+
return withFix;
|
|
123
|
+
return enrichWithReachability(withFix, this.lastReachability, this.deps.getExchanges());
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Notify the engine that some upstream state likely changed (a new
|
|
127
|
+
* exchange, log line, etc.). Debounced — multiple calls inside the
|
|
128
|
+
* debounce window collapse to a single posture pass.
|
|
129
|
+
*/
|
|
130
|
+
scheduleRefresh() {
|
|
131
|
+
if (this.postureTimer)
|
|
132
|
+
return;
|
|
133
|
+
this.postureTimer = setTimeout(() => {
|
|
134
|
+
this.postureTimer = null;
|
|
135
|
+
this.rebuildAndEmit();
|
|
136
|
+
}, POSTURE_DEBOUNCE_MS);
|
|
137
|
+
}
|
|
138
|
+
/** Cancel any pending refresh — called from `StudioAgent.stop()`. */
|
|
139
|
+
stop() {
|
|
140
|
+
if (this.postureTimer) {
|
|
141
|
+
clearTimeout(this.postureTimer);
|
|
142
|
+
this.postureTimer = null;
|
|
143
|
+
}
|
|
144
|
+
this.osvCache.flush();
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Rebuild the report from current state and emit if its finding-id
|
|
148
|
+
* hash changed (or if the scan state changed). This is the single
|
|
149
|
+
* point where we decide whether to disturb the WS stream.
|
|
150
|
+
*
|
|
151
|
+
* On a posture-only refresh we also re-run reachability against the
|
|
152
|
+
* latest exchange buffer so the chips ("confirmed: 5 hits") update
|
|
153
|
+
* without a full audit.
|
|
154
|
+
*/
|
|
155
|
+
rebuildAndEmit() {
|
|
156
|
+
const posture = analyzePosture({
|
|
157
|
+
routes: this.deps.getRoutes(),
|
|
158
|
+
structure: this.deps.getStructure(),
|
|
159
|
+
exchanges: this.deps.getExchanges(),
|
|
160
|
+
logs: this.deps.getLogs(),
|
|
161
|
+
});
|
|
162
|
+
// Light-touch refresh: reachability snapshot only depends on src/
|
|
163
|
+
// (rarely changes during a session) so we reuse the cached one,
|
|
164
|
+
// but we re-run `enrichWithReachability` to fold in the latest
|
|
165
|
+
// exchange counts.
|
|
166
|
+
if (this.lastReachability && this.lastDependencies.length > 0) {
|
|
167
|
+
this.lastDependencies = enrichWithReachability(this.lastDependencies, this.lastReachability, this.deps.getExchanges());
|
|
168
|
+
// Reachability changes can move findings between groups (severity
|
|
169
|
+
// tie-break uses reachability), so we rebuild groups too.
|
|
170
|
+
this.lastFixGroups = buildFixGroups(this.lastDependencies);
|
|
171
|
+
}
|
|
172
|
+
const report = buildSecurityReport({
|
|
173
|
+
dependencies: this.lastDependencies,
|
|
174
|
+
posture,
|
|
175
|
+
fixGroups: this.lastFixGroups,
|
|
176
|
+
scanState: this.snapshotScanState(Date.now()),
|
|
177
|
+
});
|
|
178
|
+
const nextHash = hashFindingIds(report) +
|
|
179
|
+
'|' +
|
|
180
|
+
scanStateHash(report.scanState) +
|
|
181
|
+
'|' +
|
|
182
|
+
hashReachability(report.dependencies);
|
|
183
|
+
this.lastReport = report;
|
|
184
|
+
if (nextHash !== this.lastHash) {
|
|
185
|
+
this.lastHash = nextHash;
|
|
186
|
+
this.listener?.(report);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
/** Emit a transient "scan running / error" frame even when findings haven't changed. */
|
|
190
|
+
emitProgress() {
|
|
191
|
+
const report = buildSecurityReport({
|
|
192
|
+
dependencies: this.lastDependencies,
|
|
193
|
+
posture: this.lastReport.posture,
|
|
194
|
+
fixGroups: this.lastFixGroups,
|
|
195
|
+
scanState: this.snapshotScanState(this.lastReport.scanState.postureLastRunAt),
|
|
196
|
+
});
|
|
197
|
+
this.lastReport = report;
|
|
198
|
+
// Force-emit: scan state transitions are user-visible.
|
|
199
|
+
this.lastHash =
|
|
200
|
+
hashFindingIds(report) +
|
|
201
|
+
'|' +
|
|
202
|
+
scanStateHash(report.scanState) +
|
|
203
|
+
'|' +
|
|
204
|
+
hashReachability(report.dependencies);
|
|
205
|
+
this.listener?.(report);
|
|
206
|
+
}
|
|
207
|
+
/**
|
|
208
|
+
* Apply a remediation. The caller (agent) wires the per-line progress
|
|
209
|
+
* callback into a `fix_progress` WS broadcast. Returns the final
|
|
210
|
+
* `FixResultMessage` once the spawned command exits.
|
|
211
|
+
*
|
|
212
|
+
* The method enforces two invariants:
|
|
213
|
+
* 1. Only one fix runs at a time (`fixInFlight` is awaited).
|
|
214
|
+
* 2. After every fix attempt — success or failure — we trigger a
|
|
215
|
+
* full rescan. The post-scan `security` frame is what tells the
|
|
216
|
+
* UI whether the change actually cleared the advisory.
|
|
217
|
+
*/
|
|
218
|
+
async applyFix(input, onProgress) {
|
|
219
|
+
if (this.fixInFlight) {
|
|
220
|
+
return {
|
|
221
|
+
targetId: input.targetId,
|
|
222
|
+
success: false,
|
|
223
|
+
exitCode: null,
|
|
224
|
+
durationMs: 0,
|
|
225
|
+
command: '',
|
|
226
|
+
summary: 'Another fix is already running. Please wait.',
|
|
227
|
+
};
|
|
228
|
+
}
|
|
229
|
+
const target = this.resolveFixTarget(input);
|
|
230
|
+
if (!target) {
|
|
231
|
+
return {
|
|
232
|
+
targetId: input.targetId,
|
|
233
|
+
success: false,
|
|
234
|
+
exitCode: null,
|
|
235
|
+
durationMs: 0,
|
|
236
|
+
command: '',
|
|
237
|
+
summary: 'Fix target not found. Rescan and try again.',
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
const job = (async () => {
|
|
241
|
+
this.fixState = {
|
|
242
|
+
state: 'running',
|
|
243
|
+
targetId: input.targetId,
|
|
244
|
+
command: target.pretty,
|
|
245
|
+
};
|
|
246
|
+
this.emitProgress();
|
|
247
|
+
const result = await runFix({
|
|
248
|
+
cwd: this.deps.cwd,
|
|
249
|
+
kind: target.kind,
|
|
250
|
+
package: target.package,
|
|
251
|
+
version: target.version,
|
|
252
|
+
targetId: input.targetId,
|
|
253
|
+
}, (line, stream) => onProgress({
|
|
254
|
+
targetId: input.targetId,
|
|
255
|
+
stream,
|
|
256
|
+
line,
|
|
257
|
+
timestamp: Date.now(),
|
|
258
|
+
}));
|
|
259
|
+
const summary = result.state === 'success'
|
|
260
|
+
? `${target.pretty} completed in ${(result.durationMs / 1000).toFixed(1)}s`
|
|
261
|
+
: `${target.pretty} failed (${result.exitCode ?? 'no exit code'})`;
|
|
262
|
+
const finalMsg = {
|
|
263
|
+
targetId: input.targetId,
|
|
264
|
+
success: result.state === 'success',
|
|
265
|
+
exitCode: result.exitCode,
|
|
266
|
+
durationMs: result.durationMs,
|
|
267
|
+
command: result.command || target.pretty,
|
|
268
|
+
summary,
|
|
269
|
+
errorTail: result.state === 'success'
|
|
270
|
+
? undefined
|
|
271
|
+
: result.stderrTail.slice(-4096) || result.stdoutTail.slice(-4096),
|
|
272
|
+
};
|
|
273
|
+
this.fixState = {
|
|
274
|
+
state: result.state === 'success' ? 'success' : 'error',
|
|
275
|
+
targetId: input.targetId,
|
|
276
|
+
command: result.command || target.pretty,
|
|
277
|
+
error: result.state === 'success' ? undefined : summary,
|
|
278
|
+
};
|
|
279
|
+
// Always rescan — even on failure — so the UI agrees with the
|
|
280
|
+
// lockfile's actual current state, not what we *hoped* would change.
|
|
281
|
+
await this.runFullScan();
|
|
282
|
+
// Clear the fix banner once the rescan has updated everything else.
|
|
283
|
+
this.fixState = undefined;
|
|
284
|
+
this.emitProgress();
|
|
285
|
+
return finalMsg;
|
|
286
|
+
})();
|
|
287
|
+
this.fixInFlight = job;
|
|
288
|
+
try {
|
|
289
|
+
return await job;
|
|
290
|
+
}
|
|
291
|
+
finally {
|
|
292
|
+
this.fixInFlight = null;
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
/**
|
|
296
|
+
* Look up the FixSpec corresponding to the user's request and convert
|
|
297
|
+
* it into the argv tuple `runFix` expects. Returns `null` for unknown
|
|
298
|
+
* ids / nothing-to-do specs.
|
|
299
|
+
*/
|
|
300
|
+
resolveFixTarget(input) {
|
|
301
|
+
const spec = input.targetKind === 'fix-group'
|
|
302
|
+
? this.lastFixGroups.find((g) => g.id === input.targetId)?.fix
|
|
303
|
+
: this.lastDependencies.find((f) => f.id === input.targetId)?.fix;
|
|
304
|
+
if (!spec)
|
|
305
|
+
return null;
|
|
306
|
+
let kind;
|
|
307
|
+
switch (spec.kind) {
|
|
308
|
+
case 'install':
|
|
309
|
+
kind = 'install';
|
|
310
|
+
break;
|
|
311
|
+
case 'audit-fix':
|
|
312
|
+
kind = 'audit-fix';
|
|
313
|
+
break;
|
|
314
|
+
case 'audit-fix-force':
|
|
315
|
+
kind = input.allowMajor ? 'audit-fix-force' : 'audit-fix-force';
|
|
316
|
+
break;
|
|
317
|
+
case 'override':
|
|
318
|
+
case 'none':
|
|
319
|
+
return null;
|
|
320
|
+
}
|
|
321
|
+
// For `install` we need to break the version out of the command.
|
|
322
|
+
let pkg;
|
|
323
|
+
let ver;
|
|
324
|
+
if (kind === 'install') {
|
|
325
|
+
const m = spec.command.match(/^npm install\s+(.+?)@([^@]+)$/);
|
|
326
|
+
if (!m)
|
|
327
|
+
return null;
|
|
328
|
+
pkg = m[1].trim();
|
|
329
|
+
ver = m[2].trim().replace(/^['"]|['"]$/g, '');
|
|
330
|
+
}
|
|
331
|
+
const argv = buildFixArgs({
|
|
332
|
+
cwd: this.deps.cwd,
|
|
333
|
+
kind,
|
|
334
|
+
package: pkg,
|
|
335
|
+
version: ver,
|
|
336
|
+
targetId: input.targetId,
|
|
337
|
+
});
|
|
338
|
+
if (!argv)
|
|
339
|
+
return null;
|
|
340
|
+
return { kind, pretty: argv.pretty, package: pkg, version: ver };
|
|
341
|
+
}
|
|
342
|
+
snapshotScanState(postureLastRunAt) {
|
|
343
|
+
return {
|
|
344
|
+
audit: this.auditState,
|
|
345
|
+
postureLastRunAt,
|
|
346
|
+
auditError: this.auditError,
|
|
347
|
+
missingLockfile: this.missingLockfile || undefined,
|
|
348
|
+
fix: this.fixState,
|
|
349
|
+
};
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
/**
|
|
353
|
+
* Reachability counts are part of the report payload — when a recorded
|
|
354
|
+
* exchange flips a finding from `likely` → `confirmed` we want the UI
|
|
355
|
+
* to update even if the finding-id set hasn't changed.
|
|
356
|
+
*/
|
|
357
|
+
function hashReachability(deps) {
|
|
358
|
+
const parts = [];
|
|
359
|
+
for (const f of deps) {
|
|
360
|
+
const r = f.reachability;
|
|
361
|
+
if (!r)
|
|
362
|
+
continue;
|
|
363
|
+
parts.push(`${f.id}:${r.level}:${r.runtimeHits}`);
|
|
364
|
+
}
|
|
365
|
+
parts.sort();
|
|
366
|
+
return parts.join('|');
|
|
367
|
+
}
|
|
368
|
+
function scanStateHash(s) {
|
|
369
|
+
return [
|
|
370
|
+
s.audit,
|
|
371
|
+
s.missingLockfile ? '1' : '0',
|
|
372
|
+
s.auditError ?? '',
|
|
373
|
+
s.fix?.state ?? '',
|
|
374
|
+
s.fix?.targetId ?? '',
|
|
375
|
+
].join('|');
|
|
376
|
+
}
|
|
377
|
+
/**
|
|
378
|
+
* Combine findings from `npm audit` and OSV, deduping by id. We trust
|
|
379
|
+
* npm audit's transitive `path` (it knows the lockfile) and OSV's
|
|
380
|
+
* severity/refs (richer than what npm reports). When both sources
|
|
381
|
+
* produce a finding for the same id, we merge.
|
|
382
|
+
*/
|
|
383
|
+
function reconcileDependencyFindings(fromNpm, fromOsv) {
|
|
384
|
+
const byId = new Map();
|
|
385
|
+
for (const f of fromNpm)
|
|
386
|
+
byId.set(f.id, { ...f });
|
|
387
|
+
for (const f of fromOsv) {
|
|
388
|
+
const existing = byId.get(f.id);
|
|
389
|
+
if (!existing) {
|
|
390
|
+
byId.set(f.id, f);
|
|
391
|
+
continue;
|
|
392
|
+
}
|
|
393
|
+
// Merge: keep npm's path (it knows the resolution graph), prefer
|
|
394
|
+
// OSV's references/summary/cvss (they're richer), and pick the
|
|
395
|
+
// higher severity to avoid silently downgrading anything.
|
|
396
|
+
byId.set(f.id, {
|
|
397
|
+
...existing,
|
|
398
|
+
summary: f.summary || existing.summary,
|
|
399
|
+
references: dedupe([...existing.references, ...f.references]),
|
|
400
|
+
severity: pickHigherSeverity(existing.severity, f.severity),
|
|
401
|
+
cvss: f.cvss ?? existing.cvss,
|
|
402
|
+
fixedVersion: f.fixedVersion ?? existing.fixedVersion,
|
|
403
|
+
});
|
|
404
|
+
}
|
|
405
|
+
return [...byId.values()];
|
|
406
|
+
}
|
|
407
|
+
function pickHigherSeverity(a, b) {
|
|
408
|
+
const order = [
|
|
409
|
+
'INFO',
|
|
410
|
+
'LOW',
|
|
411
|
+
'MEDIUM',
|
|
412
|
+
'HIGH',
|
|
413
|
+
'CRITICAL',
|
|
414
|
+
];
|
|
415
|
+
return order.indexOf(a) >= order.indexOf(b) ? a : b;
|
|
416
|
+
}
|
|
417
|
+
function dedupe(arr) {
|
|
418
|
+
return [...new Set(arr)];
|
|
419
|
+
}
|
|
420
|
+
/**
|
|
421
|
+
* Read the host's `package.json` and return a flat list of direct
|
|
422
|
+
* dependencies to query OSV for. We don't traverse `node_modules` here
|
|
423
|
+
* — npm audit already covers transitive vulns; querying OSV for every
|
|
424
|
+
* transitive dep would blow up the batch size and add latency for
|
|
425
|
+
* diminishing returns.
|
|
426
|
+
*/
|
|
427
|
+
function readInstalledPackages(cwd) {
|
|
428
|
+
const file = path.join(cwd, 'package.json');
|
|
429
|
+
if (!fs.existsSync(file))
|
|
430
|
+
return [];
|
|
431
|
+
try {
|
|
432
|
+
const raw = fs.readFileSync(file, 'utf-8');
|
|
433
|
+
const parsed = JSON.parse(raw);
|
|
434
|
+
const out = [];
|
|
435
|
+
for (const map of [parsed.dependencies, parsed.devDependencies]) {
|
|
436
|
+
if (!map)
|
|
437
|
+
continue;
|
|
438
|
+
for (const [name, version] of Object.entries(map)) {
|
|
439
|
+
out.push({ name, version: stripSemverPrefix(version) });
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
return out;
|
|
443
|
+
}
|
|
444
|
+
catch {
|
|
445
|
+
return [];
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
function stripSemverPrefix(version) {
|
|
449
|
+
return version.replace(/^[\^~>=<]+/, '').trim();
|
|
450
|
+
}
|
|
451
|
+
export { OsvCache } from './osv-cache.js';
|
|
452
|
+
export { OsvClient } from './osv-client.js';
|
|
453
|
+
export { runNpmAudit } from './npm-audit.js';
|
|
454
|
+
export { analyzePosture } from './posture-analyzer.js';
|
|
455
|
+
export { buildSecurityReport, hashFindingIds, emptyReport } from './score.js';
|
|
456
|
+
export { LockfileGraph } from './lockfile-graph.js';
|
|
457
|
+
export { enrichFindings as enrichFindingsWithFixes, buildFixGroups, } from './fix-resolver.js';
|
|
458
|
+
export { buildReachabilitySnapshot, enrichWithReachability, } from './reachability.js';
|
|
459
|
+
export { runFix, buildFixArgs } from './fix-runner.js';
|
|
460
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/security/index.ts"],"names":[],"mappings":"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"}
|