@lockhawk/core 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Faith Onyebueke
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,558 @@
1
+ /** Qualitative severity bands (CVSS-aligned, plus `unknown` for un-scored advisories). */
2
+ type Severity = 'critical' | 'high' | 'medium' | 'low' | 'none' | 'unknown';
3
+ /** Ordering used for `--severity-threshold` and `--fail-on` comparisons. */
4
+ declare const SEVERITY_RANK: Record<Severity, number>;
5
+ /** `true` when `level` is at least as severe as `threshold`. */
6
+ declare function severityAtLeast(level: Severity, threshold: Severity): boolean;
7
+ type PackageManager = 'npm' | 'yarn' | 'pnpm';
8
+ /** How a dependency is reachable from the project root. */
9
+ type DepScope = 'prod' | 'dev' | 'optional' | 'peer';
10
+ /** Unique identity of an installed package: `name@version`. */
11
+ type PkgKey = string;
12
+ /** Build a {@link PkgKey} from a name and version. */
13
+ declare function pkgKey(name: string, version: string): PkgKey;
14
+ /** A single installed package in the dependency graph. */
15
+ interface DepNode {
16
+ /** `name@version` — the node's key in {@link DependencyGraph.nodes}. */
17
+ key: PkgKey;
18
+ name: string;
19
+ version: string;
20
+ /** Strongest scope by which this node is reachable (prod beats dev/optional). */
21
+ scope: DepScope;
22
+ /** Whether this is a direct dependency of the project root. */
23
+ direct: boolean;
24
+ /** Keys of packages this node depends on (edges out). */
25
+ dependencies: PkgKey[];
26
+ /** Set when the package has no queryable registry version (file:/link:/git:). */
27
+ unscannable?: {
28
+ reason: string;
29
+ };
30
+ }
31
+ /** The normalized dependency tree, independent of which package manager produced it. */
32
+ interface DependencyGraph {
33
+ manager: PackageManager;
34
+ lockfilePath: string;
35
+ lockfileVersion?: string | number;
36
+ root: {
37
+ name: string;
38
+ version?: string;
39
+ };
40
+ /** All installed packages, keyed by {@link PkgKey}. */
41
+ nodes: Record<PkgKey, DepNode>;
42
+ /** Keys of the project's direct dependencies. */
43
+ directKeys: PkgKey[];
44
+ }
45
+ interface OsvEvent {
46
+ introduced?: string;
47
+ fixed?: string;
48
+ last_affected?: string;
49
+ limit?: string;
50
+ }
51
+ interface OsvRange {
52
+ type: 'SEMVER' | 'ECOSYSTEM' | 'GIT';
53
+ events: OsvEvent[];
54
+ repo?: string;
55
+ }
56
+ interface OsvSeverity {
57
+ /** e.g. `CVSS_V3`, `CVSS_V4`. */
58
+ type: string;
59
+ /** CVSS vector string. */
60
+ score: string;
61
+ }
62
+ interface OsvAffected {
63
+ package?: {
64
+ ecosystem?: string;
65
+ name?: string;
66
+ purl?: string;
67
+ };
68
+ ranges?: OsvRange[];
69
+ versions?: string[];
70
+ severity?: OsvSeverity[];
71
+ ecosystem_specific?: Record<string, unknown>;
72
+ database_specific?: Record<string, unknown>;
73
+ }
74
+ interface OsvReference {
75
+ type?: string;
76
+ url: string;
77
+ }
78
+ interface OsvVulnerability {
79
+ id: string;
80
+ modified?: string;
81
+ published?: string;
82
+ /** RFC3339 timestamp; when present the advisory has been retracted and must be ignored. */
83
+ withdrawn?: string;
84
+ aliases?: string[];
85
+ related?: string[];
86
+ summary?: string;
87
+ details?: string;
88
+ severity?: OsvSeverity[];
89
+ affected?: OsvAffected[];
90
+ references?: OsvReference[];
91
+ database_specific?: Record<string, unknown>;
92
+ }
93
+ /** Resolved severity for a finding, with provenance so the UI never shows a bare number. */
94
+ interface SeverityInfo {
95
+ level: Severity;
96
+ /** Numeric CVSS base score (0–10) when computable. */
97
+ score?: number;
98
+ /** CVSS vector string the score was derived from. */
99
+ vector?: string;
100
+ /** e.g. `CVSS:3.1`, `CVSS:4.0`. */
101
+ cvssVersion?: string;
102
+ source: 'cvss' | 'database' | 'unknown';
103
+ }
104
+ /** One vulnerability affecting one installed package version. */
105
+ interface Finding {
106
+ /** Canonical id (prefers CVE, then GHSA, then the OSV id). */
107
+ id: string;
108
+ /** All known identifiers for this advisory, including the canonical one. */
109
+ aliases: string[];
110
+ packageName: string;
111
+ version: string;
112
+ scope: DepScope;
113
+ direct: boolean;
114
+ severity: SeverityInfo;
115
+ summary: string;
116
+ details?: string;
117
+ references: string[];
118
+ /** Versions that resolve this advisory (derived from OSV `fixed` events). */
119
+ fixedVersions: string[];
120
+ /** Human-readable mitigation guidance. */
121
+ recommendation?: string;
122
+ /** Each path runs root → … → `name@version`, explaining why the package is present. */
123
+ dependencyPaths: PkgKey[][];
124
+ source: string;
125
+ }
126
+ interface DatabaseInfo {
127
+ source: 'offline' | 'online' | 'mixed';
128
+ recordCount?: number;
129
+ /** ISO timestamp of when the offline DB was last refreshed. */
130
+ lastUpdated?: string;
131
+ ageHours?: number;
132
+ stale: boolean;
133
+ warnings: string[];
134
+ }
135
+ interface ScanSummary {
136
+ total: number;
137
+ critical: number;
138
+ high: number;
139
+ medium: number;
140
+ low: number;
141
+ unknown: number;
142
+ none: number;
143
+ /** Number of distinct vulnerable packages (not advisories). */
144
+ vulnerablePackages: number;
145
+ /** Number of findings that have at least one known fixed version. */
146
+ fixable: number;
147
+ }
148
+ interface ScanStats {
149
+ totalPackages: number;
150
+ uniquePackages: number;
151
+ directDependencies: number;
152
+ unscannable: number;
153
+ durationMs?: number;
154
+ }
155
+ interface UnscannablePackage {
156
+ name: string;
157
+ version: string;
158
+ reason: string;
159
+ }
160
+ /** The complete output of a scan — what reporters serialize and the dashboard renders. */
161
+ interface ScanResult {
162
+ schemaVersion: 1;
163
+ tool: {
164
+ name: string;
165
+ version: string;
166
+ };
167
+ /** ISO timestamp. */
168
+ scannedAt: string;
169
+ target: {
170
+ path: string;
171
+ manager: PackageManager;
172
+ lockfile: string;
173
+ root: {
174
+ name: string;
175
+ version?: string;
176
+ };
177
+ };
178
+ database: DatabaseInfo;
179
+ summary: ScanSummary;
180
+ stats: ScanStats;
181
+ findings: Finding[];
182
+ unscannable: UnscannablePackage[];
183
+ }
184
+ type SourceMode = 'auto' | 'offline' | 'online';
185
+ interface ScanOptions {
186
+ /** Project directory to scan (defaults to cwd). */
187
+ path?: string;
188
+ /** Vulnerability-source strategy. */
189
+ mode?: SourceMode;
190
+ cacheDir?: string;
191
+ cacheTtlHours?: number;
192
+ noCache?: boolean;
193
+ concurrency?: number;
194
+ /** Ignore dev dependencies. */
195
+ prodOnly?: boolean;
196
+ /** Advisory ids (CVE/GHSA/OSV) to suppress. */
197
+ ignore?: string[];
198
+ /** Fail hard on network errors instead of degrading to cache (fail-open). */
199
+ strictNetwork?: boolean;
200
+ /** Limit the scan to a single workspace by name. */
201
+ workspace?: string;
202
+ /** Drop findings below this severity from the report. */
203
+ severityThreshold?: Severity;
204
+ /** Progress callback (used by the CLI spinner). */
205
+ onProgress?: (message: string) => void;
206
+ }
207
+
208
+ interface DetectedLockfile {
209
+ manager: PackageManager;
210
+ /** Absolute or relative path to the lockfile. */
211
+ path: string;
212
+ filename: string;
213
+ }
214
+ /**
215
+ * Find the lockfile to scan in `dir`. If several are present we prefer pnpm,
216
+ * then yarn, then npm — mirroring how a project that committed multiple
217
+ * lockfiles is most likely actually installed.
218
+ */
219
+ declare function detectLockfile(dir: string): DetectedLockfile | undefined;
220
+ interface RootManifest {
221
+ name: string;
222
+ version?: string;
223
+ dependencies: Record<string, string>;
224
+ devDependencies: Record<string, string>;
225
+ optionalDependencies: Record<string, string>;
226
+ peerDependencies: Record<string, string>;
227
+ }
228
+ /** Read and normalize the project's `package.json` (used to determine direct deps). */
229
+ declare function readRootManifest(dir: string): RootManifest;
230
+
231
+ /**
232
+ * Manager-agnostic parser output. Each lockfile parser produces one of these;
233
+ * a single {@link buildDependencyGraph} step then classifies scopes by
234
+ * reachability and assembles the final {@link DependencyGraph}.
235
+ *
236
+ * Computing scope from reachability (rather than per-package `dev` flags) is
237
+ * what lets us treat npm, yarn and pnpm uniformly — pnpm v9 lockfiles, for
238
+ * instance, no longer carry a per-package `dev` field.
239
+ */
240
+ interface RawNode {
241
+ key: PkgKey;
242
+ name: string;
243
+ version: string;
244
+ /** Keys of packages this node depends on (edges out). May contain duplicates. */
245
+ dependencies: PkgKey[];
246
+ /** Set when there is no queryable registry version (file:/link:/git:/workspace). */
247
+ unscannable?: {
248
+ reason: string;
249
+ };
250
+ }
251
+ interface RawGraph {
252
+ manager: PackageManager;
253
+ lockfilePath: string;
254
+ lockfileVersion?: string | number;
255
+ root: {
256
+ name: string;
257
+ version?: string;
258
+ };
259
+ nodes: Record<PkgKey, RawNode>;
260
+ /** Direct production dependencies of the project root. */
261
+ directProd: PkgKey[];
262
+ /** Direct dev dependencies of the project root. */
263
+ directDev: PkgKey[];
264
+ /** Direct optional dependencies of the project root. */
265
+ directOptional: PkgKey[];
266
+ unscannable: UnscannablePackage[];
267
+ }
268
+ /**
269
+ * Classify every node's scope by reachability and assemble the normalized graph.
270
+ * Priority when a node is reachable multiple ways: prod > optional > dev.
271
+ */
272
+ declare function buildDependencyGraph(raw: RawGraph): DependencyGraph;
273
+
274
+ declare class LockfileError extends Error {
275
+ constructor(message: string);
276
+ }
277
+ /** Parse a detected lockfile into the manager-agnostic {@link RawGraph}. */
278
+ declare function parseLockfile(detected: DetectedLockfile): RawGraph;
279
+ /**
280
+ * Detect, parse and normalize the lockfile in `dir` into a {@link DependencyGraph}.
281
+ * Throws {@link LockfileError} when no lockfile is present or parsing fails.
282
+ */
283
+ declare function loadDependencyGraph(dir: string): DependencyGraph;
284
+
285
+ /** Label for the project root used as the first element of a dependency path. */
286
+ declare function rootLabel(graph: DependencyGraph): string;
287
+ /**
288
+ * Shortest dependency path from the project root to `targetKey`, answering
289
+ * "why is this package in my tree?". Returns `[root, …, target]` or `null` if
290
+ * the target is not reachable. Uses BFS from a virtual root connected to the
291
+ * project's direct dependencies.
292
+ */
293
+ declare function shortestPath(graph: DependencyGraph, targetKey: PkgKey): PkgKey[] | null;
294
+
295
+ /**
296
+ * Whether `version` is affected by a single OSV `affected` entry — the heart of
297
+ * accuracy. We honor enumerated `versions[]` exactly and walk `ranges[].events`
298
+ * with the canonical OSV sweep. We never coerce a non-semver installed version
299
+ * into a range match (that would manufacture false positives); such versions
300
+ * only match via the explicit `versions[]` list.
301
+ */
302
+ declare function isVersionAffected(version: string, affected: OsvAffected): boolean;
303
+ interface VulnMatch {
304
+ affected: boolean;
305
+ /** Distinct `fixed` versions across the matching affected entries. */
306
+ fixedVersions: string[];
307
+ }
308
+ /**
309
+ * Whether a whole OSV record affects `name@version` for the npm ecosystem, and
310
+ * which fixed versions it advertises. Withdrawn advisories never match.
311
+ */
312
+ declare function vulnerabilityAffects(name: string, version: string, vuln: OsvVulnerability): VulnMatch;
313
+ /**
314
+ * The lowest advertised fixed version strictly greater than `installed`
315
+ * (the nearest safe upgrade), or `undefined` if none is known.
316
+ */
317
+ declare function nearestFix(installed: string, fixedVersions: string[]): string | undefined;
318
+
319
+ /** Pick the human-facing id: prefer CVE, then GHSA, then the raw OSV id. */
320
+ declare function canonicalId(ids: Iterable<string>): string;
321
+ /**
322
+ * Collapse advisories that describe the same vulnerability. Records are grouped
323
+ * by the connected components of their shared ids/aliases (a CVE surfaced under
324
+ * both a GHSA and an OSV id becomes one finding), then each group is merged into
325
+ * a single representative carrying the union of aliases, references and severity.
326
+ */
327
+ declare function dedupeVulnerabilities(vulns: OsvVulnerability[]): OsvVulnerability[];
328
+
329
+ interface FindingsOptions {
330
+ /** Skip dev-only dependencies. */
331
+ prodOnly?: boolean;
332
+ /** Advisory ids/aliases to suppress. */
333
+ ignore?: Set<string>;
334
+ }
335
+ /**
336
+ * Turn a dependency graph plus a per-package advisory lookup into findings.
337
+ *
338
+ * `candidatesFor(name)` returns advisories that *name* the package; this
339
+ * function performs the authoritative version match, de-duplicates aliased
340
+ * advisories, scores severity and traces the dependency path. Keeping the
341
+ * version match here (rather than trusting the source) means offline and online
342
+ * sources share one accuracy-critical code path.
343
+ */
344
+ declare function buildFindings(graph: DependencyGraph, candidatesFor: (name: string) => OsvVulnerability[], options?: FindingsOptions): Finding[];
345
+
346
+ interface CvssResult {
347
+ score: number;
348
+ level: Severity;
349
+ /** e.g. `CVSS:3.1`. */
350
+ version: string;
351
+ }
352
+ /** Map a numeric CVSS base score to its qualitative band. */
353
+ declare function levelFromScore(score: number): Severity;
354
+ /** Map a qualitative label (GHSA-style) to a {@link Severity}. */
355
+ declare function levelFromLabel(label: string): Severity;
356
+ /** Compute a base score from a CVSS v3.0/v3.1/v4.0 vector string, or null if unparseable. */
357
+ declare function scoreFromVector(vector: string): CvssResult | null;
358
+ /**
359
+ * Resolve the severity of a whole advisory, with provenance. Prefers a computed
360
+ * CVSS v3 score (highest across all vectors), then the advisory's qualitative
361
+ * `database_specific.severity`, then `unknown`.
362
+ */
363
+ declare function resolveSeverity(vuln: OsvVulnerability): SeverityInfo;
364
+
365
+ /**
366
+ * In-memory index of OSV advisories for the npm ecosystem, keyed by package
367
+ * name. Populated either from the downloaded offline DB zip (M3) or from online
368
+ * API responses, then queried per installed package during a scan.
369
+ *
370
+ * Withdrawn advisories are dropped on insert so they can never produce a finding.
371
+ */
372
+ declare class OsvDatabase {
373
+ private readonly byName;
374
+ private readonly ids;
375
+ add(vuln: OsvVulnerability): void;
376
+ addAll(vulns: Iterable<OsvVulnerability>): void;
377
+ /** Candidate advisories that name `packageName` (still need a version check). */
378
+ vulnerabilitiesFor(packageName: string): OsvVulnerability[];
379
+ /** Number of distinct advisories indexed. */
380
+ get size(): number;
381
+ }
382
+
383
+ interface PackageRef {
384
+ name: string;
385
+ version: string;
386
+ }
387
+ interface OnlineClientOptions {
388
+ cacheDir: string;
389
+ concurrency?: number;
390
+ noCache?: boolean;
391
+ cacheTtlHours?: number;
392
+ retries?: number;
393
+ }
394
+ /**
395
+ * Online OSV source. Uses the batch endpoint (which returns only matched ids)
396
+ * then hydrates each unique id to a full record — caching records by id so
397
+ * repeat scans do no network work. All matching is re-validated downstream by
398
+ * `buildFindings`, so this client only has to gather candidate records.
399
+ */
400
+ declare class OsvClient {
401
+ private readonly opts;
402
+ private readonly limit;
403
+ constructor(opts: OnlineClientOptions);
404
+ /** Fetch advisories affecting the given packages, keyed by package name. */
405
+ fetchAdvisories(packages: PackageRef[]): Promise<Map<string, OsvVulnerability[]>>;
406
+ private queryBatch;
407
+ private hydrate;
408
+ private getVulnerability;
409
+ private request;
410
+ private readCache;
411
+ private writeCache;
412
+ }
413
+
414
+ /** A source prepared for a specific package set: synchronous lookup + provenance. */
415
+ interface ResolvedSource {
416
+ candidatesFor(name: string): OsvVulnerability[];
417
+ database: DatabaseInfo;
418
+ }
419
+ /** Strategy for obtaining advisories (offline DB, online API, or auto). */
420
+ interface VulnSource {
421
+ prepare(packages: PackageRef[]): Promise<ResolvedSource>;
422
+ }
423
+ interface SourceOptions {
424
+ cacheDir: string;
425
+ concurrency?: number;
426
+ noCache?: boolean;
427
+ cacheTtlHours?: number;
428
+ /** Fail hard on network errors instead of degrading to cache. */
429
+ strictNetwork?: boolean;
430
+ /** Offline DB older than this many hours is flagged stale (default 24). */
431
+ staleAfterHours?: number;
432
+ }
433
+ /** Reads advisories from the on-disk offline database. */
434
+ declare class OfflineSource implements VulnSource {
435
+ private readonly opts;
436
+ constructor(opts: SourceOptions);
437
+ prepare(packages: PackageRef[]): Promise<ResolvedSource>;
438
+ }
439
+ /** Queries the live OSV.dev API. Fail-open unless `strictNetwork` is set. */
440
+ declare class OnlineSource implements VulnSource {
441
+ private readonly opts;
442
+ constructor(opts: SourceOptions);
443
+ prepare(packages: PackageRef[]): Promise<ResolvedSource>;
444
+ }
445
+ /**
446
+ * Prefers a fresh offline DB (fast, zero network), falls back to the online API,
447
+ * and finally to a stale offline DB if the network is down — always degrading
448
+ * gracefully so a scan never hard-fails on a transient outage.
449
+ */
450
+ declare class AutoSource implements VulnSource {
451
+ private readonly opts;
452
+ constructor(opts: SourceOptions);
453
+ prepare(packages: PackageRef[]): Promise<ResolvedSource>;
454
+ }
455
+ /** Build the right source for the requested mode. */
456
+ declare function createSource(mode: SourceMode, opts: SourceOptions): VulnSource;
457
+
458
+ declare const TOOL: {
459
+ readonly name: "lockhawk";
460
+ readonly version: "0.1.0";
461
+ };
462
+ /**
463
+ * Run a full scan: load and normalize the lockfile, query OSV for the unique
464
+ * scannable packages, build findings, and assemble a {@link ScanResult}.
465
+ *
466
+ * A `source` can be injected (used by tests and by the CLI once it has resolved
467
+ * options); otherwise one is created from `options.mode`.
468
+ */
469
+ declare function scan(options?: ScanOptions, source?: VulnSource): Promise<ScanResult>;
470
+ /** Unique `name@version` packages worth querying (skips local/unscannable, optionally dev). */
471
+ declare function uniqueScannablePackages(graph: DependencyGraph, prodOnly?: boolean): PackageRef[];
472
+ /** Lowest severity that should trigger a non-zero exit given a `--fail-on` threshold. */
473
+ declare function shouldFail(summary: ScanSummary, failOn: Severity): boolean;
474
+
475
+ interface OfflineMeta {
476
+ ecosystem: 'npm';
477
+ source: string;
478
+ /** ISO timestamp of the last successful refresh/check. */
479
+ lastUpdated: string;
480
+ recordCount: number;
481
+ packageCount: number;
482
+ etag?: string;
483
+ lastModified?: string;
484
+ }
485
+ interface UpdateResult {
486
+ status: 'updated' | 'unchanged';
487
+ meta: OfflineMeta;
488
+ }
489
+ declare class OfflineDbMissingError extends Error {
490
+ constructor();
491
+ }
492
+ /** Read the offline DB metadata, or `undefined` if it has never been built. */
493
+ declare function readOfflineMeta(cacheDir: string): Promise<OfflineMeta | undefined>;
494
+ /**
495
+ * Download (or conditionally refresh) the npm OSV database and rebuild the
496
+ * per-package shards. Uses ETag / Last-Modified so an unchanged DB costs a
497
+ * single 304 and no rewrite. `nowIso` is injected so callers stamp the time
498
+ * (the engine never calls Date.now itself in pure paths).
499
+ */
500
+ declare function updateOfflineDatabase(opts: {
501
+ cacheDir: string;
502
+ nowIso: string;
503
+ force?: boolean;
504
+ onProgress?: (message: string) => void;
505
+ }): Promise<UpdateResult>;
506
+ /**
507
+ * Load advisories for the given package names. Names are grouped by shard
508
+ * bucket so each bucket file is read at most once, and a scan only touches the
509
+ * buckets its dependencies fall into.
510
+ */
511
+ declare function loadAdvisoriesForPackages(cacheDir: string, names: Iterable<string>): Promise<Map<string, OsvVulnerability[]>>;
512
+
513
+ /**
514
+ * Resolve the on-disk cache directory, honoring (in priority order):
515
+ * 1. an explicit override (the `--cache-dir` flag)
516
+ * 2. the `LOCKHAWK_CACHE` environment variable
517
+ * 3. the OS-conventional cache location (via env-paths)
518
+ */
519
+ declare function resolveCacheDir(override?: string): string;
520
+ /** Directory holding the offline OSV database for the npm ecosystem. */
521
+ declare function offlineDbDir(cacheDir: string): string;
522
+ /** Number of advisory shard buckets — bounds the on-disk file count for CI caching. */
523
+ declare const SHARD_BUCKETS = 4096;
524
+ /** FNV-1a hash → stable shard bucket for a package name. */
525
+ declare function shardBucket(packageName: string): number;
526
+ /** Metadata file describing offline-DB freshness. */
527
+ declare function offlineMetaPath(cacheDir: string): string;
528
+
529
+ /** Serialize a scan result as pretty-printed JSON. */
530
+ declare function toJson(result: ScanResult): string;
531
+
532
+ interface SarifLog {
533
+ $schema: string;
534
+ version: '2.1.0';
535
+ runs: unknown[];
536
+ }
537
+ /**
538
+ * Convert a scan result to SARIF 2.1.0 for GitHub code scanning / Azure DevOps.
539
+ * One rule per advisory; results carry stable `partialFingerprints` so the same
540
+ * finding is not re-flagged as new on every run.
541
+ */
542
+ declare function toSarif(result: ScanResult): SarifLog;
543
+ /** Stringified SARIF log. */
544
+ declare function toSarifString(result: ScanResult): string;
545
+
546
+ /** Placeholder the report UI shell embeds so we can inject scan data at scan time. */
547
+ declare const DATA_MARKER = "<!--LOCKHAWK_DATA-->";
548
+ /**
549
+ * Produce a self-contained HTML report. When given the built report-UI `shell`
550
+ * (M6), the scan data is injected into it; otherwise a dependency-free fallback
551
+ * template is rendered so `--format html` always works.
552
+ */
553
+ declare function toHtml(result: ScanResult, shell?: string): string;
554
+
555
+ /** Render a scan result as JUnit XML (one failed test per finding). */
556
+ declare function toJunit(result: ScanResult): string;
557
+
558
+ export { AutoSource, type CvssResult, DATA_MARKER, type DatabaseInfo, type DepNode, type DepScope, type DependencyGraph, type DetectedLockfile, type Finding, type FindingsOptions, LockfileError, OfflineDbMissingError, type OfflineMeta, OfflineSource, type OnlineClientOptions, OnlineSource, type OsvAffected, OsvClient, OsvDatabase, type OsvEvent, type OsvRange, type OsvReference, type OsvSeverity, type OsvVulnerability, type PackageManager, type PackageRef, type PkgKey, type RawGraph, type RawNode, type ResolvedSource, type RootManifest, SEVERITY_RANK, SHARD_BUCKETS, type SarifLog, type ScanOptions, type ScanResult, type ScanStats, type ScanSummary, type Severity, type SeverityInfo, type SourceMode, type SourceOptions, TOOL, type UnscannablePackage, type UpdateResult, type VulnMatch, type VulnSource, buildDependencyGraph, buildFindings, canonicalId, createSource, dedupeVulnerabilities, detectLockfile, isVersionAffected, levelFromLabel, levelFromScore, loadAdvisoriesForPackages, loadDependencyGraph, nearestFix, offlineDbDir, offlineMetaPath, parseLockfile, pkgKey, readOfflineMeta, readRootManifest, resolveCacheDir, resolveSeverity, rootLabel, scan, scoreFromVector, severityAtLeast, shardBucket, shortestPath, shouldFail, toHtml, toJson, toJunit, toSarif, toSarifString, uniqueScannablePackages, updateOfflineDatabase, vulnerabilityAffects };