@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.
Files changed (86) hide show
  1. package/README.md +143 -0
  2. package/dist/agent.d.ts +127 -0
  3. package/dist/agent.d.ts.map +1 -0
  4. package/dist/agent.js +1031 -0
  5. package/dist/agent.js.map +1 -0
  6. package/dist/discovery/index.d.ts +2 -0
  7. package/dist/discovery/index.d.ts.map +1 -0
  8. package/dist/discovery/index.js +2 -0
  9. package/dist/discovery/index.js.map +1 -0
  10. package/dist/discovery/route-scanner.d.ts +35 -0
  11. package/dist/discovery/route-scanner.d.ts.map +1 -0
  12. package/dist/discovery/route-scanner.js +385 -0
  13. package/dist/discovery/route-scanner.js.map +1 -0
  14. package/dist/index.d.ts +15 -0
  15. package/dist/index.d.ts.map +1 -0
  16. package/dist/index.js +15 -0
  17. package/dist/index.js.map +1 -0
  18. package/dist/instrumentation/index.d.ts +2 -0
  19. package/dist/instrumentation/index.d.ts.map +1 -0
  20. package/dist/instrumentation/index.js +2 -0
  21. package/dist/instrumentation/index.js.map +1 -0
  22. package/dist/instrumentation/tracer.d.ts +40 -0
  23. package/dist/instrumentation/tracer.d.ts.map +1 -0
  24. package/dist/instrumentation/tracer.js +190 -0
  25. package/dist/instrumentation/tracer.js.map +1 -0
  26. package/dist/introspection/container-introspector.d.ts +81 -0
  27. package/dist/introspection/container-introspector.d.ts.map +1 -0
  28. package/dist/introspection/container-introspector.js +251 -0
  29. package/dist/introspection/container-introspector.js.map +1 -0
  30. package/dist/logging/log-capture.d.ts +58 -0
  31. package/dist/logging/log-capture.d.ts.map +1 -0
  32. package/dist/logging/log-capture.js +184 -0
  33. package/dist/logging/log-capture.js.map +1 -0
  34. package/dist/recording/index.d.ts +2 -0
  35. package/dist/recording/index.d.ts.map +1 -0
  36. package/dist/recording/index.js +2 -0
  37. package/dist/recording/index.js.map +1 -0
  38. package/dist/recording/request-recorder.d.ts +43 -0
  39. package/dist/recording/request-recorder.d.ts.map +1 -0
  40. package/dist/recording/request-recorder.js +373 -0
  41. package/dist/recording/request-recorder.js.map +1 -0
  42. package/dist/security/fix-resolver.d.ts +40 -0
  43. package/dist/security/fix-resolver.d.ts.map +1 -0
  44. package/dist/security/fix-resolver.js +283 -0
  45. package/dist/security/fix-resolver.js.map +1 -0
  46. package/dist/security/fix-runner.d.ts +60 -0
  47. package/dist/security/fix-runner.d.ts.map +1 -0
  48. package/dist/security/fix-runner.js +188 -0
  49. package/dist/security/fix-runner.js.map +1 -0
  50. package/dist/security/index.d.ts +140 -0
  51. package/dist/security/index.d.ts.map +1 -0
  52. package/dist/security/index.js +460 -0
  53. package/dist/security/index.js.map +1 -0
  54. package/dist/security/lockfile-graph.d.ts +69 -0
  55. package/dist/security/lockfile-graph.d.ts.map +1 -0
  56. package/dist/security/lockfile-graph.js +245 -0
  57. package/dist/security/lockfile-graph.js.map +1 -0
  58. package/dist/security/npm-audit.d.ts +67 -0
  59. package/dist/security/npm-audit.d.ts.map +1 -0
  60. package/dist/security/npm-audit.js +320 -0
  61. package/dist/security/npm-audit.js.map +1 -0
  62. package/dist/security/osv-cache.d.ts +51 -0
  63. package/dist/security/osv-cache.d.ts.map +1 -0
  64. package/dist/security/osv-cache.js +99 -0
  65. package/dist/security/osv-cache.js.map +1 -0
  66. package/dist/security/osv-client.d.ts +47 -0
  67. package/dist/security/osv-client.d.ts.map +1 -0
  68. package/dist/security/osv-client.js +247 -0
  69. package/dist/security/osv-client.js.map +1 -0
  70. package/dist/security/posture-analyzer.d.ts +44 -0
  71. package/dist/security/posture-analyzer.d.ts.map +1 -0
  72. package/dist/security/posture-analyzer.js +397 -0
  73. package/dist/security/posture-analyzer.js.map +1 -0
  74. package/dist/security/reachability.d.ts +59 -0
  75. package/dist/security/reachability.d.ts.map +1 -0
  76. package/dist/security/reachability.js +302 -0
  77. package/dist/security/reachability.js.map +1 -0
  78. package/dist/security/score.d.ts +36 -0
  79. package/dist/security/score.d.ts.map +1 -0
  80. package/dist/security/score.js +94 -0
  81. package/dist/security/score.js.map +1 -0
  82. package/dist/types/index.d.ts +587 -0
  83. package/dist/types/index.d.ts.map +1 -0
  84. package/dist/types/index.js +14 -0
  85. package/dist/types/index.js.map +1 -0
  86. package/package.json +75 -0
@@ -0,0 +1,247 @@
1
+ /**
2
+ * OSV.dev client — supply-chain advisory lookups for installed packages.
3
+ *
4
+ * Why OSV: it's an open vulnerability database maintained by Google,
5
+ * exposes a programmatic JSON API, doesn't require auth, and aggregates
6
+ * GHSA, npm, OSS-Fuzz and other sources behind one query shape. It's
7
+ * also the source `npm audit` increasingly delegates to internally, so
8
+ * we get the same data without scraping anything.
9
+ *
10
+ * This client batches per-scan: one POST to `/v1/querybatch` covering
11
+ * every direct dependency. Results that aren't already in cache are
12
+ * persisted via `OsvCache` so subsequent scans inside the TTL skip the
13
+ * network entirely.
14
+ *
15
+ * Network failures degrade gracefully: we return an empty findings
16
+ * array so the engine can still ship a report using only `npm audit`.
17
+ */
18
+ const OSV_BATCH_URL = 'https://api.osv.dev/v1/querybatch';
19
+ const OSV_VULN_URL = 'https://api.osv.dev/v1/vulns';
20
+ const REQUEST_TIMEOUT_MS = 10_000;
21
+ /** OSV's batch endpoint accepts up to 1000 queries per request. */
22
+ const MAX_BATCH_SIZE = 500;
23
+ export class OsvClient {
24
+ cache;
25
+ constructor(cache) {
26
+ this.cache = cache;
27
+ }
28
+ /**
29
+ * Look up advisories for every (package, version) pair and return
30
+ * normalised findings. Cache hits are served without touching the
31
+ * network; the rest are batched into one (or more, if very large)
32
+ * POSTs to OSV.
33
+ */
34
+ async lookup(packages) {
35
+ if (packages.length === 0)
36
+ return [];
37
+ const findings = [];
38
+ const cacheMisses = [];
39
+ // Phase 1: serve from cache.
40
+ for (const pkg of packages) {
41
+ const cached = this.cache.get('npm', pkg.name, pkg.version);
42
+ if (cached) {
43
+ findings.push(...advisoriesToFindings(cached, pkg));
44
+ }
45
+ else {
46
+ cacheMisses.push(pkg);
47
+ }
48
+ }
49
+ if (cacheMisses.length === 0)
50
+ return findings;
51
+ // Phase 2: batch-query OSV for the misses.
52
+ let advisoryIdsByPkg;
53
+ try {
54
+ advisoryIdsByPkg = await this.fetchAdvisoryIds(cacheMisses);
55
+ }
56
+ catch {
57
+ // Network failure → cache nothing, fall back to whatever cache hits
58
+ // we already produced. Better than refusing to ship a report.
59
+ return findings;
60
+ }
61
+ // Phase 3: hydrate each unique advisory id. OSV returns just an id
62
+ // list from the batch endpoint — we have to fetch full records to
63
+ // get severity, summary, references etc.
64
+ const uniqueIds = new Set();
65
+ for (const ids of advisoryIdsByPkg.values()) {
66
+ ids.forEach((id) => uniqueIds.add(id));
67
+ }
68
+ const fullAdvisories = await this.fetchFullAdvisories([...uniqueIds]);
69
+ // Phase 4: rebuild per-package advisory lists and store in cache.
70
+ for (const pkg of cacheMisses) {
71
+ const ids = advisoryIdsByPkg.get(pkgKey(pkg)) ?? [];
72
+ const advisories = ids
73
+ .map((id) => fullAdvisories.get(id))
74
+ .filter((v) => Boolean(v))
75
+ .map((v) => fullToCacheAdvisory(v, pkg.name));
76
+ this.cache.put('npm', pkg.name, pkg.version, advisories);
77
+ findings.push(...advisoriesToFindings(advisories, pkg));
78
+ }
79
+ this.cache.flush();
80
+ return findings;
81
+ }
82
+ /**
83
+ * POST a batch of queries to OSV's `/v1/querybatch`. Splits oversize
84
+ * input into multiple requests to stay under the per-call limit.
85
+ */
86
+ async fetchAdvisoryIds(queries) {
87
+ const out = new Map();
88
+ for (let i = 0; i < queries.length; i += MAX_BATCH_SIZE) {
89
+ const batch = queries.slice(i, i + MAX_BATCH_SIZE);
90
+ const body = {
91
+ queries: batch.map((q) => ({
92
+ package: { name: q.name, ecosystem: 'npm' },
93
+ version: q.version,
94
+ })),
95
+ };
96
+ const resp = await fetchWithTimeout(OSV_BATCH_URL, {
97
+ method: 'POST',
98
+ headers: { 'content-type': 'application/json' },
99
+ body: JSON.stringify(body),
100
+ });
101
+ if (!resp.ok) {
102
+ // Treat as empty results for this batch; the caller will simply
103
+ // produce no findings for these packages.
104
+ continue;
105
+ }
106
+ const parsed = (await resp.json());
107
+ const results = parsed.results ?? [];
108
+ for (let j = 0; j < batch.length; j++) {
109
+ const pkg = batch[j];
110
+ const ids = (results[j]?.vulns ?? []).map((v) => v.id);
111
+ out.set(pkgKey(pkg), ids);
112
+ }
113
+ }
114
+ return out;
115
+ }
116
+ /**
117
+ * Hydrate advisory id list into full records. OSV requires one GET
118
+ * per id (no batch endpoint for full records yet) — we run them in
119
+ * parallel with a hard concurrency cap so we don't open hundreds of
120
+ * sockets at once.
121
+ */
122
+ async fetchFullAdvisories(ids) {
123
+ const out = new Map();
124
+ if (ids.length === 0)
125
+ return out;
126
+ const CONCURRENCY = 8;
127
+ let cursor = 0;
128
+ const workers = Array.from({ length: Math.min(CONCURRENCY, ids.length) }, async () => {
129
+ while (cursor < ids.length) {
130
+ const idx = cursor++;
131
+ const id = ids[idx];
132
+ try {
133
+ const resp = await fetchWithTimeout(`${OSV_VULN_URL}/${encodeURIComponent(id)}`);
134
+ if (!resp.ok)
135
+ continue;
136
+ const json = (await resp.json());
137
+ out.set(id, json);
138
+ }
139
+ catch {
140
+ // Skip — partial coverage is fine.
141
+ }
142
+ }
143
+ });
144
+ await Promise.all(workers);
145
+ return out;
146
+ }
147
+ }
148
+ function pkgKey(p) {
149
+ return `${p.name}@${p.version}`;
150
+ }
151
+ /**
152
+ * Translate the cached / hydrated advisory into our wire-stable
153
+ * `DependencyFinding` shape. Severity is the trickiest bit — OSV stores
154
+ * CVSS vectors rather than the npm-style word ("CRITICAL"); we map both.
155
+ */
156
+ function advisoriesToFindings(advisories, pkg) {
157
+ return advisories.map((adv) => ({
158
+ id: adv.id,
159
+ package: pkg.name,
160
+ installedVersion: pkg.version,
161
+ fixedVersion: adv.fixedVersion,
162
+ severity: numericSeverityToLabel(adv.severity),
163
+ cvss: adv.severity,
164
+ title: adv.summary || adv.id,
165
+ summary: adv.details ?? adv.summary ?? '',
166
+ references: adv.references,
167
+ path: [pkg.name],
168
+ }));
169
+ }
170
+ function fullToCacheAdvisory(full, pkgName) {
171
+ return {
172
+ id: full.id,
173
+ aliases: full.aliases ?? [],
174
+ summary: full.summary ?? '',
175
+ details: full.details,
176
+ severity: extractCvssScore(full),
177
+ references: (full.references ?? []).map((r) => r.url).filter(Boolean),
178
+ fixedVersion: extractFixedVersion(full, pkgName),
179
+ };
180
+ }
181
+ /** Pull a CVSS v3.x base score out of an OSV record, if present. */
182
+ function extractCvssScore(full) {
183
+ for (const s of full.severity ?? []) {
184
+ if (s.type === 'CVSS_V3' || s.type === 'CVSS_V4') {
185
+ // OSV stores the vector string; the base score is the first
186
+ // numeric value after the vector header. We don't ship a full
187
+ // CVSS parser — just look for a familiar "/AV:..." vector and
188
+ // pull the base score from `database_specific` when present.
189
+ const numeric = Number((full.database_specific?.severity ?? '').match(/(\d+\.\d+)/)?.[1]);
190
+ if (Number.isFinite(numeric))
191
+ return numeric;
192
+ }
193
+ }
194
+ return undefined;
195
+ }
196
+ /**
197
+ * Find the first `fixed` version for the given package across all
198
+ * `affected` ranges. OSV records list every event (introduced / fixed)
199
+ * in order; we use the first `fixed` that follows an `introduced`.
200
+ */
201
+ function extractFixedVersion(full, pkgName) {
202
+ for (const aff of full.affected ?? []) {
203
+ if (aff.package?.name !== pkgName)
204
+ continue;
205
+ for (const range of aff.ranges ?? []) {
206
+ for (const ev of range.events ?? []) {
207
+ if (ev.fixed)
208
+ return ev.fixed;
209
+ }
210
+ }
211
+ }
212
+ return undefined;
213
+ }
214
+ /**
215
+ * Map a numeric CVSS base score to our severity vocabulary using the
216
+ * standard NVD bands. Falls back to `MEDIUM` when no score is known so
217
+ * findings don't all collapse into `INFO`.
218
+ */
219
+ function numericSeverityToLabel(score) {
220
+ if (typeof score !== 'number' || !Number.isFinite(score))
221
+ return 'MEDIUM';
222
+ if (score >= 9.0)
223
+ return 'CRITICAL';
224
+ if (score >= 7.0)
225
+ return 'HIGH';
226
+ if (score >= 4.0)
227
+ return 'MEDIUM';
228
+ if (score > 0)
229
+ return 'LOW';
230
+ return 'INFO';
231
+ }
232
+ /**
233
+ * `fetch` with an `AbortController` timeout. Centralised here so every
234
+ * OSV call has the same timeout semantics — a network hang must not be
235
+ * able to stall the agent's WS event loop.
236
+ */
237
+ async function fetchWithTimeout(url, init) {
238
+ const controller = new AbortController();
239
+ const timer = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS);
240
+ try {
241
+ return await fetch(url, { ...init, signal: controller.signal });
242
+ }
243
+ finally {
244
+ clearTimeout(timer);
245
+ }
246
+ }
247
+ //# sourceMappingURL=osv-client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"osv-client.js","sourceRoot":"","sources":["../../src/security/osv-client.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,44 @@
1
+ /**
2
+ * Runtime security posture analyzer.
3
+ *
4
+ * This is Studio's unique contribution: Snyk-style scanners only see
5
+ * `package.json`, but Studio has the recorded traffic, the routes, the
6
+ * DI graph and live logs. The analyzer turns all of that into a set
7
+ * of OWASP-mapped findings the user can act on directly from the UI.
8
+ *
9
+ * Rules implemented here are intentionally conservative — false
10
+ * positives erode trust quickly in a security tool. Every rule has
11
+ * either (a) a high-confidence runtime signal (e.g. literal
12
+ * `Access-Control-Allow-Origin: *`) or (b) an explicit "advisory"
13
+ * severity so users know the finding is heuristic.
14
+ *
15
+ * All passes are pure functions over a snapshot of in-memory state.
16
+ * The engine is responsible for debouncing and change detection.
17
+ */
18
+ import type { AppStructure, PostureFinding, PostureEvidence, RecordedExchange, RouteInfo } from '../types/index.js';
19
+ import type { LogEntry } from '../logging/log-capture.js';
20
+ /**
21
+ * Inputs the analyzer needs. We accept them as an opaque object so the
22
+ * engine can compose its in-memory state without exposing the agent
23
+ * itself to the rules.
24
+ */
25
+ export interface PostureInputs {
26
+ routes: RouteInfo[];
27
+ structure: AppStructure | null;
28
+ exchanges: RecordedExchange[];
29
+ logs: LogEntry[];
30
+ /**
31
+ * Resolved absolute path to the host project's source root (best-effort).
32
+ * Some checks (e.g. "missing helmet" / "no validation") want to read
33
+ * `app.ts` / `main.ts`. We pass it in rather than discovering inside
34
+ * the analyzer so tests can override it.
35
+ */
36
+ srcRoot?: string;
37
+ }
38
+ /**
39
+ * Run every posture check over the given snapshot. Returns an
40
+ * unsorted list — the engine aggregates and dedupes by `id`.
41
+ */
42
+ export declare function analyzePosture(input: PostureInputs): PostureFinding[];
43
+ export type { PostureEvidence };
44
+ //# sourceMappingURL=posture-analyzer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"posture-analyzer.d.ts","sourceRoot":"","sources":["../../src/security/posture-analyzer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,KAAK,EACV,YAAY,EAEZ,cAAc,EACd,eAAe,EACf,gBAAgB,EAChB,SAAS,EAEV,MAAM,mBAAmB,CAAC;AAC3B,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,2BAA2B,CAAC;AAG1D;;;;GAIG;AACH,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,SAAS,EAAE,CAAC;IACpB,SAAS,EAAE,YAAY,GAAG,IAAI,CAAC;IAC/B,SAAS,EAAE,gBAAgB,EAAE,CAAC;IAC9B,IAAI,EAAE,QAAQ,EAAE,CAAC;IACjB;;;;;OAKG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,aAAa,GAAG,cAAc,EAAE,CAWrE;AA4ZD,YAAY,EAAE,eAAe,EAAE,CAAC"}