@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,302 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Runtime reachability analysis for supply-chain findings.
|
|
3
|
+
*
|
|
4
|
+
* This is the single feature that justifies "why Studio over Snyk?". A
|
|
5
|
+
* static scanner can tell you that `lodash@4.17.10` has CVE-X; only a
|
|
6
|
+
* tool sitting inside the running app can tell you that:
|
|
7
|
+
*
|
|
8
|
+
* - `src/users/users.service.ts` actually imports lodash
|
|
9
|
+
* - that service is wired into `UsersController`, which owns `GET /users`
|
|
10
|
+
* - and `GET /users` has been hit 47 times in the last hour
|
|
11
|
+
*
|
|
12
|
+
* We combine three signals to land on one of four labels per finding:
|
|
13
|
+
*
|
|
14
|
+
* - **confirmed**: a route that imports (transitively) the vulnerable
|
|
15
|
+
* package has been exercised in the current session.
|
|
16
|
+
* - **likely**: the package is imported from `src/` but we haven't
|
|
17
|
+
* seen a request hit a route that reaches it yet.
|
|
18
|
+
* - **unreachable**: we have a complete import graph for `src/` and
|
|
19
|
+
* the package doesn't appear anywhere. Usually means it's only
|
|
20
|
+
* pulled in by a dev tool or another transitive dep.
|
|
21
|
+
* - **unknown**: we can't compute the graph (no src dir, scanning
|
|
22
|
+
* disabled). Default for transitive packages with no direct
|
|
23
|
+
* imports — we don't follow node_modules → node_modules edges.
|
|
24
|
+
*
|
|
25
|
+
* The analyzer is intentionally cheap: regex-based import extraction
|
|
26
|
+
* over the same source tree the route scanner already walked. We
|
|
27
|
+
* don't reach for a TypeScript AST — it'd be 100× slower for almost
|
|
28
|
+
* no precision gain at this granularity (we only need package names,
|
|
29
|
+
* not symbols).
|
|
30
|
+
*/
|
|
31
|
+
import * as fs from 'node:fs';
|
|
32
|
+
import * as path from 'node:path';
|
|
33
|
+
import { glob } from 'glob';
|
|
34
|
+
const IMPORT_PATTERNS = [
|
|
35
|
+
// ESM: import ... from 'pkg' / import 'pkg'
|
|
36
|
+
/import\s+(?:[^'"]*?\s+from\s+)?['"]([^'"]+)['"]/g,
|
|
37
|
+
// Re-exports: export ... from 'pkg'
|
|
38
|
+
/export\s+(?:[^'"]*?\s+from\s+)?['"]([^'"]+)['"]/g,
|
|
39
|
+
// Dynamic import: import('pkg')
|
|
40
|
+
/import\s*\(\s*['"]([^'"]+)['"]\s*\)/g,
|
|
41
|
+
// CJS: require('pkg')
|
|
42
|
+
/require\s*\(\s*['"]([^'"]+)['"]\s*\)/g,
|
|
43
|
+
];
|
|
44
|
+
/**
|
|
45
|
+
* Build a reachability snapshot for the host project. Returns an empty
|
|
46
|
+
* snapshot if `srcPath` doesn't exist — callers should treat that as
|
|
47
|
+
* "always emit `unknown`" rather than failing.
|
|
48
|
+
*
|
|
49
|
+
* Safe to call on every full scan; for a 1000-file project it usually
|
|
50
|
+
* completes in <100 ms.
|
|
51
|
+
*/
|
|
52
|
+
export async function buildReachabilitySnapshot(cwd, structure) {
|
|
53
|
+
const srcPath = findSrcPath(cwd);
|
|
54
|
+
const importedByPkg = new Map();
|
|
55
|
+
const filesByImports = new Map();
|
|
56
|
+
if (!srcPath) {
|
|
57
|
+
return { importedByPkg, routesByFile: new Map() };
|
|
58
|
+
}
|
|
59
|
+
const files = await glob('**/*.{ts,tsx,js,mjs,cjs}', {
|
|
60
|
+
cwd: srcPath,
|
|
61
|
+
ignore: ['**/node_modules/**', '**/*.spec.ts', '**/*.test.ts', '**/*.d.ts'],
|
|
62
|
+
absolute: true,
|
|
63
|
+
});
|
|
64
|
+
for (const file of files) {
|
|
65
|
+
let content;
|
|
66
|
+
try {
|
|
67
|
+
content = fs.readFileSync(file, 'utf-8');
|
|
68
|
+
}
|
|
69
|
+
catch {
|
|
70
|
+
continue;
|
|
71
|
+
}
|
|
72
|
+
const pkgs = extractPackageImports(content);
|
|
73
|
+
if (pkgs.size === 0)
|
|
74
|
+
continue;
|
|
75
|
+
filesByImports.set(file, pkgs);
|
|
76
|
+
for (const pkg of pkgs) {
|
|
77
|
+
const existing = importedByPkg.get(pkg) ?? new Set();
|
|
78
|
+
existing.add(file);
|
|
79
|
+
importedByPkg.set(pkg, existing);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
const routesByFile = buildRoutesByFile(structure, filesByImports);
|
|
83
|
+
return { importedByPkg, routesByFile };
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Attach a `ReachabilityInfo` block to every finding in `findings`.
|
|
87
|
+
* Pure given the snapshot + exchanges; same call with the same inputs
|
|
88
|
+
* always produces the same output, which keeps hashing stable.
|
|
89
|
+
*/
|
|
90
|
+
export function enrichWithReachability(findings, snapshot, exchanges) {
|
|
91
|
+
// Index exchanges by `method:path` so we can count hits per route
|
|
92
|
+
// in O(N) over routes regardless of exchange count.
|
|
93
|
+
const exchangeCountsByRoute = new Map();
|
|
94
|
+
for (const ex of exchanges) {
|
|
95
|
+
const method = ex.request.method;
|
|
96
|
+
const matchPath = ex.request.path || ex.request.url;
|
|
97
|
+
if (!matchPath)
|
|
98
|
+
continue;
|
|
99
|
+
const key = `${method}:${normalisePath(matchPath)}`;
|
|
100
|
+
exchangeCountsByRoute.set(key, (exchangeCountsByRoute.get(key) ?? 0) + 1);
|
|
101
|
+
}
|
|
102
|
+
return findings.map((finding) => {
|
|
103
|
+
// For transitive findings, prefer the root package — that's the
|
|
104
|
+
// one the user's source actually imports. We fall back to the
|
|
105
|
+
// vulnerable package name when no root cause is set (direct dep
|
|
106
|
+
// or unknown lockfile).
|
|
107
|
+
const probe = finding.rootCause?.rootPackage ?? finding.package;
|
|
108
|
+
const info = computeReachability(probe, snapshot, exchangeCountsByRoute, Boolean(finding.rootCause?.isDirect ?? snapshot.importedByPkg.has(probe)));
|
|
109
|
+
return { ...finding, reachability: info };
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
// ────────────────────────────────────────────────────────────────────────
|
|
113
|
+
// Internals
|
|
114
|
+
// ────────────────────────────────────────────────────────────────────────
|
|
115
|
+
function computeReachability(pkg, snapshot, exchangeCounts, hasGraph) {
|
|
116
|
+
const importers = snapshot.importedByPkg.get(pkg);
|
|
117
|
+
if (!importers || importers.size === 0) {
|
|
118
|
+
if (!hasGraph && snapshot.importedByPkg.size === 0) {
|
|
119
|
+
// We have no source-graph data at all (e.g. no `src/` dir,
|
|
120
|
+
// analyzer disabled). Be honest about it rather than calling
|
|
121
|
+
// every dep "unreachable".
|
|
122
|
+
return {
|
|
123
|
+
level: 'unknown',
|
|
124
|
+
importedBy: [],
|
|
125
|
+
routes: [],
|
|
126
|
+
runtimeHits: 0,
|
|
127
|
+
reason: 'No source graph available — Studio could not analyse `src/`.',
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
if (snapshot.importedByPkg.size > 0) {
|
|
131
|
+
// We scanned src/ successfully and the package never appears.
|
|
132
|
+
// For transitive packages this is the common case: they're
|
|
133
|
+
// only used internally by other deps.
|
|
134
|
+
return {
|
|
135
|
+
level: 'unreachable',
|
|
136
|
+
importedBy: [],
|
|
137
|
+
routes: [],
|
|
138
|
+
runtimeHits: 0,
|
|
139
|
+
reason: 'No source files import this package directly.',
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
return {
|
|
143
|
+
level: 'unknown',
|
|
144
|
+
importedBy: [],
|
|
145
|
+
routes: [],
|
|
146
|
+
runtimeHits: 0,
|
|
147
|
+
reason: 'No matching source files found.',
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
const importedBy = Array.from(importers);
|
|
151
|
+
const routes = new Map();
|
|
152
|
+
for (const file of importedBy) {
|
|
153
|
+
const fileRoutes = snapshot.routesByFile.get(file) ?? [];
|
|
154
|
+
for (const r of fileRoutes) {
|
|
155
|
+
routes.set(`${r.method}:${r.path}`, r);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
const routesArr = Array.from(routes.values()).map((r) => ({
|
|
159
|
+
method: r.method,
|
|
160
|
+
path: r.path,
|
|
161
|
+
}));
|
|
162
|
+
let hits = 0;
|
|
163
|
+
for (const r of routesArr) {
|
|
164
|
+
hits += exchangeCounts.get(`${r.method}:${normalisePath(r.path)}`) ?? 0;
|
|
165
|
+
}
|
|
166
|
+
let level;
|
|
167
|
+
let reason;
|
|
168
|
+
if (hits > 0) {
|
|
169
|
+
level = 'confirmed';
|
|
170
|
+
reason = `${routesArr.length} route${routesArr.length === 1 ? '' : 's'} reach this package and ${hits} request${hits === 1 ? '' : 's'} hit them in the current session.`;
|
|
171
|
+
}
|
|
172
|
+
else if (routesArr.length > 0) {
|
|
173
|
+
level = 'likely';
|
|
174
|
+
reason = `${routesArr.length} route${routesArr.length === 1 ? '' : 's'} reach this package, but none have been exercised yet.`;
|
|
175
|
+
}
|
|
176
|
+
else {
|
|
177
|
+
// Imported, but we couldn't tie it back to any route — could be a
|
|
178
|
+
// utility module, a bootstrap helper, or a worker. Still "likely"
|
|
179
|
+
// because the package *is* in your code path.
|
|
180
|
+
level = 'likely';
|
|
181
|
+
reason = `Imported by ${importedBy.length} file${importedBy.length === 1 ? '' : 's'} but no route reaches it directly.`;
|
|
182
|
+
}
|
|
183
|
+
return {
|
|
184
|
+
level,
|
|
185
|
+
importedBy,
|
|
186
|
+
routes: routesArr,
|
|
187
|
+
runtimeHits: hits,
|
|
188
|
+
reason,
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* Walk `content` looking for bare-package imports. Bare = not relative
|
|
193
|
+
* (`./foo`) and not absolute (`/foo`). Returns deduped package names
|
|
194
|
+
* (scoped packages keep their `@scope/` prefix).
|
|
195
|
+
*/
|
|
196
|
+
function extractPackageImports(content) {
|
|
197
|
+
const out = new Set();
|
|
198
|
+
for (const re of IMPORT_PATTERNS) {
|
|
199
|
+
re.lastIndex = 0;
|
|
200
|
+
let m;
|
|
201
|
+
while ((m = re.exec(content)) !== null) {
|
|
202
|
+
const spec = m[1];
|
|
203
|
+
if (!spec || spec.startsWith('.') || spec.startsWith('/'))
|
|
204
|
+
continue;
|
|
205
|
+
// Strip subpath: `foo/bar/baz` → `foo`, `@scope/pkg/sub` → `@scope/pkg`.
|
|
206
|
+
const parts = spec.split('/');
|
|
207
|
+
const name = spec.startsWith('@')
|
|
208
|
+
? parts.slice(0, 2).join('/')
|
|
209
|
+
: parts[0];
|
|
210
|
+
if (name)
|
|
211
|
+
out.add(name);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
return out;
|
|
215
|
+
}
|
|
216
|
+
/**
|
|
217
|
+
* Resolve `src/` for the host project. We try a few common spellings
|
|
218
|
+
* because not every ExpressoTS app uses literal `./src` (monorepo
|
|
219
|
+
* roots sometimes don't).
|
|
220
|
+
*/
|
|
221
|
+
function findSrcPath(cwd) {
|
|
222
|
+
const candidates = ['src', 'lib', 'app'];
|
|
223
|
+
for (const c of candidates) {
|
|
224
|
+
const full = path.join(cwd, c);
|
|
225
|
+
if (fs.existsSync(full) && fs.statSync(full).isDirectory()) {
|
|
226
|
+
return full;
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
return null;
|
|
230
|
+
}
|
|
231
|
+
/**
|
|
232
|
+
* Reverse-index controllers/services by file so each importing file
|
|
233
|
+
* can be answered "which routes does this serve?".
|
|
234
|
+
*
|
|
235
|
+
* The mapping isn't perfect (an imported util file may not be a
|
|
236
|
+
* controller itself), so we also fold in transitive reach: if a
|
|
237
|
+
* service file imports lodash and a controller depends on that
|
|
238
|
+
* service, the controller's routes are credited.
|
|
239
|
+
*/
|
|
240
|
+
function buildRoutesByFile(structure, filesByImports) {
|
|
241
|
+
const out = new Map();
|
|
242
|
+
if (!structure)
|
|
243
|
+
return out;
|
|
244
|
+
const controllersByFile = new Map();
|
|
245
|
+
for (const c of structure.controllers) {
|
|
246
|
+
controllersByFile.set(normalisePath(c.filePath), c);
|
|
247
|
+
}
|
|
248
|
+
const servicesByName = new Map();
|
|
249
|
+
for (const s of [...structure.services, ...structure.providers]) {
|
|
250
|
+
servicesByName.set(s.name, s);
|
|
251
|
+
}
|
|
252
|
+
// 1. Direct: a controller's own file gets all of its routes.
|
|
253
|
+
for (const c of structure.controllers) {
|
|
254
|
+
out.set(c.filePath, c.routes.slice());
|
|
255
|
+
}
|
|
256
|
+
// 2. Transitive: walk each controller's dependency graph and credit
|
|
257
|
+
// every file reachable through services it imports.
|
|
258
|
+
for (const c of structure.controllers) {
|
|
259
|
+
const visited = new Set();
|
|
260
|
+
const queue = [...c.dependencies];
|
|
261
|
+
while (queue.length > 0) {
|
|
262
|
+
const dep = queue.shift();
|
|
263
|
+
if (visited.has(dep))
|
|
264
|
+
continue;
|
|
265
|
+
visited.add(dep);
|
|
266
|
+
const svc = servicesByName.get(dep);
|
|
267
|
+
if (!svc)
|
|
268
|
+
continue;
|
|
269
|
+
// Credit the service's own file with the controller's routes.
|
|
270
|
+
const existing = out.get(svc.filePath) ?? [];
|
|
271
|
+
out.set(svc.filePath, mergeRoutes(existing, c.routes));
|
|
272
|
+
for (const next of svc.dependencies ?? [])
|
|
273
|
+
queue.push(next);
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
// 3. Mark every other source file we saw imports for as having no
|
|
277
|
+
// routes (so `unreachable` becomes the natural label when nothing
|
|
278
|
+
// references it).
|
|
279
|
+
for (const file of filesByImports.keys()) {
|
|
280
|
+
if (!out.has(file))
|
|
281
|
+
out.set(file, []);
|
|
282
|
+
}
|
|
283
|
+
return out;
|
|
284
|
+
}
|
|
285
|
+
function mergeRoutes(into, add) {
|
|
286
|
+
const seen = new Set(into.map((r) => `${r.method}:${r.path}`));
|
|
287
|
+
for (const r of add) {
|
|
288
|
+
const k = `${r.method}:${r.path}`;
|
|
289
|
+
if (!seen.has(k)) {
|
|
290
|
+
into.push(r);
|
|
291
|
+
seen.add(k);
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
return into;
|
|
295
|
+
}
|
|
296
|
+
/** Strip query string / trailing slash so route-key matching is stable. */
|
|
297
|
+
function normalisePath(p) {
|
|
298
|
+
const noQuery = p.split('?')[0] ?? p;
|
|
299
|
+
const trimmed = noQuery.replace(/\/+$/, '');
|
|
300
|
+
return trimmed || '/';
|
|
301
|
+
}
|
|
302
|
+
//# sourceMappingURL=reachability.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reachability.js","sourceRoot":"","sources":["../../src/security/reachability.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Aggregate scoring for the security report.
|
|
3
|
+
*
|
|
4
|
+
* Distils a heterogeneous list of findings into a single letter grade
|
|
5
|
+
* (A..F) so the StatusDashboard card can show a tight summary, and the
|
|
6
|
+
* UI can sort/route on it. Also bundles the per-severity counts that
|
|
7
|
+
* the Security view consumes for its severity-grouped sections.
|
|
8
|
+
*
|
|
9
|
+
* The grading function is *not* CVSS-aware on purpose — a project with
|
|
10
|
+
* one critical CVE that has no exploit in the wild shouldn't be
|
|
11
|
+
* indistinguishable from one with ten. We follow these rules instead:
|
|
12
|
+
*
|
|
13
|
+
* F → any CRITICAL severity finding (supply-chain or posture)
|
|
14
|
+
* D → ≥3 HIGH, or ≥1 HIGH supply-chain
|
|
15
|
+
* C → any HIGH, or ≥5 MEDIUM
|
|
16
|
+
* B → any MEDIUM
|
|
17
|
+
* A → only LOW / INFO (or zero findings)
|
|
18
|
+
*/
|
|
19
|
+
import type { DependencyFinding, FixGroup, PostureFinding, SecurityReport, Severity } from '../types/index.js';
|
|
20
|
+
declare const SEVERITY_KEYS: Severity[];
|
|
21
|
+
export declare function buildSecurityReport(args: {
|
|
22
|
+
dependencies: DependencyFinding[];
|
|
23
|
+
posture: PostureFinding[];
|
|
24
|
+
fixGroups: FixGroup[];
|
|
25
|
+
scanState: SecurityReport['scanState'];
|
|
26
|
+
}): SecurityReport;
|
|
27
|
+
/**
|
|
28
|
+
* Hash the set of finding ids. The engine uses this to decide whether
|
|
29
|
+
* a freshly-built report actually differs from the last one — we only
|
|
30
|
+
* broadcast on transitions to keep WS traffic flat for stable apps.
|
|
31
|
+
*/
|
|
32
|
+
export declare function hashFindingIds(report: SecurityReport): string;
|
|
33
|
+
/** Empty / "nothing recorded yet" baseline used before the first scan completes. */
|
|
34
|
+
export declare function emptyReport(scanState: SecurityReport['scanState']): SecurityReport;
|
|
35
|
+
export { SEVERITY_KEYS };
|
|
36
|
+
//# sourceMappingURL=score.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"score.d.ts","sourceRoot":"","sources":["../../src/security/score.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,KAAK,EACV,iBAAiB,EACjB,QAAQ,EACR,cAAc,EACd,cAAc,EACd,QAAQ,EACT,MAAM,mBAAmB,CAAC;AAE3B,QAAA,MAAM,aAAa,EAAE,QAAQ,EAAkD,CAAC;AAEhF,wBAAgB,mBAAmB,CAAC,IAAI,EAAE;IACxC,YAAY,EAAE,iBAAiB,EAAE,CAAC;IAClC,OAAO,EAAE,cAAc,EAAE,CAAC;IAC1B,SAAS,EAAE,QAAQ,EAAE,CAAC;IACtB,SAAS,EAAE,cAAc,CAAC,WAAW,CAAC,CAAC;CACxC,GAAG,cAAc,CAqBjB;AAcD;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,cAAc,GAAG,MAAM,CAU7D;AAED,oFAAoF;AACpF,wBAAgB,WAAW,CAAC,SAAS,EAAE,cAAc,CAAC,WAAW,CAAC,GAAG,cAAc,CAiBlF;AAID,OAAO,EAAE,aAAa,EAAE,CAAC"}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Aggregate scoring for the security report.
|
|
3
|
+
*
|
|
4
|
+
* Distils a heterogeneous list of findings into a single letter grade
|
|
5
|
+
* (A..F) so the StatusDashboard card can show a tight summary, and the
|
|
6
|
+
* UI can sort/route on it. Also bundles the per-severity counts that
|
|
7
|
+
* the Security view consumes for its severity-grouped sections.
|
|
8
|
+
*
|
|
9
|
+
* The grading function is *not* CVSS-aware on purpose — a project with
|
|
10
|
+
* one critical CVE that has no exploit in the wild shouldn't be
|
|
11
|
+
* indistinguishable from one with ten. We follow these rules instead:
|
|
12
|
+
*
|
|
13
|
+
* F → any CRITICAL severity finding (supply-chain or posture)
|
|
14
|
+
* D → ≥3 HIGH, or ≥1 HIGH supply-chain
|
|
15
|
+
* C → any HIGH, or ≥5 MEDIUM
|
|
16
|
+
* B → any MEDIUM
|
|
17
|
+
* A → only LOW / INFO (or zero findings)
|
|
18
|
+
*/
|
|
19
|
+
const SEVERITY_KEYS = ['CRITICAL', 'HIGH', 'MEDIUM', 'LOW', 'INFO'];
|
|
20
|
+
export function buildSecurityReport(args) {
|
|
21
|
+
const counts = {
|
|
22
|
+
CRITICAL: 0,
|
|
23
|
+
HIGH: 0,
|
|
24
|
+
MEDIUM: 0,
|
|
25
|
+
LOW: 0,
|
|
26
|
+
INFO: 0,
|
|
27
|
+
};
|
|
28
|
+
for (const f of args.dependencies)
|
|
29
|
+
counts[f.severity]++;
|
|
30
|
+
for (const f of args.posture)
|
|
31
|
+
counts[f.severity]++;
|
|
32
|
+
return {
|
|
33
|
+
generatedAt: Date.now(),
|
|
34
|
+
score: gradeFromCounts(counts, args.dependencies),
|
|
35
|
+
counts,
|
|
36
|
+
dependencies: args.dependencies,
|
|
37
|
+
posture: args.posture,
|
|
38
|
+
fixGroups: args.fixGroups,
|
|
39
|
+
scanState: args.scanState,
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
function gradeFromCounts(counts, dependencies) {
|
|
43
|
+
if (counts.CRITICAL > 0)
|
|
44
|
+
return 'F';
|
|
45
|
+
const supplyChainHigh = dependencies.filter((d) => d.severity === 'HIGH').length;
|
|
46
|
+
if (counts.HIGH >= 3 || supplyChainHigh >= 1)
|
|
47
|
+
return 'D';
|
|
48
|
+
if (counts.HIGH > 0 || counts.MEDIUM >= 5)
|
|
49
|
+
return 'C';
|
|
50
|
+
if (counts.MEDIUM > 0)
|
|
51
|
+
return 'B';
|
|
52
|
+
return 'A';
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Hash the set of finding ids. The engine uses this to decide whether
|
|
56
|
+
* a freshly-built report actually differs from the last one — we only
|
|
57
|
+
* broadcast on transitions to keep WS traffic flat for stable apps.
|
|
58
|
+
*/
|
|
59
|
+
export function hashFindingIds(report) {
|
|
60
|
+
// The hash is intentionally simple — sort to get a stable order
|
|
61
|
+
// (analysis passes may emit findings in non-deterministic order if
|
|
62
|
+
// they iterate Maps), then join. Avoids the cost of a crypto digest
|
|
63
|
+
// when we just need a comparable string.
|
|
64
|
+
const ids = [];
|
|
65
|
+
for (const f of report.dependencies)
|
|
66
|
+
ids.push(`d:${f.id}`);
|
|
67
|
+
for (const f of report.posture)
|
|
68
|
+
ids.push(`p:${f.id}`);
|
|
69
|
+
ids.sort();
|
|
70
|
+
return ids.join('|');
|
|
71
|
+
}
|
|
72
|
+
/** Empty / "nothing recorded yet" baseline used before the first scan completes. */
|
|
73
|
+
export function emptyReport(scanState) {
|
|
74
|
+
const counts = {
|
|
75
|
+
CRITICAL: 0,
|
|
76
|
+
HIGH: 0,
|
|
77
|
+
MEDIUM: 0,
|
|
78
|
+
LOW: 0,
|
|
79
|
+
INFO: 0,
|
|
80
|
+
};
|
|
81
|
+
return {
|
|
82
|
+
generatedAt: Date.now(),
|
|
83
|
+
score: 'A',
|
|
84
|
+
counts,
|
|
85
|
+
dependencies: [],
|
|
86
|
+
posture: [],
|
|
87
|
+
fixGroups: [],
|
|
88
|
+
scanState,
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
// Re-exported so consumers don't have to mention the type module twice
|
|
92
|
+
// when iterating counts.
|
|
93
|
+
export { SEVERITY_KEYS };
|
|
94
|
+
//# sourceMappingURL=score.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"score.js","sourceRoot":"","sources":["../../src/security/score.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAUH,MAAM,aAAa,GAAe,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,CAAC,CAAC;AAEhF,MAAM,UAAU,mBAAmB,CAAC,IAKnC;IACC,MAAM,MAAM,GAA6B;QACvC,QAAQ,EAAE,CAAC;QACX,IAAI,EAAE,CAAC;QACP,MAAM,EAAE,CAAC;QACT,GAAG,EAAE,CAAC;QACN,IAAI,EAAE,CAAC;KACR,CAAC;IAEF,KAAK,MAAM,CAAC,IAAI,IAAI,CAAC,YAAY;QAAE,MAAM,CAAC,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC;IACxD,KAAK,MAAM,CAAC,IAAI,IAAI,CAAC,OAAO;QAAE,MAAM,CAAC,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC;IAEnD,OAAO;QACL,WAAW,EAAE,IAAI,CAAC,GAAG,EAAE;QACvB,KAAK,EAAE,eAAe,CAAC,MAAM,EAAE,IAAI,CAAC,YAAY,CAAC;QACjD,MAAM;QACN,YAAY,EAAE,IAAI,CAAC,YAAY;QAC/B,OAAO,EAAE,IAAI,CAAC,OAAO;QACrB,SAAS,EAAE,IAAI,CAAC,SAAS;QACzB,SAAS,EAAE,IAAI,CAAC,SAAS;KAC1B,CAAC;AACJ,CAAC;AAED,SAAS,eAAe,CACtB,MAAgC,EAChC,YAAiC;IAEjC,IAAI,MAAM,CAAC,QAAQ,GAAG,CAAC;QAAE,OAAO,GAAG,CAAC;IACpC,MAAM,eAAe,GAAG,YAAY,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,MAAM,CAAC,CAAC,MAAM,CAAC;IACjF,IAAI,MAAM,CAAC,IAAI,IAAI,CAAC,IAAI,eAAe,IAAI,CAAC;QAAE,OAAO,GAAG,CAAC;IACzD,IAAI,MAAM,CAAC,IAAI,GAAG,CAAC,IAAI,MAAM,CAAC,MAAM,IAAI,CAAC;QAAE,OAAO,GAAG,CAAC;IACtD,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC;QAAE,OAAO,GAAG,CAAC;IAClC,OAAO,GAAG,CAAC;AACb,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,cAAc,CAAC,MAAsB;IACnD,gEAAgE;IAChE,mEAAmE;IACnE,oEAAoE;IACpE,yCAAyC;IACzC,MAAM,GAAG,GAAa,EAAE,CAAC;IACzB,KAAK,MAAM,CAAC,IAAI,MAAM,CAAC,YAAY;QAAE,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IAC3D,KAAK,MAAM,CAAC,IAAI,MAAM,CAAC,OAAO;QAAE,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IACtD,GAAG,CAAC,IAAI,EAAE,CAAC;IACX,OAAO,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AACvB,CAAC;AAED,oFAAoF;AACpF,MAAM,UAAU,WAAW,CAAC,SAAsC;IAChE,MAAM,MAAM,GAA6B;QACvC,QAAQ,EAAE,CAAC;QACX,IAAI,EAAE,CAAC;QACP,MAAM,EAAE,CAAC;QACT,GAAG,EAAE,CAAC;QACN,IAAI,EAAE,CAAC;KACR,CAAC;IACF,OAAO;QACL,WAAW,EAAE,IAAI,CAAC,GAAG,EAAE;QACvB,KAAK,EAAE,GAAG;QACV,MAAM;QACN,YAAY,EAAE,EAAE;QAChB,OAAO,EAAE,EAAE;QACX,SAAS,EAAE,EAAE;QACb,SAAS;KACV,CAAC;AACJ,CAAC;AAED,uEAAuE;AACvE,yBAAyB;AACzB,OAAO,EAAE,aAAa,EAAE,CAAC"}
|