@quantakrypto/core 0.4.3 → 0.5.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 +9 -0
- package/dist/cbom-merge.d.ts.map +1 -0
- package/dist/cbom-merge.js +121 -0
- package/dist/cbom-merge.js.map +1 -0
- 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 +6 -26
- package/dist/comments.d.ts.map +1 -1
- package/dist/comments.js +77 -38
- package/dist/comments.js.map +1 -1
- package/dist/dependencies.d.ts.map +1 -1
- package/dist/dependencies.js +1 -1
- package/dist/dependencies.js.map +1 -1
- package/dist/detect-utils.d.ts +59 -21
- package/dist/detect-utils.d.ts.map +1 -1
- package/dist/detect-utils.js +143 -8
- 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 +4 -2
- package/dist/detectors/c.d.ts.map +1 -1
- package/dist/detectors/c.js +222 -4
- package/dist/detectors/c.js.map +1 -1
- package/dist/detectors/cicd.d.ts +27 -0
- package/dist/detectors/cicd.d.ts.map +1 -0
- package/dist/detectors/cicd.js +129 -0
- package/dist/detectors/cicd.js.map +1 -0
- package/dist/detectors/cloud-kms.d.ts +24 -0
- package/dist/detectors/cloud-kms.d.ts.map +1 -0
- package/dist/detectors/cloud-kms.js +99 -0
- package/dist/detectors/cloud-kms.js.map +1 -0
- package/dist/detectors/cloudformation.d.ts +38 -0
- package/dist/detectors/cloudformation.d.ts.map +1 -0
- package/dist/detectors/cloudformation.js +201 -0
- package/dist/detectors/cloudformation.js.map +1 -0
- 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 +19 -0
- package/dist/detectors/database.d.ts.map +1 -0
- package/dist/detectors/database.js +97 -0
- package/dist/detectors/database.js.map +1 -0
- 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 +55 -0
- package/dist/detectors/dnssec.d.ts.map +1 -0
- package/dist/detectors/dnssec.js +116 -0
- package/dist/detectors/dnssec.js.map +1 -0
- package/dist/detectors/elixir.d.ts +20 -0
- package/dist/detectors/elixir.d.ts.map +1 -0
- package/dist/detectors/elixir.js +227 -0
- package/dist/detectors/elixir.js.map +1 -0
- package/dist/detectors/java.d.ts.map +1 -1
- package/dist/detectors/java.js +34 -4
- package/dist/detectors/java.js.map +1 -1
- package/dist/detectors/jose.d.ts +20 -0
- package/dist/detectors/jose.d.ts.map +1 -0
- package/dist/detectors/jose.js +72 -0
- package/dist/detectors/jose.js.map +1 -0
- package/dist/detectors/jwk.d.ts +25 -0
- package/dist/detectors/jwk.d.ts.map +1 -0
- package/dist/detectors/jwk.js +131 -0
- package/dist/detectors/jwk.js.map +1 -0
- package/dist/detectors/k8s.d.ts +17 -0
- package/dist/detectors/k8s.d.ts.map +1 -0
- package/dist/detectors/k8s.js +88 -0
- package/dist/detectors/k8s.js.map +1 -0
- 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 +34 -0
- package/dist/detectors/mesh.d.ts.map +1 -0
- package/dist/detectors/mesh.js +91 -0
- package/dist/detectors/mesh.js.map +1 -0
- package/dist/detectors/messaging.d.ts +16 -0
- package/dist/detectors/messaging.d.ts.map +1 -0
- package/dist/detectors/messaging.js +79 -0
- package/dist/detectors/messaging.js.map +1 -0
- 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 +33 -1
- package/dist/detectors/pem.js.map +1 -1
- package/dist/detectors/php.d.ts +25 -0
- package/dist/detectors/php.d.ts.map +1 -0
- package/dist/detectors/php.js +171 -0
- package/dist/detectors/php.js.map +1 -0
- 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/python.d.ts.map +1 -1
- package/dist/detectors/python.js +70 -0
- package/dist/detectors/python.js.map +1 -1
- package/dist/detectors/rust.d.ts.map +1 -1
- package/dist/detectors/rust.js +73 -0
- package/dist/detectors/rust.js.map +1 -1
- package/dist/detectors/secrets.d.ts +24 -0
- package/dist/detectors/secrets.d.ts.map +1 -0
- package/dist/detectors/secrets.js +63 -0
- package/dist/detectors/secrets.js.map +1 -0
- 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 +243 -53
- 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/stateful-hbs.d.ts.map +1 -1
- package/dist/detectors/stateful-hbs.js +9 -6
- package/dist/detectors/stateful-hbs.js.map +1 -1
- 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 +21 -0
- package/dist/detectors/terraform.d.ts.map +1 -0
- package/dist/detectors/terraform.js +150 -0
- package/dist/detectors/terraform.js.map +1 -0
- 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 +22 -0
- package/dist/detectors/vpn.d.ts.map +1 -0
- package/dist/detectors/vpn.js +73 -0
- package/dist/detectors/vpn.js.map +1 -0
- 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 +79 -4
- package/dist/evidence.d.ts.map +1 -1
- package/dist/evidence.js +76 -0
- package/dist/evidence.js.map +1 -1
- package/dist/index.d.ts +17 -9
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +21 -9
- 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/policy.d.ts +67 -0
- package/dist/policy.d.ts.map +1 -0
- package/dist/policy.js +115 -0
- package/dist/policy.js.map +1 -0
- package/dist/registry.d.ts +9 -4
- package/dist/registry.d.ts.map +1 -1
- package/dist/registry.js +81 -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 +23 -2
- 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 +9 -0
- package/dist/report.d.ts.map +1 -1
- package/dist/report.js +38 -1
- 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/standards.d.ts +96 -0
- package/dist/standards.d.ts.map +1 -0
- package/dist/standards.js +107 -0
- package/dist/standards.js.map +1 -0
- package/dist/types.d.ts +6 -5
- 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 +8 -3
- package/dist/walk.d.ts.map +1 -1
- package/dist/walk.js +32 -1
- package/dist/walk.js.map +1 -1
- package/package.json +1 -1
- package/src/cbom-merge.ts +146 -0
- package/src/cbom.ts +153 -39
- package/src/comments.ts +84 -39
- package/src/dependencies.ts +2 -1
- package/src/detect-utils.ts +146 -12
- 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 +240 -6
- package/src/detectors/cicd.ts +171 -0
- package/src/detectors/cloud-kms.ts +141 -0
- package/src/detectors/cloudformation.ts +250 -0
- 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 +141 -0
- package/src/detectors/dkim.ts +172 -0
- package/src/detectors/dnssec.ts +195 -0
- package/src/detectors/elixir.ts +268 -0
- package/src/detectors/java.ts +39 -4
- package/src/detectors/jose.ts +110 -0
- package/src/detectors/jwk.ts +170 -0
- package/src/detectors/k8s.ts +115 -0
- package/src/detectors/keystore.ts +109 -0
- package/src/detectors/mesh.ts +130 -0
- package/src/detectors/messaging.ts +106 -0
- package/src/detectors/objc.ts +209 -0
- package/src/detectors/openpgp.ts +194 -0
- package/src/detectors/pem.ts +31 -1
- package/src/detectors/php.ts +230 -0
- package/src/detectors/pkcs11.ts +113 -0
- package/src/detectors/proxy.ts +236 -0
- package/src/detectors/pulumi.ts +106 -0
- package/src/detectors/python.ts +71 -0
- package/src/detectors/rust.ts +76 -0
- package/src/detectors/secrets.ts +103 -0
- package/src/detectors/solidity.ts +176 -0
- package/src/detectors/source.ts +283 -73
- package/src/detectors/spire.ts +143 -0
- package/src/detectors/ssh-ca.ts +215 -0
- package/src/detectors/stateful-hbs.ts +9 -6
- package/src/detectors/supply-chain.ts +117 -0
- package/src/detectors/swift.ts +149 -0
- package/src/detectors/terraform.ts +188 -0
- package/src/detectors/vault.ts +120 -0
- package/src/detectors/vpn.ts +103 -0
- 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 +138 -4
- package/src/index.ts +50 -12
- package/src/inventory.ts +15 -7
- package/src/policy.ts +180 -0
- package/src/registry.ts +81 -4
- package/src/remediate-pipeline.ts +26 -3
- package/src/remediation.ts +39 -0
- package/src/report.ts +43 -1
- package/src/scan-worker.ts +5 -3
- package/src/scan.ts +41 -6
- package/src/standards-profiles.ts +136 -0
- package/src/standards.ts +186 -0
- package/src/types.ts +11 -4
- package/src/version.ts +1 -1
- package/src/vex.ts +122 -0
- package/src/walk.ts +33 -2
package/dist/walk.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"walk.js","sourceRoot":"","sources":["../src/walk.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Filesystem walker. A zero-dependency recursive async generator that yields\n * scannable text files as relative POSIX paths. It honours a default ignore\n * list, user-supplied exclude patterns, a max file size, and a binary-extension\n * filter. The root may be a directory or a single file.\n */\nimport { readdir, stat } from \"node:fs/promises\";\nimport type { Dirent } from \"node:fs\";\nimport * as path from \"node:path\";\n\nimport { isManifestFile } from \"./dependencies.js\";\n\n/** Directories ignored by default (can be disabled with noDefaultIgnores). */\nexport const DEFAULT_IGNORES: readonly string[] = [\n \"node_modules\",\n \".git\",\n \"dist\",\n \"build\",\n \".next\",\n \"out\",\n \"coverage\",\n \"vendor\",\n \".turbo\",\n \".cache\",\n];\n\n/** Default maximum file size to read: 2 MiB. */\nexport const DEFAULT_MAX_FILE_SIZE = 2 * 1024 * 1024;\n\n/**\n * File extensions we treat as binary / non-text and therefore skip. Keeping this\n * as an extension allow-list-by-exclusion is cheap and avoids reading bytes.\n */\nconst BINARY_EXTENSIONS = new Set<string>([\n // images\n \".png\",\n \".jpg\",\n \".jpeg\",\n \".gif\",\n \".webp\",\n \".bmp\",\n \".ico\",\n \".tiff\",\n \".avif\",\n // fonts\n \".woff\",\n \".woff2\",\n \".ttf\",\n \".otf\",\n \".eot\",\n // archives / compressed\n \".zip\",\n \".gz\",\n \".tgz\",\n \".bz2\",\n \".xz\",\n \".7z\",\n \".rar\",\n \".tar\",\n // media\n \".mp3\",\n \".mp4\",\n \".mov\",\n \".avi\",\n \".mkv\",\n \".wav\",\n \".flac\",\n \".ogg\",\n \".webm\",\n // documents / binaries\n \".pdf\",\n \".doc\",\n \".docx\",\n \".xls\",\n \".xlsx\",\n \".ppt\",\n \".pptx\",\n \".exe\",\n \".dll\",\n \".so\",\n \".dylib\",\n \".bin\",\n \".o\",\n \".a\",\n \".class\",\n \".wasm\",\n // data blobs / db\n \".db\",\n \".sqlite\",\n \".sqlite3\",\n \".dat\",\n \".pack\",\n \".idx\",\n // misc\n \".lock\",\n \".map\",\n \".min.js\",\n \".node\",\n]);\n\n/** Options accepted by {@link walkFiles}. */\nexport interface WalkOptions {\n /**\n * Restrict to paths matching one of these include patterns (substring or\n * relative-path-prefix match). When omitted/empty, all files pass.\n */\n include?: string[];\n /** Extra exclude patterns (substring or relative-path-prefix match). */\n exclude?: string[];\n /** Disable the built-in directory ignore list. */\n noDefaultIgnores?: boolean;\n /** Max file size in bytes; larger files are skipped. */\n maxFileSize?: number;\n}\n\n/** Normalise a path to forward-slash POSIX separators. */\nexport function toPosix(p: string): string {\n return p.split(path.sep).join(\"/\");\n}\n\n/** A pattern is treated as a glob when it contains one of these metacharacters. */\nfunction hasGlobMeta(pattern: string): boolean {\n return /[*?[]/.test(pattern);\n}\n\n/**\n * Translate a glob into an anchored RegExp: `*` matches within a path segment\n * (not `/`), `**` matches across segments, a leading `**` before a slash is an\n * optional path prefix, `?` matches a single non-`/` char, `[...]` is a\n * character class. Other regex\n * metacharacters are escaped.\n */\nfunction globToRegExp(glob: string): RegExp {\n let re = \"\";\n for (let i = 0; i < glob.length; i++) {\n const c = glob[i];\n if (c === \"*\") {\n if (glob[i + 1] === \"*\") {\n i++; // consume the second '*'\n if (glob[i + 1] === \"/\") {\n i++; // consume the trailing '/'\n re += \"(?:.*/)?\"; // zero or more path segments\n } else {\n re += \".*\"; // '**' spanning segments\n }\n } else {\n re += \"[^/]*\"; // '*' within a segment\n }\n } else if (c === \"?\") {\n re += \"[^/]\";\n } else if (c === \"[\") {\n // Pass a character class through; find its closing ']'.\n const end = glob.indexOf(\"]\", i + 1);\n if (end === -1) {\n re += \"\\\\[\";\n } else {\n re += glob.slice(i, end + 1);\n i = end;\n }\n } else if (\"\\\\^$.|+(){}\".includes(c)) {\n re += \"\\\\\" + c;\n } else {\n re += c;\n }\n }\n return new RegExp(`^${re}$`);\n}\n\n/** Memoized glob→RegExp so a pattern is compiled once, not once per file. */\nconst GLOB_CACHE = new Map<string, RegExp>();\nfunction globRegExp(pattern: string): RegExp {\n let re = GLOB_CACHE.get(pattern);\n if (!re) {\n re = globToRegExp(pattern);\n GLOB_CACHE.set(pattern, re);\n }\n return re;\n}\n\n/**\n * True if `rel` (a POSIX relative path) matches any pattern. A pattern with a\n * glob metacharacter (`*`, `?`, `[`) is matched as an anchored glob; a plain\n * pattern keeps the historical substring / path-prefix semantics (so `\"src\"`\n * still matches `src/a.ts` and `\"secrets\"` matches anywhere).\n */\nexport function matchesAny(rel: string, patterns: readonly string[]): boolean {\n for (const pattern of patterns) {\n if (!pattern) continue;\n const p = toPosix(pattern).replace(/\\/+$/, \"\");\n if (hasGlobMeta(p)) {\n if (globRegExp(p).test(rel)) return true;\n continue;\n }\n // Plain pattern: substring match (handles \"src/legacy\" or \"secrets\")...\n if (rel.includes(p)) return true;\n // ...and explicit path-prefix match (\"foo\" should match \"foo/bar.ts\").\n if (rel === p || rel.startsWith(`${p}/`)) return true;\n }\n return false;\n}\n\n/** True if `rel` (a POSIX relative path) matches any exclude pattern. */\nfunction isExcluded(rel: string, exclude: readonly string[]): boolean {\n return matchesAny(rel, exclude);\n}\n\n/**\n * True if `rel` passes the include filter. An empty include list means \"include\n * everything\"; otherwise the file must match at least one include pattern.\n */\nfunction isIncluded(rel: string, include: readonly string[]): boolean {\n if (include.length === 0) return true;\n return matchesAny(rel, include);\n}\n\n/** True if the file's extension marks it as binary / non-text. */\nexport function isBinaryPath(rel: string): boolean {\n const lower = rel.toLowerCase();\n // Handle compound extensions like \".min.js\" first.\n if (lower.endsWith(\".min.js\")) return true;\n const ext = path.posix.extname(lower);\n return BINARY_EXTENSIONS.has(ext);\n}\n\n/**\n * Compound / pattern extensions that mark generated or bundled output we skip\n * by default (beyond `.min.js` / `.map`, which {@link isBinaryPath} handles).\n */\nconst GENERATED_PATH_RE =\n /(?:\\.min\\.[mc]?js|[.-]min\\.[mc]?js|\\.bundle\\.[mc]?js|\\.chunk\\.[mc]?js|\\.generated\\.[jt]sx?|_pb\\.js|\\.pb\\.go)$/i;\n\n/** True if the path looks like generated / bundled output (by name). */\nexport function isGeneratedPath(rel: string): boolean {\n return GENERATED_PATH_RE.test(rel.toLowerCase());\n}\n\n/**\n * Heuristic content check for machine-minified / generated files with no\n * telltale extension: a very long average line length, or any single line over\n * ~50 KB, in the first ~64 KB sampled. Used at read time, not in the walker.\n */\nexport function looksMinified(content: string): boolean {\n const sample = content.length > 65_536 ? content.slice(0, 65_536) : content;\n if (sample.length === 0) return false;\n let maxLine = 0;\n let cur = 0;\n let lines = 1;\n for (let i = 0; i < sample.length; i++) {\n if (sample.charCodeAt(i) === 10 /* \\n */) {\n if (cur > maxLine) maxLine = cur;\n cur = 0;\n lines++;\n } else {\n cur++;\n }\n }\n if (cur > maxLine) maxLine = cur;\n if (maxLine > 50_000) return true;\n const avgLine = sample.length / lines;\n return avgLine > 1_000;\n}\n\n/**\n * Recursively yield scannable file paths (relative to `root`, POSIX) under a\n * directory. If `root` points at a single file, yields just that file's\n * basename (subject to the size / binary filters).\n */\nexport async function* walkFiles(root: string, options: WalkOptions = {}): AsyncGenerator<string> {\n const include = options.include ?? [];\n const exclude = options.exclude ?? [];\n const maxFileSize = options.maxFileSize ?? DEFAULT_MAX_FILE_SIZE;\n const ignores = options.noDefaultIgnores ? [] : DEFAULT_IGNORES;\n\n const rootStat = await stat(root);\n\n // Single-file mode: yield the file itself (by basename) if it passes filters.\n if (rootStat.isFile()) {\n const name = toPosix(path.basename(root));\n if (\n !isBinaryPath(name) &&\n isIncluded(name, include) &&\n passesSizeLimit(name, rootStat.size, maxFileSize)\n ) {\n yield name;\n }\n return;\n }\n\n yield* walkDir(root, \"\", { include, exclude, maxFileSize, ignores });\n}\n\ninterface WalkContext {\n include: readonly string[];\n exclude: readonly string[];\n maxFileSize: number;\n ignores: readonly string[];\n}\n\n/**\n * True if a file passes the size limit. Dependency manifests (package.json /\n * package-lock.json) are exempt from the cap so large lockfiles still get\n * scanned for vulnerable dependencies instead of being silently dropped.\n */\nfunction passesSizeLimit(rel: string, size: number, maxFileSize: number): boolean {\n // Dependency manifests are always read (they can exceed the size cap but carry\n // the whole dependency tree). Uses the single {@link isManifestFile} definition.\n if (isManifestFile(rel)) return true;\n return size <= maxFileSize;\n}\n\n/** Internal recursive directory walker. `relDir` is POSIX-relative to the root. */\nasync function* walkDir(absDir: string, relDir: string, ctx: WalkContext): AsyncGenerator<string> {\n let entries: Dirent[];\n try {\n entries = await readdir(absDir, { withFileTypes: true });\n } catch {\n // Unreadable directory (permissions, transient races) — skip silently.\n return;\n }\n\n // Stable, deterministic ordering for reproducible scans.\n entries.sort((a, b) => (a.name < b.name ? -1 : a.name > b.name ? 1 : 0));\n\n for (const entry of entries) {\n const rel = relDir ? `${relDir}/${entry.name}` : entry.name;\n const abs = path.join(absDir, entry.name);\n\n if (entry.isSymbolicLink()) {\n // Don't follow symlinks: avoids cycles and escaping the root.\n continue;\n }\n\n if (entry.isDirectory()) {\n if (ctx.ignores.includes(entry.name)) continue;\n if (isExcluded(rel, ctx.exclude)) continue;\n yield* walkDir(abs, rel, ctx);\n continue;\n }\n\n if (!entry.isFile()) continue;\n if (isExcluded(rel, ctx.exclude)) continue;\n if (!isIncluded(rel, ctx.include)) continue;\n // Dependency manifests (incl. yarn.lock / pnpm-lock.yaml) are always read —\n // they carry the dependency tree and must not be dropped as binary/generated.\n const manifest = isManifestFile(rel);\n if (!manifest && isBinaryPath(rel)) continue;\n if (!manifest && isGeneratedPath(rel)) continue;\n\n try {\n const s = await stat(abs);\n if (!passesSizeLimit(rel, s.size, ctx.maxFileSize)) continue;\n } catch {\n continue;\n }\n\n yield rel;\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"walk.js","sourceRoot":"","sources":["../src/walk.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Filesystem walker. A zero-dependency recursive async generator that yields\n * scannable text files as relative POSIX paths. It honours a default ignore\n * list, user-supplied exclude patterns, a max file size, and a binary-extension\n * filter. The root may be a directory or a single file.\n */\nimport { readdir, stat } from \"node:fs/promises\";\nimport type { Dirent } from \"node:fs\";\nimport * as path from \"node:path\";\n\nimport { isManifestFile } from \"./dependencies.js\";\n\n/** Directories ignored by default (can be disabled with noDefaultIgnores). */\nconst DEFAULT_IGNORES: readonly string[] = [\n \"node_modules\",\n \".git\",\n \"dist\",\n \"build\",\n \".next\",\n \"out\",\n \"coverage\",\n \"vendor\",\n \".turbo\",\n \".cache\",\n];\n\n/** Default maximum file size to read: 2 MiB. */\nexport const DEFAULT_MAX_FILE_SIZE = 2 * 1024 * 1024;\n\n/**\n * File extensions we treat as binary / non-text and therefore skip. Keeping this\n * as an extension allow-list-by-exclusion is cheap and avoids reading bytes.\n */\nconst BINARY_EXTENSIONS = new Set<string>([\n // images\n \".png\",\n \".jpg\",\n \".jpeg\",\n \".gif\",\n \".webp\",\n \".bmp\",\n \".ico\",\n \".tiff\",\n \".avif\",\n // fonts\n \".woff\",\n \".woff2\",\n \".ttf\",\n \".otf\",\n \".eot\",\n // archives / compressed\n \".zip\",\n \".gz\",\n \".tgz\",\n \".bz2\",\n \".xz\",\n \".7z\",\n \".rar\",\n \".tar\",\n // media\n \".mp3\",\n \".mp4\",\n \".mov\",\n \".avi\",\n \".mkv\",\n \".wav\",\n \".flac\",\n \".ogg\",\n \".webm\",\n // documents / binaries\n \".pdf\",\n \".doc\",\n \".docx\",\n \".xls\",\n \".xlsx\",\n \".ppt\",\n \".pptx\",\n \".exe\",\n \".dll\",\n \".so\",\n \".dylib\",\n \".bin\",\n \".o\",\n \".a\",\n \".class\",\n \".wasm\",\n // data blobs / db\n \".db\",\n \".sqlite\",\n \".sqlite3\",\n \".dat\",\n \".pack\",\n \".idx\",\n // misc\n \".lock\",\n \".map\",\n \".min.js\",\n \".node\",\n]);\n\n/** Options accepted by {@link walkFiles}. */\ninterface WalkOptions {\n /**\n * Restrict to paths matching one of these include patterns (substring or\n * relative-path-prefix match). When omitted/empty, all files pass.\n */\n include?: string[];\n /** Extra exclude patterns (substring or relative-path-prefix match). */\n exclude?: string[];\n /** Disable the built-in directory ignore list. */\n noDefaultIgnores?: boolean;\n /** Max file size in bytes; larger files are skipped. */\n maxFileSize?: number;\n}\n\n/** Normalise a path to forward-slash POSIX separators. */\nexport function toPosix(p: string): string {\n return p.split(path.sep).join(\"/\");\n}\n\n/** A pattern is treated as a glob when it contains one of these metacharacters. */\nfunction hasGlobMeta(pattern: string): boolean {\n return /[*?[]/.test(pattern);\n}\n\n/**\n * Translate a glob into an anchored RegExp: `*` matches within a path segment\n * (not `/`), `**` matches across segments, a leading `**` before a slash is an\n * optional path prefix, `?` matches a single non-`/` char, `[...]` is a\n * character class. Other regex\n * metacharacters are escaped.\n */\nfunction globToRegExp(glob: string): RegExp {\n let re = \"\";\n for (let i = 0; i < glob.length; i++) {\n const c = glob[i];\n if (c === \"*\") {\n if (glob[i + 1] === \"*\") {\n i++; // consume the second '*'\n if (glob[i + 1] === \"/\") {\n i++; // consume the trailing '/'\n re += \"(?:.*/)?\"; // zero or more path segments\n } else {\n re += \".*\"; // '**' spanning segments\n }\n } else {\n re += \"[^/]*\"; // '*' within a segment\n }\n } else if (c === \"?\") {\n re += \"[^/]\";\n } else if (c === \"[\") {\n // Pass a character class through; find its closing ']'.\n const end = glob.indexOf(\"]\", i + 1);\n if (end === -1) {\n re += \"\\\\[\";\n } else {\n re += glob.slice(i, end + 1);\n i = end;\n }\n } else if (\"\\\\^$.|+(){}\".includes(c)) {\n re += \"\\\\\" + c;\n } else {\n re += c;\n }\n }\n return new RegExp(`^${re}$`);\n}\n\n/** Memoized glob→RegExp so a pattern is compiled once, not once per file. */\nconst GLOB_CACHE = new Map<string, RegExp>();\nfunction globRegExp(pattern: string): RegExp {\n let re = GLOB_CACHE.get(pattern);\n if (!re) {\n re = globToRegExp(pattern);\n GLOB_CACHE.set(pattern, re);\n }\n return re;\n}\n\n/**\n * True if `rel` (a POSIX relative path) matches any pattern. A pattern with a\n * glob metacharacter (`*`, `?`, `[`) is matched as an anchored glob; a plain\n * pattern keeps the historical substring / path-prefix semantics (so `\"src\"`\n * still matches `src/a.ts` and `\"secrets\"` matches anywhere).\n */\nexport function matchesAny(rel: string, patterns: readonly string[]): boolean {\n for (const pattern of patterns) {\n if (!pattern) continue;\n const p = toPosix(pattern).replace(/\\/+$/, \"\");\n if (hasGlobMeta(p)) {\n if (globRegExp(p).test(rel)) return true;\n continue;\n }\n // Plain pattern: substring match (handles \"src/legacy\" or \"secrets\")...\n if (rel.includes(p)) return true;\n // ...and explicit path-prefix match (\"foo\" should match \"foo/bar.ts\").\n if (rel === p || rel.startsWith(`${p}/`)) return true;\n }\n return false;\n}\n\n/** True if `rel` (a POSIX relative path) matches any exclude pattern. */\nfunction isExcluded(rel: string, exclude: readonly string[]): boolean {\n return matchesAny(rel, exclude);\n}\n\n/**\n * True if `rel` passes the include filter. An empty include list means \"include\n * everything\"; otherwise the file must match at least one include pattern.\n */\nfunction isIncluded(rel: string, include: readonly string[]): boolean {\n if (include.length === 0) return true;\n return matchesAny(rel, include);\n}\n\n/** True if the file's extension marks it as binary / non-text. */\nexport function isBinaryPath(rel: string): boolean {\n const lower = rel.toLowerCase();\n // Handle compound extensions like \".min.js\" first.\n if (lower.endsWith(\".min.js\")) return true;\n const ext = path.posix.extname(lower);\n return BINARY_EXTENSIONS.has(ext);\n}\n\n/**\n * Binary cryptographic key containers we DO want to scan — Java/PKCS#12 keystores\n * and binary OpenPGP keyrings/messages. Unlike the images/archives above these are\n * read byte-preserving (latin1) and exempted from the minified skip, so the\n * keystore + openpgp detectors can identify them by magic number / packet tag.\n * Kept separate from {@link BINARY_EXTENSIONS} so those detectors can opt in.\n */\nconst KEYSTORE_EXTENSIONS = new Set<string>([\n \".jks\",\n \".keystore\",\n \".jceks\",\n \".bks\",\n \".p12\",\n \".pfx\",\n // Binary OpenPGP keyrings / messages (armored .asc is text; handled elsewhere).\n \".gpg\",\n \".pgp\",\n \".kbx\",\n]);\n\n/**\n * True if the path is a binary cryptographic key container we scan byte-preserving.\n * Uses a suffix match (not `extname`) so it agrees with the detectors' `hasExtension`\n * even for a bare dotfile literally named e.g. `.gpg` (where `extname` returns \"\").\n */\nexport function isKeystorePath(rel: string): boolean {\n const lower = rel.toLowerCase();\n for (const ext of KEYSTORE_EXTENSIONS) if (lower.endsWith(ext)) return true;\n return false;\n}\n\n/**\n * Compound / pattern extensions that mark generated or bundled output we skip\n * by default (beyond `.min.js` / `.map`, which {@link isBinaryPath} handles).\n */\nconst GENERATED_PATH_RE =\n /(?:\\.min\\.[mc]?js|[.-]min\\.[mc]?js|\\.bundle\\.[mc]?js|\\.chunk\\.[mc]?js|\\.generated\\.[jt]sx?|_pb\\.js|\\.pb\\.go)$/i;\n\n/** True if the path looks like generated / bundled output (by name). */\nexport function isGeneratedPath(rel: string): boolean {\n return GENERATED_PATH_RE.test(rel.toLowerCase());\n}\n\n/**\n * Heuristic content check for machine-minified / generated files with no\n * telltale extension: a very long average line length, or any single line over\n * ~50 KB, in the first ~64 KB sampled. Used at read time, not in the walker.\n */\nexport function looksMinified(content: string): boolean {\n const sample = content.length > 65_536 ? content.slice(0, 65_536) : content;\n if (sample.length === 0) return false;\n let maxLine = 0;\n let cur = 0;\n let lines = 1;\n for (let i = 0; i < sample.length; i++) {\n if (sample.charCodeAt(i) === 10 /* \\n */) {\n if (cur > maxLine) maxLine = cur;\n cur = 0;\n lines++;\n } else {\n cur++;\n }\n }\n if (cur > maxLine) maxLine = cur;\n if (maxLine > 50_000) return true;\n const avgLine = sample.length / lines;\n return avgLine > 1_000;\n}\n\n/**\n * Recursively yield scannable file paths (relative to `root`, POSIX) under a\n * directory. If `root` points at a single file, yields just that file's\n * basename (subject to the size / binary filters).\n */\nexport async function* walkFiles(root: string, options: WalkOptions = {}): AsyncGenerator<string> {\n const include = options.include ?? [];\n const exclude = options.exclude ?? [];\n const maxFileSize = options.maxFileSize ?? DEFAULT_MAX_FILE_SIZE;\n const ignores = options.noDefaultIgnores ? [] : DEFAULT_IGNORES;\n\n const rootStat = await stat(root);\n\n // Single-file mode: yield the file itself (by basename) if it passes filters.\n if (rootStat.isFile()) {\n const name = toPosix(path.basename(root));\n if (\n !isBinaryPath(name) &&\n isIncluded(name, include) &&\n passesSizeLimit(name, rootStat.size, maxFileSize)\n ) {\n yield name;\n }\n return;\n }\n\n yield* walkDir(root, \"\", { include, exclude, maxFileSize, ignores });\n}\n\ninterface WalkContext {\n include: readonly string[];\n exclude: readonly string[];\n maxFileSize: number;\n ignores: readonly string[];\n}\n\n/**\n * True if a file passes the size limit. Dependency manifests (package.json /\n * package-lock.json) are exempt from the cap so large lockfiles still get\n * scanned for vulnerable dependencies instead of being silently dropped.\n */\nfunction passesSizeLimit(rel: string, size: number, maxFileSize: number): boolean {\n // Dependency manifests are always read (they can exceed the size cap but carry\n // the whole dependency tree). Uses the single {@link isManifestFile} definition.\n if (isManifestFile(rel)) return true;\n return size <= maxFileSize;\n}\n\n/** Internal recursive directory walker. `relDir` is POSIX-relative to the root. */\nasync function* walkDir(absDir: string, relDir: string, ctx: WalkContext): AsyncGenerator<string> {\n let entries: Dirent[];\n try {\n entries = await readdir(absDir, { withFileTypes: true });\n } catch {\n // Unreadable directory (permissions, transient races) — skip silently.\n return;\n }\n\n // Stable, deterministic ordering for reproducible scans.\n entries.sort((a, b) => (a.name < b.name ? -1 : a.name > b.name ? 1 : 0));\n\n for (const entry of entries) {\n const rel = relDir ? `${relDir}/${entry.name}` : entry.name;\n const abs = path.join(absDir, entry.name);\n\n if (entry.isSymbolicLink()) {\n // Don't follow symlinks: avoids cycles and escaping the root.\n continue;\n }\n\n if (entry.isDirectory()) {\n if (ctx.ignores.includes(entry.name)) continue;\n if (isExcluded(rel, ctx.exclude)) continue;\n yield* walkDir(abs, rel, ctx);\n continue;\n }\n\n if (!entry.isFile()) continue;\n if (isExcluded(rel, ctx.exclude)) continue;\n if (!isIncluded(rel, ctx.include)) continue;\n // Dependency manifests (incl. yarn.lock / pnpm-lock.yaml) are always read —\n // they carry the dependency tree and must not be dropped as binary/generated.\n const manifest = isManifestFile(rel);\n if (!manifest && isBinaryPath(rel)) continue;\n if (!manifest && isGeneratedPath(rel)) continue;\n\n try {\n const s = await stat(abs);\n if (!passesSizeLimit(rel, s.size, ctx.maxFileSize)) continue;\n } catch {\n continue;\n }\n\n yield rel;\n }\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@quantakrypto/core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "Shared post-quantum readiness library: crypto detectors, vulnerable-dependency database, inventory + SARIF reporting. Zero runtime dependencies.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"author": "Dandelion Labs <hello@dandelionlabs.io> (https://dandelionlabs.io)",
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Merge multiple CycloneDX 1.6 CBOMs into one combined cryptographic bill of
|
|
3
|
+
* materials. This is what turns the "code + infra + live-endpoint" story into a
|
|
4
|
+
* single artifact: a qScan CBOM (code + infrastructure-as-code/config) and a
|
|
5
|
+
* qProbe CBOM (live endpoints) both emit `cryptographic-asset` components keyed by
|
|
6
|
+
* a deterministic `bom-ref` per (algorithm, primitive), so merging is a union with
|
|
7
|
+
* dedup — the same crypto seen by two planes collapses to one component whose
|
|
8
|
+
* occurrence evidence spans both.
|
|
9
|
+
*
|
|
10
|
+
* Output is deterministic (components sorted by bom-ref, occurrences sorted by
|
|
11
|
+
* location, a stable serial derived from the merged content), so re-merging the
|
|
12
|
+
* same inputs yields byte-identical output.
|
|
13
|
+
*/
|
|
14
|
+
import { createHash } from "node:crypto";
|
|
15
|
+
import type { CycloneDxBom, CbomComponent } from "./cbom.js";
|
|
16
|
+
|
|
17
|
+
/** Occurrence record shape inside a component's `evidence.occurrences`. */
|
|
18
|
+
interface Occurrence {
|
|
19
|
+
location: string;
|
|
20
|
+
additionalContext?: string;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function occurrencesOf(c: CbomComponent): Occurrence[] {
|
|
24
|
+
const ev = c.evidence as { occurrences?: unknown } | undefined;
|
|
25
|
+
const occ = ev?.occurrences;
|
|
26
|
+
return Array.isArray(occ) ? (occ as Occurrence[]) : [];
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const HNDL_PROP = "quantakrypto:harvestNowDecryptLater";
|
|
30
|
+
|
|
31
|
+
function hndlOf(c: CbomComponent): boolean {
|
|
32
|
+
// The flag is carried as a CycloneDX component `property` (name/value). Fall back
|
|
33
|
+
// to the legacy `cryptoProperties.harvestNowDecryptLater` location so an
|
|
34
|
+
// externally-authored / older CBOM still merges correctly.
|
|
35
|
+
const prop = c.properties?.find((p) => p.name === HNDL_PROP);
|
|
36
|
+
if (prop) return prop.value === "true";
|
|
37
|
+
return (
|
|
38
|
+
(c.cryptoProperties as { harvestNowDecryptLater?: unknown } | undefined)
|
|
39
|
+
?.harvestNowDecryptLater === true
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/** Return a copy of `props` with the HNDL flag set to `value`. */
|
|
44
|
+
function withHndl(
|
|
45
|
+
props: { name: string; value: string }[] | undefined,
|
|
46
|
+
value: boolean,
|
|
47
|
+
): { name: string; value: string }[] {
|
|
48
|
+
const rest = (props ?? []).filter((p) => p.name !== HNDL_PROP);
|
|
49
|
+
return [...rest, { name: HNDL_PROP, value: String(value) }];
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/** Union + dedup occurrences by `location`, sorted deterministically. */
|
|
53
|
+
function mergeOccurrences(a: Occurrence[], b: Occurrence[]): Occurrence[] {
|
|
54
|
+
const byLoc = new Map<string, Occurrence>();
|
|
55
|
+
for (const o of [...a, ...b]) if (!byLoc.has(o.location)) byLoc.set(o.location, o);
|
|
56
|
+
return [...byLoc.values()].sort((x, y) =>
|
|
57
|
+
x.location < y.location ? -1 : x.location > y.location ? 1 : 0,
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Merge CBOMs into one. Components with the same `bom-ref` (same algorithm +
|
|
63
|
+
* primitive) are combined; their occurrence evidence is unioned and their
|
|
64
|
+
* harvest-now-decrypt-later flag is OR-ed. Tool and root metadata from every input
|
|
65
|
+
* is preserved.
|
|
66
|
+
*/
|
|
67
|
+
export function mergeCboms(boms: readonly CycloneDxBom[]): CycloneDxBom {
|
|
68
|
+
const byRef = new Map<string, CbomComponent>();
|
|
69
|
+
const toolComponents: unknown[] = [];
|
|
70
|
+
const toolSeen = new Set<string>();
|
|
71
|
+
const roots: string[] = [];
|
|
72
|
+
|
|
73
|
+
for (const bom of boms) {
|
|
74
|
+
// Collect tool + root metadata (dedup tools by name@version).
|
|
75
|
+
const tools = (bom.metadata as { tools?: { components?: unknown[] } })?.tools?.components;
|
|
76
|
+
if (Array.isArray(tools)) {
|
|
77
|
+
for (const t of tools) {
|
|
78
|
+
const tc = t as { name?: string; version?: string };
|
|
79
|
+
const key = `${tc.name}@${tc.version}`;
|
|
80
|
+
if (!toolSeen.has(key)) {
|
|
81
|
+
toolSeen.add(key);
|
|
82
|
+
toolComponents.push(t);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
const root = (bom.metadata as { component?: { name?: string } })?.component?.name;
|
|
87
|
+
if (typeof root === "string" && root && !roots.includes(root)) roots.push(root);
|
|
88
|
+
|
|
89
|
+
// `components` is optional in the CycloneDX 1.6 spec — a legal CBOM can carry none
|
|
90
|
+
// (a scan that found nothing). Treat a missing array as empty instead of crashing.
|
|
91
|
+
for (const c of bom.components ?? []) {
|
|
92
|
+
const ref = c["bom-ref"];
|
|
93
|
+
const existing = byRef.get(ref);
|
|
94
|
+
if (!existing) {
|
|
95
|
+
// Deep-ish copy so we can mutate evidence/properties without touching the input.
|
|
96
|
+
byRef.set(ref, {
|
|
97
|
+
...c,
|
|
98
|
+
cryptoProperties: { ...c.cryptoProperties },
|
|
99
|
+
properties: c.properties ? [...c.properties] : undefined,
|
|
100
|
+
evidence: { occurrences: occurrencesOf(c) },
|
|
101
|
+
});
|
|
102
|
+
} else {
|
|
103
|
+
const merged = mergeOccurrences(occurrencesOf(existing), occurrencesOf(c));
|
|
104
|
+
existing.evidence = { occurrences: merged };
|
|
105
|
+
// OR the harvest-now-decrypt-later flag across the merged copies.
|
|
106
|
+
existing.properties = withHndl(existing.properties, hndlOf(existing) || hndlOf(c));
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const components = [...byRef.values()].sort((a, b) =>
|
|
112
|
+
a["bom-ref"] < b["bom-ref"] ? -1 : a["bom-ref"] > b["bom-ref"] ? 1 : 0,
|
|
113
|
+
);
|
|
114
|
+
|
|
115
|
+
// Content-address the serial over bom-refs AND their occurrence locations (+ roots),
|
|
116
|
+
// so two merges that differ only in occurrence evidence get distinct serials — a
|
|
117
|
+
// CycloneDX serialNumber is meant to identify a BOM *instance*, and downstream
|
|
118
|
+
// bom-link/dedup tooling relies on that. Occurrences are already sorted.
|
|
119
|
+
const serialSeed =
|
|
120
|
+
components
|
|
121
|
+
.map(
|
|
122
|
+
(c) =>
|
|
123
|
+
`${c["bom-ref"]}#${occurrencesOf(c)
|
|
124
|
+
.map((o) => o.location)
|
|
125
|
+
.join(",")}`,
|
|
126
|
+
)
|
|
127
|
+
.join("|") + `|${roots.join(",")}`;
|
|
128
|
+
const h = createHash("sha256").update(serialSeed, "utf8").digest("hex");
|
|
129
|
+
const serial = `${h.slice(0, 8)}-${h.slice(8, 12)}-4${h.slice(13, 16)}-8${h.slice(17, 20)}-${h.slice(20, 32)}`;
|
|
130
|
+
|
|
131
|
+
return {
|
|
132
|
+
bomFormat: "CycloneDX",
|
|
133
|
+
specVersion: "1.6",
|
|
134
|
+
serialNumber: `urn:uuid:${serial}`,
|
|
135
|
+
version: 1,
|
|
136
|
+
metadata: {
|
|
137
|
+
tools: { components: toolComponents },
|
|
138
|
+
component: {
|
|
139
|
+
type: "application",
|
|
140
|
+
"bom-ref": "root",
|
|
141
|
+
name: roots.length ? `combined: ${roots.join(" + ")}` : "combined",
|
|
142
|
+
},
|
|
143
|
+
},
|
|
144
|
+
components,
|
|
145
|
+
};
|
|
146
|
+
}
|
package/src/cbom.ts
CHANGED
|
@@ -5,9 +5,12 @@
|
|
|
5
5
|
* for compliance / supply-chain tooling.
|
|
6
6
|
*
|
|
7
7
|
* Reference: CycloneDX 1.6 cryptography properties
|
|
8
|
-
* (https://cyclonedx.org/capabilities/cbom/).
|
|
9
|
-
* `
|
|
10
|
-
*
|
|
8
|
+
* (https://cyclonedx.org/capabilities/cbom/). Each finding is classified into
|
|
9
|
+
* its CycloneDX `assetType` — `algorithm` (usage), `certificate` (X.509),
|
|
10
|
+
* `related-crypto-material` (private/public key material), or `protocol` (TLS) —
|
|
11
|
+
* and we emit one `cryptographic-asset` component per distinct
|
|
12
|
+
* (assetType, algorithm, discriminator), with occurrence evidence pointing back
|
|
13
|
+
* at the findings. Every asset carries the quantum posture flags.
|
|
11
14
|
*/
|
|
12
15
|
import { createHash } from "node:crypto";
|
|
13
16
|
|
|
@@ -30,10 +33,12 @@ export interface CbomComponent {
|
|
|
30
33
|
"bom-ref": string;
|
|
31
34
|
name: string;
|
|
32
35
|
cryptoProperties: Record<string, unknown>;
|
|
36
|
+
/** CycloneDX component name/value properties (carries the quantum posture flags). */
|
|
37
|
+
properties?: { name: string; value: string }[];
|
|
33
38
|
evidence?: Record<string, unknown>;
|
|
34
39
|
}
|
|
35
40
|
|
|
36
|
-
/** CycloneDX cryptographic primitive for a finding category. */
|
|
41
|
+
/** CycloneDX cryptographic primitive for a finding category (algorithm assets only). */
|
|
37
42
|
function primitiveFor(category: FindingCategory): string {
|
|
38
43
|
switch (category) {
|
|
39
44
|
case "kem":
|
|
@@ -42,18 +47,73 @@ function primitiveFor(category: FindingCategory): string {
|
|
|
42
47
|
return "key-agree";
|
|
43
48
|
case "signature":
|
|
44
49
|
return "signature";
|
|
45
|
-
case "
|
|
46
|
-
|
|
47
|
-
// value; use "other" so the CBOM validates (audit: quantum #3). Modeling
|
|
48
|
-
// certificates as assetType:"certificate" is a future refinement.
|
|
49
|
-
return "other";
|
|
50
|
-
case "tls":
|
|
51
|
-
return "other";
|
|
50
|
+
case "hash":
|
|
51
|
+
return "hash";
|
|
52
52
|
default:
|
|
53
|
+
// `certificate` / `tls` no longer route here (they become their own
|
|
54
|
+
// assetType — see {@link classifyAsset}); `dependency` / `rng` and any
|
|
55
|
+
// future category fall back to the valid "other" primitive.
|
|
53
56
|
return "other";
|
|
54
57
|
}
|
|
55
58
|
}
|
|
56
59
|
|
|
60
|
+
/** The CycloneDX 1.6 `cryptoProperties.assetType` a finding maps to. */
|
|
61
|
+
type CbomAssetType = "algorithm" | "certificate" | "protocol" | "related-crypto-material";
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Classify a finding into its CycloneDX 1.6 asset type. Most findings are
|
|
65
|
+
* algorithm USAGE (`kem` / `key-exchange` / `signature` / `hash` / …). The
|
|
66
|
+
* `certificate` category is a mix of real X.509 certificates and raw key
|
|
67
|
+
* material, disambiguated by rule id; `tls` findings are protocol configuration.
|
|
68
|
+
*
|
|
69
|
+
* `discriminator` keeps distinct kinds in separate components (it is the algorithm
|
|
70
|
+
* primitive for `algorithm`, the material type for `related-crypto-material`, the
|
|
71
|
+
* protocol type for `protocol`, and empty for `certificate`).
|
|
72
|
+
*/
|
|
73
|
+
function classifyAsset(f: Finding): {
|
|
74
|
+
assetType: CbomAssetType;
|
|
75
|
+
discriminator: string;
|
|
76
|
+
materialType?: string;
|
|
77
|
+
protocolType?: string;
|
|
78
|
+
} {
|
|
79
|
+
if (f.category === "tls") {
|
|
80
|
+
return { assetType: "protocol", discriminator: "tls", protocolType: "tls" };
|
|
81
|
+
}
|
|
82
|
+
if (f.category === "certificate") {
|
|
83
|
+
const id = f.ruleId.toLowerCase();
|
|
84
|
+
// Route the explicitly key-SHAPED material to related-crypto-material…
|
|
85
|
+
if (id.includes("private-key") || id.includes("keystore")) {
|
|
86
|
+
return {
|
|
87
|
+
assetType: "related-crypto-material",
|
|
88
|
+
discriminator: "private-key",
|
|
89
|
+
materialType: "private-key",
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
if (id.includes("public-key")) {
|
|
93
|
+
return {
|
|
94
|
+
assetType: "related-crypto-material",
|
|
95
|
+
discriminator: "public-key",
|
|
96
|
+
materialType: "public-key",
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
if (id.includes("message")) {
|
|
100
|
+
return {
|
|
101
|
+
assetType: "related-crypto-material",
|
|
102
|
+
discriminator: "ciphertext",
|
|
103
|
+
materialType: "ciphertext",
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
// …and DEFAULT the rest of the `certificate` category to assetType
|
|
107
|
+
// "certificate" — the category's own meaning. This covers real X.509 certs
|
|
108
|
+
// and CSRs (pem-certificate/pem-cert-request) plus PKI trust material whose
|
|
109
|
+
// ids don't spell "cert": ACM certs (cfn-acm-*), Vault PKI (vault-pki-*),
|
|
110
|
+
// mesh identity, and SPIFFE X.509-SVIDs (spire-*). Previously these fell
|
|
111
|
+
// through to related-crypto-material "key", mislabelling certificates.
|
|
112
|
+
return { assetType: "certificate", discriminator: "" };
|
|
113
|
+
}
|
|
114
|
+
return { assetType: "algorithm", discriminator: primitiveFor(f.category) };
|
|
115
|
+
}
|
|
116
|
+
|
|
57
117
|
/**
|
|
58
118
|
* Every CBOM asset here is derived from a quantakrypto finding, and the detectors
|
|
59
119
|
* only ever fire on classical (Shor-broken) public-key crypto — so an asset is
|
|
@@ -102,19 +162,28 @@ function bomRef(key: string): string {
|
|
|
102
162
|
* finding. Output is deterministic (components and occurrences are sorted).
|
|
103
163
|
*/
|
|
104
164
|
export function toCbom(result: ScanResult): CycloneDxBom {
|
|
105
|
-
// Group findings by (algorithm |
|
|
165
|
+
// Group findings by (assetType | algorithm | discriminator) so each distinct
|
|
166
|
+
// cryptographic asset — an algorithm usage, a certificate, key material, or a
|
|
167
|
+
// protocol — becomes its own component.
|
|
106
168
|
const groups = new Map<
|
|
107
169
|
string,
|
|
108
|
-
{
|
|
170
|
+
{
|
|
171
|
+
assetType: CbomAssetType;
|
|
172
|
+
algorithm: AlgorithmFamily;
|
|
173
|
+
discriminator: string;
|
|
174
|
+
materialType?: string;
|
|
175
|
+
protocolType?: string;
|
|
176
|
+
findings: Finding[];
|
|
177
|
+
}
|
|
109
178
|
>();
|
|
110
179
|
|
|
111
180
|
for (const f of result.findings) {
|
|
112
181
|
const algorithm: AlgorithmFamily = f.algorithm ?? "unknown";
|
|
113
|
-
const
|
|
114
|
-
const key = `${algorithm}|${
|
|
182
|
+
const cls = classifyAsset(f);
|
|
183
|
+
const key = `${cls.assetType}|${algorithm}|${cls.discriminator}`;
|
|
115
184
|
let g = groups.get(key);
|
|
116
185
|
if (!g) {
|
|
117
|
-
g = {
|
|
186
|
+
g = { ...cls, algorithm, findings: [] };
|
|
118
187
|
groups.set(key, g);
|
|
119
188
|
}
|
|
120
189
|
g.findings.push(f);
|
|
@@ -132,30 +201,69 @@ export function toCbom(result: ScanResult): CycloneDxBom {
|
|
|
132
201
|
|
|
133
202
|
const anyHndl = g.findings.some((f) => f.hndl);
|
|
134
203
|
|
|
204
|
+
// The type-specific CycloneDX properties block + a human label for the name.
|
|
205
|
+
let typeProps: Record<string, unknown>;
|
|
206
|
+
let label: string;
|
|
207
|
+
switch (g.assetType) {
|
|
208
|
+
case "certificate":
|
|
209
|
+
// A certificate / CSR: we know the public-key family but not the X.509
|
|
210
|
+
// fields (subject, validity, refs) from a lexical scan, so the block is
|
|
211
|
+
// left minimal — all certificateProperties are optional in 1.6.
|
|
212
|
+
typeProps = { certificateProperties: {} };
|
|
213
|
+
label = "certificate";
|
|
214
|
+
break;
|
|
215
|
+
case "related-crypto-material":
|
|
216
|
+
typeProps = {
|
|
217
|
+
relatedCryptoMaterialProperties: { type: g.materialType ?? "key" },
|
|
218
|
+
};
|
|
219
|
+
label = g.materialType ?? "key";
|
|
220
|
+
break;
|
|
221
|
+
case "protocol":
|
|
222
|
+
typeProps = { protocolProperties: { type: g.protocolType ?? "tls" } };
|
|
223
|
+
label = g.protocolType ?? "tls";
|
|
224
|
+
break;
|
|
225
|
+
case "algorithm":
|
|
226
|
+
default:
|
|
227
|
+
typeProps = {
|
|
228
|
+
algorithmProperties: {
|
|
229
|
+
primitive: g.discriminator,
|
|
230
|
+
parameterSetIdentifier: g.algorithm,
|
|
231
|
+
executionEnvironment: "software-plain-ram",
|
|
232
|
+
classicalSecurityLevel: classicalSecurityLevelFor(g.algorithm),
|
|
233
|
+
nistQuantumSecurityLevel: 0,
|
|
234
|
+
cryptoFunctions:
|
|
235
|
+
g.discriminator === "signature"
|
|
236
|
+
? ["sign", "verify"]
|
|
237
|
+
: g.discriminator === "kem"
|
|
238
|
+
? ["encapsulate", "decapsulate"]
|
|
239
|
+
: // CycloneDX 1.6 has no "keyagree" cryptoFunction; "other" is
|
|
240
|
+
// the valid value for a key-agreement primitive.
|
|
241
|
+
["other"],
|
|
242
|
+
},
|
|
243
|
+
};
|
|
244
|
+
label = g.discriminator;
|
|
245
|
+
break;
|
|
246
|
+
}
|
|
247
|
+
|
|
135
248
|
return {
|
|
136
249
|
type: "cryptographic-asset" as const,
|
|
137
250
|
"bom-ref": bomRef(key),
|
|
138
|
-
name: `${g.algorithm} (${
|
|
251
|
+
name: `${g.algorithm} (${label})`,
|
|
139
252
|
cryptoProperties: {
|
|
140
|
-
assetType:
|
|
141
|
-
|
|
142
|
-
primitive: g.primitive,
|
|
143
|
-
parameterSetIdentifier: g.algorithm,
|
|
144
|
-
executionEnvironment: "software-plain-ram",
|
|
145
|
-
classicalSecurityLevel: classicalSecurityLevelFor(g.algorithm),
|
|
146
|
-
nistQuantumSecurityLevel: 0,
|
|
147
|
-
cryptoFunctions:
|
|
148
|
-
g.primitive === "signature"
|
|
149
|
-
? ["sign", "verify"]
|
|
150
|
-
: g.primitive === "kem"
|
|
151
|
-
? ["encapsulate", "decapsulate"]
|
|
152
|
-
: g.primitive === "key-agree"
|
|
153
|
-
? ["keyagree"]
|
|
154
|
-
: ["other"],
|
|
155
|
-
},
|
|
156
|
-
quantumVulnerable: isQuantumVulnerable(g.algorithm),
|
|
157
|
-
harvestNowDecryptLater: anyHndl,
|
|
253
|
+
assetType: g.assetType,
|
|
254
|
+
...typeProps,
|
|
158
255
|
},
|
|
256
|
+
// The quantum posture flags are carried as CycloneDX component `properties`
|
|
257
|
+
// (an open name/value list) rather than inside `cryptoProperties`, whose
|
|
258
|
+
// 1.6 schema is `additionalProperties: false` — so a strict validator
|
|
259
|
+
// accepts the BOM. Namespaced to avoid clashing with other tools' keys.
|
|
260
|
+
properties: [
|
|
261
|
+
{
|
|
262
|
+
name: "quantakrypto:quantumVulnerable",
|
|
263
|
+
value: String(isQuantumVulnerable(g.algorithm)),
|
|
264
|
+
},
|
|
265
|
+
{ name: "quantakrypto:harvestNowDecryptLater", value: String(anyHndl) },
|
|
266
|
+
],
|
|
159
267
|
evidence: { occurrences },
|
|
160
268
|
};
|
|
161
269
|
});
|
|
@@ -190,12 +298,18 @@ export function toCbom(result: ScanResult): CycloneDxBom {
|
|
|
190
298
|
|
|
191
299
|
/**
|
|
192
300
|
* Derive a stable UUID-shaped serial from the scan result so re-exporting the
|
|
193
|
-
* same result yields the same serial number (deterministic output).
|
|
301
|
+
* same result yields the same serial number (deterministic output). Hashed over the
|
|
302
|
+
* actual finding set (ruleId + location per finding), NOT just the COUNT — two scans
|
|
303
|
+
* with the same number of totally different findings must not collide, since a
|
|
304
|
+
* CycloneDX serialNumber identifies a specific BOM instance. Findings are already in
|
|
305
|
+
* a stable order (scan.ts sorts by file/line/ruleId).
|
|
194
306
|
*/
|
|
195
307
|
function stableUuid(result: ScanResult): string {
|
|
196
|
-
const
|
|
197
|
-
|
|
198
|
-
.
|
|
308
|
+
const hash = createHash("sha256").update(`${result.root}|${result.toolVersion}`, "utf8");
|
|
309
|
+
for (const f of result.findings) {
|
|
310
|
+
hash.update(`\n${f.ruleId}@${f.location.file}:${f.location.line}:${f.location.column ?? 0}`);
|
|
311
|
+
}
|
|
312
|
+
const h = hash.digest("hex");
|
|
199
313
|
// Shape as a v4-ish UUID (variant/version nibbles forced).
|
|
200
314
|
return `${h.slice(0, 8)}-${h.slice(8, 12)}-4${h.slice(13, 16)}-8${h.slice(17, 20)}-${h.slice(20, 32)}`;
|
|
201
315
|
}
|