@quantakrypto/core 0.4.4 → 0.6.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/README.md +76 -27
- package/dist/cbom-merge.d.ts.map +1 -1
- package/dist/cbom-merge.js +30 -8
- package/dist/cbom-merge.js.map +1 -1
- package/dist/cbom.d.ts +5 -0
- package/dist/cbom.d.ts.map +1 -1
- package/dist/cbom.js +132 -37
- package/dist/cbom.js.map +1 -1
- package/dist/comments.d.ts +2 -23
- package/dist/comments.d.ts.map +1 -1
- package/dist/comments.js +24 -5
- package/dist/comments.js.map +1 -1
- package/dist/crypto-agility.d.ts +158 -0
- package/dist/crypto-agility.d.ts.map +1 -0
- package/dist/crypto-agility.js +285 -0
- package/dist/crypto-agility.js.map +1 -0
- package/dist/dependencies.d.ts.map +1 -1
- package/dist/dependencies.js +180 -18
- package/dist/dependencies.js.map +1 -1
- package/dist/detect-utils.d.ts +55 -21
- package/dist/detect-utils.d.ts.map +1 -1
- package/dist/detect-utils.js +133 -7
- package/dist/detect-utils.js.map +1 -1
- package/dist/detectors/age.d.ts +16 -0
- package/dist/detectors/age.d.ts.map +1 -0
- package/dist/detectors/age.js +40 -0
- package/dist/detectors/age.js.map +1 -0
- package/dist/detectors/ansible.d.ts +13 -0
- package/dist/detectors/ansible.d.ts.map +1 -0
- package/dist/detectors/ansible.js +108 -0
- package/dist/detectors/ansible.js.map +1 -0
- package/dist/detectors/bicep.d.ts +20 -0
- package/dist/detectors/bicep.d.ts.map +1 -0
- package/dist/detectors/bicep.js +74 -0
- package/dist/detectors/bicep.js.map +1 -0
- package/dist/detectors/c.d.ts.map +1 -1
- package/dist/detectors/c.js +30 -3
- package/dist/detectors/c.js.map +1 -1
- package/dist/detectors/cicd.d.ts.map +1 -1
- package/dist/detectors/cicd.js +18 -5
- package/dist/detectors/cicd.js.map +1 -1
- package/dist/detectors/cloud-kms.d.ts +8 -5
- package/dist/detectors/cloud-kms.d.ts.map +1 -1
- package/dist/detectors/cloud-kms.js +62 -20
- package/dist/detectors/cloud-kms.js.map +1 -1
- package/dist/detectors/cloudformation.d.ts +8 -0
- package/dist/detectors/cloudformation.d.ts.map +1 -1
- package/dist/detectors/cloudformation.js +41 -10
- package/dist/detectors/cloudformation.js.map +1 -1
- package/dist/detectors/codesign.d.ts +50 -0
- package/dist/detectors/codesign.d.ts.map +1 -0
- package/dist/detectors/codesign.js +143 -0
- package/dist/detectors/codesign.js.map +1 -0
- package/dist/detectors/csharp.d.ts.map +1 -1
- package/dist/detectors/csharp.js +13 -3
- package/dist/detectors/csharp.js.map +1 -1
- package/dist/detectors/dart.d.ts +35 -0
- package/dist/detectors/dart.d.ts.map +1 -0
- package/dist/detectors/dart.js +177 -0
- package/dist/detectors/dart.js.map +1 -0
- package/dist/detectors/database.d.ts +5 -3
- package/dist/detectors/database.d.ts.map +1 -1
- package/dist/detectors/database.js +45 -8
- package/dist/detectors/database.js.map +1 -1
- package/dist/detectors/dkim.d.ts +54 -0
- package/dist/detectors/dkim.d.ts.map +1 -0
- package/dist/detectors/dkim.js +92 -0
- package/dist/detectors/dkim.js.map +1 -0
- package/dist/detectors/dnssec.d.ts.map +1 -1
- package/dist/detectors/dnssec.js +19 -10
- package/dist/detectors/dnssec.js.map +1 -1
- package/dist/detectors/elixir.d.ts.map +1 -1
- package/dist/detectors/elixir.js +34 -2
- package/dist/detectors/elixir.js.map +1 -1
- package/dist/detectors/java.d.ts.map +1 -1
- package/dist/detectors/java.js +33 -3
- package/dist/detectors/java.js.map +1 -1
- package/dist/detectors/jose.d.ts.map +1 -1
- package/dist/detectors/jose.js +13 -3
- package/dist/detectors/jose.js.map +1 -1
- package/dist/detectors/jwk.d.ts +7 -7
- package/dist/detectors/jwk.d.ts.map +1 -1
- package/dist/detectors/jwk.js +44 -8
- package/dist/detectors/jwk.js.map +1 -1
- package/dist/detectors/k8s.d.ts.map +1 -1
- package/dist/detectors/k8s.js +5 -2
- package/dist/detectors/k8s.js.map +1 -1
- package/dist/detectors/keystore.d.ts +19 -0
- package/dist/detectors/keystore.d.ts.map +1 -0
- package/dist/detectors/keystore.js +76 -0
- package/dist/detectors/keystore.js.map +1 -0
- package/dist/detectors/mesh.d.ts +7 -8
- package/dist/detectors/mesh.d.ts.map +1 -1
- package/dist/detectors/mesh.js +17 -35
- package/dist/detectors/mesh.js.map +1 -1
- package/dist/detectors/messaging.d.ts +2 -2
- package/dist/detectors/messaging.d.ts.map +1 -1
- package/dist/detectors/messaging.js +19 -12
- package/dist/detectors/messaging.js.map +1 -1
- package/dist/detectors/objc.d.ts +41 -0
- package/dist/detectors/objc.d.ts.map +1 -0
- package/dist/detectors/objc.js +146 -0
- package/dist/detectors/objc.js.map +1 -0
- package/dist/detectors/openpgp.d.ts +20 -0
- package/dist/detectors/openpgp.d.ts.map +1 -0
- package/dist/detectors/openpgp.js +151 -0
- package/dist/detectors/openpgp.js.map +1 -0
- package/dist/detectors/pem.d.ts.map +1 -1
- package/dist/detectors/pem.js +32 -0
- package/dist/detectors/pem.js.map +1 -1
- package/dist/detectors/php.d.ts +2 -1
- package/dist/detectors/php.d.ts.map +1 -1
- package/dist/detectors/php.js +7 -3
- package/dist/detectors/php.js.map +1 -1
- package/dist/detectors/pkcs11.d.ts +17 -0
- package/dist/detectors/pkcs11.d.ts.map +1 -0
- package/dist/detectors/pkcs11.js +87 -0
- package/dist/detectors/pkcs11.js.map +1 -0
- package/dist/detectors/proxy.d.ts +52 -0
- package/dist/detectors/proxy.d.ts.map +1 -0
- package/dist/detectors/proxy.js +157 -0
- package/dist/detectors/proxy.js.map +1 -0
- package/dist/detectors/pulumi.d.ts +19 -0
- package/dist/detectors/pulumi.d.ts.map +1 -0
- package/dist/detectors/pulumi.js +80 -0
- package/dist/detectors/pulumi.js.map +1 -0
- package/dist/detectors/rust.d.ts.map +1 -1
- package/dist/detectors/rust.js +5 -0
- package/dist/detectors/rust.js.map +1 -1
- package/dist/detectors/secrets.d.ts +4 -1
- package/dist/detectors/secrets.d.ts.map +1 -1
- package/dist/detectors/secrets.js +5 -21
- package/dist/detectors/secrets.js.map +1 -1
- package/dist/detectors/solidity.d.ts +50 -0
- package/dist/detectors/solidity.d.ts.map +1 -0
- package/dist/detectors/solidity.js +108 -0
- package/dist/detectors/solidity.js.map +1 -0
- package/dist/detectors/source.d.ts.map +1 -1
- package/dist/detectors/source.js +105 -6
- package/dist/detectors/source.js.map +1 -1
- package/dist/detectors/spire.d.ts +49 -0
- package/dist/detectors/spire.d.ts.map +1 -0
- package/dist/detectors/spire.js +77 -0
- package/dist/detectors/spire.js.map +1 -0
- package/dist/detectors/ssh-ca.d.ts +72 -0
- package/dist/detectors/ssh-ca.d.ts.map +1 -0
- package/dist/detectors/ssh-ca.js +119 -0
- package/dist/detectors/ssh-ca.js.map +1 -0
- package/dist/detectors/supply-chain.d.ts +17 -0
- package/dist/detectors/supply-chain.d.ts.map +1 -0
- package/dist/detectors/supply-chain.js +89 -0
- package/dist/detectors/supply-chain.js.map +1 -0
- package/dist/detectors/swift.d.ts +22 -0
- package/dist/detectors/swift.d.ts.map +1 -0
- package/dist/detectors/swift.js +120 -0
- package/dist/detectors/swift.js.map +1 -0
- package/dist/detectors/terraform.d.ts.map +1 -1
- package/dist/detectors/terraform.js +35 -8
- package/dist/detectors/terraform.js.map +1 -1
- package/dist/detectors/vault.d.ts +15 -0
- package/dist/detectors/vault.d.ts.map +1 -0
- package/dist/detectors/vault.js +97 -0
- package/dist/detectors/vault.js.map +1 -0
- package/dist/detectors/vpn.d.ts +8 -8
- package/dist/detectors/vpn.d.ts.map +1 -1
- package/dist/detectors/vpn.js +12 -38
- package/dist/detectors/vpn.js.map +1 -1
- package/dist/detectors/weak-hash.d.ts +71 -0
- package/dist/detectors/weak-hash.d.ts.map +1 -0
- package/dist/detectors/weak-hash.js +116 -0
- package/dist/detectors/weak-hash.js.map +1 -0
- package/dist/detectors/webauthn.d.ts +61 -0
- package/dist/detectors/webauthn.d.ts.map +1 -0
- package/dist/detectors/webauthn.js +109 -0
- package/dist/detectors/webauthn.js.map +1 -0
- package/dist/detectors/xmldsig.d.ts +23 -0
- package/dist/detectors/xmldsig.d.ts.map +1 -0
- package/dist/detectors/xmldsig.js +84 -0
- package/dist/detectors/xmldsig.js.map +1 -0
- package/dist/evidence.d.ts +74 -4
- package/dist/evidence.d.ts.map +1 -1
- package/dist/evidence.js +71 -0
- package/dist/evidence.js.map +1 -1
- package/dist/hndl.d.ts +241 -0
- package/dist/hndl.d.ts.map +1 -0
- package/dist/hndl.js +752 -0
- package/dist/hndl.js.map +1 -0
- package/dist/index.d.ts +18 -11
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +21 -11
- package/dist/index.js.map +1 -1
- package/dist/inventory.d.ts +0 -2
- package/dist/inventory.d.ts.map +1 -1
- package/dist/inventory.js +15 -6
- package/dist/inventory.js.map +1 -1
- package/dist/parallel.d.ts.map +1 -1
- package/dist/parallel.js +22 -23
- package/dist/parallel.js.map +1 -1
- package/dist/registry.d.ts +9 -4
- package/dist/registry.d.ts.map +1 -1
- package/dist/registry.js +51 -4
- package/dist/registry.js.map +1 -1
- package/dist/remediate-pipeline.d.ts +0 -3
- package/dist/remediate-pipeline.d.ts.map +1 -1
- package/dist/remediate-pipeline.js +1 -1
- package/dist/remediate-pipeline.js.map +1 -1
- package/dist/remediation.d.ts +10 -0
- package/dist/remediation.d.ts.map +1 -1
- package/dist/remediation.js +32 -0
- package/dist/remediation.js.map +1 -1
- package/dist/report.d.ts +17 -0
- package/dist/report.d.ts.map +1 -1
- package/dist/report.js +106 -20
- package/dist/report.js.map +1 -1
- package/dist/scan-worker.js +5 -3
- package/dist/scan-worker.js.map +1 -1
- package/dist/scan.d.ts.map +1 -1
- package/dist/scan.js +35 -6
- package/dist/scan.js.map +1 -1
- package/dist/standards-profiles.d.ts +58 -0
- package/dist/standards-profiles.d.ts.map +1 -0
- package/dist/standards-profiles.js +100 -0
- package/dist/standards-profiles.js.map +1 -0
- package/dist/types.d.ts +7 -6
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/version.js.map +1 -1
- package/dist/vex.d.ts +39 -0
- package/dist/vex.d.ts.map +1 -0
- package/dist/vex.js +87 -0
- package/dist/vex.js.map +1 -0
- package/dist/walk.d.ts +39 -4
- package/dist/walk.d.ts.map +1 -1
- package/dist/walk.js +62 -10
- package/dist/walk.js.map +1 -1
- package/package.json +1 -1
- package/src/cbom-merge.ts +38 -8
- package/src/cbom.ts +153 -39
- package/src/comments.ts +25 -6
- package/src/crypto-agility.ts +407 -0
- package/src/dependencies.ts +182 -16
- package/src/detect-utils.ts +133 -11
- package/src/detectors/age.ts +60 -0
- package/src/detectors/ansible.ts +140 -0
- package/src/detectors/bicep.ts +107 -0
- package/src/detectors/c.ts +31 -3
- package/src/detectors/cicd.ts +18 -5
- package/src/detectors/cloud-kms.ts +79 -25
- package/src/detectors/cloudformation.ts +45 -10
- package/src/detectors/codesign.ts +214 -0
- package/src/detectors/csharp.ts +13 -3
- package/src/detectors/dart.ts +237 -0
- package/src/detectors/database.ts +64 -11
- package/src/detectors/dkim.ts +172 -0
- package/src/detectors/dnssec.ts +19 -10
- package/src/detectors/elixir.ts +33 -2
- package/src/detectors/java.ts +37 -3
- package/src/detectors/jose.ts +19 -3
- package/src/detectors/jwk.ts +59 -17
- package/src/detectors/k8s.ts +5 -2
- package/src/detectors/keystore.ts +109 -0
- package/src/detectors/mesh.ts +21 -45
- package/src/detectors/messaging.ts +22 -14
- package/src/detectors/objc.ts +209 -0
- package/src/detectors/openpgp.ts +194 -0
- package/src/detectors/pem.ts +30 -0
- package/src/detectors/php.ts +9 -4
- package/src/detectors/pkcs11.ts +113 -0
- package/src/detectors/proxy.ts +236 -0
- package/src/detectors/pulumi.ts +106 -0
- package/src/detectors/rust.ts +5 -0
- package/src/detectors/secrets.ts +15 -26
- package/src/detectors/solidity.ts +176 -0
- package/src/detectors/source.ts +110 -5
- package/src/detectors/spire.ts +143 -0
- package/src/detectors/ssh-ca.ts +215 -0
- package/src/detectors/supply-chain.ts +117 -0
- package/src/detectors/swift.ts +149 -0
- package/src/detectors/terraform.ts +42 -8
- package/src/detectors/vault.ts +120 -0
- package/src/detectors/vpn.ts +20 -48
- package/src/detectors/weak-hash.ts +217 -0
- package/src/detectors/webauthn.ts +205 -0
- package/src/detectors/xmldsig.ts +115 -0
- package/src/evidence.ts +127 -4
- package/src/hndl.ts +1012 -0
- package/src/index.ts +93 -13
- package/src/inventory.ts +15 -7
- package/src/parallel.ts +21 -20
- package/src/registry.ts +51 -4
- package/src/remediate-pipeline.ts +1 -1
- package/src/remediation.ts +39 -0
- package/src/report.ts +120 -20
- package/src/scan-worker.ts +5 -3
- package/src/scan.ts +41 -6
- package/src/standards-profiles.ts +136 -0
- package/src/types.ts +18 -5
- package/src/version.ts +1 -1
- package/src/vex.ts +122 -0
- package/src/walk.ts +80 -12
package/dist/parallel.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"parallel.js","sourceRoot":"","sources":["../src/parallel.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Parallel scanning over a worker-thread pool (P2-1). The scan is embarrassingly\n * parallel: each file is read and regex-scanned independently, and the merge is\n * a concatenation + the existing deterministic sort. `Finding` is plain\n * structured-cloneable data, so it crosses the worker boundary cleanly.\n *\n * The merge and chunking logic are pure and exported for direct unit testing.\n * `scanParallel` falls back to the in-process serial path below the crossover\n * (small file counts / small total bytes) and whenever workers are unavailable.\n */\nimport { stat } from \"node:fs/promises\";\nimport { existsSync } from \"node:fs\";\nimport * as os from \"node:os\";\nimport * as path from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\n\nimport type { Worker as NodeWorker } from \"node:worker_threads\";\n\nimport type { Finding, ParallelScanOptions, ScanResult } from \"./types.js\";\nimport { walkFiles } from \"./walk.js\";\nimport { isAnalyzableSource } from \"./detect-utils.js\";\nimport { buildInventory } from \"./inventory.js\";\nimport { compareFindings, filterExplicitFileList, scan } from \"./scan.js\";\nimport { AbortError, BudgetExceededError } from \"./errors.js\";\nimport { VERSION } from \"./version.js\";\n\n/** One unit of work dispatched to a worker. */\nexport interface ScanChunk {\n files: string[];\n}\n\n/** What a worker returns for a chunk. */\nexport interface ChunkResult {\n findings: Finding[];\n filesScanned: number;\n /** Files that couldn't be read in this chunk (optional; older workers omit it). */\n unreadable?: number;\n /** Files skipped as minified in this chunk (optional; older workers omit it). */\n skippedMinified?: number;\n}\n\nconst DEFAULT_PARALLEL_THRESHOLD_BYTES = 2 * 1024 * 1024;\nconst DEFAULT_PARALLEL_FILE_THRESHOLD = 200;\nconst DEFAULT_CHUNK_BYTES = 4 * 1024 * 1024;\n\n/** A file plus its byte size, used for byte-balanced chunking. */\nexport interface SizedFile {\n rel: string;\n size: number;\n}\n\n/**\n * Bucket files into chunks of roughly `chunkBytes` total bytes each, so one\n * large file doesn't starve a worker holding many tiny files. Pure +\n * deterministic: input order is preserved within and across chunks.\n */\nexport function chunkByBytes(files: readonly SizedFile[], chunkBytes: number): ScanChunk[] {\n const limit = Math.max(1, chunkBytes);\n const chunks: ScanChunk[] = [];\n let current: string[] = [];\n let currentBytes = 0;\n for (const f of files) {\n if (current.length > 0 && currentBytes + f.size > limit) {\n chunks.push({ files: current });\n current = [];\n currentBytes = 0;\n }\n current.push(f.rel);\n currentBytes += f.size;\n }\n if (current.length > 0) chunks.push({ files: current });\n return chunks;\n}\n\n/**\n * Merge per-chunk results into a single ordered finding set + total file count.\n * Pure: applies the SAME comparator as the serial scan, so the result is\n * byte-identical regardless of chunk completion order. Exported for unit tests.\n */\nexport function mergeChunkResults(results: readonly ChunkResult[]): ChunkResult {\n const findings: Finding[] = [];\n let filesScanned = 0;\n let unreadable = 0;\n let skippedMinified = 0;\n for (const r of results) {\n for (const f of r.findings) findings.push(f);\n filesScanned += r.filesScanned;\n unreadable += r.unreadable ?? 0;\n skippedMinified += r.skippedMinified ?? 0;\n }\n findings.sort(compareFindings);\n return { findings, filesScanned, unreadable, skippedMinified };\n}\n\n/** Resolve the worker count (>= 1). */\nfunction resolveConcurrency(options: ParallelScanOptions): number {\n const raw = options.concurrency;\n if (typeof raw === \"number\" && raw >= 1) return Math.floor(raw);\n const avail =\n typeof os.availableParallelism === \"function\" ? os.availableParallelism() : os.cpus().length;\n return Math.max(1, avail);\n}\n\n/** Decide whether the workload is large enough to justify spawning workers. */\nfunction shouldParallelize(options: ParallelScanOptions, files: SizedFile[]): boolean {\n const totalBytes = files.reduce((acc, f) => acc + f.size, 0);\n const byteFloor = options.parallelThresholdBytes ?? DEFAULT_PARALLEL_THRESHOLD_BYTES;\n const fileFloor = options.parallelFileThreshold ?? DEFAULT_PARALLEL_FILE_THRESHOLD;\n if (resolveConcurrency(options) <= 1) return false;\n return totalBytes >= byteFloor && files.length >= fileFloor;\n}\n\n/**\n * Enumerate the files to scan (relative POSIX paths + sizes), honouring an\n * explicit `files` list or the walker. Sizes power byte-balanced chunking.\n */\nasync function enumerateFiles(options: ParallelScanOptions, baseDir: string): Promise<SizedFile[]> {\n const rels: string[] = [];\n if (options.files) {\n // Apply the SAME include/exclude/binary filtering the serial path uses via\n // `filterExplicitFiles`, so `--parallel` is byte-for-byte identical to serial.\n for (const rel of filterExplicitFileList(options.files, options)) rels.push(rel);\n } else {\n for await (const rel of walkFiles(options.root, {\n include: options.include,\n exclude: options.exclude,\n noDefaultIgnores: options.noDefaultIgnores,\n maxFileSize: options.maxFileSize,\n })) {\n rels.push(rel);\n }\n }\n\n const sized: SizedFile[] = [];\n for (const rel of rels) {\n let size = 0;\n try {\n size = (await stat(path.join(baseDir, ...rel.split(\"/\")))).size;\n } catch {\n // Unreadable now; keep with size 0 — worker read will skip if it's gone.\n }\n sized.push({ rel, size });\n }\n return sized;\n}\n\n/**\n * Resolve the worker entry next to this module. In a normal build it's\n * `dist/scan-worker.js`. When running from source under a TypeScript loader\n * (tsx) the built JS doesn't exist, so fall back to `scan-worker.ts` and tell\n * the worker to load tsx as well — so the parallel scanner works in dev/source\n * mode (and is exercisable by tests), not only after a build.\n */\nfunction workerEntry(): { entry: string; execArgv?: string[] } {\n const here = fileURLToPath(import.meta.url);\n const dir = path.dirname(here);\n const js = path.join(dir, \"scan-worker.js\");\n if (existsSync(js)) return { entry: js };\n const ts = path.join(dir, \"scan-worker.ts\");\n if (existsSync(ts)) return { entry: ts, execArgv: [\"--import\", \"tsx\"] };\n return { entry: js }; // neither present: let Worker surface a clear ENOENT\n}\n\n/**\n * Scan in parallel across a worker-thread pool, falling back to the in-process\n * serial {@link scan} for small workloads or when workers can't be used. The\n * result is deterministic and identical to the serial path.\n */\nexport async function scanParallel(options: ParallelScanOptions): Promise<ScanResult> {\n const startedAt = new Date();\n\n const rootStat = await stat(options.root);\n const baseDir = rootStat.isFile() ? path.dirname(options.root) : options.root;\n\n // Single-file roots, the override-detectors path, and the scan cache always\n // run serially: detectors may not be structured-cloneable across the worker\n // boundary, and the cache read/write is owned by the in-process `scan()`.\n if (rootStat.isFile() || options.detectors || options.cacheFile) {\n return scan(options);\n }\n\n const files = await enumerateFiles(options, baseDir);\n\n // Budget + cancellation parity with the serial path. The parallel path\n // enumerates upfront, so budgets are enforced against the whole file set\n // before any worker is dispatched.\n if (options.signal?.aborted) throw new AbortError();\n if (typeof options.maxFiles === \"number\" && files.length > options.maxFiles) {\n throw new BudgetExceededError(`maxFiles budget exceeded (limit: ${options.maxFiles}).`);\n }\n if (typeof options.maxBytes === \"number\") {\n const totalBytes = files.reduce((n, f) => n + f.size, 0);\n if (totalBytes > options.maxBytes) {\n throw new BudgetExceededError(`maxBytes budget exceeded (limit: ${options.maxBytes}).`);\n }\n }\n\n if (!shouldParallelize(options, files)) {\n // In-process: reuse the exact serial path over the same file list.\n return scan({ ...options, files: files.map((f) => f.rel) });\n }\n\n let WorkerCtor: typeof import(\"node:worker_threads\").Worker;\n try {\n ({ Worker: WorkerCtor } = await import(\"node:worker_threads\"));\n } catch {\n return scan({ ...options, files: files.map((f) => f.rel) });\n }\n\n const chunks = chunkByBytes(files, options.chunkBytes ?? DEFAULT_CHUNK_BYTES);\n const concurrency = Math.min(resolveConcurrency(options), chunks.length);\n const { entry, execArgv } = workerEntry();\n\n const toggles = {\n source: options.source !== false,\n config: options.config !== false,\n deps: options.dependencies !== false,\n scanMinified: options.scanMinified === true,\n // Plain string array — structured-cloneable, so it crosses the worker boundary.\n disabledRules: options.disabledRules,\n };\n\n let results: ChunkResult[];\n try {\n results = await runPool(\n WorkerCtor,\n entry,\n execArgv,\n baseDir,\n toggles,\n chunks,\n concurrency,\n options.onFile,\n options.signal,\n );\n } catch (err) {\n // Cancellation / budget overflow must propagate, not silently degrade.\n if (err instanceof AbortError || err instanceof BudgetExceededError) throw err;\n // Any other worker failure → safe fallback to the serial path.\n return scan({ ...options, files: files.map((f) => f.rel) });\n }\n\n const merged = mergeChunkResults(results);\n const inventory = buildInventory(merged.findings);\n const finishedAt = new Date();\n\n // Coverage: count analyzable-source files in the enumerated set. Computed from\n // the file list (not per-worker) to avoid worker-boundary plumbing; on the\n // parallel path this can include a minified analyzable file the workers\n // skipped, but that is vanishingly rare and never under-reports coverage.\n const analyzedFiles = files.reduce((n, f) => (isAnalyzableSource(f.rel) ? n + 1 : n), 0);\n\n return {\n root: options.root,\n findings: merged.findings,\n filesScanned: merged.filesScanned,\n analyzedFiles,\n diagnostics: {\n unreadable: merged.unreadable ?? 0,\n skippedMinified: merged.skippedMinified ?? 0,\n },\n inventory,\n startedAt: startedAt.toISOString(),\n finishedAt: finishedAt.toISOString(),\n toolVersion: VERSION,\n };\n}\n\n/** Worker-pool driver: at most `concurrency` workers, each pulls chunks. */\nfunction runPool(\n WorkerCtor: typeof import(\"node:worker_threads\").Worker,\n entry: string,\n execArgv: string[] | undefined,\n baseDir: string,\n toggles: {\n source: boolean;\n config: boolean;\n deps: boolean;\n scanMinified: boolean;\n disabledRules?: string[];\n },\n chunks: ScanChunk[],\n concurrency: number,\n onFile?: (file: string) => void,\n signal?: AbortSignal,\n): Promise<ChunkResult[]> {\n return new Promise((resolve, reject) => {\n const results: ChunkResult[] = new Array(chunks.length);\n let next = 0;\n let done = 0;\n let failed = false;\n const workers: Array<NodeWorker> = [];\n\n const onAbort = (): void => {\n if (failed) return;\n failed = true;\n cleanup();\n reject(new AbortError());\n };\n\n const cleanup = () => {\n if (signal) signal.removeEventListener(\"abort\", onAbort);\n for (const w of workers) void w.terminate();\n };\n\n // Cooperative cancellation: stop dispatching and tear down on abort.\n if (signal) {\n if (signal.aborted) {\n reject(new AbortError());\n return;\n }\n signal.addEventListener(\"abort\", onAbort, { once: true });\n }\n\n // Workers we intentionally terminate (no more chunks / on cleanup). Their\n // `exit` event is expected and must NOT be treated as a crash.\n const retired = new WeakSet<NodeWorker>();\n\n const dispatch = (w: NodeWorker): void => {\n if (failed) return;\n if (next >= chunks.length) {\n retired.add(w);\n void w.terminate();\n return;\n }\n const idx = next++;\n w.postMessage({ index: idx, files: chunks[idx].files });\n };\n\n const spawn = (): NodeWorker => {\n const w = new WorkerCtor(entry, {\n workerData: { baseDir, toggles },\n ...(execArgv ? { execArgv } : {}),\n });\n w.on(\n \"message\",\n (msg: { index: number; result?: ChunkResult; files?: string[]; error?: string }) => {\n if (msg.error) {\n if (!failed) {\n failed = true;\n cleanup();\n reject(new Error(msg.error));\n }\n return;\n }\n if (msg.files && onFile) for (const f of msg.files) onFile(f);\n if (msg.result) {\n results[msg.index] = msg.result;\n done++;\n if (done === chunks.length) {\n cleanup();\n resolve(results);\n return;\n }\n dispatch(w);\n }\n },\n );\n w.on(\"error\", (err) => {\n if (!failed) {\n failed = true;\n cleanup();\n reject(err);\n }\n });\n // A worker that dies WITHOUT an `error` or `message` (e.g. a `process.exit`\n // in loaded code) would otherwise leave the pool pending forever. Reject so\n // scanParallel falls back to the serial path (audit: arch #5). Expected\n // exits (retired workers, post-completion cleanup) are ignored.\n w.on(\"exit\", (code) => {\n if (failed || retired.has(w) || done === chunks.length) return;\n failed = true;\n cleanup();\n reject(new Error(`scan worker exited early (code ${code}) before completing its chunk`));\n });\n return w;\n };\n\n const n = Math.max(1, Math.min(concurrency, chunks.length));\n for (let i = 0; i < n; i++) {\n const w = spawn();\n workers.push(w);\n dispatch(w);\n }\n });\n}\n"]}
|
|
1
|
+
{"version":3,"file":"parallel.js","sourceRoot":"","sources":["../src/parallel.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Parallel scanning over a worker-thread pool (P2-1). The scan is embarrassingly\n * parallel: each file is read and regex-scanned independently, and the merge is\n * a concatenation + the existing deterministic sort. `Finding` is plain\n * structured-cloneable data, so it crosses the worker boundary cleanly.\n *\n * The merge and chunking logic are pure and exported for direct unit testing.\n * `scanParallel` falls back to the in-process serial path below the crossover\n * (small file counts / small total bytes) and whenever workers are unavailable.\n */\nimport { stat } from \"node:fs/promises\";\nimport { existsSync } from \"node:fs\";\nimport * as os from \"node:os\";\nimport * as path from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\n\nimport type { Worker as NodeWorker } from \"node:worker_threads\";\n\nimport type { Finding, ParallelScanOptions, ScanResult } from \"./types.js\";\nimport { walkFilesSized } from \"./walk.js\";\nimport { isAnalyzableSource } from \"./detect-utils.js\";\nimport { buildInventory } from \"./inventory.js\";\nimport { compareFindings, filterExplicitFileList, scan } from \"./scan.js\";\nimport { AbortError, BudgetExceededError } from \"./errors.js\";\nimport { VERSION } from \"./version.js\";\n\n/** One unit of work dispatched to a worker. */\nexport interface ScanChunk {\n files: string[];\n}\n\n/** What a worker returns for a chunk. */\nexport interface ChunkResult {\n findings: Finding[];\n filesScanned: number;\n /** Files that couldn't be read in this chunk (optional; older workers omit it). */\n unreadable?: number;\n /** Files skipped as minified in this chunk (optional; older workers omit it). */\n skippedMinified?: number;\n}\n\nconst DEFAULT_PARALLEL_THRESHOLD_BYTES = 2 * 1024 * 1024;\nconst DEFAULT_PARALLEL_FILE_THRESHOLD = 200;\nconst DEFAULT_CHUNK_BYTES = 4 * 1024 * 1024;\n\n/** A file plus its byte size, used for byte-balanced chunking. */\nexport interface SizedFile {\n rel: string;\n size: number;\n}\n\n/**\n * Bucket files into chunks of roughly `chunkBytes` total bytes each, so one\n * large file doesn't starve a worker holding many tiny files. Pure +\n * deterministic: input order is preserved within and across chunks.\n */\nexport function chunkByBytes(files: readonly SizedFile[], chunkBytes: number): ScanChunk[] {\n const limit = Math.max(1, chunkBytes);\n const chunks: ScanChunk[] = [];\n let current: string[] = [];\n let currentBytes = 0;\n for (const f of files) {\n if (current.length > 0 && currentBytes + f.size > limit) {\n chunks.push({ files: current });\n current = [];\n currentBytes = 0;\n }\n current.push(f.rel);\n currentBytes += f.size;\n }\n if (current.length > 0) chunks.push({ files: current });\n return chunks;\n}\n\n/**\n * Merge per-chunk results into a single ordered finding set + total file count.\n * Pure: applies the SAME comparator as the serial scan, so the result is\n * byte-identical regardless of chunk completion order. Exported for unit tests.\n */\nexport function mergeChunkResults(results: readonly ChunkResult[]): ChunkResult {\n const findings: Finding[] = [];\n let filesScanned = 0;\n let unreadable = 0;\n let skippedMinified = 0;\n for (const r of results) {\n for (const f of r.findings) findings.push(f);\n filesScanned += r.filesScanned;\n unreadable += r.unreadable ?? 0;\n skippedMinified += r.skippedMinified ?? 0;\n }\n findings.sort(compareFindings);\n return { findings, filesScanned, unreadable, skippedMinified };\n}\n\n/** Resolve the worker count (>= 1). */\nfunction resolveConcurrency(options: ParallelScanOptions): number {\n const raw = options.concurrency;\n if (typeof raw === \"number\" && raw >= 1) return Math.floor(raw);\n const avail =\n typeof os.availableParallelism === \"function\" ? os.availableParallelism() : os.cpus().length;\n return Math.max(1, avail);\n}\n\n/** Decide whether the workload is large enough to justify spawning workers. */\nfunction shouldParallelize(options: ParallelScanOptions, files: SizedFile[]): boolean {\n const totalBytes = files.reduce((acc, f) => acc + f.size, 0);\n const byteFloor = options.parallelThresholdBytes ?? DEFAULT_PARALLEL_THRESHOLD_BYTES;\n const fileFloor = options.parallelFileThreshold ?? DEFAULT_PARALLEL_FILE_THRESHOLD;\n if (resolveConcurrency(options) <= 1) return false;\n return totalBytes >= byteFloor && files.length >= fileFloor;\n}\n\n/**\n * Enumerate the files to scan (relative POSIX paths + sizes), honouring an\n * explicit `files` list or the walker. Sizes power byte-balanced chunking.\n */\nasync function enumerateFiles(options: ParallelScanOptions, baseDir: string): Promise<SizedFile[]> {\n const sized: SizedFile[] = [];\n if (options.files) {\n // Explicit file list: the walker isn't involved, so stat each once here.\n // Apply the SAME include/exclude/binary filtering the serial path uses via\n // `filterExplicitFileList`, so `--parallel` is byte-for-byte identical to serial.\n for (const rel of filterExplicitFileList(options.files, options)) {\n let size = 0;\n try {\n size = (await stat(path.join(baseDir, ...rel.split(\"/\")))).size;\n } catch {\n // Unreadable now; keep with size 0 (the worker read will skip if it's gone).\n }\n sized.push({ rel, size });\n }\n return sized;\n }\n\n // Directory walk: reuse the size the walker already stat'd while enforcing the\n // size limit, so files are not stat'd a second time here (double-stat fix).\n for await (const { rel, size } of walkFilesSized(options.root, {\n include: options.include,\n exclude: options.exclude,\n noDefaultIgnores: options.noDefaultIgnores,\n maxFileSize: options.maxFileSize,\n })) {\n sized.push({ rel, size });\n }\n return sized;\n}\n\n/**\n * Resolve the worker entry next to this module. In a normal build it's\n * `dist/scan-worker.js`. When running from source under a TypeScript loader\n * (tsx) the built JS doesn't exist, so fall back to `scan-worker.ts` and tell\n * the worker to load tsx as well — so the parallel scanner works in dev/source\n * mode (and is exercisable by tests), not only after a build.\n */\nfunction workerEntry(): { entry: string; execArgv?: string[] } {\n const here = fileURLToPath(import.meta.url);\n const dir = path.dirname(here);\n const js = path.join(dir, \"scan-worker.js\");\n if (existsSync(js)) return { entry: js };\n const ts = path.join(dir, \"scan-worker.ts\");\n if (existsSync(ts)) return { entry: ts, execArgv: [\"--import\", \"tsx\"] };\n return { entry: js }; // neither present: let Worker surface a clear ENOENT\n}\n\n/**\n * Scan in parallel across a worker-thread pool, falling back to the in-process\n * serial {@link scan} for small workloads or when workers can't be used. The\n * result is deterministic and identical to the serial path.\n */\nexport async function scanParallel(options: ParallelScanOptions): Promise<ScanResult> {\n const startedAt = new Date();\n\n const rootStat = await stat(options.root);\n const baseDir = rootStat.isFile() ? path.dirname(options.root) : options.root;\n\n // Single-file roots, the override-detectors path, and the scan cache always\n // run serially: detectors may not be structured-cloneable across the worker\n // boundary, and the cache read/write is owned by the in-process `scan()`.\n if (rootStat.isFile() || options.detectors || options.cacheFile) {\n return scan(options);\n }\n\n const files = await enumerateFiles(options, baseDir);\n\n // Budget + cancellation parity with the serial path. The parallel path\n // enumerates upfront, so budgets are enforced against the whole file set\n // before any worker is dispatched.\n if (options.signal?.aborted) throw new AbortError();\n if (typeof options.maxFiles === \"number\" && files.length > options.maxFiles) {\n throw new BudgetExceededError(`maxFiles budget exceeded (limit: ${options.maxFiles}).`);\n }\n if (typeof options.maxBytes === \"number\") {\n const totalBytes = files.reduce((n, f) => n + f.size, 0);\n if (totalBytes > options.maxBytes) {\n throw new BudgetExceededError(`maxBytes budget exceeded (limit: ${options.maxBytes}).`);\n }\n }\n\n if (!shouldParallelize(options, files)) {\n // In-process: reuse the exact serial path over the same file list.\n return scan({ ...options, files: files.map((f) => f.rel) });\n }\n\n let WorkerCtor: typeof import(\"node:worker_threads\").Worker;\n try {\n ({ Worker: WorkerCtor } = await import(\"node:worker_threads\"));\n } catch {\n return scan({ ...options, files: files.map((f) => f.rel) });\n }\n\n const chunks = chunkByBytes(files, options.chunkBytes ?? DEFAULT_CHUNK_BYTES);\n const concurrency = Math.min(resolveConcurrency(options), chunks.length);\n const { entry, execArgv } = workerEntry();\n\n const toggles = {\n source: options.source !== false,\n config: options.config !== false,\n deps: options.dependencies !== false,\n scanMinified: options.scanMinified === true,\n // Plain string array — structured-cloneable, so it crosses the worker boundary.\n disabledRules: options.disabledRules,\n };\n\n let results: ChunkResult[];\n try {\n results = await runPool(\n WorkerCtor,\n entry,\n execArgv,\n baseDir,\n toggles,\n chunks,\n concurrency,\n options.onFile,\n options.signal,\n );\n } catch (err) {\n // Cancellation / budget overflow must propagate, not silently degrade.\n if (err instanceof AbortError || err instanceof BudgetExceededError) throw err;\n // Any other worker failure → safe fallback to the serial path.\n return scan({ ...options, files: files.map((f) => f.rel) });\n }\n\n const merged = mergeChunkResults(results);\n const inventory = buildInventory(merged.findings);\n const finishedAt = new Date();\n\n // Coverage: count analyzable-source files in the enumerated set. Computed from\n // the file list (not per-worker) to avoid worker-boundary plumbing; on the\n // parallel path this can include a minified analyzable file the workers\n // skipped, but that is vanishingly rare and never under-reports coverage.\n const analyzedFiles = files.reduce((n, f) => (isAnalyzableSource(f.rel) ? n + 1 : n), 0);\n\n return {\n root: options.root,\n findings: merged.findings,\n filesScanned: merged.filesScanned,\n analyzedFiles,\n diagnostics: {\n unreadable: merged.unreadable ?? 0,\n skippedMinified: merged.skippedMinified ?? 0,\n },\n inventory,\n startedAt: startedAt.toISOString(),\n finishedAt: finishedAt.toISOString(),\n toolVersion: VERSION,\n };\n}\n\n/** Worker-pool driver: at most `concurrency` workers, each pulls chunks. */\nfunction runPool(\n WorkerCtor: typeof import(\"node:worker_threads\").Worker,\n entry: string,\n execArgv: string[] | undefined,\n baseDir: string,\n toggles: {\n source: boolean;\n config: boolean;\n deps: boolean;\n scanMinified: boolean;\n disabledRules?: string[];\n },\n chunks: ScanChunk[],\n concurrency: number,\n onFile?: (file: string) => void,\n signal?: AbortSignal,\n): Promise<ChunkResult[]> {\n return new Promise((resolve, reject) => {\n const results: ChunkResult[] = new Array(chunks.length);\n let next = 0;\n let done = 0;\n let failed = false;\n const workers: Array<NodeWorker> = [];\n\n const onAbort = (): void => {\n if (failed) return;\n failed = true;\n cleanup();\n reject(new AbortError());\n };\n\n const cleanup = () => {\n if (signal) signal.removeEventListener(\"abort\", onAbort);\n for (const w of workers) void w.terminate();\n };\n\n // Cooperative cancellation: stop dispatching and tear down on abort.\n if (signal) {\n if (signal.aborted) {\n reject(new AbortError());\n return;\n }\n signal.addEventListener(\"abort\", onAbort, { once: true });\n }\n\n // Workers we intentionally terminate (no more chunks / on cleanup). Their\n // `exit` event is expected and must NOT be treated as a crash.\n const retired = new WeakSet<NodeWorker>();\n\n const dispatch = (w: NodeWorker): void => {\n if (failed) return;\n if (next >= chunks.length) {\n retired.add(w);\n void w.terminate();\n return;\n }\n const idx = next++;\n w.postMessage({ index: idx, files: chunks[idx].files });\n };\n\n const spawn = (): NodeWorker => {\n const w = new WorkerCtor(entry, {\n workerData: { baseDir, toggles },\n ...(execArgv ? { execArgv } : {}),\n });\n w.on(\n \"message\",\n (msg: { index: number; result?: ChunkResult; files?: string[]; error?: string }) => {\n if (msg.error) {\n if (!failed) {\n failed = true;\n cleanup();\n reject(new Error(msg.error));\n }\n return;\n }\n if (msg.files && onFile) for (const f of msg.files) onFile(f);\n if (msg.result) {\n results[msg.index] = msg.result;\n done++;\n if (done === chunks.length) {\n cleanup();\n resolve(results);\n return;\n }\n dispatch(w);\n }\n },\n );\n w.on(\"error\", (err) => {\n if (!failed) {\n failed = true;\n cleanup();\n reject(err);\n }\n });\n // A worker that dies WITHOUT an `error` or `message` (e.g. a `process.exit`\n // in loaded code) would otherwise leave the pool pending forever. Reject so\n // scanParallel falls back to the serial path (audit: arch #5). Expected\n // exits (retired workers, post-completion cleanup) are ignored.\n w.on(\"exit\", (code) => {\n if (failed || retired.has(w) || done === chunks.length) return;\n failed = true;\n cleanup();\n reject(new Error(`scan worker exited early (code ${code}) before completing its chunk`));\n });\n return w;\n };\n\n const n = Math.max(1, Math.min(concurrency, chunks.length));\n for (let i = 0; i < n; i++) {\n const w = spawn();\n workers.push(w);\n dispatch(w);\n }\n });\n}\n"]}
|
package/dist/registry.d.ts
CHANGED
|
@@ -49,10 +49,15 @@ export declare class DetectorRegistry {
|
|
|
49
49
|
clone(): DetectorRegistry;
|
|
50
50
|
}
|
|
51
51
|
/**
|
|
52
|
-
* The built-in detectors, in run order: the JS/TS source + config
|
|
53
|
-
*
|
|
54
|
-
*
|
|
55
|
-
*
|
|
52
|
+
* The built-in detectors, in run order: the JS/TS source + language-agnostic config
|
|
53
|
+
* detectors, the per-language source packs (Python, Go, Java/Kotlin/Scala, C#, Rust,
|
|
54
|
+
* Ruby, PHP, Elixir, C/C++, Swift, Objective-C, Dart, Solidity/Move/Cairo), the
|
|
55
|
+
* infrastructure/config detectors (Terraform, Bicep, Pulumi, CloudFormation, cloud-KMS,
|
|
56
|
+
* k8s, mesh, DNSSEC, Vault, database/TDE, messaging, VPN, Ansible, supply-chain, CI/CD,
|
|
57
|
+
* secrets, age, keystore, OpenPGP, JWK, XML-DSig/SAML, PKCS#11/HSM, DKIM, SSH-CA,
|
|
58
|
+
* SPIFFE/SPIRE, reverse-proxy/gRPC TLS, WebAuthn/FIDO2, code-signing, weak-hash-in-
|
|
59
|
+
* signature), and the stateful-HBS (SP 800-208) detector. The manifest (dependency)
|
|
60
|
+
* scanner is handled separately by `scan()`.
|
|
56
61
|
*
|
|
57
62
|
* This is the single source of truth for the default detector set: both
|
|
58
63
|
* {@link defaultRegistry} and the public `detectors` export (re-exported from
|
package/dist/registry.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../src/registry.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,OAAO,KAAK,EAAE,QAAQ,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../src/registry.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,OAAO,KAAK,EAAE,QAAQ,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAgDpE,6EAA6E;AAC7E,wBAAgB,aAAa,CAAC,CAAC,EAAE,QAAQ,GAAG,aAAa,CAExD;AAED,+FAA+F;AAC/F,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,QAAQ,CAAC;IACf,QAAQ,EAAE,QAAQ,CAAC;CACpB;AAED;;;GAGG;AACH,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,QAAQ,CAAC,IAAI,CAA+B;IACpD,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAgB;IAEtC,4EAA4E;gBAChE,OAAO,GAAE,SAAS,QAAQ,EAAO;IAI7C,kFAAkF;IAClF,QAAQ,CAAC,CAAC,EAAE,QAAQ,GAAG,IAAI;IAS3B,wDAAwD;IACxD,GAAG,CAAC,EAAE,EAAE,MAAM,GAAG,QAAQ,GAAG,SAAS;IAIrC,qDAAqD;IACrD,GAAG,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO;IAIxB,uDAAuD;IACvD,GAAG,IAAI,QAAQ,EAAE;IAIjB;;;;;;OAMG;IACH,WAAW,IAAI,QAAQ,EAAE;IAezB,gFAAgF;IAChF,OAAO,CAAC,MAAM,EAAE,MAAM,GAAG,gBAAgB,GAAG,SAAS;IASrD,uEAAuE;IACvE,KAAK,IAAI,gBAAgB;CAG1B;AAED;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,gBAAgB,EAAE,QAAQ,EA+CtC,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,eAAe,kBAAyC,CAAC"}
|
package/dist/registry.js
CHANGED
|
@@ -8,6 +8,10 @@ import { rubyDetector } from "./detectors/ruby.js";
|
|
|
8
8
|
import { phpDetector } from "./detectors/php.js";
|
|
9
9
|
import { elixirDetector } from "./detectors/elixir.js";
|
|
10
10
|
import { cDetector } from "./detectors/c.js";
|
|
11
|
+
import { swiftDetector } from "./detectors/swift.js";
|
|
12
|
+
import { objcDetector } from "./detectors/objc.js";
|
|
13
|
+
import { dartDetector } from "./detectors/dart.js";
|
|
14
|
+
import { solidityDetector } from "./detectors/solidity.js";
|
|
11
15
|
import { pemDetector } from "./detectors/pem.js";
|
|
12
16
|
import { jwkDetector } from "./detectors/jwk.js";
|
|
13
17
|
import { terraformDetector } from "./detectors/terraform.js";
|
|
@@ -18,10 +22,27 @@ import { joseDetector } from "./detectors/jose.js";
|
|
|
18
22
|
import { k8sDetector } from "./detectors/k8s.js";
|
|
19
23
|
import { messagingDetector } from "./detectors/messaging.js";
|
|
20
24
|
import { databaseDetector } from "./detectors/database.js";
|
|
25
|
+
import { xmldsigDetector } from "./detectors/xmldsig.js";
|
|
26
|
+
import { pkcs11Detector } from "./detectors/pkcs11.js";
|
|
27
|
+
import { dkimDetector } from "./detectors/dkim.js";
|
|
28
|
+
import { sshCaDetector } from "./detectors/ssh-ca.js";
|
|
29
|
+
import { spireDetector } from "./detectors/spire.js";
|
|
30
|
+
import { proxyDetector } from "./detectors/proxy.js";
|
|
31
|
+
import { webauthnDetector } from "./detectors/webauthn.js";
|
|
32
|
+
import { codesignDetector } from "./detectors/codesign.js";
|
|
33
|
+
import { weakHashDetector } from "./detectors/weak-hash.js";
|
|
21
34
|
import { cloudformationDetector } from "./detectors/cloudformation.js";
|
|
35
|
+
import { bicepDetector } from "./detectors/bicep.js";
|
|
36
|
+
import { pulumiDetector } from "./detectors/pulumi.js";
|
|
22
37
|
import { meshDetector } from "./detectors/mesh.js";
|
|
23
38
|
import { dnssecDetector } from "./detectors/dnssec.js";
|
|
24
39
|
import { vpnDetector } from "./detectors/vpn.js";
|
|
40
|
+
import { ansibleDetector } from "./detectors/ansible.js";
|
|
41
|
+
import { ageDetector } from "./detectors/age.js";
|
|
42
|
+
import { supplyChainDetector } from "./detectors/supply-chain.js";
|
|
43
|
+
import { vaultDetector } from "./detectors/vault.js";
|
|
44
|
+
import { keystoreDetector } from "./detectors/keystore.js";
|
|
45
|
+
import { openpgpDetector } from "./detectors/openpgp.js";
|
|
25
46
|
import { statefulHbsDetector } from "./detectors/stateful-hbs.js";
|
|
26
47
|
/** Normalised scope of a detector (defaults to "source" when undeclared). */
|
|
27
48
|
export function detectorScope(d) {
|
|
@@ -97,10 +118,15 @@ export class DetectorRegistry {
|
|
|
97
118
|
}
|
|
98
119
|
}
|
|
99
120
|
/**
|
|
100
|
-
* The built-in detectors, in run order: the JS/TS source + config
|
|
101
|
-
*
|
|
102
|
-
*
|
|
103
|
-
*
|
|
121
|
+
* The built-in detectors, in run order: the JS/TS source + language-agnostic config
|
|
122
|
+
* detectors, the per-language source packs (Python, Go, Java/Kotlin/Scala, C#, Rust,
|
|
123
|
+
* Ruby, PHP, Elixir, C/C++, Swift, Objective-C, Dart, Solidity/Move/Cairo), the
|
|
124
|
+
* infrastructure/config detectors (Terraform, Bicep, Pulumi, CloudFormation, cloud-KMS,
|
|
125
|
+
* k8s, mesh, DNSSEC, Vault, database/TDE, messaging, VPN, Ansible, supply-chain, CI/CD,
|
|
126
|
+
* secrets, age, keystore, OpenPGP, JWK, XML-DSig/SAML, PKCS#11/HSM, DKIM, SSH-CA,
|
|
127
|
+
* SPIFFE/SPIRE, reverse-proxy/gRPC TLS, WebAuthn/FIDO2, code-signing, weak-hash-in-
|
|
128
|
+
* signature), and the stateful-HBS (SP 800-208) detector. The manifest (dependency)
|
|
129
|
+
* scanner is handled separately by `scan()`.
|
|
104
130
|
*
|
|
105
131
|
* This is the single source of truth for the default detector set: both
|
|
106
132
|
* {@link defaultRegistry} and the public `detectors` export (re-exported from
|
|
@@ -117,6 +143,10 @@ export const builtinDetectors = [
|
|
|
117
143
|
phpDetector,
|
|
118
144
|
elixirDetector,
|
|
119
145
|
cDetector,
|
|
146
|
+
swiftDetector,
|
|
147
|
+
objcDetector,
|
|
148
|
+
dartDetector,
|
|
149
|
+
solidityDetector,
|
|
120
150
|
pemDetector,
|
|
121
151
|
jwkDetector,
|
|
122
152
|
terraformDetector,
|
|
@@ -127,10 +157,27 @@ export const builtinDetectors = [
|
|
|
127
157
|
k8sDetector,
|
|
128
158
|
messagingDetector,
|
|
129
159
|
databaseDetector,
|
|
160
|
+
xmldsigDetector,
|
|
161
|
+
pkcs11Detector,
|
|
162
|
+
dkimDetector,
|
|
163
|
+
sshCaDetector,
|
|
164
|
+
spireDetector,
|
|
165
|
+
proxyDetector,
|
|
166
|
+
webauthnDetector,
|
|
167
|
+
codesignDetector,
|
|
168
|
+
weakHashDetector,
|
|
130
169
|
cloudformationDetector,
|
|
170
|
+
bicepDetector,
|
|
171
|
+
pulumiDetector,
|
|
131
172
|
meshDetector,
|
|
132
173
|
dnssecDetector,
|
|
133
174
|
vpnDetector,
|
|
175
|
+
ansibleDetector,
|
|
176
|
+
ageDetector,
|
|
177
|
+
supplyChainDetector,
|
|
178
|
+
vaultDetector,
|
|
179
|
+
keystoreDetector,
|
|
180
|
+
openpgpDetector,
|
|
134
181
|
statefulHbsDetector,
|
|
135
182
|
];
|
|
136
183
|
/**
|
package/dist/registry.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"registry.js","sourceRoot":"","sources":["../src/registry.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Detector registry — the plugin point for source/config detectors.\n *\n * Instead of `scan()` closing over a hardcoded array and inferring scope from\n * ruleId prefixes, detectors are registered with a declared `scope` and\n * `language` (see {@link Detector}). `scan()` consults a registry (the\n * {@link defaultRegistry} by default, or an explicit `detectors` override) and\n * honours the source/config toggles by each detector's declared scope.\n *\n * To add a language or detector, see the \"Adding a detector / language\" section\n * of the package README.\n */\nimport type { Detector, DetectorScope, RuleMeta } from \"./types.js\";\nimport { sourceDetectors } from \"./detectors/source.js\";\nimport { pythonDetector } from \"./detectors/python.js\";\nimport { goDetector } from \"./detectors/go.js\";\nimport { javaDetector } from \"./detectors/java.js\";\nimport { csharpDetector } from \"./detectors/csharp.js\";\nimport { rustDetector } from \"./detectors/rust.js\";\nimport { rubyDetector } from \"./detectors/ruby.js\";\nimport { phpDetector } from \"./detectors/php.js\";\nimport { elixirDetector } from \"./detectors/elixir.js\";\nimport { cDetector } from \"./detectors/c.js\";\nimport { pemDetector } from \"./detectors/pem.js\";\nimport { jwkDetector } from \"./detectors/jwk.js\";\nimport { terraformDetector } from \"./detectors/terraform.js\";\nimport { cloudKmsDetector } from \"./detectors/cloud-kms.js\";\nimport { cicdDetector } from \"./detectors/cicd.js\";\nimport { secretsDetector } from \"./detectors/secrets.js\";\nimport { joseDetector } from \"./detectors/jose.js\";\nimport { k8sDetector } from \"./detectors/k8s.js\";\nimport { messagingDetector } from \"./detectors/messaging.js\";\nimport { databaseDetector } from \"./detectors/database.js\";\nimport { cloudformationDetector } from \"./detectors/cloudformation.js\";\nimport { meshDetector } from \"./detectors/mesh.js\";\nimport { dnssecDetector } from \"./detectors/dnssec.js\";\nimport { vpnDetector } from \"./detectors/vpn.js\";\nimport { statefulHbsDetector } from \"./detectors/stateful-hbs.js\";\n\n/** Normalised scope of a detector (defaults to \"source\" when undeclared). */\nexport function detectorScope(d: Detector): DetectorScope {\n return d.scope ?? \"source\";\n}\n\n/** A rule plus the detector that emits it — the result of {@link DetectorRegistry.forRule}. */\nexport interface RuleCatalogEntry {\n rule: RuleMeta;\n detector: Detector;\n}\n\n/**\n * An ordered, id-indexed collection of detectors. Registration order is\n * preserved by {@link all} for deterministic scan output. Ids must be unique.\n */\nexport class DetectorRegistry {\n private readonly byId = new Map<string, Detector>();\n private readonly order: string[] = [];\n\n /** Construct a registry, optionally seeded with an initial detector set. */\n constructor(initial: readonly Detector[] = []) {\n for (const d of initial) this.register(d);\n }\n\n /** Register a detector. Throws on a duplicate id. Returns `this` for chaining. */\n register(d: Detector): this {\n if (this.byId.has(d.id)) {\n throw new Error(`duplicate detector id: ${d.id}`);\n }\n this.byId.set(d.id, d);\n this.order.push(d.id);\n return this;\n }\n\n /** Look up a detector by its id (exact, not prefix). */\n get(id: string): Detector | undefined {\n return this.byId.get(id);\n }\n\n /** True if a detector with this id is registered. */\n has(id: string): boolean {\n return this.byId.has(id);\n }\n\n /** All registered detectors, in registration order. */\n all(): Detector[] {\n return this.order.map((id) => this.byId.get(id)!);\n }\n\n /**\n * The flattened rule catalog: every {@link RuleMeta} declared by every\n * registered detector, in detector-registration then in-detector order. This\n * is the single source of truth for rule metadata consumed by SARIF\n * `rules[]`, the MCP `explain_finding` resolver, and per-rule enable/disable.\n * Duplicate rule ids across detectors throw (ids are globally unique).\n */\n ruleCatalog(): RuleMeta[] {\n const out: RuleMeta[] = [];\n const seen = new Set<string>();\n for (const det of this.all()) {\n for (const rule of det.rules ?? []) {\n if (seen.has(rule.id)) {\n throw new Error(`duplicate rule id in catalog: ${rule.id}`);\n }\n seen.add(rule.id);\n out.push(rule);\n }\n }\n return out;\n }\n\n /** Resolve a rule id to its {@link RuleMeta} and the detector that emits it. */\n forRule(ruleId: string): RuleCatalogEntry | undefined {\n for (const det of this.all()) {\n for (const rule of det.rules ?? []) {\n if (rule.id === ruleId) return { rule, detector: det };\n }\n }\n return undefined;\n }\n\n /** A shallow copy of this registry (useful to extend the defaults). */\n clone(): DetectorRegistry {\n return new DetectorRegistry(this.all());\n }\n}\n\n/**\n * The built-in detectors, in run order: the JS/TS source + config detectors,\n * then the per-language detectors (Python, Go, Java, C#, Rust, Ruby, C/C++),\n * then the language-agnostic PEM and stateful-HBS (SP 800-208) detectors. The\n * manifest (dependency) scanner is handled separately by `scan()`.\n *\n * This is the single source of truth for the default detector set: both\n * {@link defaultRegistry} and the public `detectors` export (re-exported from\n * `scan.ts`) are built from it, so the two can never drift out of sync.\n */\nexport const builtinDetectors: Detector[] = [\n ...sourceDetectors,\n pythonDetector,\n goDetector,\n javaDetector,\n csharpDetector,\n rustDetector,\n rubyDetector,\n phpDetector,\n elixirDetector,\n cDetector,\n pemDetector,\n jwkDetector,\n terraformDetector,\n cloudKmsDetector,\n cicdDetector,\n secretsDetector,\n joseDetector,\n k8sDetector,\n messagingDetector,\n databaseDetector,\n cloudformationDetector,\n meshDetector,\n dnssecDetector,\n vpnDetector,\n statefulHbsDetector,\n];\n\n/**\n * The default registry, preloaded with {@link builtinDetectors}. Used by\n * `scan()` whenever `options.detectors` is not supplied.\n */\nexport const defaultRegistry = new DetectorRegistry(builtinDetectors);\n"]}
|
|
1
|
+
{"version":3,"file":"registry.js","sourceRoot":"","sources":["../src/registry.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Detector registry — the plugin point for source/config detectors.\n *\n * Instead of `scan()` closing over a hardcoded array and inferring scope from\n * ruleId prefixes, detectors are registered with a declared `scope` and\n * `language` (see {@link Detector}). `scan()` consults a registry (the\n * {@link defaultRegistry} by default, or an explicit `detectors` override) and\n * honours the source/config toggles by each detector's declared scope.\n *\n * To add a language or detector, see the \"Adding a detector / language\" section\n * of the package README.\n */\nimport type { Detector, DetectorScope, RuleMeta } from \"./types.js\";\nimport { sourceDetectors } from \"./detectors/source.js\";\nimport { pythonDetector } from \"./detectors/python.js\";\nimport { goDetector } from \"./detectors/go.js\";\nimport { javaDetector } from \"./detectors/java.js\";\nimport { csharpDetector } from \"./detectors/csharp.js\";\nimport { rustDetector } from \"./detectors/rust.js\";\nimport { rubyDetector } from \"./detectors/ruby.js\";\nimport { phpDetector } from \"./detectors/php.js\";\nimport { elixirDetector } from \"./detectors/elixir.js\";\nimport { cDetector } from \"./detectors/c.js\";\nimport { swiftDetector } from \"./detectors/swift.js\";\nimport { objcDetector } from \"./detectors/objc.js\";\nimport { dartDetector } from \"./detectors/dart.js\";\nimport { solidityDetector } from \"./detectors/solidity.js\";\nimport { pemDetector } from \"./detectors/pem.js\";\nimport { jwkDetector } from \"./detectors/jwk.js\";\nimport { terraformDetector } from \"./detectors/terraform.js\";\nimport { cloudKmsDetector } from \"./detectors/cloud-kms.js\";\nimport { cicdDetector } from \"./detectors/cicd.js\";\nimport { secretsDetector } from \"./detectors/secrets.js\";\nimport { joseDetector } from \"./detectors/jose.js\";\nimport { k8sDetector } from \"./detectors/k8s.js\";\nimport { messagingDetector } from \"./detectors/messaging.js\";\nimport { databaseDetector } from \"./detectors/database.js\";\nimport { xmldsigDetector } from \"./detectors/xmldsig.js\";\nimport { pkcs11Detector } from \"./detectors/pkcs11.js\";\nimport { dkimDetector } from \"./detectors/dkim.js\";\nimport { sshCaDetector } from \"./detectors/ssh-ca.js\";\nimport { spireDetector } from \"./detectors/spire.js\";\nimport { proxyDetector } from \"./detectors/proxy.js\";\nimport { webauthnDetector } from \"./detectors/webauthn.js\";\nimport { codesignDetector } from \"./detectors/codesign.js\";\nimport { weakHashDetector } from \"./detectors/weak-hash.js\";\nimport { cloudformationDetector } from \"./detectors/cloudformation.js\";\nimport { bicepDetector } from \"./detectors/bicep.js\";\nimport { pulumiDetector } from \"./detectors/pulumi.js\";\nimport { meshDetector } from \"./detectors/mesh.js\";\nimport { dnssecDetector } from \"./detectors/dnssec.js\";\nimport { vpnDetector } from \"./detectors/vpn.js\";\nimport { ansibleDetector } from \"./detectors/ansible.js\";\nimport { ageDetector } from \"./detectors/age.js\";\nimport { supplyChainDetector } from \"./detectors/supply-chain.js\";\nimport { vaultDetector } from \"./detectors/vault.js\";\nimport { keystoreDetector } from \"./detectors/keystore.js\";\nimport { openpgpDetector } from \"./detectors/openpgp.js\";\nimport { statefulHbsDetector } from \"./detectors/stateful-hbs.js\";\n\n/** Normalised scope of a detector (defaults to \"source\" when undeclared). */\nexport function detectorScope(d: Detector): DetectorScope {\n return d.scope ?? \"source\";\n}\n\n/** A rule plus the detector that emits it — the result of {@link DetectorRegistry.forRule}. */\nexport interface RuleCatalogEntry {\n rule: RuleMeta;\n detector: Detector;\n}\n\n/**\n * An ordered, id-indexed collection of detectors. Registration order is\n * preserved by {@link all} for deterministic scan output. Ids must be unique.\n */\nexport class DetectorRegistry {\n private readonly byId = new Map<string, Detector>();\n private readonly order: string[] = [];\n\n /** Construct a registry, optionally seeded with an initial detector set. */\n constructor(initial: readonly Detector[] = []) {\n for (const d of initial) this.register(d);\n }\n\n /** Register a detector. Throws on a duplicate id. Returns `this` for chaining. */\n register(d: Detector): this {\n if (this.byId.has(d.id)) {\n throw new Error(`duplicate detector id: ${d.id}`);\n }\n this.byId.set(d.id, d);\n this.order.push(d.id);\n return this;\n }\n\n /** Look up a detector by its id (exact, not prefix). */\n get(id: string): Detector | undefined {\n return this.byId.get(id);\n }\n\n /** True if a detector with this id is registered. */\n has(id: string): boolean {\n return this.byId.has(id);\n }\n\n /** All registered detectors, in registration order. */\n all(): Detector[] {\n return this.order.map((id) => this.byId.get(id)!);\n }\n\n /**\n * The flattened rule catalog: every {@link RuleMeta} declared by every\n * registered detector, in detector-registration then in-detector order. This\n * is the single source of truth for rule metadata consumed by SARIF\n * `rules[]`, the MCP `explain_finding` resolver, and per-rule enable/disable.\n * Duplicate rule ids across detectors throw (ids are globally unique).\n */\n ruleCatalog(): RuleMeta[] {\n const out: RuleMeta[] = [];\n const seen = new Set<string>();\n for (const det of this.all()) {\n for (const rule of det.rules ?? []) {\n if (seen.has(rule.id)) {\n throw new Error(`duplicate rule id in catalog: ${rule.id}`);\n }\n seen.add(rule.id);\n out.push(rule);\n }\n }\n return out;\n }\n\n /** Resolve a rule id to its {@link RuleMeta} and the detector that emits it. */\n forRule(ruleId: string): RuleCatalogEntry | undefined {\n for (const det of this.all()) {\n for (const rule of det.rules ?? []) {\n if (rule.id === ruleId) return { rule, detector: det };\n }\n }\n return undefined;\n }\n\n /** A shallow copy of this registry (useful to extend the defaults). */\n clone(): DetectorRegistry {\n return new DetectorRegistry(this.all());\n }\n}\n\n/**\n * The built-in detectors, in run order: the JS/TS source + language-agnostic config\n * detectors, the per-language source packs (Python, Go, Java/Kotlin/Scala, C#, Rust,\n * Ruby, PHP, Elixir, C/C++, Swift, Objective-C, Dart, Solidity/Move/Cairo), the\n * infrastructure/config detectors (Terraform, Bicep, Pulumi, CloudFormation, cloud-KMS,\n * k8s, mesh, DNSSEC, Vault, database/TDE, messaging, VPN, Ansible, supply-chain, CI/CD,\n * secrets, age, keystore, OpenPGP, JWK, XML-DSig/SAML, PKCS#11/HSM, DKIM, SSH-CA,\n * SPIFFE/SPIRE, reverse-proxy/gRPC TLS, WebAuthn/FIDO2, code-signing, weak-hash-in-\n * signature), and the stateful-HBS (SP 800-208) detector. The manifest (dependency)\n * scanner is handled separately by `scan()`.\n *\n * This is the single source of truth for the default detector set: both\n * {@link defaultRegistry} and the public `detectors` export (re-exported from\n * `scan.ts`) are built from it, so the two can never drift out of sync.\n */\nexport const builtinDetectors: Detector[] = [\n ...sourceDetectors,\n pythonDetector,\n goDetector,\n javaDetector,\n csharpDetector,\n rustDetector,\n rubyDetector,\n phpDetector,\n elixirDetector,\n cDetector,\n swiftDetector,\n objcDetector,\n dartDetector,\n solidityDetector,\n pemDetector,\n jwkDetector,\n terraformDetector,\n cloudKmsDetector,\n cicdDetector,\n secretsDetector,\n joseDetector,\n k8sDetector,\n messagingDetector,\n databaseDetector,\n xmldsigDetector,\n pkcs11Detector,\n dkimDetector,\n sshCaDetector,\n spireDetector,\n proxyDetector,\n webauthnDetector,\n codesignDetector,\n weakHashDetector,\n cloudformationDetector,\n bicepDetector,\n pulumiDetector,\n meshDetector,\n dnssecDetector,\n vpnDetector,\n ansibleDetector,\n ageDetector,\n supplyChainDetector,\n vaultDetector,\n keystoreDetector,\n openpgpDetector,\n statefulHbsDetector,\n];\n\n/**\n * The default registry, preloaded with {@link builtinDetectors}. Used by\n * `scan()` whenever `options.detectors` is not supplied.\n */\nexport const defaultRegistry = new DetectorRegistry(builtinDetectors);\n"]}
|
|
@@ -33,9 +33,6 @@ export interface RemediateOptions {
|
|
|
33
33
|
/** Which files a patch is allowed to touch. */
|
|
34
34
|
policy: PolicyContext;
|
|
35
35
|
}
|
|
36
|
-
/** Max changed lines allowed in an auto-verified LLM patch — a real crypto fix
|
|
37
|
-
* is localized; a sprawling rewrite is not reviewable as "just the fix". */
|
|
38
|
-
export declare const LLM_PATCH_MAX_CHANGED_LINES = 60;
|
|
39
36
|
/**
|
|
40
37
|
* Run each finding through patchSource → policy → verify, collecting the patches
|
|
41
38
|
* that survive both gates and the reasons the rest were dropped.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"remediate-pipeline.d.ts","sourceRoot":"","sources":["../src/remediate-pipeline.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAC1C,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AAE9C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAGvD,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,EAAE,KAAK,CAAC;CACd;AACD,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;CAChB;AACD,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,aAAa,EAAE,CAAC;IACzB,QAAQ,EAAE,aAAa,EAAE,CAAC;CAC3B;AAED,MAAM,WAAW,gBAAgB;IAC/B,sDAAsD;IACtD,WAAW,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,OAAO,CAAC,MAAM,CAAC,GAAG,MAAM,CAAC;IAC5D,yEAAyE;IACzE,WAAW,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC,KAAK,GAAG,IAAI,CAAC,GAAG,KAAK,GAAG,IAAI,CAAC;IACzF,+CAA+C;IAC/C,MAAM,EAAE,aAAa,CAAC;CACvB;
|
|
1
|
+
{"version":3,"file":"remediate-pipeline.d.ts","sourceRoot":"","sources":["../src/remediate-pipeline.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAC1C,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AAE9C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAGvD,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,EAAE,KAAK,CAAC;CACd;AACD,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;CAChB;AACD,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,aAAa,EAAE,CAAC;IACzB,QAAQ,EAAE,aAAa,EAAE,CAAC;CAC3B;AAED,MAAM,WAAW,gBAAgB;IAC/B,sDAAsD;IACtD,WAAW,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,OAAO,CAAC,MAAM,CAAC,GAAG,MAAM,CAAC;IAC5D,yEAAyE;IACzE,WAAW,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC,KAAK,GAAG,IAAI,CAAC,GAAG,KAAK,GAAG,IAAI,CAAC;IACzF,+CAA+C;IAC/C,MAAM,EAAE,aAAa,CAAC;CACvB;AA8ED;;;GAGG;AACH,wBAAsB,iBAAiB,CACrC,QAAQ,EAAE,SAAS,OAAO,EAAE,EAC5B,IAAI,EAAE,gBAAgB,GACrB,OAAO,CAAC,iBAAiB,CAAC,CAmC5B"}
|
|
@@ -26,7 +26,7 @@ const NEW_SINK_RE = new RegExp("\\b(?:fetch|XMLHttpRequest|WebSocket|navigator\\
|
|
|
26
26
|
const NEW_SINK_LINE_RE = new RegExp(NEW_SINK_RE.source);
|
|
27
27
|
/** Max changed lines allowed in an auto-verified LLM patch — a real crypto fix
|
|
28
28
|
* is localized; a sprawling rewrite is not reviewable as "just the fix". */
|
|
29
|
-
|
|
29
|
+
const LLM_PATCH_MAX_CHANGED_LINES = 60;
|
|
30
30
|
function countMatches(re, s) {
|
|
31
31
|
return (s.match(re) ?? []).length;
|
|
32
32
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"remediate-pipeline.js","sourceRoot":"","sources":["../src/remediate-pipeline.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The remediation pipeline: turn candidate patches (from codemods or the LLM)\n * into VERIFIED, in-policy patches. Pure and deterministic — the verify gate\n * runs `verifyFix` on the patched content in memory, so this needs no\n * filesystem or git. Callers (the `qremediate` CLI) decide what to DO with the\n * verified patches (print a diff, write them, or open a PR in a worktree).\n *\n * Two gates, both must pass:\n * 1. patch-policy — the patch may only touch sanctioned files.\n * 2. verify_fix — the patched content clears the target finding, adds no\n * new finding type, and nets fewer findings than before.\n */\nimport type { Finding } from \"./types.js\";\nimport type { Patch } from \"./agent-types.js\";\nimport { checkPatchPolicy } from \"./patch-policy.js\";\nimport type { PolicyContext } from \"./patch-policy.js\";\nimport { verifyFix } from \"./verify.js\";\n\nexport interface VerifiedPatch {\n finding: Finding;\n patch: Patch;\n}\nexport interface RejectedPatch {\n finding: Finding;\n reason: string;\n}\nexport interface RemediationResult {\n applied: VerifiedPatch[];\n rejected: RejectedPatch[];\n}\n\nexport interface RemediateOptions {\n /** Current content of the file a finding lives in. */\n readContent: (finding: Finding) => Promise<string> | string;\n /** Produce a candidate patch for a finding (codemod or LLM), or null. */\n patchSource: (finding: Finding, content: string) => Promise<Patch | null> | Patch | null;\n /** Which files a patch is allowed to touch. */\n policy: PolicyContext;\n}\n\n/** True when `after` is a strict improvement over `before` for `finding`. */\nfunction passesVerify(before: Finding[], after: Finding[], finding: Finding): boolean {\n const targetGone = !after.some((x) => x.ruleId === finding.ruleId);\n const noNewRuleTypes = after.every((x) => before.some((b) => b.ruleId === x.ruleId));\n return targetGone && noNewRuleTypes && after.length < before.length;\n}\n\n/**\n * Exfiltration / RCE primitives an LLM \"crypto fix\" should never *newly*\n * introduce. `verifyFix` only proves the crypto finding is gone — it is blind to\n * the rest of a full-file rewrite, so an injected/hostile model could drop the\n * RSA call and add `fetch(evil + process.env.SECRET)` and still pass. This guard\n * runs ONLY on `source: \"llm\"` patches (codemods are deterministic + trusted).\n */\n// Node built-in exfil/RCE modules, matched with an OPTIONAL `node:` prefix\n// (idiomatic modern Node — the bare-only form was bypassable) and in BOTH\n// require()/import() call forms AND static `import … from \"…\"` — the static\n// form has no parens so the old dynamic-only pattern missed it entirely.\nconst SINK_MODULES = \"child_process|http|https|http2|net|tls|dns|dgram\";\nconst NEW_SINK_RE = new RegExp(\n \"\\\\b(?:fetch|XMLHttpRequest|WebSocket|navigator\\\\.sendBeacon|child_process|execSync|execFileSync|spawnSync|exec(?:File)?\\\\s*\\\\(|spawn\\\\s*\\\\(|eval\\\\s*\\\\(|new\\\\s+Function|os\\\\.system|subprocess|Runtime\\\\.getRuntime\" +\n `|require\\\\s*\\\\(\\\\s*['\"](?:node:)?(?:${SINK_MODULES})['\"]` +\n `|import\\\\s*\\\\(\\\\s*['\"](?:node:)?(?:${SINK_MODULES})['\"]` +\n `|import\\\\s+[^;\\\\n]{0,200}?from\\\\s*['\"](?:node:)?(?:${SINK_MODULES})['\"])`,\n \"g\",\n);\n/** Stateless (non-global) sibling of {@link NEW_SINK_RE} for per-line `.test()`. */\nconst NEW_SINK_LINE_RE = new RegExp(NEW_SINK_RE.source);\n\n/** Max changed lines allowed in an auto-verified LLM patch — a real crypto fix\n * is localized; a sprawling rewrite is not reviewable as \"just the fix\". */\nexport const LLM_PATCH_MAX_CHANGED_LINES = 60;\n\nfunction countMatches(re: RegExp, s: string): number {\n return (s.match(re) ?? []).length;\n}\n\n/** Changed lines (removed + added) between two revisions, ignoring the shared\n * prefix/suffix — same shape the CLI's unified-diff uses. */\nfunction changedLineCount(before: string, after: string): number {\n const a = before.split(\"\\n\");\n const b = after.split(\"\\n\");\n let start = 0;\n while (start < a.length && start < b.length && a[start] === b[start]) start++;\n let endA = a.length - 1;\n let endB = b.length - 1;\n while (endA >= start && endB >= start && a[endA] === b[endB]) {\n endA--;\n endB--;\n }\n return Math.max(0, endA - start + 1) + Math.max(0, endB - start + 1);\n}\n\n/** Reason an LLM patch is unsafe to auto-verify, or null when it is bounded. */\nfunction llmPatchRisk(before: string, patch: Patch): string | null {\n // Reject any exfil/RCE sink on a line the patch ADDS. More robust than a bare\n // count: it catches a sink SWAPPED into a file that already had one, and does\n // not depend on the total count rising (trimmed line-set membership).\n const beforeLines = new Set(before.split(\"\\n\").map((l) => l.trim()));\n const addsSink = patch.newContent\n .split(\"\\n\")\n .some((l) => !beforeLines.has(l.trim()) && NEW_SINK_LINE_RE.test(l));\n if (addsSink) {\n return \"LLM patch adds a network/exec/eval sink on a new line (possible prompt-injection); rejected — review the diff manually\";\n }\n // Backstop: the total sink count must not rise either.\n if (countMatches(NEW_SINK_RE, patch.newContent) > countMatches(NEW_SINK_RE, before)) {\n return \"LLM patch introduces a new network/exec/eval sink (possible prompt-injection); rejected — review the diff manually\";\n }\n const changed = changedLineCount(before, patch.newContent);\n if (changed > LLM_PATCH_MAX_CHANGED_LINES) {\n return `LLM patch changes ${changed} lines (> ${LLM_PATCH_MAX_CHANGED_LINES}); too broad to auto-verify — review the diff manually`;\n }\n return null;\n}\n\n/**\n * Run each finding through patchSource → policy → verify, collecting the patches\n * that survive both gates and the reasons the rest were dropped.\n */\nexport async function remediateFindings(\n findings: readonly Finding[],\n opts: RemediateOptions,\n): Promise<RemediationResult> {\n const applied: VerifiedPatch[] = [];\n const rejected: RejectedPatch[] = [];\n\n for (const finding of findings) {\n const content = await opts.readContent(finding);\n const patch = await opts.patchSource(finding, content);\n if (!patch) {\n rejected.push({ finding, reason: \"no deterministic fix available\" });\n continue;\n }\n const decision = checkPatchPolicy(patch, opts.policy);\n if (!decision.allowed) {\n rejected.push({ finding, reason: decision.reason ?? \"rejected by patch policy\" });\n continue;\n }\n // Untrusted full-file LLM rewrites get a blast-radius gate the crypto-only\n // verify step can't provide (new-sink + change-size bound).\n if (patch.source === \"llm\") {\n const risk = llmPatchRisk(content, patch);\n if (risk) {\n rejected.push({ finding, reason: risk });\n continue;\n }\n }\n const before = verifyFix(content, { filename: finding.location.file }).findings;\n const after = verifyFix(patch.newContent, { filename: patch.path }).findings;\n if (!passesVerify(before, after, finding)) {\n rejected.push({ finding, reason: \"patch did not pass the verify_fix gate\" });\n continue;\n }\n applied.push({ finding, patch });\n }\n\n return { applied, rejected };\n}\n"]}
|
|
1
|
+
{"version":3,"file":"remediate-pipeline.js","sourceRoot":"","sources":["../src/remediate-pipeline.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The remediation pipeline: turn candidate patches (from codemods or the LLM)\n * into VERIFIED, in-policy patches. Pure and deterministic — the verify gate\n * runs `verifyFix` on the patched content in memory, so this needs no\n * filesystem or git. Callers (the `qremediate` CLI) decide what to DO with the\n * verified patches (print a diff, write them, or open a PR in a worktree).\n *\n * Two gates, both must pass:\n * 1. patch-policy — the patch may only touch sanctioned files.\n * 2. verify_fix — the patched content clears the target finding, adds no\n * new finding type, and nets fewer findings than before.\n */\nimport type { Finding } from \"./types.js\";\nimport type { Patch } from \"./agent-types.js\";\nimport { checkPatchPolicy } from \"./patch-policy.js\";\nimport type { PolicyContext } from \"./patch-policy.js\";\nimport { verifyFix } from \"./verify.js\";\n\nexport interface VerifiedPatch {\n finding: Finding;\n patch: Patch;\n}\nexport interface RejectedPatch {\n finding: Finding;\n reason: string;\n}\nexport interface RemediationResult {\n applied: VerifiedPatch[];\n rejected: RejectedPatch[];\n}\n\nexport interface RemediateOptions {\n /** Current content of the file a finding lives in. */\n readContent: (finding: Finding) => Promise<string> | string;\n /** Produce a candidate patch for a finding (codemod or LLM), or null. */\n patchSource: (finding: Finding, content: string) => Promise<Patch | null> | Patch | null;\n /** Which files a patch is allowed to touch. */\n policy: PolicyContext;\n}\n\n/** True when `after` is a strict improvement over `before` for `finding`. */\nfunction passesVerify(before: Finding[], after: Finding[], finding: Finding): boolean {\n const targetGone = !after.some((x) => x.ruleId === finding.ruleId);\n const noNewRuleTypes = after.every((x) => before.some((b) => b.ruleId === x.ruleId));\n return targetGone && noNewRuleTypes && after.length < before.length;\n}\n\n/**\n * Exfiltration / RCE primitives an LLM \"crypto fix\" should never *newly*\n * introduce. `verifyFix` only proves the crypto finding is gone — it is blind to\n * the rest of a full-file rewrite, so an injected/hostile model could drop the\n * RSA call and add `fetch(evil + process.env.SECRET)` and still pass. This guard\n * runs ONLY on `source: \"llm\"` patches (codemods are deterministic + trusted).\n */\n// Node built-in exfil/RCE modules, matched with an OPTIONAL `node:` prefix\n// (idiomatic modern Node — the bare-only form was bypassable) and in BOTH\n// require()/import() call forms AND static `import … from \"…\"` — the static\n// form has no parens so the old dynamic-only pattern missed it entirely.\nconst SINK_MODULES = \"child_process|http|https|http2|net|tls|dns|dgram\";\nconst NEW_SINK_RE = new RegExp(\n \"\\\\b(?:fetch|XMLHttpRequest|WebSocket|navigator\\\\.sendBeacon|child_process|execSync|execFileSync|spawnSync|exec(?:File)?\\\\s*\\\\(|spawn\\\\s*\\\\(|eval\\\\s*\\\\(|new\\\\s+Function|os\\\\.system|subprocess|Runtime\\\\.getRuntime\" +\n `|require\\\\s*\\\\(\\\\s*['\"](?:node:)?(?:${SINK_MODULES})['\"]` +\n `|import\\\\s*\\\\(\\\\s*['\"](?:node:)?(?:${SINK_MODULES})['\"]` +\n `|import\\\\s+[^;\\\\n]{0,200}?from\\\\s*['\"](?:node:)?(?:${SINK_MODULES})['\"])`,\n \"g\",\n);\n/** Stateless (non-global) sibling of {@link NEW_SINK_RE} for per-line `.test()`. */\nconst NEW_SINK_LINE_RE = new RegExp(NEW_SINK_RE.source);\n\n/** Max changed lines allowed in an auto-verified LLM patch — a real crypto fix\n * is localized; a sprawling rewrite is not reviewable as \"just the fix\". */\nconst LLM_PATCH_MAX_CHANGED_LINES = 60;\n\nfunction countMatches(re: RegExp, s: string): number {\n return (s.match(re) ?? []).length;\n}\n\n/** Changed lines (removed + added) between two revisions, ignoring the shared\n * prefix/suffix — same shape the CLI's unified-diff uses. */\nfunction changedLineCount(before: string, after: string): number {\n const a = before.split(\"\\n\");\n const b = after.split(\"\\n\");\n let start = 0;\n while (start < a.length && start < b.length && a[start] === b[start]) start++;\n let endA = a.length - 1;\n let endB = b.length - 1;\n while (endA >= start && endB >= start && a[endA] === b[endB]) {\n endA--;\n endB--;\n }\n return Math.max(0, endA - start + 1) + Math.max(0, endB - start + 1);\n}\n\n/** Reason an LLM patch is unsafe to auto-verify, or null when it is bounded. */\nfunction llmPatchRisk(before: string, patch: Patch): string | null {\n // Reject any exfil/RCE sink on a line the patch ADDS. More robust than a bare\n // count: it catches a sink SWAPPED into a file that already had one, and does\n // not depend on the total count rising (trimmed line-set membership).\n const beforeLines = new Set(before.split(\"\\n\").map((l) => l.trim()));\n const addsSink = patch.newContent\n .split(\"\\n\")\n .some((l) => !beforeLines.has(l.trim()) && NEW_SINK_LINE_RE.test(l));\n if (addsSink) {\n return \"LLM patch adds a network/exec/eval sink on a new line (possible prompt-injection); rejected — review the diff manually\";\n }\n // Backstop: the total sink count must not rise either.\n if (countMatches(NEW_SINK_RE, patch.newContent) > countMatches(NEW_SINK_RE, before)) {\n return \"LLM patch introduces a new network/exec/eval sink (possible prompt-injection); rejected — review the diff manually\";\n }\n const changed = changedLineCount(before, patch.newContent);\n if (changed > LLM_PATCH_MAX_CHANGED_LINES) {\n return `LLM patch changes ${changed} lines (> ${LLM_PATCH_MAX_CHANGED_LINES}); too broad to auto-verify — review the diff manually`;\n }\n return null;\n}\n\n/**\n * Run each finding through patchSource → policy → verify, collecting the patches\n * that survive both gates and the reasons the rest were dropped.\n */\nexport async function remediateFindings(\n findings: readonly Finding[],\n opts: RemediateOptions,\n): Promise<RemediationResult> {\n const applied: VerifiedPatch[] = [];\n const rejected: RejectedPatch[] = [];\n\n for (const finding of findings) {\n const content = await opts.readContent(finding);\n const patch = await opts.patchSource(finding, content);\n if (!patch) {\n rejected.push({ finding, reason: \"no deterministic fix available\" });\n continue;\n }\n const decision = checkPatchPolicy(patch, opts.policy);\n if (!decision.allowed) {\n rejected.push({ finding, reason: decision.reason ?? \"rejected by patch policy\" });\n continue;\n }\n // Untrusted full-file LLM rewrites get a blast-radius gate the crypto-only\n // verify step can't provide (new-sink + change-size bound).\n if (patch.source === \"llm\") {\n const risk = llmPatchRisk(content, patch);\n if (risk) {\n rejected.push({ finding, reason: risk });\n continue;\n }\n }\n const before = verifyFix(content, { filename: finding.location.file }).findings;\n const after = verifyFix(patch.newContent, { filename: patch.path }).findings;\n if (!passesVerify(before, after, finding)) {\n rejected.push({ finding, reason: \"patch did not pass the verify_fix gate\" });\n continue;\n }\n applied.push({ finding, patch });\n }\n\n return { applied, rejected };\n}\n"]}
|
package/dist/remediation.d.ts
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
* standards to track are captured in {@link PQC_TRANSITION_NOTE}.
|
|
7
7
|
*/
|
|
8
8
|
import type { AlgorithmFamily, Remediation } from "./types.js";
|
|
9
|
+
import type { StandardsProfile } from "./standards-profiles.js";
|
|
9
10
|
/** Look up the recommended post-quantum remediation for a classical algorithm. */
|
|
10
11
|
export declare function remediationFor(algorithm: AlgorithmFamily): Remediation | undefined;
|
|
11
12
|
/** Convenience: just the short recommendation string for a family (always defined). */
|
|
@@ -40,6 +41,15 @@ export declare const TIER_PARAMS: Record<SecurityTier, {
|
|
|
40
41
|
* commercial default.
|
|
41
42
|
*/
|
|
42
43
|
export declare function remediationForTier(algorithm: AlgorithmFamily, tier?: SecurityTier): Remediation;
|
|
44
|
+
/**
|
|
45
|
+
* Regime-aware remediation. Composes the base family guidance with a
|
|
46
|
+
* {@link StandardsProfile}'s parameter sets, HYBRID STANCE, and deprecation
|
|
47
|
+
* deadlines, so an ANSSI/BSI audience is told hybrid is *required* while a CNSA 2.0
|
|
48
|
+
* audience is told it is *optional* (and pointed at the 1024-level hybrid). This is
|
|
49
|
+
* the profile-driven successor to {@link remediationForTier}; `--tier category-5`
|
|
50
|
+
* resolves to the `cnsa-2.0` profile.
|
|
51
|
+
*/
|
|
52
|
+
export declare function remediationForProfile(algorithm: AlgorithmFamily, profile: StandardsProfile): Remediation;
|
|
43
53
|
/**
|
|
44
54
|
* Guidance for stateful hash-based signatures (SP 800-208: LMS / XMSS / HSS).
|
|
45
55
|
* These are NIST-approved for firmware / boot signing but are STATEFUL — each
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"remediation.d.ts","sourceRoot":"","sources":["../src/remediation.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,KAAK,EAAE,eAAe,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"remediation.d.ts","sourceRoot":"","sources":["../src/remediation.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,KAAK,EAAE,eAAe,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAC/D,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAoFhE,kFAAkF;AAClF,wBAAgB,cAAc,CAAC,SAAS,EAAE,eAAe,GAAG,WAAW,GAAG,SAAS,CAElF;AAED,uFAAuF;AACvF,wBAAgB,eAAe,CAAC,SAAS,EAAE,eAAe,GAAG,MAAM,CAElE;AAED;;;;;;GAMG;AACH,wBAAgB,uBAAuB,CAAC,SAAS,EAAE,eAAe,GAAG,OAAO,CAS3E;AAED,qFAAqF;AACrF,wBAAgB,iBAAiB,CAAC,SAAS,EAAE,eAAe,GAAG,OAAO,CAIrE;AAMD;;;;;GAKG;AACH,MAAM,MAAM,YAAY,GAAG,YAAY,GAAG,YAAY,CAAC;AAEvD,+CAA+C;AAC/C,eAAO,MAAM,WAAW,EAAE,MAAM,CAAC,YAAY,EAAE;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAW9F,CAAC;AAEF;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAChC,SAAS,EAAE,eAAe,EAC1B,IAAI,GAAE,YAA2B,GAChC,WAAW,CA2Bb;AAED;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CACnC,SAAS,EAAE,eAAe,EAC1B,OAAO,EAAE,gBAAgB,GACxB,WAAW,CAyBb;AAED;;;;;;GAMG;AACH,eAAO,MAAM,iBAAiB,QAKO,CAAC;AAEtC;;;GAGG;AACH,eAAO,MAAM,mBAAmB,QAMkC,CAAC;AAEnE,oFAAoF;AACpF,wBAAgB,kBAAkB,CAAC,SAAS,EAAE,eAAe,GAAG,OAAO,CAGtE"}
|
package/dist/remediation.js
CHANGED
|
@@ -141,6 +141,38 @@ export function remediationForTier(algorithm, tier = "category-3") {
|
|
|
141
141
|
detail: `${base.detail} ${params.note} For this tier use ${params.kem} (KEM) and ${params.signature} (signatures).`,
|
|
142
142
|
};
|
|
143
143
|
}
|
|
144
|
+
/**
|
|
145
|
+
* Regime-aware remediation. Composes the base family guidance with a
|
|
146
|
+
* {@link StandardsProfile}'s parameter sets, HYBRID STANCE, and deprecation
|
|
147
|
+
* deadlines, so an ANSSI/BSI audience is told hybrid is *required* while a CNSA 2.0
|
|
148
|
+
* audience is told it is *optional* (and pointed at the 1024-level hybrid). This is
|
|
149
|
+
* the profile-driven successor to {@link remediationForTier}; `--tier category-5`
|
|
150
|
+
* resolves to the `cnsa-2.0` profile.
|
|
151
|
+
*/
|
|
152
|
+
export function remediationForProfile(algorithm, profile) {
|
|
153
|
+
const base = REMEDIATIONS[algorithm];
|
|
154
|
+
const isConf = isConfidentialityFamily(algorithm);
|
|
155
|
+
const primary = isConf ? profile.paramSets.kem : profile.paramSets.signature;
|
|
156
|
+
// The hybrid stance governs KEY ESTABLISHMENT, so it is surfaced for confidentiality
|
|
157
|
+
// families; signature families migrate to a stateless PQC signer directly.
|
|
158
|
+
const stanceWord = profile.hybridStance === "required"
|
|
159
|
+
? "hybrid required"
|
|
160
|
+
: profile.hybridStance === "recommended"
|
|
161
|
+
? "hybrid recommended"
|
|
162
|
+
: "hybrids optional";
|
|
163
|
+
const recommendation = isConf
|
|
164
|
+
? `${primary} — ${profile.name}: ${stanceWord}`
|
|
165
|
+
: `${primary} — ${profile.name}`;
|
|
166
|
+
const hybridClause = isConf ? ` ${profile.hybridGuidance}` : "";
|
|
167
|
+
return {
|
|
168
|
+
algorithm,
|
|
169
|
+
recommendation,
|
|
170
|
+
detail: `${base.detail} Under ${profile.name} (${profile.citation}), use ${profile.paramSets.kem} for ` +
|
|
171
|
+
`key establishment and ${profile.paramSets.signature} for signatures; classical public-key ` +
|
|
172
|
+
`crypto is deprecated after ${profile.deprecateAfter} and disallowed after ${profile.disallowAfter}.` +
|
|
173
|
+
hybridClause,
|
|
174
|
+
};
|
|
175
|
+
}
|
|
144
176
|
/**
|
|
145
177
|
* Guidance for stateful hash-based signatures (SP 800-208: LMS / XMSS / HSS).
|
|
146
178
|
* These are NIST-approved for firmware / boot signing but are STATEFUL — each
|
package/dist/remediation.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"remediation.js","sourceRoot":"","sources":["../src/remediation.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Post-quantum remediation guidance for each classical asymmetric algorithm\n * family. The recommendations follow NIST's finalized PQC algorithms — ML-KEM\n * (FIPS 203), ML-DSA (FIPS 204), SLH-DSA (FIPS 205) — and the IETF hybrid\n * key-exchange groups (X25519MLKEM768, SecP384r1MLKEM1024). Forward-looking\n * standards to track are captured in {@link PQC_TRANSITION_NOTE}.\n */\nimport type { AlgorithmFamily, Remediation } from \"./types.js\";\n\n/** Canonical remediation table, keyed by algorithm family. */\nconst REMEDIATIONS: Record<AlgorithmFamily, Remediation> = {\n RSA: {\n algorithm: \"RSA\",\n recommendation: \"ML-KEM-768 for encryption/KEM; ML-DSA-65 for signatures\",\n detail:\n \"RSA is broken by Shor's algorithm. For key transport / encryption move to \" +\n \"ML-KEM-768 (FIPS 203), ideally as the hybrid X25519MLKEM768. For digital \" +\n \"signatures move to ML-DSA-65 (FIPS 204) or SLH-DSA (FIPS 205).\",\n },\n ECDH: {\n algorithm: \"ECDH\",\n recommendation: \"hybrid X25519MLKEM768 (ML-KEM-768)\",\n detail:\n \"Elliptic-curve Diffie-Hellman is broken by Shor's algorithm and is exposed \" +\n \"to harvest-now-decrypt-later. Adopt the hybrid X25519MLKEM768 key exchange so \" +\n \"confidentiality survives even if one component is broken.\",\n },\n ECDSA: {\n algorithm: \"ECDSA\",\n recommendation: \"ML-DSA-65 (FIPS 204)\",\n detail:\n \"ECDSA signatures can be forged by a quantum attacker via Shor's algorithm. \" +\n \"Migrate to ML-DSA (Dilithium, FIPS 204) or SLH-DSA (SPHINCS+, FIPS 205) for \" +\n \"long-lived signatures.\",\n },\n EdDSA: {\n algorithm: \"EdDSA\",\n recommendation: \"ML-DSA-65 (FIPS 204)\",\n detail:\n \"Ed25519 / Ed448 (EdDSA) are classical signatures broken by Shor's algorithm. \" +\n \"Replace with ML-DSA-65 (FIPS 204) or SLH-DSA (FIPS 205) for forgery resistance.\",\n },\n DH: {\n algorithm: \"DH\",\n recommendation: \"hybrid X25519MLKEM768 (ML-KEM-768)\",\n detail:\n \"Finite-field Diffie-Hellman is broken by Shor's algorithm and exposed to \" +\n \"harvest-now-decrypt-later. Move to a hybrid PQC KEM such as X25519MLKEM768.\",\n },\n DSA: {\n algorithm: \"DSA\",\n recommendation: \"ML-DSA-65 (FIPS 204)\",\n detail:\n \"DSA is a classical, quantum-broken signature scheme (and already deprecated). \" +\n \"Replace with ML-DSA-65 (FIPS 204) or SLH-DSA (FIPS 205).\",\n },\n X25519: {\n algorithm: \"X25519\",\n recommendation: \"hybrid X25519MLKEM768 (ML-KEM-768)\",\n detail:\n \"X25519 is a modern, well-built classical key-agreement primitive but is still \" +\n \"broken by Shor's algorithm. Wrap it in the hybrid X25519MLKEM768 construction \" +\n \"so it stays useful during the PQC transition.\",\n },\n X448: {\n algorithm: \"X448\",\n recommendation: \"hybrid SecP384r1MLKEM1024 (or X25519MLKEM768)\",\n detail:\n \"X448 (Goldilocks curve) is a modern classical key-agreement primitive at a \" +\n \"higher classical security level, but it is still broken by Shor's algorithm. \" +\n \"To preserve that assurance level, prefer the SecP384r1MLKEM1024 hybrid \" +\n \"(ML-KEM-1024); X25519MLKEM768 is acceptable at the commercial tier.\",\n },\n ECIES: {\n algorithm: \"ECIES\",\n recommendation: \"ML-KEM-768 hybrid encryption (X-Wing for HPKE)\",\n detail:\n \"ECIES relies on classical ECDH for its key encapsulation and is exposed to \" +\n \"harvest-now-decrypt-later. Replace the KEM step with ML-KEM-768 (FIPS 203) in \" +\n \"a hybrid construction — for HPKE-style application-layer encryption, X-Wing \" +\n \"(X25519 + ML-KEM-768) is the emerging hybrid KEM target.\",\n },\n unknown: {\n algorithm: \"unknown\",\n recommendation: \"review for post-quantum migration\",\n detail:\n \"This usage involves classical public-key cryptography. Audit it and plan a \" +\n \"migration to NIST PQC standards (ML-KEM / FIPS 203, ML-DSA / FIPS 204).\",\n },\n};\n\n/** Look up the recommended post-quantum remediation for a classical algorithm. */\nexport function remediationFor(algorithm: AlgorithmFamily): Remediation | undefined {\n return REMEDIATIONS[algorithm];\n}\n\n/** Convenience: just the short recommendation string for a family (always defined). */\nexport function remediationText(algorithm: AlgorithmFamily): string {\n return REMEDIATIONS[algorithm].recommendation;\n}\n\n/**\n * True when a family's PQC replacement is a KEM — i.e. it's used for\n * confidentiality / key-agreement (and is therefore harvest-now-decrypt-later\n * exposed). RSA is in both this set and {@link isSignatureFamily}. Single\n * source of truth for the family→target taxonomy used by tier guidance and the\n * multi-family remediation composer.\n */\nexport function isConfidentialityFamily(algorithm: AlgorithmFamily): boolean {\n return (\n algorithm === \"RSA\" ||\n algorithm === \"ECDH\" ||\n algorithm === \"DH\" ||\n algorithm === \"X25519\" ||\n algorithm === \"X448\" ||\n algorithm === \"ECIES\"\n );\n}\n\n/** True when a family's PQC replacement is a signature scheme (ML-DSA / SLH-DSA). */\nexport function isSignatureFamily(algorithm: AlgorithmFamily): boolean {\n return (\n algorithm === \"RSA\" || algorithm === \"ECDSA\" || algorithm === \"EdDSA\" || algorithm === \"DSA\"\n );\n}\n\n/* -------------------------------------------------------------------------- */\n/* Security-tier guidance (CNSA 2.0 Category 5) + stateful HBS (SP 800-208) */\n/* -------------------------------------------------------------------------- */\n\n/**\n * Security tier for remediation guidance.\n * - `\"category-3\"` (default): commercial use — ML-KEM-768 / ML-DSA-65.\n * - `\"category-5\"`: CNSA 2.0 national-security-systems / long-lived secrets —\n * ML-KEM-1024 / ML-DSA-87.\n */\nexport type SecurityTier = \"category-3\" | \"category-5\";\n\n/** Per-tier KEM / signature parameter sets. */\nexport const TIER_PARAMS: Record<SecurityTier, { kem: string; signature: string; note: string }> = {\n \"category-3\": {\n kem: \"ML-KEM-768 (FIPS 203)\",\n signature: \"ML-DSA-65 (FIPS 204)\",\n note: \"NIST Category 3 — default for general commercial use.\",\n },\n \"category-5\": {\n kem: \"ML-KEM-1024 (FIPS 203)\",\n signature: \"ML-DSA-87 (FIPS 204)\",\n note: \"NIST Category 5 — CNSA 2.0 for national-security systems and long-lived secrets (2030/2033 milestones).\",\n },\n};\n\n/**\n * Tier-aware remediation. Returns the base family remediation plus the\n * parameter sets for the requested CNSA tier. Category 5 surfaces the\n * ML-KEM-1024 / ML-DSA-87 sets mandated by CNSA 2.0; category 3 is the\n * commercial default.\n */\nexport function remediationForTier(\n algorithm: AlgorithmFamily,\n tier: SecurityTier = \"category-3\",\n): Remediation {\n const base = REMEDIATIONS[algorithm];\n const params = TIER_PARAMS[tier];\n // Confidentiality families lean on the KEM; signature families on the signer.\n const isConf = isConfidentialityFamily(algorithm);\n const primary = isConf ? params.kem : params.signature;\n\n // Category-5 (CNSA 2.0 / NSS) mandates the ML-KEM-1024 / ML-DSA-87 parameter\n // sets and must NOT surface the category-3 X25519MLKEM768 hybrid (its PQC\n // component is ML-KEM-768 — sub-CNSA). Lead with the mandated set; only point\n // at a hybrid TLS group at the 1024 level (audit: quantum #1, crypto S5).\n if (tier === \"category-5\") {\n const hybridNote = isConf\n ? \" If a hybrid TLS group is required, use SecP384r1MLKEM1024 (draft-ietf-tls-ecdhe-mlkem) — not X25519MLKEM768, whose ML-KEM-768 component does not meet CNSA 2.0.\"\n : \"\";\n return {\n algorithm,\n recommendation: `${primary} — CNSA 2.0 mandates this parameter set (hybrids optional)`,\n detail: `${base.detail} ${params.note} For CNSA 2.0 / national-security systems use ${params.kem} (KEM) and ${params.signature} (signatures).${hybridNote}`,\n };\n }\n\n return {\n algorithm,\n recommendation: `${base.recommendation} — ${tier}: ${primary}`,\n detail: `${base.detail} ${params.note} For this tier use ${params.kem} (KEM) and ${params.signature} (signatures).`,\n };\n}\n\n/**\n * Guidance for stateful hash-based signatures (SP 800-208: LMS / XMSS / HSS).\n * These are NIST-approved for firmware / boot signing but are STATEFUL — each\n * private key may sign a bounded number of messages and the state MUST be\n * managed to avoid catastrophic key reuse. Surfaced where a long-lived,\n * low-volume signing root is appropriate.\n */\nexport const STATEFUL_HBS_NOTE =\n \"For firmware / secure-boot signing, the stateful hash-based signatures \" +\n \"LMS, XMSS and HSS (NIST SP 800-208) are approved alternatives to ML-DSA, \" +\n \"but they are STATEFUL: the signer must never reuse a one-time key index. \" +\n \"Use only with rigorous state management; otherwise prefer stateless ML-DSA \" +\n \"(FIPS 204) or SLH-DSA (FIPS 205).\";\n\n/**\n * Forward-looking PQC standards worth tracking beyond the current FIPS 203/204/205\n * targets. Surfaced for operators planning multi-year migrations.\n */\nexport const PQC_TRANSITION_NOTE =\n \"Migration urgency: NIST IR 8547 deprecates classical public-key crypto after \" +\n \"2030 and disallows it after 2035 — long-lived (harvest-now-decrypt-later) data \" +\n \"must move sooner. Standards to track: HQC (NIST's code-based backup KEM, \" +\n \"selected March 2025; draft FIPS expected ~2026) as a diversity hedge against \" +\n \"ML-KEM; FN-DSA / Falcon (draft FIPS 206) for compact lattice signatures; and \" +\n \"X-Wing (X25519 + ML-KEM-768) for HPKE-style hybrid encryption.\";\n\n/** True when stateful HBS (SP 800-208) is a reasonable alternative for a family. */\nexport function statefulHbsApplies(algorithm: AlgorithmFamily): boolean {\n // Signature families only — LMS/XMSS are signatures, not KEMs.\n return isSignatureFamily(algorithm);\n}\n"]}
|
|
1
|
+
{"version":3,"file":"remediation.js","sourceRoot":"","sources":["../src/remediation.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Post-quantum remediation guidance for each classical asymmetric algorithm\n * family. The recommendations follow NIST's finalized PQC algorithms — ML-KEM\n * (FIPS 203), ML-DSA (FIPS 204), SLH-DSA (FIPS 205) — and the IETF hybrid\n * key-exchange groups (X25519MLKEM768, SecP384r1MLKEM1024). Forward-looking\n * standards to track are captured in {@link PQC_TRANSITION_NOTE}.\n */\nimport type { AlgorithmFamily, Remediation } from \"./types.js\";\nimport type { StandardsProfile } from \"./standards-profiles.js\";\n\n/** Canonical remediation table, keyed by algorithm family. */\nconst REMEDIATIONS: Record<AlgorithmFamily, Remediation> = {\n RSA: {\n algorithm: \"RSA\",\n recommendation: \"ML-KEM-768 for encryption/KEM; ML-DSA-65 for signatures\",\n detail:\n \"RSA is broken by Shor's algorithm. For key transport / encryption move to \" +\n \"ML-KEM-768 (FIPS 203), ideally as the hybrid X25519MLKEM768. For digital \" +\n \"signatures move to ML-DSA-65 (FIPS 204) or SLH-DSA (FIPS 205).\",\n },\n ECDH: {\n algorithm: \"ECDH\",\n recommendation: \"hybrid X25519MLKEM768 (ML-KEM-768)\",\n detail:\n \"Elliptic-curve Diffie-Hellman is broken by Shor's algorithm and is exposed \" +\n \"to harvest-now-decrypt-later. Adopt the hybrid X25519MLKEM768 key exchange so \" +\n \"confidentiality survives even if one component is broken.\",\n },\n ECDSA: {\n algorithm: \"ECDSA\",\n recommendation: \"ML-DSA-65 (FIPS 204)\",\n detail:\n \"ECDSA signatures can be forged by a quantum attacker via Shor's algorithm. \" +\n \"Migrate to ML-DSA (Dilithium, FIPS 204) or SLH-DSA (SPHINCS+, FIPS 205) for \" +\n \"long-lived signatures.\",\n },\n EdDSA: {\n algorithm: \"EdDSA\",\n recommendation: \"ML-DSA-65 (FIPS 204)\",\n detail:\n \"Ed25519 / Ed448 (EdDSA) are classical signatures broken by Shor's algorithm. \" +\n \"Replace with ML-DSA-65 (FIPS 204) or SLH-DSA (FIPS 205) for forgery resistance.\",\n },\n DH: {\n algorithm: \"DH\",\n recommendation: \"hybrid X25519MLKEM768 (ML-KEM-768)\",\n detail:\n \"Finite-field Diffie-Hellman is broken by Shor's algorithm and exposed to \" +\n \"harvest-now-decrypt-later. Move to a hybrid PQC KEM such as X25519MLKEM768.\",\n },\n DSA: {\n algorithm: \"DSA\",\n recommendation: \"ML-DSA-65 (FIPS 204)\",\n detail:\n \"DSA is a classical, quantum-broken signature scheme (and already deprecated). \" +\n \"Replace with ML-DSA-65 (FIPS 204) or SLH-DSA (FIPS 205).\",\n },\n X25519: {\n algorithm: \"X25519\",\n recommendation: \"hybrid X25519MLKEM768 (ML-KEM-768)\",\n detail:\n \"X25519 is a modern, well-built classical key-agreement primitive but is still \" +\n \"broken by Shor's algorithm. Wrap it in the hybrid X25519MLKEM768 construction \" +\n \"so it stays useful during the PQC transition.\",\n },\n X448: {\n algorithm: \"X448\",\n recommendation: \"hybrid SecP384r1MLKEM1024 (or X25519MLKEM768)\",\n detail:\n \"X448 (Goldilocks curve) is a modern classical key-agreement primitive at a \" +\n \"higher classical security level, but it is still broken by Shor's algorithm. \" +\n \"To preserve that assurance level, prefer the SecP384r1MLKEM1024 hybrid \" +\n \"(ML-KEM-1024); X25519MLKEM768 is acceptable at the commercial tier.\",\n },\n ECIES: {\n algorithm: \"ECIES\",\n recommendation: \"ML-KEM-768 hybrid encryption (X-Wing for HPKE)\",\n detail:\n \"ECIES relies on classical ECDH for its key encapsulation and is exposed to \" +\n \"harvest-now-decrypt-later. Replace the KEM step with ML-KEM-768 (FIPS 203) in \" +\n \"a hybrid construction — for HPKE-style application-layer encryption, X-Wing \" +\n \"(X25519 + ML-KEM-768) is the emerging hybrid KEM target.\",\n },\n unknown: {\n algorithm: \"unknown\",\n recommendation: \"review for post-quantum migration\",\n detail:\n \"This usage involves classical public-key cryptography. Audit it and plan a \" +\n \"migration to NIST PQC standards (ML-KEM / FIPS 203, ML-DSA / FIPS 204).\",\n },\n};\n\n/** Look up the recommended post-quantum remediation for a classical algorithm. */\nexport function remediationFor(algorithm: AlgorithmFamily): Remediation | undefined {\n return REMEDIATIONS[algorithm];\n}\n\n/** Convenience: just the short recommendation string for a family (always defined). */\nexport function remediationText(algorithm: AlgorithmFamily): string {\n return REMEDIATIONS[algorithm].recommendation;\n}\n\n/**\n * True when a family's PQC replacement is a KEM — i.e. it's used for\n * confidentiality / key-agreement (and is therefore harvest-now-decrypt-later\n * exposed). RSA is in both this set and {@link isSignatureFamily}. Single\n * source of truth for the family→target taxonomy used by tier guidance and the\n * multi-family remediation composer.\n */\nexport function isConfidentialityFamily(algorithm: AlgorithmFamily): boolean {\n return (\n algorithm === \"RSA\" ||\n algorithm === \"ECDH\" ||\n algorithm === \"DH\" ||\n algorithm === \"X25519\" ||\n algorithm === \"X448\" ||\n algorithm === \"ECIES\"\n );\n}\n\n/** True when a family's PQC replacement is a signature scheme (ML-DSA / SLH-DSA). */\nexport function isSignatureFamily(algorithm: AlgorithmFamily): boolean {\n return (\n algorithm === \"RSA\" || algorithm === \"ECDSA\" || algorithm === \"EdDSA\" || algorithm === \"DSA\"\n );\n}\n\n/* -------------------------------------------------------------------------- */\n/* Security-tier guidance (CNSA 2.0 Category 5) + stateful HBS (SP 800-208) */\n/* -------------------------------------------------------------------------- */\n\n/**\n * Security tier for remediation guidance.\n * - `\"category-3\"` (default): commercial use — ML-KEM-768 / ML-DSA-65.\n * - `\"category-5\"`: CNSA 2.0 national-security-systems / long-lived secrets —\n * ML-KEM-1024 / ML-DSA-87.\n */\nexport type SecurityTier = \"category-3\" | \"category-5\";\n\n/** Per-tier KEM / signature parameter sets. */\nexport const TIER_PARAMS: Record<SecurityTier, { kem: string; signature: string; note: string }> = {\n \"category-3\": {\n kem: \"ML-KEM-768 (FIPS 203)\",\n signature: \"ML-DSA-65 (FIPS 204)\",\n note: \"NIST Category 3 — default for general commercial use.\",\n },\n \"category-5\": {\n kem: \"ML-KEM-1024 (FIPS 203)\",\n signature: \"ML-DSA-87 (FIPS 204)\",\n note: \"NIST Category 5 — CNSA 2.0 for national-security systems and long-lived secrets (2030/2033 milestones).\",\n },\n};\n\n/**\n * Tier-aware remediation. Returns the base family remediation plus the\n * parameter sets for the requested CNSA tier. Category 5 surfaces the\n * ML-KEM-1024 / ML-DSA-87 sets mandated by CNSA 2.0; category 3 is the\n * commercial default.\n */\nexport function remediationForTier(\n algorithm: AlgorithmFamily,\n tier: SecurityTier = \"category-3\",\n): Remediation {\n const base = REMEDIATIONS[algorithm];\n const params = TIER_PARAMS[tier];\n // Confidentiality families lean on the KEM; signature families on the signer.\n const isConf = isConfidentialityFamily(algorithm);\n const primary = isConf ? params.kem : params.signature;\n\n // Category-5 (CNSA 2.0 / NSS) mandates the ML-KEM-1024 / ML-DSA-87 parameter\n // sets and must NOT surface the category-3 X25519MLKEM768 hybrid (its PQC\n // component is ML-KEM-768 — sub-CNSA). Lead with the mandated set; only point\n // at a hybrid TLS group at the 1024 level (audit: quantum #1, crypto S5).\n if (tier === \"category-5\") {\n const hybridNote = isConf\n ? \" If a hybrid TLS group is required, use SecP384r1MLKEM1024 (draft-ietf-tls-ecdhe-mlkem) — not X25519MLKEM768, whose ML-KEM-768 component does not meet CNSA 2.0.\"\n : \"\";\n return {\n algorithm,\n recommendation: `${primary} — CNSA 2.0 mandates this parameter set (hybrids optional)`,\n detail: `${base.detail} ${params.note} For CNSA 2.0 / national-security systems use ${params.kem} (KEM) and ${params.signature} (signatures).${hybridNote}`,\n };\n }\n\n return {\n algorithm,\n recommendation: `${base.recommendation} — ${tier}: ${primary}`,\n detail: `${base.detail} ${params.note} For this tier use ${params.kem} (KEM) and ${params.signature} (signatures).`,\n };\n}\n\n/**\n * Regime-aware remediation. Composes the base family guidance with a\n * {@link StandardsProfile}'s parameter sets, HYBRID STANCE, and deprecation\n * deadlines, so an ANSSI/BSI audience is told hybrid is *required* while a CNSA 2.0\n * audience is told it is *optional* (and pointed at the 1024-level hybrid). This is\n * the profile-driven successor to {@link remediationForTier}; `--tier category-5`\n * resolves to the `cnsa-2.0` profile.\n */\nexport function remediationForProfile(\n algorithm: AlgorithmFamily,\n profile: StandardsProfile,\n): Remediation {\n const base = REMEDIATIONS[algorithm];\n const isConf = isConfidentialityFamily(algorithm);\n const primary = isConf ? profile.paramSets.kem : profile.paramSets.signature;\n // The hybrid stance governs KEY ESTABLISHMENT, so it is surfaced for confidentiality\n // families; signature families migrate to a stateless PQC signer directly.\n const stanceWord =\n profile.hybridStance === \"required\"\n ? \"hybrid required\"\n : profile.hybridStance === \"recommended\"\n ? \"hybrid recommended\"\n : \"hybrids optional\";\n const recommendation = isConf\n ? `${primary} — ${profile.name}: ${stanceWord}`\n : `${primary} — ${profile.name}`;\n const hybridClause = isConf ? ` ${profile.hybridGuidance}` : \"\";\n return {\n algorithm,\n recommendation,\n detail:\n `${base.detail} Under ${profile.name} (${profile.citation}), use ${profile.paramSets.kem} for ` +\n `key establishment and ${profile.paramSets.signature} for signatures; classical public-key ` +\n `crypto is deprecated after ${profile.deprecateAfter} and disallowed after ${profile.disallowAfter}.` +\n hybridClause,\n };\n}\n\n/**\n * Guidance for stateful hash-based signatures (SP 800-208: LMS / XMSS / HSS).\n * These are NIST-approved for firmware / boot signing but are STATEFUL — each\n * private key may sign a bounded number of messages and the state MUST be\n * managed to avoid catastrophic key reuse. Surfaced where a long-lived,\n * low-volume signing root is appropriate.\n */\nexport const STATEFUL_HBS_NOTE =\n \"For firmware / secure-boot signing, the stateful hash-based signatures \" +\n \"LMS, XMSS and HSS (NIST SP 800-208) are approved alternatives to ML-DSA, \" +\n \"but they are STATEFUL: the signer must never reuse a one-time key index. \" +\n \"Use only with rigorous state management; otherwise prefer stateless ML-DSA \" +\n \"(FIPS 204) or SLH-DSA (FIPS 205).\";\n\n/**\n * Forward-looking PQC standards worth tracking beyond the current FIPS 203/204/205\n * targets. Surfaced for operators planning multi-year migrations.\n */\nexport const PQC_TRANSITION_NOTE =\n \"Migration urgency: NIST IR 8547 deprecates classical public-key crypto after \" +\n \"2030 and disallows it after 2035 — long-lived (harvest-now-decrypt-later) data \" +\n \"must move sooner. Standards to track: HQC (NIST's code-based backup KEM, \" +\n \"selected March 2025; draft FIPS expected ~2026) as a diversity hedge against \" +\n \"ML-KEM; FN-DSA / Falcon (draft FIPS 206) for compact lattice signatures; and \" +\n \"X-Wing (X25519 + ML-KEM-768) for HPKE-style hybrid encryption.\";\n\n/** True when stateful HBS (SP 800-208) is a reasonable alternative for a family. */\nexport function statefulHbsApplies(algorithm: AlgorithmFamily): boolean {\n // Signature families only — LMS/XMSS are signatures, not KEMs.\n return isSignatureFamily(algorithm);\n}\n"]}
|
package/dist/report.d.ts
CHANGED
|
@@ -5,6 +5,8 @@
|
|
|
5
5
|
*/
|
|
6
6
|
import type { RuleMeta, ScanResult } from "./types.js";
|
|
7
7
|
import type { SecurityTier } from "./remediation.js";
|
|
8
|
+
import type { StandardsProfile } from "./standards-profiles.js";
|
|
9
|
+
import type { HndlReport } from "./hndl.js";
|
|
8
10
|
/** Minimal SARIF 2.1.0 log shape (kept permissive on purpose). */
|
|
9
11
|
export interface SarifLog {
|
|
10
12
|
$schema: string;
|
|
@@ -28,6 +30,13 @@ export interface ReportOptions {
|
|
|
28
30
|
* {@link toJson}.
|
|
29
31
|
*/
|
|
30
32
|
catalog?: RuleMeta[];
|
|
33
|
+
/**
|
|
34
|
+
* Optional HNDL exposure analysis ({@link computeHndl}). When supplied, each
|
|
35
|
+
* finding gains its `exposure` fields (score, bound data asset, rationale)
|
|
36
|
+
* keyed by fingerprint, and the report carries the repo-level HNDL summary.
|
|
37
|
+
* Purely additive: it never changes finding identity, ordering, or exit codes.
|
|
38
|
+
*/
|
|
39
|
+
hndl?: HndlReport;
|
|
31
40
|
}
|
|
32
41
|
/** Serialize a scan result as SARIF 2.1.0. */
|
|
33
42
|
export declare function toSarif(result: ScanResult, opts?: ReportOptions): SarifLog;
|
|
@@ -48,4 +57,12 @@ export declare function formatSummary(result: ScanResult, options?: {
|
|
|
48
57
|
* long-lived secrets. Returns plain (un-coloured) lines; the caller styles them.
|
|
49
58
|
*/
|
|
50
59
|
export declare function formatTierGuidance(byAlgorithm: Record<string, number>, tier: SecurityTier): string[];
|
|
60
|
+
/**
|
|
61
|
+
* Per-family migration targets tailored to a selected {@link StandardsProfile}
|
|
62
|
+
* (`--profile`). Unlike {@link formatTierGuidance} (CNSA-tier only), this surfaces the
|
|
63
|
+
* regime's parameter sets AND its hybrid stance — required (ANSSI/BSI) vs recommended
|
|
64
|
+
* (NIST/NCSC) vs optional (CNSA 2.0) — so guidance isn't regime-wrong. Returns plain
|
|
65
|
+
* (un-coloured) lines; the caller styles them.
|
|
66
|
+
*/
|
|
67
|
+
export declare function formatProfileGuidance(byAlgorithm: Record<string, number>, profile: StandardsProfile): string[];
|
|
51
68
|
//# sourceMappingURL=report.d.ts.map
|
package/dist/report.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"report.d.ts","sourceRoot":"","sources":["../src/report.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,KAAK,EAA4B,QAAQ,EAAE,UAAU,EAAY,MAAM,YAAY,CAAC;AAK3F,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"report.d.ts","sourceRoot":"","sources":["../src/report.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,KAAK,EAA4B,QAAQ,EAAE,UAAU,EAAY,MAAM,YAAY,CAAC;AAK3F,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AACrD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAGhE,OAAO,KAAK,EAAmB,UAAU,EAAE,MAAM,WAAW,CAAC;AAE7D,kEAAkE;AAClE,MAAM,WAAW,QAAQ;IACvB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,OAAO,CAAC;IACjB,IAAI,EAAE,OAAO,EAAE,CAAC;CACjB;AAED,qFAAqF;AACrF,MAAM,WAAW,aAAa;IAC5B;;;;;OAKG;IACH,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB;;;;;;OAMG;IACH,OAAO,CAAC,EAAE,QAAQ,EAAE,CAAC;IACrB;;;;;OAKG;IACH,IAAI,CAAC,EAAE,UAAU,CAAC;CACnB;AA+FD,8CAA8C;AAC9C,wBAAgB,OAAO,CAAC,MAAM,EAAE,UAAU,EAAE,IAAI,CAAC,EAAE,aAAa,GAAG,QAAQ,CAyI1E;AAkBD,+DAA+D;AAC/D,wBAAgB,MAAM,CAAC,MAAM,EAAE,UAAU,EAAE,IAAI,CAAC,EAAE,aAAa,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CA6DxF;AAwCD;;;GAGG;AACH,wBAAgB,aAAa,CAC3B,MAAM,EAAE,UAAU,EAClB,OAAO,CAAC,EAAE;IAAE,KAAK,CAAC,EAAE,OAAO,CAAC;IAAC,IAAI,CAAC,EAAE,YAAY,CAAA;CAAE,GACjD,MAAM,CAyGR;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAChC,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EACnC,IAAI,EAAE,YAAY,GACjB,MAAM,EAAE,CAmBV;AAED;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CACnC,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EACnC,OAAO,EAAE,gBAAgB,GACxB,MAAM,EAAE,CAsBV"}
|