@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/detectors/source.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { JS_TS_EXTENSIONS, DOC_EXTENSIONS, JWT_HOST_EXTENSIONS, eachMatch, findingFromRule, hasExtension, nearSortedCall, } from "../detect-utils.js";
|
|
1
|
+
import { JS_TS_EXTENSIONS, DOC_EXTENSIONS, JWT_HOST_EXTENSIONS, eachMatch, enclosingObject, findingFromRule, hasExtension, nearSortedCall, } from "../detect-utils.js";
|
|
2
2
|
import { CWE_BROKEN_CRYPTO, CWE_CERT_VALIDATION, CWE_WEAK_STRENGTH } from "../cwe.js";
|
|
3
3
|
/* -------------------------------------------------------------------------- */
|
|
4
4
|
/* Precompiled regexes (module scope — never recreated per file) */
|
|
@@ -6,6 +6,14 @@ import { CWE_BROKEN_CRYPTO, CWE_CERT_VALIDATION, CWE_WEAK_STRENGTH } from "../cw
|
|
|
6
6
|
// `rsa-pss` is listed before `rsa` so the alternation consumes the full token
|
|
7
7
|
// (ordered alternation would otherwise match `rsa` and reject the `-pss` tail).
|
|
8
8
|
const RE_GENERATE_KEYPAIR = /generateKeyPair(?:Sync)?\s*\(\s*['"`](rsa-pss|rsa|ec|dsa|dh|x25519|x448|ed25519|ed448)['"`]/g;
|
|
9
|
+
// Variable-typed key generation: `generateKeyPair(kind, …)` where the algorithm
|
|
10
|
+
// argument is an IDENTIFIER (a variable), not a quoted literal, so the concrete
|
|
11
|
+
// family is unknown at scan time but a classical key is still being generated.
|
|
12
|
+
// The lookbehind anchors the call to a word boundary so it does not fire inside
|
|
13
|
+
// a longer identifier (e.g. `myGenerateKeyPair(`); the first argument must be a
|
|
14
|
+
// bare identifier immediately followed by `,` or `)` so a quoted-literal call
|
|
15
|
+
// (handled by RE_GENERATE_KEYPAIR) never matches here.
|
|
16
|
+
const RE_GENERATE_KEYPAIR_VAR = /(?<![\w$])generateKeyPair(?:Sync)?\s*\(\s*([A-Za-z_$][\w$]*)\s*[,)]/g;
|
|
9
17
|
const KEYGEN_INFO = {
|
|
10
18
|
rsa: { algo: "RSA", cat: "kem", sev: "high", hndl: true, label: "RSA" },
|
|
11
19
|
// RSA-PSS is signature-only, so classify it as a (forgeable) signature
|
|
@@ -108,6 +116,18 @@ function escapeRe(s) {
|
|
|
108
116
|
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
109
117
|
}
|
|
110
118
|
const RE_CREATE_SIGN_VERIFY = /create(?:Sign|Verify)\s*\(/g;
|
|
119
|
+
// Computed-member (bracket) access to a Node `crypto` method:
|
|
120
|
+
// `crypto['createSign'](…)`, `c["createECDH"](…)`. Bracket access defeats the
|
|
121
|
+
// dotted-call regexes above (which require `name(`), so match a quoted method
|
|
122
|
+
// name inside a `[ '…' ]( ` computed call and route it to the right rule. The
|
|
123
|
+
// method names here take no key-type argument, so no argument parsing is needed;
|
|
124
|
+
// `generateKeyPair(Sync)` bracket access is handled separately (it carries the
|
|
125
|
+
// key-type literal). None of these overlap the dotted regexes (those require the
|
|
126
|
+
// name immediately before `(`, whereas here it is followed by a closing quote).
|
|
127
|
+
const RE_BRACKET_CRYPTO_METHOD = /\[\s*['"`](createSign|createVerify|createECDH|createDiffieHellman|createDiffieHellmanGroup|publicEncrypt|privateDecrypt)['"`]\s*\]\s*\(/g;
|
|
128
|
+
// `crypto['generateKeyPairSync']('rsa', …)`: bracket access carrying the quoted
|
|
129
|
+
// key-type literal, classified exactly like the dotted `generateKeyPair` call.
|
|
130
|
+
const RE_BRACKET_KEYGEN = /\[\s*['"`]generateKeyPair(?:Sync)?['"`]\s*\]\s*\(\s*['"`](rsa-pss|rsa|ec|dsa|dh|x25519|x448|ed25519|ed448)['"`]/g;
|
|
111
131
|
// One-shot crypto.sign/verify(algorithm, data, key). A LOOKBEHIND (not a
|
|
112
132
|
// consumed char) anchors it so it doesn't fire inside identifiers like `assign(`
|
|
113
133
|
// or `createSign(` (handled by the dedicated createSign/createVerify rule) —
|
|
@@ -147,10 +167,14 @@ const RE_TLS_REJECT = /rejectUnauthorized\s*:\s*false/g;
|
|
|
147
167
|
// Hardened cipher regex: bounded spans (no unbounded `[^'"`]*` straddling the
|
|
148
168
|
// alternation), single-quote-style anchoring removed in favour of {0,256} bounds
|
|
149
169
|
// so worst-case backtracking is linear in the bound, not the file (P0-6).
|
|
150
|
-
//
|
|
151
|
-
//
|
|
152
|
-
//
|
|
153
|
-
|
|
170
|
+
// Skip OpenSSL EXCLUSION syntax — `!MD5` / `:-RC4` / `!ECDHE-RSA-RC4-SHA` DISABLE
|
|
171
|
+
// those ciphers, so a hardened list must not be flagged as weak (audit: crypto #7). The
|
|
172
|
+
// single variable-length lookbehind walks back over the suite-name chars to the
|
|
173
|
+
// ELEMENT boundary (`:` / whitespace / comma / quote / start) and rejects the match
|
|
174
|
+
// when that element begins with `!` or `-` — covering a bare `!RC4`, a space-separated
|
|
175
|
+
// `HIGH !RC4`, and a full-suite `!ECDHE-RSA-RC4-SHA`. The intra-name hyphen in a
|
|
176
|
+
// genuinely-enabled `ECDHE-RSA-RC4-SHA` (no leading `!`/`-`) still matches.
|
|
177
|
+
const RE_TLS_WEAK_CIPHER = /ciphers\s*:\s*['"`][^'"`\n]{0,256}?\b(?<![:'"`,\s]\s*[!-][\w-]{0,64})(RC4|DES|3DES|MD5|NULL|EXPORT|aNULL|eNULL)\b[^'"`\n]{0,256}?['"`]/gi;
|
|
154
178
|
/* -------------------------------------------------------------------------- */
|
|
155
179
|
/* Node.js `crypto` module */
|
|
156
180
|
/* -------------------------------------------------------------------------- */
|
|
@@ -280,6 +304,19 @@ const nodeCryptoDetector = {
|
|
|
280
304
|
eachMatch(RE_GENERATE_KEYPAIR, content, (m) => {
|
|
281
305
|
pushKeygenFinding(findings, m[1], file, content, m.index, m[0].length);
|
|
282
306
|
});
|
|
307
|
+
// generateKeyPair(Sync)(<variable>, …): key type passed as a variable, so the
|
|
308
|
+
// family is unknown; emit the generic keygen rule (unknown / key-exchange /
|
|
309
|
+
// HNDL-conservative) rather than missing the classical key generation.
|
|
310
|
+
eachMatch(RE_GENERATE_KEYPAIR_VAR, content, (m) => {
|
|
311
|
+
findings.push(findingFromRule(RULE_NODE_KEYGEN, { file, content, index: m.index, matchLength: m[0].length }, {
|
|
312
|
+
title: "Classical key generation (variable key type)",
|
|
313
|
+
message: "Generates a classical asymmetric key pair (key type passed as a variable), which is not quantum-safe.",
|
|
314
|
+
}));
|
|
315
|
+
});
|
|
316
|
+
// Bracket (computed-member) key generation: crypto['generateKeyPairSync']('rsa').
|
|
317
|
+
eachMatch(RE_BRACKET_KEYGEN, content, (m) => {
|
|
318
|
+
pushKeygenFinding(findings, m[1], file, content, m.index, m[0].length);
|
|
319
|
+
});
|
|
283
320
|
// Import-alias resolution: follow `import { generateKeyPairSync as gk }` (and
|
|
284
321
|
// the CommonJS destructure-rename) so an aliased call still detects. Only the
|
|
285
322
|
// keygen / ECDH / DH constructors are resolved — their classification is
|
|
@@ -322,6 +359,25 @@ const nodeCryptoDetector = {
|
|
|
322
359
|
matchLength: m[0].length,
|
|
323
360
|
}));
|
|
324
361
|
});
|
|
362
|
+
// Bracket (computed-member) access to argument-free crypto methods:
|
|
363
|
+
// crypto['createSign'](…), c["createECDH"](…). Route each to its rule.
|
|
364
|
+
eachMatch(RE_BRACKET_CRYPTO_METHOD, content, (m) => {
|
|
365
|
+
const loc = { file, content, index: m.index, matchLength: m[0].length };
|
|
366
|
+
const method = m[1];
|
|
367
|
+
if (method === "createSign" || method === "createVerify") {
|
|
368
|
+
findings.push(findingFromRule(RULE_NODE_SIGN, loc));
|
|
369
|
+
}
|
|
370
|
+
else if (method === "createECDH") {
|
|
371
|
+
findings.push(findingFromRule(RULE_NODE_ECDH, loc));
|
|
372
|
+
}
|
|
373
|
+
else if (method === "publicEncrypt" || method === "privateDecrypt") {
|
|
374
|
+
findings.push(findingFromRule(RULE_NODE_RSA_ENCRYPT, loc));
|
|
375
|
+
}
|
|
376
|
+
else {
|
|
377
|
+
// createDiffieHellman / createDiffieHellmanGroup: finite-field DH.
|
|
378
|
+
findings.push(findingFromRule(RULE_NODE_DH, loc));
|
|
379
|
+
}
|
|
380
|
+
});
|
|
325
381
|
// One-shot crypto.sign(algorithm, data, key) / crypto.verify(...) (Node ≥ 12).
|
|
326
382
|
eachMatch(RE_ONESHOT_SIGN_VERIFY, content, (m) => {
|
|
327
383
|
findings.push(findingFromRule(RULE_NODE_SIGN_ONESHOT, {
|
|
@@ -433,7 +489,9 @@ const webCryptoDetector = {
|
|
|
433
489
|
algorithm = name === "X448" ? "X448" : "X25519";
|
|
434
490
|
category = "key-exchange";
|
|
435
491
|
hndl = true;
|
|
436
|
-
|
|
492
|
+
// `medium`, aligned with X25519/X448 key agreement in node/rust/go — HNDL-exposed
|
|
493
|
+
// key agreement scores the same regardless of which surface uses it.
|
|
494
|
+
severity = "medium";
|
|
437
495
|
}
|
|
438
496
|
else if (name === "ED25519" || name === "ED448") {
|
|
439
497
|
algorithm = "EdDSA";
|
|
@@ -659,8 +717,21 @@ const jwtDetector = {
|
|
|
659
717
|
appliesTo: (f) => hasExtension(f, JWT_HOST_EXTENSIONS),
|
|
660
718
|
detect({ file, content }) {
|
|
661
719
|
const findings = [];
|
|
720
|
+
// Deferral guards against double-counting:
|
|
721
|
+
// - webCryptoDetector owns a quoted `RSA-OAEP`/`ECDH-ES` sitting next to a
|
|
722
|
+
// `subtle.*(` call (it matches the `RSA-OAEP`/`ECDH` name). Collect the subtle
|
|
723
|
+
// call offsets so the JOSE key-management loops can skip those. (In non-JS host
|
|
724
|
+
// files there are no subtle calls, so nothing is skipped.)
|
|
725
|
+
// - jwkDetector owns any alg/`kty` that lives inside a JWK object; skip a token
|
|
726
|
+
// whose enclosing `{…}` carries a `"kty"` so a JWK's own `alg` is not counted by
|
|
727
|
+
// both detectors. Mirrors the same guard jose.ts applies for other languages.
|
|
728
|
+
const subtleCalls = [];
|
|
729
|
+
eachMatch(RE_SUBTLE_CALL, content, (m) => subtleCalls.push(m.index));
|
|
730
|
+
const inJwk = (index) => enclosingObject(content, index).includes('"kty"');
|
|
662
731
|
// Classical JWS signature alg tokens. Anchored to quotes to avoid words.
|
|
663
732
|
eachMatch(RE_JWT_ALG, content, (m) => {
|
|
733
|
+
if (inJwk(m.index))
|
|
734
|
+
return; // jwk-{rsa,ec,eddsa} owns a JWK's own alg
|
|
664
735
|
const alg = m[1];
|
|
665
736
|
let algorithm;
|
|
666
737
|
if (alg.startsWith("RS") || alg.startsWith("PS"))
|
|
@@ -677,6 +748,10 @@ const jwtDetector = {
|
|
|
677
748
|
});
|
|
678
749
|
// JOSE ECDH-ES key agreement (and ECDH-ES+A*KW) — confidentiality, HNDL.
|
|
679
750
|
eachMatch(RE_JOSE_ECDH, content, (m) => {
|
|
751
|
+
if (inJwk(m.index))
|
|
752
|
+
return; // jwk-ec owns a JWK's own alg
|
|
753
|
+
if (nearSortedCall(subtleCalls, m.index, 400))
|
|
754
|
+
return; // webcrypto owns it
|
|
680
755
|
findings.push(findingFromRule(RULE_JOSE_ECDH, { file, content, index: m.index, matchLength: m[0].length }, {
|
|
681
756
|
title: `JOSE key agreement ${m[1]}`,
|
|
682
757
|
message: `JOSE "${m[1]}" performs classical ECDH key agreement — harvest-now-decrypt-later exposed.`,
|
|
@@ -684,6 +759,12 @@ const jwtDetector = {
|
|
|
684
759
|
});
|
|
685
760
|
// JOSE RSA key transport (RSA-OAEP / RSA1_5) — classical RSA encryption, HNDL.
|
|
686
761
|
eachMatch(RE_JOSE_KEM, content, (m) => {
|
|
762
|
+
if (inJwk(m.index))
|
|
763
|
+
return; // jwk-rsa owns a JWK's own alg
|
|
764
|
+
// webCryptoDetector's regex matches `RSA-OAEP` but NOT `RSA1_5`, so only defer the
|
|
765
|
+
// OAEP form near a subtle call — deferring RSA1_5 would drop it entirely.
|
|
766
|
+
if (m[1].startsWith("RSA-OAEP") && nearSortedCall(subtleCalls, m.index, 400))
|
|
767
|
+
return;
|
|
687
768
|
findings.push(findingFromRule(RULE_JOSE_RSA_OAEP, { file, content, index: m.index, matchLength: m[0].length }, {
|
|
688
769
|
title: `JOSE RSA key transport ${m[1]}`,
|
|
689
770
|
message: `JOSE "${m[1]}" is classical RSA key transport — harvest-now-decrypt-later exposed.`,
|
|
@@ -782,6 +863,20 @@ const RE_CERT_SIG_ALG = /\b(sha(?:1|256|384|512)WithRSAEncryption|ecdsa-with-SHA
|
|
|
782
863
|
// Shor-broken key agreement — the harvest-now-decrypt-later surface a lexical
|
|
783
864
|
// scan misses when only the `crypto/*` API is matched, not the negotiated kex.
|
|
784
865
|
const RE_SSH_KEX = /\b(diffie-hellman-group(?:1|14|15|16|17|18)(?:-sha1|-sha256|-sha512)?|diffie-hellman-group-exchange-sha(?:1|256)|ecdh-sha2-nistp(?:256|384|521)|curve25519-sha256)\b/g;
|
|
866
|
+
/**
|
|
867
|
+
* True when the match at `index` sits inside a DOCUMENTATION field value — a
|
|
868
|
+
* `description` / `help` / `summary` / `doc` / `comment` / `note` assignment
|
|
869
|
+
* (`key = "…"` or `key: "…"`), as used by Terraform/Packer variable descriptions,
|
|
870
|
+
* JSON-schema docs, etc. Classical KEX/cipher algorithm names LISTED in such a field
|
|
871
|
+
* are prose ("acceptable values include ecdh-sha2-nistp256, …"), not an active
|
|
872
|
+
* setting, so a transport-token rule must not fire on them. Scans the current line's
|
|
873
|
+
* prefix only (cheap; the common single-line description case).
|
|
874
|
+
*/
|
|
875
|
+
function inDocFieldValue(content, index) {
|
|
876
|
+
const lineStart = content.lastIndexOf("\n", index - 1) + 1;
|
|
877
|
+
const prefix = content.slice(lineStart, index);
|
|
878
|
+
return /\b(?:description|help|summary|doc|comment|note)\b\s*[:=]/i.test(prefix);
|
|
879
|
+
}
|
|
785
880
|
// A bare key-type token (`ssh-rsa`, `ssh-ed25519`, …) is real SSH crypto only
|
|
786
881
|
// when it is EITHER (a) followed by base64 key material — an actual
|
|
787
882
|
// authorized_keys / known_hosts entry — OR (b) one of ≥2 DISTINCT ssh key/host-key
|
|
@@ -910,6 +1005,8 @@ const sshCertDetector = {
|
|
|
910
1005
|
});
|
|
911
1006
|
// SSH key-exchange algorithm identifiers (finite-field DH / ECDH / X25519).
|
|
912
1007
|
eachMatch(RE_SSH_KEX, content, (m) => {
|
|
1008
|
+
if (inDocFieldValue(content, m.index))
|
|
1009
|
+
return; // names listed in a doc string
|
|
913
1010
|
const tok = m[1];
|
|
914
1011
|
const algorithm = tok.startsWith("diffie-hellman")
|
|
915
1012
|
? "DH"
|
|
@@ -965,6 +1062,8 @@ const tlsClassicalKexDetector = {
|
|
|
965
1062
|
detect({ file, content }) {
|
|
966
1063
|
const findings = [];
|
|
967
1064
|
eachMatch(RE_TLS_CLASSICAL_KEX, content, (m) => {
|
|
1065
|
+
if (inDocFieldValue(content, m.index))
|
|
1066
|
+
return; // names listed in a doc string
|
|
968
1067
|
const tok = m[0];
|
|
969
1068
|
const algorithm = tok.includes("ECDH") ? "ECDH" : "DH";
|
|
970
1069
|
findings.push(findingFromRule(RULE_TLS_CLASSICAL_KEX, { file, content, index: m.index, matchLength: m[0].length }, {
|