@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/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,107 @@ 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
|
+
const KEYGEN_INFO = {
|
|
10
|
+
rsa: { algo: "RSA", cat: "kem", sev: "high", hndl: true, label: "RSA" },
|
|
11
|
+
// RSA-PSS is signature-only, so classify it as a (forgeable) signature
|
|
12
|
+
// rather than a KEM — no HNDL confidentiality exposure.
|
|
13
|
+
"rsa-pss": {
|
|
14
|
+
algo: "RSA",
|
|
15
|
+
cat: "signature",
|
|
16
|
+
sev: "high",
|
|
17
|
+
hndl: false,
|
|
18
|
+
label: "RSA-PSS",
|
|
19
|
+
message: "Generates a classical RSA-PSS signing key, which is forgeable by a quantum attacker.",
|
|
20
|
+
remediation: "ML-DSA-65 (FIPS 204) or SLH-DSA (FIPS 205)",
|
|
21
|
+
},
|
|
22
|
+
// EC keys feed BOTH ECDSA (sign) and ECDH (key agreement). ECDH is
|
|
23
|
+
// HNDL-exposed, so classify conservatively as key-exchange-capable and
|
|
24
|
+
// surface both concerns rather than asserting signature-only (P0-4).
|
|
25
|
+
ec: {
|
|
26
|
+
algo: "ECDH",
|
|
27
|
+
cat: "key-exchange",
|
|
28
|
+
sev: "high",
|
|
29
|
+
hndl: true,
|
|
30
|
+
label: "EC (ECDSA/ECDH)",
|
|
31
|
+
message: "Generates a classical EC key pair. EC keys feed BOTH ECDSA signatures " +
|
|
32
|
+
"and ECDH key agreement; the ECDH path is harvest-now-decrypt-later exposed.",
|
|
33
|
+
remediation: "For key agreement: hybrid X25519MLKEM768 (ML-KEM-768). For signatures: ML-DSA-65 (FIPS 204).",
|
|
34
|
+
},
|
|
35
|
+
dsa: { algo: "DSA", cat: "signature", sev: "high", hndl: false, label: "DSA" },
|
|
36
|
+
dh: { algo: "DH", cat: "key-exchange", sev: "high", hndl: true, label: "Diffie-Hellman" },
|
|
37
|
+
x25519: { algo: "X25519", cat: "key-exchange", sev: "medium", hndl: true, label: "X25519" },
|
|
38
|
+
x448: { algo: "X448", cat: "key-exchange", sev: "medium", hndl: true, label: "X448" },
|
|
39
|
+
ed25519: { algo: "EdDSA", cat: "signature", sev: "low", hndl: false, label: "Ed25519" },
|
|
40
|
+
ed448: { algo: "EdDSA", cat: "signature", sev: "low", hndl: false, label: "Ed448" },
|
|
41
|
+
};
|
|
42
|
+
/** Emit a keygen finding for `generateKeyPair(Sync)('<type>')` at [index]. Shared
|
|
43
|
+
* by the direct matcher and the import-alias pass so both classify identically. */
|
|
44
|
+
function pushKeygenFinding(findings, rawType, file, content, index, matchLength) {
|
|
45
|
+
const info = KEYGEN_INFO[rawType.toLowerCase()];
|
|
46
|
+
if (!info)
|
|
47
|
+
return;
|
|
48
|
+
findings.push(findingFromRule(RULE_NODE_KEYGEN, { file, content, index, matchLength }, {
|
|
49
|
+
title: `${info.label} key generation`,
|
|
50
|
+
category: info.cat,
|
|
51
|
+
severity: info.sev,
|
|
52
|
+
algorithm: info.algo,
|
|
53
|
+
hndl: info.hndl,
|
|
54
|
+
message: info.message ??
|
|
55
|
+
`Generates a classical ${info.label} key pair, which is not quantum-safe.`,
|
|
56
|
+
...(info.remediation ? { remediation: info.remediation } : {}),
|
|
57
|
+
}));
|
|
58
|
+
}
|
|
59
|
+
/** Canonical Node-crypto call names whose import-aliases we resolve. */
|
|
60
|
+
const ALIASABLE = [
|
|
61
|
+
"generateKeyPairSync",
|
|
62
|
+
"generateKeyPair",
|
|
63
|
+
"createECDH",
|
|
64
|
+
"createDiffieHellman",
|
|
65
|
+
"createDiffieHellmanGroup",
|
|
66
|
+
];
|
|
67
|
+
/**
|
|
68
|
+
* Collect import-aliases for the canonical crypto calls we resolve, so
|
|
69
|
+
* `import { generateKeyPairSync as gk } from 'node:crypto'` (or the CommonJS
|
|
70
|
+
* `const { generateKeyPairSync: gk } = require('crypto')`) is followed when `gk`
|
|
71
|
+
* is called later. Returns Map<canonical, alias[]>. An alias equal to its own
|
|
72
|
+
* canonical name is skipped (already matched directly). Precision-safe: the
|
|
73
|
+
* alias is only ever bound to a known crypto symbol, so a later `gk(` call is
|
|
74
|
+
* unambiguously that symbol.
|
|
75
|
+
*/
|
|
76
|
+
function collectCryptoAliases(content) {
|
|
77
|
+
const out = new Map();
|
|
78
|
+
const add = (canonical, alias) => {
|
|
79
|
+
if (!alias || alias === canonical)
|
|
80
|
+
return;
|
|
81
|
+
const list = out.get(canonical) ?? [];
|
|
82
|
+
if (!list.includes(alias))
|
|
83
|
+
list.push(alias);
|
|
84
|
+
out.set(canonical, list);
|
|
85
|
+
};
|
|
86
|
+
// ESM named imports: `import { A as B, C as D } from '...'`.
|
|
87
|
+
const esm = /import\s*(?:type\s+)?\{([^}]*)\}\s*from\s*['"][^'"]*['"]/g;
|
|
88
|
+
for (let m = esm.exec(content); m; m = esm.exec(content)) {
|
|
89
|
+
const spec = /([A-Za-z_$][\w$]*)\s+as\s+([A-Za-z_$][\w$]*)/g;
|
|
90
|
+
for (let s = spec.exec(m[1]); s; s = spec.exec(m[1])) {
|
|
91
|
+
if (ALIASABLE.includes(s[1]))
|
|
92
|
+
add(s[1], s[2]);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
// CommonJS destructure-rename: `const { A: B } = require('...')`.
|
|
96
|
+
const cjs = /(?:const|let|var)\s*\{([^}]*)\}\s*=\s*require\s*\(\s*['"][^'"]*['"]\s*\)/g;
|
|
97
|
+
for (let m = cjs.exec(content); m; m = cjs.exec(content)) {
|
|
98
|
+
const spec = /([A-Za-z_$][\w$]*)\s*:\s*([A-Za-z_$][\w$]*)/g;
|
|
99
|
+
for (let s = spec.exec(m[1]); s; s = spec.exec(m[1])) {
|
|
100
|
+
if (ALIASABLE.includes(s[1]))
|
|
101
|
+
add(s[1], s[2]);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
return out;
|
|
105
|
+
}
|
|
106
|
+
/** Escape a string for safe interpolation into a dynamically-built RegExp. */
|
|
107
|
+
function escapeRe(s) {
|
|
108
|
+
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
109
|
+
}
|
|
9
110
|
const RE_CREATE_SIGN_VERIFY = /create(?:Sign|Verify)\s*\(/g;
|
|
10
111
|
// One-shot crypto.sign/verify(algorithm, data, key). A LOOKBEHIND (not a
|
|
11
112
|
// consumed char) anchors it so it doesn't fire inside identifiers like `assign(`
|
|
@@ -35,7 +136,7 @@ const RE_JSRSASIGN_KEYGEN = /KEYUTIL\.generateKeypair\s*\(/g;
|
|
|
35
136
|
const RE_JSRSASIGN_SIGN = /KJUR\.crypto\.(?:Signature|ECDSA)\b/g;
|
|
36
137
|
const RE_NODE_RSA = /new\s+NodeRSA\s*\(/g;
|
|
37
138
|
// secp256k1 — direct @noble/secp256k1 / secp256k1-style API usage in source.
|
|
38
|
-
const RE_SECP256K1 = /\b(?:secp(?:256k1)?|secp)\s*\.\s*(
|
|
139
|
+
const RE_SECP256K1 = /\b(?:secp(?:256k1)?|secp)\s*\.\s*(sign|verify|getPublicKey|getSharedSecret|ecdh|recoverPublicKey)\s*\(/g;
|
|
39
140
|
// JWT/JOSE.
|
|
40
141
|
const RE_JWT_ALG = /['"`](RS(?:256|384|512)|PS(?:256|384|512)|ES(?:256|384|512|256K)|EdDSA)['"`]/g;
|
|
41
142
|
// JOSE ECDH-ES key agreement (HNDL) and COSE algorithm identifiers.
|
|
@@ -46,10 +147,14 @@ const RE_TLS_REJECT = /rejectUnauthorized\s*:\s*false/g;
|
|
|
46
147
|
// Hardened cipher regex: bounded spans (no unbounded `[^'"`]*` straddling the
|
|
47
148
|
// alternation), single-quote-style anchoring removed in favour of {0,256} bounds
|
|
48
149
|
// so worst-case backtracking is linear in the bound, not the file (P0-6).
|
|
49
|
-
//
|
|
50
|
-
//
|
|
51
|
-
//
|
|
52
|
-
|
|
150
|
+
// Skip OpenSSL EXCLUSION syntax — `!MD5` / `:-RC4` / `!ECDHE-RSA-RC4-SHA` DISABLE
|
|
151
|
+
// those ciphers, so a hardened list must not be flagged as weak (audit: crypto #7). The
|
|
152
|
+
// single variable-length lookbehind walks back over the suite-name chars to the
|
|
153
|
+
// ELEMENT boundary (`:` / whitespace / comma / quote / start) and rejects the match
|
|
154
|
+
// when that element begins with `!` or `-` — covering a bare `!RC4`, a space-separated
|
|
155
|
+
// `HIGH !RC4`, and a full-suite `!ECDHE-RSA-RC4-SHA`. The intra-name hyphen in a
|
|
156
|
+
// genuinely-enabled `ECDHE-RSA-RC4-SHA` (no leading `!`/`-`) still matches.
|
|
157
|
+
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;
|
|
53
158
|
/* -------------------------------------------------------------------------- */
|
|
54
159
|
/* Node.js `crypto` module */
|
|
55
160
|
/* -------------------------------------------------------------------------- */
|
|
@@ -177,52 +282,41 @@ const nodeCryptoDetector = {
|
|
|
177
282
|
const findings = [];
|
|
178
283
|
// generateKeyPair(Sync)('rsa' | 'ec' | 'dsa' | 'dh' | 'x25519' | 'ed25519', ...)
|
|
179
284
|
eachMatch(RE_GENERATE_KEYPAIR, content, (m) => {
|
|
180
|
-
|
|
181
|
-
const map = {
|
|
182
|
-
rsa: { algo: "RSA", cat: "kem", sev: "high", hndl: true, label: "RSA" },
|
|
183
|
-
// RSA-PSS is signature-only, so classify it as a (forgeable) signature
|
|
184
|
-
// rather than a KEM — no HNDL confidentiality exposure.
|
|
185
|
-
"rsa-pss": {
|
|
186
|
-
algo: "RSA",
|
|
187
|
-
cat: "signature",
|
|
188
|
-
sev: "high",
|
|
189
|
-
hndl: false,
|
|
190
|
-
label: "RSA-PSS",
|
|
191
|
-
message: "Generates a classical RSA-PSS signing key, which is forgeable by a quantum attacker.",
|
|
192
|
-
remediation: "ML-DSA-65 (FIPS 204) or SLH-DSA (FIPS 205)",
|
|
193
|
-
},
|
|
194
|
-
// EC keys feed BOTH ECDSA (sign) and ECDH (key agreement). ECDH is
|
|
195
|
-
// HNDL-exposed, so classify conservatively as key-exchange-capable and
|
|
196
|
-
// surface both concerns rather than asserting signature-only (P0-4).
|
|
197
|
-
ec: {
|
|
198
|
-
algo: "ECDH",
|
|
199
|
-
cat: "key-exchange",
|
|
200
|
-
sev: "high",
|
|
201
|
-
hndl: true,
|
|
202
|
-
label: "EC (ECDSA/ECDH)",
|
|
203
|
-
message: "Generates a classical EC key pair. EC keys feed BOTH ECDSA signatures " +
|
|
204
|
-
"and ECDH key agreement; the ECDH path is harvest-now-decrypt-later exposed.",
|
|
205
|
-
remediation: "For key agreement: hybrid X25519MLKEM768 (ML-KEM-768). For signatures: ML-DSA-65 (FIPS 204).",
|
|
206
|
-
},
|
|
207
|
-
dsa: { algo: "DSA", cat: "signature", sev: "high", hndl: false, label: "DSA" },
|
|
208
|
-
dh: { algo: "DH", cat: "key-exchange", sev: "high", hndl: true, label: "Diffie-Hellman" },
|
|
209
|
-
x25519: { algo: "X25519", cat: "key-exchange", sev: "medium", hndl: true, label: "X25519" },
|
|
210
|
-
x448: { algo: "X448", cat: "key-exchange", sev: "medium", hndl: true, label: "X448" },
|
|
211
|
-
ed25519: { algo: "EdDSA", cat: "signature", sev: "low", hndl: false, label: "Ed25519" },
|
|
212
|
-
ed448: { algo: "EdDSA", cat: "signature", sev: "low", hndl: false, label: "Ed448" },
|
|
213
|
-
};
|
|
214
|
-
const info = map[type];
|
|
215
|
-
findings.push(findingFromRule(RULE_NODE_KEYGEN, { file, content, index: m.index, matchLength: m[0].length }, {
|
|
216
|
-
title: `${info.label} key generation`,
|
|
217
|
-
category: info.cat,
|
|
218
|
-
severity: info.sev,
|
|
219
|
-
algorithm: info.algo,
|
|
220
|
-
hndl: info.hndl,
|
|
221
|
-
message: info.message ??
|
|
222
|
-
`Generates a classical ${info.label} key pair, which is not quantum-safe.`,
|
|
223
|
-
...(info.remediation ? { remediation: info.remediation } : {}),
|
|
224
|
-
}));
|
|
285
|
+
pushKeygenFinding(findings, m[1], file, content, m.index, m[0].length);
|
|
225
286
|
});
|
|
287
|
+
// Import-alias resolution: follow `import { generateKeyPairSync as gk }` (and
|
|
288
|
+
// the CommonJS destructure-rename) so an aliased call still detects. Only the
|
|
289
|
+
// keygen / ECDH / DH constructors are resolved — their classification is
|
|
290
|
+
// unambiguous. The alias regexes run on the ORIGINAL content, so locations
|
|
291
|
+
// stay exact; it fires only when the alias was explicitly bound to a known
|
|
292
|
+
// crypto symbol, so precision is unaffected.
|
|
293
|
+
const aliases = collectCryptoAliases(content);
|
|
294
|
+
for (const [canonical, names] of aliases) {
|
|
295
|
+
for (const alias of names) {
|
|
296
|
+
const a = escapeRe(alias);
|
|
297
|
+
if (canonical === "generateKeyPairSync" || canonical === "generateKeyPair") {
|
|
298
|
+
const re = new RegExp(`\\b${a}\\s*\\(\\s*['"\`](rsa-pss|rsa|ec|dsa|dh|x25519|x448|ed25519|ed448)['"\`]`, "g");
|
|
299
|
+
eachMatch(re, content, (m) => pushKeygenFinding(findings, m[1], file, content, m.index, m[0].length));
|
|
300
|
+
}
|
|
301
|
+
else if (canonical === "createECDH") {
|
|
302
|
+
eachMatch(new RegExp(`\\b${a}\\s*\\(`, "g"), content, (m) => findings.push(findingFromRule(RULE_NODE_ECDH, {
|
|
303
|
+
file,
|
|
304
|
+
content,
|
|
305
|
+
index: m.index,
|
|
306
|
+
matchLength: m[0].length,
|
|
307
|
+
})));
|
|
308
|
+
}
|
|
309
|
+
else {
|
|
310
|
+
// createDiffieHellman / createDiffieHellmanGroup.
|
|
311
|
+
eachMatch(new RegExp(`\\b${a}\\s*\\(`, "g"), content, (m) => findings.push(findingFromRule(RULE_NODE_DH, {
|
|
312
|
+
file,
|
|
313
|
+
content,
|
|
314
|
+
index: m.index,
|
|
315
|
+
matchLength: m[0].length,
|
|
316
|
+
})));
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
}
|
|
226
320
|
// createSign / createVerify — RSA / ECDSA / DSA signatures.
|
|
227
321
|
eachMatch(RE_CREATE_SIGN_VERIFY, content, (m) => {
|
|
228
322
|
findings.push(findingFromRule(RULE_NODE_SIGN, {
|
|
@@ -343,7 +437,9 @@ const webCryptoDetector = {
|
|
|
343
437
|
algorithm = name === "X448" ? "X448" : "X25519";
|
|
344
438
|
category = "key-exchange";
|
|
345
439
|
hndl = true;
|
|
346
|
-
|
|
440
|
+
// `medium`, aligned with X25519/X448 key agreement in node/rust/go — HNDL-exposed
|
|
441
|
+
// key agreement scores the same regardless of which surface uses it.
|
|
442
|
+
severity = "medium";
|
|
347
443
|
}
|
|
348
444
|
else if (name === "ED25519" || name === "ED448") {
|
|
349
445
|
algorithm = "EdDSA";
|
|
@@ -485,7 +581,21 @@ const libraryDetector = {
|
|
|
485
581
|
add(RE_FORGE_RSA, RULE_FORGE_RSA); // node-forge: pki.rsa.generateKeyPair(...)
|
|
486
582
|
add(RE_FORGE_ED25519, RULE_FORGE_ED25519); // node-forge: forge.ed25519.*
|
|
487
583
|
add(RE_ELLIPTIC_EC, RULE_ELLIPTIC_EC); // elliptic: new EC('secp256k1')
|
|
488
|
-
|
|
584
|
+
// secp256k1: classify per method — getSharedSecret/ecdh are key AGREEMENT
|
|
585
|
+
// (ECDH, harvest-now-decrypt-later); sign/verify/getPublicKey/recover are
|
|
586
|
+
// ECDSA signatures (not HNDL). The old rule flagged everything as ECDSA.
|
|
587
|
+
eachMatch(RE_SECP256K1, content, (m) => {
|
|
588
|
+
const kex = m[1] === "getSharedSecret" || m[1] === "ecdh";
|
|
589
|
+
findings.push(findingFromRule(RULE_SECP256K1, { file, content, index: m.index, matchLength: m[0].length }, kex
|
|
590
|
+
? {
|
|
591
|
+
title: "secp256k1 ECDH key agreement",
|
|
592
|
+
category: "key-exchange",
|
|
593
|
+
algorithm: "ECDH",
|
|
594
|
+
hndl: true,
|
|
595
|
+
message: `secp256k1 ECDH key agreement (.${m[1]}()) is classical and harvest-now-decrypt-later exposed.`,
|
|
596
|
+
}
|
|
597
|
+
: undefined));
|
|
598
|
+
});
|
|
489
599
|
add(RE_JSRSASIGN_KEYGEN, RULE_JSRSASIGN_KEYGEN); // jsrsasign: KEYUTIL.generateKeypair(...)
|
|
490
600
|
add(RE_JSRSASIGN_SIGN, RULE_JSRSASIGN_SIGN); // jsrsasign: KJUR.crypto.*
|
|
491
601
|
add(RE_NODE_RSA, RULE_NODE_RSA_LIB); // node-rsa: new NodeRSA(...)
|
|
@@ -555,8 +665,21 @@ const jwtDetector = {
|
|
|
555
665
|
appliesTo: (f) => hasExtension(f, JWT_HOST_EXTENSIONS),
|
|
556
666
|
detect({ file, content }) {
|
|
557
667
|
const findings = [];
|
|
668
|
+
// Deferral guards against double-counting:
|
|
669
|
+
// - webCryptoDetector owns a quoted `RSA-OAEP`/`ECDH-ES` sitting next to a
|
|
670
|
+
// `subtle.*(` call (it matches the `RSA-OAEP`/`ECDH` name). Collect the subtle
|
|
671
|
+
// call offsets so the JOSE key-management loops can skip those. (In non-JS host
|
|
672
|
+
// files there are no subtle calls, so nothing is skipped.)
|
|
673
|
+
// - jwkDetector owns any alg/`kty` that lives inside a JWK object; skip a token
|
|
674
|
+
// whose enclosing `{…}` carries a `"kty"` so a JWK's own `alg` is not counted by
|
|
675
|
+
// both detectors. Mirrors the same guard jose.ts applies for other languages.
|
|
676
|
+
const subtleCalls = [];
|
|
677
|
+
eachMatch(RE_SUBTLE_CALL, content, (m) => subtleCalls.push(m.index));
|
|
678
|
+
const inJwk = (index) => enclosingObject(content, index).includes('"kty"');
|
|
558
679
|
// Classical JWS signature alg tokens. Anchored to quotes to avoid words.
|
|
559
680
|
eachMatch(RE_JWT_ALG, content, (m) => {
|
|
681
|
+
if (inJwk(m.index))
|
|
682
|
+
return; // jwk-{rsa,ec,eddsa} owns a JWK's own alg
|
|
560
683
|
const alg = m[1];
|
|
561
684
|
let algorithm;
|
|
562
685
|
if (alg.startsWith("RS") || alg.startsWith("PS"))
|
|
@@ -573,6 +696,10 @@ const jwtDetector = {
|
|
|
573
696
|
});
|
|
574
697
|
// JOSE ECDH-ES key agreement (and ECDH-ES+A*KW) — confidentiality, HNDL.
|
|
575
698
|
eachMatch(RE_JOSE_ECDH, content, (m) => {
|
|
699
|
+
if (inJwk(m.index))
|
|
700
|
+
return; // jwk-ec owns a JWK's own alg
|
|
701
|
+
if (nearSortedCall(subtleCalls, m.index, 400))
|
|
702
|
+
return; // webcrypto owns it
|
|
576
703
|
findings.push(findingFromRule(RULE_JOSE_ECDH, { file, content, index: m.index, matchLength: m[0].length }, {
|
|
577
704
|
title: `JOSE key agreement ${m[1]}`,
|
|
578
705
|
message: `JOSE "${m[1]}" performs classical ECDH key agreement — harvest-now-decrypt-later exposed.`,
|
|
@@ -580,6 +707,12 @@ const jwtDetector = {
|
|
|
580
707
|
});
|
|
581
708
|
// JOSE RSA key transport (RSA-OAEP / RSA1_5) — classical RSA encryption, HNDL.
|
|
582
709
|
eachMatch(RE_JOSE_KEM, content, (m) => {
|
|
710
|
+
if (inJwk(m.index))
|
|
711
|
+
return; // jwk-rsa owns a JWK's own alg
|
|
712
|
+
// webCryptoDetector's regex matches `RSA-OAEP` but NOT `RSA1_5`, so only defer the
|
|
713
|
+
// OAEP form near a subtle call — deferring RSA1_5 would drop it entirely.
|
|
714
|
+
if (m[1].startsWith("RSA-OAEP") && nearSortedCall(subtleCalls, m.index, 400))
|
|
715
|
+
return;
|
|
583
716
|
findings.push(findingFromRule(RULE_JOSE_RSA_OAEP, { file, content, index: m.index, matchLength: m[0].length }, {
|
|
584
717
|
title: `JOSE RSA key transport ${m[1]}`,
|
|
585
718
|
message: `JOSE "${m[1]}" is classical RSA key transport — harvest-now-decrypt-later exposed.`,
|
|
@@ -678,6 +811,55 @@ const RE_CERT_SIG_ALG = /\b(sha(?:1|256|384|512)WithRSAEncryption|ecdsa-with-SHA
|
|
|
678
811
|
// Shor-broken key agreement — the harvest-now-decrypt-later surface a lexical
|
|
679
812
|
// scan misses when only the `crypto/*` API is matched, not the negotiated kex.
|
|
680
813
|
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;
|
|
814
|
+
/**
|
|
815
|
+
* True when the match at `index` sits inside a DOCUMENTATION field value — a
|
|
816
|
+
* `description` / `help` / `summary` / `doc` / `comment` / `note` assignment
|
|
817
|
+
* (`key = "…"` or `key: "…"`), as used by Terraform/Packer variable descriptions,
|
|
818
|
+
* JSON-schema docs, etc. Classical KEX/cipher algorithm names LISTED in such a field
|
|
819
|
+
* are prose ("acceptable values include ecdh-sha2-nistp256, …"), not an active
|
|
820
|
+
* setting, so a transport-token rule must not fire on them. Scans the current line's
|
|
821
|
+
* prefix only (cheap; the common single-line description case).
|
|
822
|
+
*/
|
|
823
|
+
function inDocFieldValue(content, index) {
|
|
824
|
+
const lineStart = content.lastIndexOf("\n", index - 1) + 1;
|
|
825
|
+
const prefix = content.slice(lineStart, index);
|
|
826
|
+
return /\b(?:description|help|summary|doc|comment|note)\b\s*[:=]/i.test(prefix);
|
|
827
|
+
}
|
|
828
|
+
// A bare key-type token (`ssh-rsa`, `ssh-ed25519`, …) is real SSH crypto only
|
|
829
|
+
// when it is EITHER (a) followed by base64 key material — an actual
|
|
830
|
+
// authorized_keys / known_hosts entry — OR (b) one of ≥2 DISTINCT ssh key/host-key
|
|
831
|
+
// algorithm tokens on the same line — an algorithm-preference list
|
|
832
|
+
// (`HostKeyAlgorithms ssh-ed25519,ecdsa-sha2-nistp256,ssh-rsa`, `%w[…]`, …). A
|
|
833
|
+
// lone token, e.g. a UI label / i18n value like `"ssh-rsa": "ssh-rsa"`, is
|
|
834
|
+
// neither, so it must NOT be flagged. `RE_SSH_ALGO_TOKEN` is a separate (cloned-
|
|
835
|
+
// on-use) copy so counting per line never disturbs the outer `eachMatch` state.
|
|
836
|
+
const RE_SSH_ALGO_TOKEN = /\b(ssh-rsa|ssh-ed25519|ssh-dss|ecdsa-sha2-nistp(?:256|384|521))\b/g;
|
|
837
|
+
/** How far around a token we look for a sibling algorithm on the same line. A
|
|
838
|
+
* real `HostKeyAlgorithms` list is short; bounding the window keeps the per-token
|
|
839
|
+
* work O(1) so a pathological single huge line can't make detection quadratic. */
|
|
840
|
+
const SSH_LINE_WINDOW = 512;
|
|
841
|
+
function isRealSshKeyOrAlgoList(content, index, matchLen) {
|
|
842
|
+
// (a) base64 key material immediately after the token → a real SSH public key.
|
|
843
|
+
if (/^\s+[A-Za-z0-9+/]{20,}/.test(content.slice(index + matchLen, index + matchLen + 80))) {
|
|
844
|
+
return true;
|
|
845
|
+
}
|
|
846
|
+
// (b) ≥2 distinct ssh key/host-key algorithm tokens on the same line → a list.
|
|
847
|
+
// Bound both the back-scan for the line start and the window itself so the work
|
|
848
|
+
// is constant per token and never crosses a line boundary (i18n entries live on
|
|
849
|
+
// separate lines, so a lone repeated token stays a single distinct token).
|
|
850
|
+
const from = Math.max(0, index - SSH_LINE_WINDOW);
|
|
851
|
+
const pre = content.slice(from, index);
|
|
852
|
+
const preNl = pre.lastIndexOf("\n");
|
|
853
|
+
const lineStart = preNl === -1 ? from : from + preNl + 1;
|
|
854
|
+
const to = Math.min(content.length, index + matchLen + SSH_LINE_WINDOW);
|
|
855
|
+
const post = content.slice(index, to);
|
|
856
|
+
const postNl = post.indexOf("\n");
|
|
857
|
+
const lineEnd = postNl === -1 ? to : index + postNl;
|
|
858
|
+
const distinct = new Set();
|
|
859
|
+
for (const t of content.slice(lineStart, lineEnd).matchAll(RE_SSH_ALGO_TOKEN))
|
|
860
|
+
distinct.add(t[1]);
|
|
861
|
+
return distinct.size >= 2;
|
|
862
|
+
}
|
|
681
863
|
const RULE_SSH_PUBKEY = {
|
|
682
864
|
id: "ssh-public-key",
|
|
683
865
|
title: "Classical SSH public key",
|
|
@@ -737,6 +919,10 @@ const sshCertDetector = {
|
|
|
737
919
|
const findings = [];
|
|
738
920
|
// SSH public keys: ssh-rsa AAAA…, ecdsa-sha2-nistp256 …, ssh-ed25519 …
|
|
739
921
|
eachMatch(RE_SSH_PUBKEY, content, (m) => {
|
|
922
|
+
// Skip a bare token that is neither a real key nor an algorithm list (e.g.
|
|
923
|
+
// a `"ssh-rsa": "ssh-rsa"` i18n label) — see `isRealSshKeyOrAlgoList`.
|
|
924
|
+
if (!isRealSshKeyOrAlgoList(content, m.index, m[0].length))
|
|
925
|
+
return;
|
|
740
926
|
const tok = m[1];
|
|
741
927
|
const algorithm = tok.startsWith("ssh-rsa")
|
|
742
928
|
? "RSA"
|
|
@@ -767,6 +953,8 @@ const sshCertDetector = {
|
|
|
767
953
|
});
|
|
768
954
|
// SSH key-exchange algorithm identifiers (finite-field DH / ECDH / X25519).
|
|
769
955
|
eachMatch(RE_SSH_KEX, content, (m) => {
|
|
956
|
+
if (inDocFieldValue(content, m.index))
|
|
957
|
+
return; // names listed in a doc string
|
|
770
958
|
const tok = m[1];
|
|
771
959
|
const algorithm = tok.startsWith("diffie-hellman")
|
|
772
960
|
? "DH"
|
|
@@ -822,6 +1010,8 @@ const tlsClassicalKexDetector = {
|
|
|
822
1010
|
detect({ file, content }) {
|
|
823
1011
|
const findings = [];
|
|
824
1012
|
eachMatch(RE_TLS_CLASSICAL_KEX, content, (m) => {
|
|
1013
|
+
if (inDocFieldValue(content, m.index))
|
|
1014
|
+
return; // names listed in a doc string
|
|
825
1015
|
const tok = m[0];
|
|
826
1016
|
const algorithm = tok.includes("ECDH") ? "ECDH" : "DH";
|
|
827
1017
|
findings.push(findingFromRule(RULE_TLS_CLASSICAL_KEX, { file, content, index: m.index, matchLength: m[0].length }, {
|