@quantakrypto/core 0.4.4 → 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.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/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 +53 -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/index.d.ts +14 -11
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +17 -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/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 +9 -0
- package/dist/report.d.ts.map +1 -1
- package/dist/report.js +31 -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/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 +38 -8
- package/src/cbom.ts +153 -39
- package/src/comments.ts +25 -6
- 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 +48 -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/index.ts +41 -13
- package/src/inventory.ts +15 -7
- package/src/registry.ts +51 -4
- package/src/remediate-pipeline.ts +1 -1
- package/src/remediation.ts +39 -0
- package/src/report.ts +36 -1
- package/src/scan-worker.ts +5 -3
- package/src/scan.ts +41 -6
- package/src/standards-profiles.ts +136 -0
- package/src/types.ts +9 -4
- package/src/version.ts +1 -1
- package/src/vex.ts +122 -0
- package/src/walk.ts +33 -2
package/dist/detectors/php.js
CHANGED
|
@@ -1,13 +1,17 @@
|
|
|
1
1
|
import { PHP_EXTENSIONS, eachMatch, findingFromRule, hasExtension } from "../detect-utils.js";
|
|
2
2
|
import { CWE_BROKEN_CRYPTO } from "../cwe.js";
|
|
3
3
|
const RE_PHP_PKEY_NEW = /\bopenssl_pkey_new\s*\(/g;
|
|
4
|
-
|
|
4
|
+
// public_encrypt/private_decrypt (direct RSA) plus seal/open (RSA-envelope: seal
|
|
5
|
+
// encrypts a random symmetric key under each recipient's RSA public key, open
|
|
6
|
+
// decrypts it with the private key) — all RSA key transport, harvest-now exposed.
|
|
7
|
+
const RE_PHP_RSA_CRYPT = /\bopenssl_(?:public_encrypt|private_decrypt|seal|open)\s*\(/g;
|
|
5
8
|
const RE_PHP_SIGN = /\bopenssl_(?:sign|verify)\s*\(/g;
|
|
6
9
|
// phpseclib3 factory methods. The class names are generic, so require `::createKey`.
|
|
7
10
|
const RE_PHP_SECLIB = /\b(RSA|EC|DSA|DH)::createKey\s*\(/g;
|
|
8
11
|
// libsodium X25519 (box / kx / scalarmult) and Ed25519 (sign) key pairs.
|
|
9
12
|
const RE_PHP_SODIUM_X25519 = /\bsodium_crypto_(?:box|kx)_(?:seed_)?keypair\s*\(|\bsodium_crypto_scalarmult(?:_base)?\s*\(/g;
|
|
10
|
-
|
|
13
|
+
// Also the explicit-algorithm variant sodium_crypto_sign_ed25519_keypair().
|
|
14
|
+
const RE_PHP_SODIUM_ED25519 = /\bsodium_crypto_sign_(?:ed25519_|seed_)?keypair\s*\(/g;
|
|
11
15
|
const HYBRID = "hybrid X25519MLKEM768 (ML-KEM-768) for key agreement; ML-DSA-65 (FIPS 204) to sign";
|
|
12
16
|
const RULE_PHP_KEYGEN = {
|
|
13
17
|
id: "php-openssl-keygen",
|
|
@@ -24,7 +28,7 @@ const RULE_PHP_KEYGEN = {
|
|
|
24
28
|
const RULE_PHP_RSA_CRYPT = {
|
|
25
29
|
id: "php-openssl-rsa-crypt",
|
|
26
30
|
title: "PHP openssl RSA public-key encryption",
|
|
27
|
-
description: "openssl_public_encrypt / openssl_private_decrypt",
|
|
31
|
+
description: "openssl_public_encrypt / openssl_private_decrypt / openssl_seal / openssl_open",
|
|
28
32
|
category: "kem",
|
|
29
33
|
severity: "high",
|
|
30
34
|
confidence: "high",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"php.js","sourceRoot":"","sources":["../../src/detectors/php.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Source-code detector for classical asymmetric cryptography in PHP — one of the\n * most-deployed backend languages, previously uncovered. Handles the three\n * dominant crypto surfaces:\n *\n * - **ext/openssl** — `openssl_pkey_new()` (classified by its\n * `OPENSSL_KEYTYPE_*` config, defaulting to RSA), `openssl_public_encrypt` /\n * `openssl_private_decrypt` (RSA), and `openssl_sign` / `openssl_verify`.\n * - **phpseclib3** — `RSA::createKey()` / `EC::createKey()` / `DSA::createKey()`.\n * - **libsodium** — `sodium_crypto_box`/`kx` keypairs (X25519) and\n * `sodium_crypto_sign` keypairs (Ed25519).\n *\n * Lexical, like the other packs; the `openssl_*` / `sodium_crypto_*` prefixes and\n * `OPENSSL_KEYTYPE_*` constants are distinctive, so the false-positive rate is low.\n *\n * HNDL: RSA encryption and (EC/X)DH key agreement are harvest-now-decrypt-later\n * exposed (hndl:true); ECDSA / DSA / Ed25519 signatures are hndl:false but\n * forgeable. `openssl_pkey_new` EC and `EC::createKey` are ambiguous (feed BOTH\n * ECDSA and ECDH), classified conservatively as key-exchange-capable (hndl:true).\n */\nimport type { Detector, Finding, RuleMeta } from \"../types.js\";\nimport { PHP_EXTENSIONS, eachMatch, findingFromRule, hasExtension } from \"../detect-utils.js\";\nimport { CWE_BROKEN_CRYPTO } from \"../cwe.js\";\n\nconst RE_PHP_PKEY_NEW = /\\bopenssl_pkey_new\\s*\\(/g;\nconst RE_PHP_RSA_CRYPT = /\\bopenssl_(?:public_encrypt|private_decrypt)\\s*\\(/g;\nconst RE_PHP_SIGN = /\\bopenssl_(?:sign|verify)\\s*\\(/g;\n// phpseclib3 factory methods. The class names are generic, so require `::createKey`.\nconst RE_PHP_SECLIB = /\\b(RSA|EC|DSA|DH)::createKey\\s*\\(/g;\n// libsodium X25519 (box / kx / scalarmult) and Ed25519 (sign) key pairs.\nconst RE_PHP_SODIUM_X25519 =\n /\\bsodium_crypto_(?:box|kx)_(?:seed_)?keypair\\s*\\(|\\bsodium_crypto_scalarmult(?:_base)?\\s*\\(/g;\nconst RE_PHP_SODIUM_ED25519 = /\\bsodium_crypto_sign_(?:seed_)?keypair\\s*\\(/g;\n\n/** Key-type classification for `openssl_pkey_new`, keyed by its config constant. */\ninterface KeyInfo {\n algo: Finding[\"algorithm\"];\n cat: Finding[\"category\"];\n sev: Finding[\"severity\"];\n hndl: boolean;\n label: string;\n remediation?: string;\n}\nconst HYBRID = \"hybrid X25519MLKEM768 (ML-KEM-768) for key agreement; ML-DSA-65 (FIPS 204) to sign\";\n\nconst RULE_PHP_KEYGEN: RuleMeta = {\n id: \"php-openssl-keygen\",\n title: \"PHP openssl key generation\",\n description: \"openssl_pkey_new (RSA/EC/DSA/DH, by OPENSSL_KEYTYPE_*)\",\n category: \"kem\",\n severity: \"high\",\n confidence: \"high\",\n algorithm: \"RSA\",\n hndl: true,\n cwe: CWE_BROKEN_CRYPTO,\n message: \"Generates a classical key pair via PHP openssl_pkey_new — not quantum-safe.\",\n};\nconst RULE_PHP_RSA_CRYPT: RuleMeta = {\n id: \"php-openssl-rsa-crypt\",\n title: \"PHP openssl RSA public-key encryption\",\n description: \"openssl_public_encrypt / openssl_private_decrypt\",\n category: \"kem\",\n severity: \"high\",\n confidence: \"high\",\n algorithm: \"RSA\",\n hndl: true,\n cwe: CWE_BROKEN_CRYPTO,\n message:\n \"RSA public-key encryption/decryption (PHP openssl) is harvest-now-decrypt-later exposed.\",\n};\nconst RULE_PHP_SIGN: RuleMeta = {\n id: \"php-openssl-sign\",\n title: \"PHP openssl signature\",\n description: \"openssl_sign / openssl_verify\",\n category: \"signature\",\n severity: \"high\",\n confidence: \"high\",\n algorithm: \"unknown\",\n hndl: false,\n cwe: CWE_BROKEN_CRYPTO,\n message:\n \"Classical signature via PHP openssl (RSA/ECDSA/DSA) is forgeable by a quantum attacker.\",\n remediation: \"ML-DSA-65 (FIPS 204) or SLH-DSA (FIPS 205)\",\n};\nconst RULE_PHP_SECLIB: RuleMeta = {\n id: \"php-phpseclib-keygen\",\n title: \"PHP phpseclib key generation\",\n description: \"phpseclib3 RSA/EC/DSA/DH ::createKey\",\n category: \"kem\",\n severity: \"high\",\n confidence: \"medium\",\n algorithm: \"RSA\",\n hndl: true,\n cwe: CWE_BROKEN_CRYPTO,\n message: \"Generates a classical key pair via phpseclib3 (createKey) — not quantum-safe.\",\n};\nconst RULE_PHP_SODIUM_X25519: RuleMeta = {\n id: \"php-sodium-x25519\",\n title: \"PHP libsodium X25519 key agreement\",\n description: \"sodium_crypto_box/kx keypair + scalarmult\",\n category: \"key-exchange\",\n severity: \"medium\",\n confidence: \"high\",\n algorithm: \"X25519\",\n hndl: true,\n cwe: CWE_BROKEN_CRYPTO,\n message:\n \"libsodium crypto_box/kx (PHP) uses X25519 key agreement — modern but classical, harvest-now-decrypt-later exposed.\",\n};\nconst RULE_PHP_SODIUM_ED25519: RuleMeta = {\n id: \"php-sodium-ed25519\",\n title: \"PHP libsodium Ed25519 signature\",\n description: \"sodium_crypto_sign keypair\",\n category: \"signature\",\n severity: \"low\",\n confidence: \"high\",\n algorithm: \"EdDSA\",\n hndl: false,\n cwe: CWE_BROKEN_CRYPTO,\n message:\n \"libsodium crypto_sign (PHP) uses Ed25519 — classical and forgeable by a quantum attacker.\",\n};\n\n/** phpseclib class name → its classification. */\nconst SECLIB_INFO: Record<string, KeyInfo> = {\n RSA: { algo: \"RSA\", cat: \"kem\", sev: \"high\", hndl: true, label: \"RSA\" },\n EC: {\n algo: \"ECDH\",\n cat: \"key-exchange\",\n sev: \"high\",\n hndl: true,\n label: \"EC (ECDSA/ECDH)\",\n remediation: HYBRID,\n },\n DSA: { algo: \"DSA\", cat: \"signature\", sev: \"high\", hndl: false, label: \"DSA\" },\n DH: { algo: \"DH\", cat: \"key-exchange\", sev: \"high\", hndl: true, label: \"Diffie-Hellman\" },\n};\n\n/** Classify `openssl_pkey_new` by the `OPENSSL_KEYTYPE_*` constant in its config.\n * The window is bounded to the current statement (up to the next `;`, capped at\n * 300 chars) so it can't bleed into a following `openssl_pkey_new` call's key\n * type. Defaults to RSA — openssl_pkey_new's own default private_key_type is RSA\n * when none is given. */\nfunction classifyPkeyNew(content: string, index: number): KeyInfo {\n const semi = content.indexOf(\";\", index);\n const end = Math.min(index + 300, semi === -1 ? content.length : semi);\n const w = content.slice(index, end);\n if (/\\bOPENSSL_KEYTYPE_EC\\b/.test(w)) return SECLIB_INFO.EC;\n if (/\\bOPENSSL_KEYTYPE_DSA\\b/.test(w)) return SECLIB_INFO.DSA;\n if (/\\bOPENSSL_KEYTYPE_DH\\b/.test(w)) return SECLIB_INFO.DH;\n return SECLIB_INFO.RSA;\n}\n\n/** Detects classical asymmetric crypto in PHP (openssl, phpseclib, libsodium). */\nexport const phpDetector: Detector = {\n id: \"php-crypto\",\n description: \"Classical asymmetric crypto in PHP (openssl, phpseclib3, libsodium)\",\n scope: \"source\",\n language: \"php\",\n rules: [\n RULE_PHP_KEYGEN,\n RULE_PHP_RSA_CRYPT,\n RULE_PHP_SIGN,\n RULE_PHP_SECLIB,\n RULE_PHP_SODIUM_X25519,\n RULE_PHP_SODIUM_ED25519,\n ],\n appliesTo: (f) => hasExtension(f, PHP_EXTENSIONS),\n detect({ file, content }): Finding[] {\n const findings: Finding[] = [];\n const add = (re: RegExp, rule: RuleMeta): void =>\n eachMatch(re, content, (m) =>\n findings.push(\n findingFromRule(rule, { file, content, index: m.index, matchLength: m[0].length }),\n ),\n );\n\n // openssl_pkey_new — one finding per call, classified by its key-type config.\n eachMatch(RE_PHP_PKEY_NEW, content, (m) => {\n const info = classifyPkeyNew(content, m.index);\n findings.push(\n findingFromRule(\n RULE_PHP_KEYGEN,\n { file, content, index: m.index, matchLength: m[0].length },\n {\n title: `PHP openssl ${info.label} key generation`,\n category: info.cat,\n severity: info.sev,\n algorithm: info.algo,\n hndl: info.hndl,\n message: `Generates a classical ${info.label} key pair via PHP openssl_pkey_new — not quantum-safe.`,\n ...(info.remediation ? { remediation: info.remediation } : {}),\n },\n ),\n );\n });\n\n add(RE_PHP_RSA_CRYPT, RULE_PHP_RSA_CRYPT);\n add(RE_PHP_SIGN, RULE_PHP_SIGN);\n\n // phpseclib3 ::createKey — classify by the class (RSA/EC/DSA/DH).\n eachMatch(RE_PHP_SECLIB, content, (m) => {\n const info = SECLIB_INFO[m[1]];\n findings.push(\n findingFromRule(\n RULE_PHP_SECLIB,\n { file, content, index: m.index, matchLength: m[0].length },\n {\n title: `PHP phpseclib ${info.label} key generation`,\n category: info.cat,\n severity: info.sev,\n algorithm: info.algo,\n hndl: info.hndl,\n message: `Generates a classical ${info.label} key pair via phpseclib3 createKey — not quantum-safe.`,\n ...(info.remediation ? { remediation: info.remediation } : {}),\n },\n ),\n );\n });\n\n add(RE_PHP_SODIUM_X25519, RULE_PHP_SODIUM_X25519);\n add(RE_PHP_SODIUM_ED25519, RULE_PHP_SODIUM_ED25519);\n return findings;\n },\n};\n"]}
|
|
1
|
+
{"version":3,"file":"php.js","sourceRoot":"","sources":["../../src/detectors/php.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Source-code detector for classical asymmetric cryptography in PHP — one of the\n * most-deployed backend languages, previously uncovered. Handles the three\n * dominant crypto surfaces:\n *\n * - **ext/openssl** — `openssl_pkey_new()` (classified by its\n * `OPENSSL_KEYTYPE_*` config, defaulting to RSA), `openssl_public_encrypt` /\n * `openssl_private_decrypt` / `openssl_seal` / `openssl_open` (RSA), and\n * `openssl_sign` / `openssl_verify`.\n * - **phpseclib3** — `RSA::createKey()` / `EC::createKey()` / `DSA::createKey()`.\n * - **libsodium** — `sodium_crypto_box`/`kx` keypairs (X25519) and\n * `sodium_crypto_sign` keypairs (Ed25519).\n *\n * Lexical, like the other packs; the `openssl_*` / `sodium_crypto_*` prefixes and\n * `OPENSSL_KEYTYPE_*` constants are distinctive, so the false-positive rate is low.\n *\n * HNDL: RSA encryption and (EC/X)DH key agreement are harvest-now-decrypt-later\n * exposed (hndl:true); ECDSA / DSA / Ed25519 signatures are hndl:false but\n * forgeable. `openssl_pkey_new` EC and `EC::createKey` are ambiguous (feed BOTH\n * ECDSA and ECDH), classified conservatively as key-exchange-capable (hndl:true).\n */\nimport type { Detector, Finding, RuleMeta } from \"../types.js\";\nimport { PHP_EXTENSIONS, eachMatch, findingFromRule, hasExtension } from \"../detect-utils.js\";\nimport { CWE_BROKEN_CRYPTO } from \"../cwe.js\";\n\nconst RE_PHP_PKEY_NEW = /\\bopenssl_pkey_new\\s*\\(/g;\n// public_encrypt/private_decrypt (direct RSA) plus seal/open (RSA-envelope: seal\n// encrypts a random symmetric key under each recipient's RSA public key, open\n// decrypts it with the private key) — all RSA key transport, harvest-now exposed.\nconst RE_PHP_RSA_CRYPT = /\\bopenssl_(?:public_encrypt|private_decrypt|seal|open)\\s*\\(/g;\nconst RE_PHP_SIGN = /\\bopenssl_(?:sign|verify)\\s*\\(/g;\n// phpseclib3 factory methods. The class names are generic, so require `::createKey`.\nconst RE_PHP_SECLIB = /\\b(RSA|EC|DSA|DH)::createKey\\s*\\(/g;\n// libsodium X25519 (box / kx / scalarmult) and Ed25519 (sign) key pairs.\nconst RE_PHP_SODIUM_X25519 =\n /\\bsodium_crypto_(?:box|kx)_(?:seed_)?keypair\\s*\\(|\\bsodium_crypto_scalarmult(?:_base)?\\s*\\(/g;\n// Also the explicit-algorithm variant sodium_crypto_sign_ed25519_keypair().\nconst RE_PHP_SODIUM_ED25519 = /\\bsodium_crypto_sign_(?:ed25519_|seed_)?keypair\\s*\\(/g;\n\n/** Key-type classification for `openssl_pkey_new`, keyed by its config constant. */\ninterface KeyInfo {\n algo: Finding[\"algorithm\"];\n cat: Finding[\"category\"];\n sev: Finding[\"severity\"];\n hndl: boolean;\n label: string;\n remediation?: string;\n}\nconst HYBRID = \"hybrid X25519MLKEM768 (ML-KEM-768) for key agreement; ML-DSA-65 (FIPS 204) to sign\";\n\nconst RULE_PHP_KEYGEN: RuleMeta = {\n id: \"php-openssl-keygen\",\n title: \"PHP openssl key generation\",\n description: \"openssl_pkey_new (RSA/EC/DSA/DH, by OPENSSL_KEYTYPE_*)\",\n category: \"kem\",\n severity: \"high\",\n confidence: \"high\",\n algorithm: \"RSA\",\n hndl: true,\n cwe: CWE_BROKEN_CRYPTO,\n message: \"Generates a classical key pair via PHP openssl_pkey_new — not quantum-safe.\",\n};\nconst RULE_PHP_RSA_CRYPT: RuleMeta = {\n id: \"php-openssl-rsa-crypt\",\n title: \"PHP openssl RSA public-key encryption\",\n description: \"openssl_public_encrypt / openssl_private_decrypt / openssl_seal / openssl_open\",\n category: \"kem\",\n severity: \"high\",\n confidence: \"high\",\n algorithm: \"RSA\",\n hndl: true,\n cwe: CWE_BROKEN_CRYPTO,\n message:\n \"RSA public-key encryption/decryption (PHP openssl) is harvest-now-decrypt-later exposed.\",\n};\nconst RULE_PHP_SIGN: RuleMeta = {\n id: \"php-openssl-sign\",\n title: \"PHP openssl signature\",\n description: \"openssl_sign / openssl_verify\",\n category: \"signature\",\n severity: \"high\",\n confidence: \"high\",\n algorithm: \"unknown\",\n hndl: false,\n cwe: CWE_BROKEN_CRYPTO,\n message:\n \"Classical signature via PHP openssl (RSA/ECDSA/DSA) is forgeable by a quantum attacker.\",\n remediation: \"ML-DSA-65 (FIPS 204) or SLH-DSA (FIPS 205)\",\n};\nconst RULE_PHP_SECLIB: RuleMeta = {\n id: \"php-phpseclib-keygen\",\n title: \"PHP phpseclib key generation\",\n description: \"phpseclib3 RSA/EC/DSA/DH ::createKey\",\n category: \"kem\",\n severity: \"high\",\n confidence: \"medium\",\n algorithm: \"RSA\",\n hndl: true,\n cwe: CWE_BROKEN_CRYPTO,\n message: \"Generates a classical key pair via phpseclib3 (createKey) — not quantum-safe.\",\n};\nconst RULE_PHP_SODIUM_X25519: RuleMeta = {\n id: \"php-sodium-x25519\",\n title: \"PHP libsodium X25519 key agreement\",\n description: \"sodium_crypto_box/kx keypair + scalarmult\",\n category: \"key-exchange\",\n severity: \"medium\",\n confidence: \"high\",\n algorithm: \"X25519\",\n hndl: true,\n cwe: CWE_BROKEN_CRYPTO,\n message:\n \"libsodium crypto_box/kx (PHP) uses X25519 key agreement — modern but classical, harvest-now-decrypt-later exposed.\",\n};\nconst RULE_PHP_SODIUM_ED25519: RuleMeta = {\n id: \"php-sodium-ed25519\",\n title: \"PHP libsodium Ed25519 signature\",\n description: \"sodium_crypto_sign keypair\",\n category: \"signature\",\n severity: \"low\",\n confidence: \"high\",\n algorithm: \"EdDSA\",\n hndl: false,\n cwe: CWE_BROKEN_CRYPTO,\n message:\n \"libsodium crypto_sign (PHP) uses Ed25519 — classical and forgeable by a quantum attacker.\",\n};\n\n/** phpseclib class name → its classification. */\nconst SECLIB_INFO: Record<string, KeyInfo> = {\n RSA: { algo: \"RSA\", cat: \"kem\", sev: \"high\", hndl: true, label: \"RSA\" },\n EC: {\n algo: \"ECDH\",\n cat: \"key-exchange\",\n sev: \"high\",\n hndl: true,\n label: \"EC (ECDSA/ECDH)\",\n remediation: HYBRID,\n },\n DSA: { algo: \"DSA\", cat: \"signature\", sev: \"high\", hndl: false, label: \"DSA\" },\n DH: { algo: \"DH\", cat: \"key-exchange\", sev: \"high\", hndl: true, label: \"Diffie-Hellman\" },\n};\n\n/** Classify `openssl_pkey_new` by the `OPENSSL_KEYTYPE_*` constant in its config.\n * The window is bounded to the current statement (up to the next `;`, capped at\n * 300 chars) so it can't bleed into a following `openssl_pkey_new` call's key\n * type. Defaults to RSA — openssl_pkey_new's own default private_key_type is RSA\n * when none is given. */\nfunction classifyPkeyNew(content: string, index: number): KeyInfo {\n const semi = content.indexOf(\";\", index);\n const end = Math.min(index + 300, semi === -1 ? content.length : semi);\n const w = content.slice(index, end);\n if (/\\bOPENSSL_KEYTYPE_EC\\b/.test(w)) return SECLIB_INFO.EC;\n if (/\\bOPENSSL_KEYTYPE_DSA\\b/.test(w)) return SECLIB_INFO.DSA;\n if (/\\bOPENSSL_KEYTYPE_DH\\b/.test(w)) return SECLIB_INFO.DH;\n return SECLIB_INFO.RSA;\n}\n\n/** Detects classical asymmetric crypto in PHP (openssl, phpseclib, libsodium). */\nexport const phpDetector: Detector = {\n id: \"php-crypto\",\n description: \"Classical asymmetric crypto in PHP (openssl, phpseclib3, libsodium)\",\n scope: \"source\",\n language: \"php\",\n rules: [\n RULE_PHP_KEYGEN,\n RULE_PHP_RSA_CRYPT,\n RULE_PHP_SIGN,\n RULE_PHP_SECLIB,\n RULE_PHP_SODIUM_X25519,\n RULE_PHP_SODIUM_ED25519,\n ],\n appliesTo: (f) => hasExtension(f, PHP_EXTENSIONS),\n detect({ file, content }): Finding[] {\n const findings: Finding[] = [];\n const add = (re: RegExp, rule: RuleMeta): void =>\n eachMatch(re, content, (m) =>\n findings.push(\n findingFromRule(rule, { file, content, index: m.index, matchLength: m[0].length }),\n ),\n );\n\n // openssl_pkey_new — one finding per call, classified by its key-type config.\n eachMatch(RE_PHP_PKEY_NEW, content, (m) => {\n const info = classifyPkeyNew(content, m.index);\n findings.push(\n findingFromRule(\n RULE_PHP_KEYGEN,\n { file, content, index: m.index, matchLength: m[0].length },\n {\n title: `PHP openssl ${info.label} key generation`,\n category: info.cat,\n severity: info.sev,\n algorithm: info.algo,\n hndl: info.hndl,\n message: `Generates a classical ${info.label} key pair via PHP openssl_pkey_new — not quantum-safe.`,\n ...(info.remediation ? { remediation: info.remediation } : {}),\n },\n ),\n );\n });\n\n add(RE_PHP_RSA_CRYPT, RULE_PHP_RSA_CRYPT);\n add(RE_PHP_SIGN, RULE_PHP_SIGN);\n\n // phpseclib3 ::createKey — classify by the class (RSA/EC/DSA/DH).\n eachMatch(RE_PHP_SECLIB, content, (m) => {\n const info = SECLIB_INFO[m[1]];\n findings.push(\n findingFromRule(\n RULE_PHP_SECLIB,\n { file, content, index: m.index, matchLength: m[0].length },\n {\n title: `PHP phpseclib ${info.label} key generation`,\n category: info.cat,\n severity: info.sev,\n algorithm: info.algo,\n hndl: info.hndl,\n message: `Generates a classical ${info.label} key pair via phpseclib3 createKey — not quantum-safe.`,\n ...(info.remediation ? { remediation: info.remediation } : {}),\n },\n ),\n );\n });\n\n add(RE_PHP_SODIUM_X25519, RULE_PHP_SODIUM_X25519);\n add(RE_PHP_SODIUM_ED25519, RULE_PHP_SODIUM_ED25519);\n return findings;\n },\n};\n"]}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Config detector: classical asymmetric keys held in / generated by a PKCS#11 HSM
|
|
3
|
+
* or token. HSMs hold an organization's longest-lived roots (CA keys, code-signing
|
|
4
|
+
* keys), so they are exactly the keys a PQC migration must find first — yet they
|
|
5
|
+
* live behind a PKCS#11 layer the source packs never see.
|
|
6
|
+
*
|
|
7
|
+
* Covered, distinctive lexical surfaces:
|
|
8
|
+
* - `pkcs11-tool --keypairgen --key-type rsa:2048 | EC:secp256r1` — the standard
|
|
9
|
+
* OpenSC keygen invocation recorded in ops scripts / runbooks.
|
|
10
|
+
* - `CKM_RSA_PKCS(_KEY_PAIR_GEN|_OAEP|_PSS)` / `CKM_EC(DSA|DH)` / `CKM_DSA` — the
|
|
11
|
+
* PKCS#11 mechanism constants (SoftHSM, SunPKCS11, pkcs11js, python-pkcs11), which
|
|
12
|
+
* name the exact algorithm.
|
|
13
|
+
*/
|
|
14
|
+
import type { Detector } from "../types.js";
|
|
15
|
+
/** Detects classical asymmetric keys behind a PKCS#11 HSM / token. */
|
|
16
|
+
export declare const pkcs11Detector: Detector;
|
|
17
|
+
//# sourceMappingURL=pkcs11.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pkcs11.d.ts","sourceRoot":"","sources":["../../src/detectors/pkcs11.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,OAAO,KAAK,EAAE,QAAQ,EAAqB,MAAM,aAAa,CAAC;AAuE/D,sEAAsE;AACtE,eAAO,MAAM,cAAc,EAAE,QA2B5B,CAAC"}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { DOC_EXTENSIONS, eachMatch, findingFromRule, hasExtension } from "../detect-utils.js";
|
|
2
|
+
import { CWE_BROKEN_CRYPTO } from "../cwe.js";
|
|
3
|
+
// `pkcs11-tool --keypairgen --key-type rsa:2048` / `EC:secp256r1` (OpenSC).
|
|
4
|
+
const RE_PKCS11_TOOL_RSA = /--key-type\s+rsa:\d+/gi;
|
|
5
|
+
const RE_PKCS11_TOOL_EC = /--key-type\s+EC:[A-Za-z0-9]+/gi;
|
|
6
|
+
// PKCS#11 mechanism constants (CKM_*), which name the algorithm exactly.
|
|
7
|
+
const RE_CKM_RSA = /\bCKM_RSA_(?:PKCS(?:_KEY_PAIR_GEN|_OAEP|_PSS)?|X_509|9796)\b/g;
|
|
8
|
+
const RE_CKM_EC = /\bCKM_EC(?:DSA(?:_SHA\d+)?|DH1?_DERIVE|_KEY_PAIR_GEN)\b/g;
|
|
9
|
+
const RE_CKM_DSA = /\bCKM_DSA(?:_SHA\d+|_KEY_PAIR_GEN)?\b/g;
|
|
10
|
+
const RE_CKM_DH = /\bCKM_DH_PKCS_(?:KEY_PAIR_GEN|DERIVE)\b/g;
|
|
11
|
+
const RULE_PKCS11_RSA = {
|
|
12
|
+
id: "pkcs11-rsa",
|
|
13
|
+
title: "PKCS#11 (HSM) RSA key",
|
|
14
|
+
description: "PKCS#11 RSA key generation / mechanism (pkcs11-tool, CKM_RSA_*)",
|
|
15
|
+
category: "kem",
|
|
16
|
+
severity: "high",
|
|
17
|
+
confidence: "high",
|
|
18
|
+
algorithm: "RSA",
|
|
19
|
+
hndl: true,
|
|
20
|
+
cwe: CWE_BROKEN_CRYPTO,
|
|
21
|
+
message: "A classical RSA key in a PKCS#11 HSM / token — likely a long-lived root (CA / code-signing), and not quantum-safe.",
|
|
22
|
+
remediation: "Inventory HSM-held RSA keys first (longest-lived); plan re-keying to PQC (ML-KEM-768 / ML-DSA-65) as HSM firmware adds support.",
|
|
23
|
+
};
|
|
24
|
+
const RULE_PKCS11_EC = {
|
|
25
|
+
id: "pkcs11-ec",
|
|
26
|
+
title: "PKCS#11 (HSM) EC key",
|
|
27
|
+
description: "PKCS#11 EC key generation / mechanism (CKM_EC*, --key-type EC)",
|
|
28
|
+
category: "key-exchange",
|
|
29
|
+
severity: "high",
|
|
30
|
+
confidence: "high",
|
|
31
|
+
algorithm: "ECDH",
|
|
32
|
+
hndl: true,
|
|
33
|
+
cwe: CWE_BROKEN_CRYPTO,
|
|
34
|
+
message: "A classical EC key in a PKCS#11 HSM / token; EC keys feed ECDSA signatures and ECDH key agreement (the ECDH path is harvest-now-decrypt-later exposed).",
|
|
35
|
+
remediation: "For key agreement: hybrid X25519MLKEM768 (ML-KEM-768). For signatures: ML-DSA-65 (FIPS 204). Prioritise HSM-held keys.",
|
|
36
|
+
};
|
|
37
|
+
const RULE_PKCS11_DSA = {
|
|
38
|
+
id: "pkcs11-dsa",
|
|
39
|
+
title: "PKCS#11 (HSM) DSA key",
|
|
40
|
+
description: "PKCS#11 DSA key generation / mechanism (CKM_DSA*)",
|
|
41
|
+
category: "signature",
|
|
42
|
+
severity: "medium",
|
|
43
|
+
confidence: "high",
|
|
44
|
+
algorithm: "DSA",
|
|
45
|
+
hndl: false,
|
|
46
|
+
cwe: CWE_BROKEN_CRYPTO,
|
|
47
|
+
message: "A classical DSA key in a PKCS#11 HSM / token — deprecated and quantum-forgeable.",
|
|
48
|
+
remediation: "Rotate off DSA; migrate to ML-DSA-65 (FIPS 204).",
|
|
49
|
+
};
|
|
50
|
+
const RULE_PKCS11_DH = {
|
|
51
|
+
id: "pkcs11-dh",
|
|
52
|
+
title: "PKCS#11 (HSM) Diffie-Hellman",
|
|
53
|
+
description: "PKCS#11 finite-field DH key generation / derive (CKM_DH_PKCS_*)",
|
|
54
|
+
category: "key-exchange",
|
|
55
|
+
severity: "medium",
|
|
56
|
+
confidence: "high",
|
|
57
|
+
algorithm: "DH",
|
|
58
|
+
hndl: true,
|
|
59
|
+
cwe: CWE_BROKEN_CRYPTO,
|
|
60
|
+
message: "Finite-field Diffie-Hellman in a PKCS#11 HSM / token; classical key agreement, harvest-now-decrypt-later exposed.",
|
|
61
|
+
remediation: "Migrate key agreement to hybrid X25519MLKEM768 (ML-KEM-768).",
|
|
62
|
+
};
|
|
63
|
+
/** Detects classical asymmetric keys behind a PKCS#11 HSM / token. */
|
|
64
|
+
export const pkcs11Detector = {
|
|
65
|
+
id: "pkcs11-crypto",
|
|
66
|
+
description: "Classical asymmetric keys in a PKCS#11 HSM / token",
|
|
67
|
+
scope: "config",
|
|
68
|
+
language: "any",
|
|
69
|
+
appliesTo: (f) => !hasExtension(f, DOC_EXTENSIONS),
|
|
70
|
+
rules: [RULE_PKCS11_RSA, RULE_PKCS11_EC, RULE_PKCS11_DSA, RULE_PKCS11_DH],
|
|
71
|
+
detect({ file, content }) {
|
|
72
|
+
// Fast reject: only proceed on a distinctive PKCS#11 marker.
|
|
73
|
+
if (!content.includes("CKM_") && !content.includes("--key-type")) {
|
|
74
|
+
return [];
|
|
75
|
+
}
|
|
76
|
+
const findings = [];
|
|
77
|
+
const add = (re, rule) => eachMatch(re, content, (m) => findings.push(findingFromRule(rule, { file, content, index: m.index, matchLength: m[0].length })));
|
|
78
|
+
add(RE_PKCS11_TOOL_RSA, RULE_PKCS11_RSA);
|
|
79
|
+
add(RE_PKCS11_TOOL_EC, RULE_PKCS11_EC);
|
|
80
|
+
add(RE_CKM_RSA, RULE_PKCS11_RSA);
|
|
81
|
+
add(RE_CKM_EC, RULE_PKCS11_EC);
|
|
82
|
+
add(RE_CKM_DSA, RULE_PKCS11_DSA);
|
|
83
|
+
add(RE_CKM_DH, RULE_PKCS11_DH);
|
|
84
|
+
return findings;
|
|
85
|
+
},
|
|
86
|
+
};
|
|
87
|
+
//# sourceMappingURL=pkcs11.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pkcs11.js","sourceRoot":"","sources":["../../src/detectors/pkcs11.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Config detector: classical asymmetric keys held in / generated by a PKCS#11 HSM\n * or token. HSMs hold an organization's longest-lived roots (CA keys, code-signing\n * keys), so they are exactly the keys a PQC migration must find first — yet they\n * live behind a PKCS#11 layer the source packs never see.\n *\n * Covered, distinctive lexical surfaces:\n * - `pkcs11-tool --keypairgen --key-type rsa:2048 | EC:secp256r1` — the standard\n * OpenSC keygen invocation recorded in ops scripts / runbooks.\n * - `CKM_RSA_PKCS(_KEY_PAIR_GEN|_OAEP|_PSS)` / `CKM_EC(DSA|DH)` / `CKM_DSA` — the\n * PKCS#11 mechanism constants (SoftHSM, SunPKCS11, pkcs11js, python-pkcs11), which\n * name the exact algorithm.\n */\nimport type { Detector, Finding, RuleMeta } from \"../types.js\";\nimport { DOC_EXTENSIONS, eachMatch, findingFromRule, hasExtension } from \"../detect-utils.js\";\nimport { CWE_BROKEN_CRYPTO } from \"../cwe.js\";\n\n// `pkcs11-tool --keypairgen --key-type rsa:2048` / `EC:secp256r1` (OpenSC).\nconst RE_PKCS11_TOOL_RSA = /--key-type\\s+rsa:\\d+/gi;\nconst RE_PKCS11_TOOL_EC = /--key-type\\s+EC:[A-Za-z0-9]+/gi;\n// PKCS#11 mechanism constants (CKM_*), which name the algorithm exactly.\nconst RE_CKM_RSA = /\\bCKM_RSA_(?:PKCS(?:_KEY_PAIR_GEN|_OAEP|_PSS)?|X_509|9796)\\b/g;\nconst RE_CKM_EC = /\\bCKM_EC(?:DSA(?:_SHA\\d+)?|DH1?_DERIVE|_KEY_PAIR_GEN)\\b/g;\nconst RE_CKM_DSA = /\\bCKM_DSA(?:_SHA\\d+|_KEY_PAIR_GEN)?\\b/g;\nconst RE_CKM_DH = /\\bCKM_DH_PKCS_(?:KEY_PAIR_GEN|DERIVE)\\b/g;\n\nconst RULE_PKCS11_RSA: RuleMeta = {\n id: \"pkcs11-rsa\",\n title: \"PKCS#11 (HSM) RSA key\",\n description: \"PKCS#11 RSA key generation / mechanism (pkcs11-tool, CKM_RSA_*)\",\n category: \"kem\",\n severity: \"high\",\n confidence: \"high\",\n algorithm: \"RSA\",\n hndl: true,\n cwe: CWE_BROKEN_CRYPTO,\n message:\n \"A classical RSA key in a PKCS#11 HSM / token — likely a long-lived root (CA / code-signing), and not quantum-safe.\",\n remediation:\n \"Inventory HSM-held RSA keys first (longest-lived); plan re-keying to PQC (ML-KEM-768 / ML-DSA-65) as HSM firmware adds support.\",\n};\nconst RULE_PKCS11_EC: RuleMeta = {\n id: \"pkcs11-ec\",\n title: \"PKCS#11 (HSM) EC key\",\n description: \"PKCS#11 EC key generation / mechanism (CKM_EC*, --key-type EC)\",\n category: \"key-exchange\",\n severity: \"high\",\n confidence: \"high\",\n algorithm: \"ECDH\",\n hndl: true,\n cwe: CWE_BROKEN_CRYPTO,\n message:\n \"A classical EC key in a PKCS#11 HSM / token; EC keys feed ECDSA signatures and ECDH key agreement (the ECDH path is harvest-now-decrypt-later exposed).\",\n remediation:\n \"For key agreement: hybrid X25519MLKEM768 (ML-KEM-768). For signatures: ML-DSA-65 (FIPS 204). Prioritise HSM-held keys.\",\n};\nconst RULE_PKCS11_DSA: RuleMeta = {\n id: \"pkcs11-dsa\",\n title: \"PKCS#11 (HSM) DSA key\",\n description: \"PKCS#11 DSA key generation / mechanism (CKM_DSA*)\",\n category: \"signature\",\n severity: \"medium\",\n confidence: \"high\",\n algorithm: \"DSA\",\n hndl: false,\n cwe: CWE_BROKEN_CRYPTO,\n message: \"A classical DSA key in a PKCS#11 HSM / token — deprecated and quantum-forgeable.\",\n remediation: \"Rotate off DSA; migrate to ML-DSA-65 (FIPS 204).\",\n};\nconst RULE_PKCS11_DH: RuleMeta = {\n id: \"pkcs11-dh\",\n title: \"PKCS#11 (HSM) Diffie-Hellman\",\n description: \"PKCS#11 finite-field DH key generation / derive (CKM_DH_PKCS_*)\",\n category: \"key-exchange\",\n severity: \"medium\",\n confidence: \"high\",\n algorithm: \"DH\",\n hndl: true,\n cwe: CWE_BROKEN_CRYPTO,\n message:\n \"Finite-field Diffie-Hellman in a PKCS#11 HSM / token; classical key agreement, harvest-now-decrypt-later exposed.\",\n remediation: \"Migrate key agreement to hybrid X25519MLKEM768 (ML-KEM-768).\",\n};\n\n/** Detects classical asymmetric keys behind a PKCS#11 HSM / token. */\nexport const pkcs11Detector: Detector = {\n id: \"pkcs11-crypto\",\n description: \"Classical asymmetric keys in a PKCS#11 HSM / token\",\n scope: \"config\",\n language: \"any\",\n appliesTo: (f) => !hasExtension(f, DOC_EXTENSIONS),\n rules: [RULE_PKCS11_RSA, RULE_PKCS11_EC, RULE_PKCS11_DSA, RULE_PKCS11_DH],\n detect({ file, content }): Finding[] {\n // Fast reject: only proceed on a distinctive PKCS#11 marker.\n if (!content.includes(\"CKM_\") && !content.includes(\"--key-type\")) {\n return [];\n }\n const findings: Finding[] = [];\n const add = (re: RegExp, rule: RuleMeta): void =>\n eachMatch(re, content, (m) =>\n findings.push(\n findingFromRule(rule, { file, content, index: m.index, matchLength: m[0].length }),\n ),\n );\n add(RE_PKCS11_TOOL_RSA, RULE_PKCS11_RSA);\n add(RE_PKCS11_TOOL_EC, RULE_PKCS11_EC);\n add(RE_CKM_RSA, RULE_PKCS11_RSA);\n add(RE_CKM_EC, RULE_PKCS11_EC);\n add(RE_CKM_DSA, RULE_PKCS11_DSA);\n add(RE_CKM_DH, RULE_PKCS11_DH);\n return findings;\n },\n};\n"]}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Config detector: classical-certificate TLS termination in standalone
|
|
3
|
+
* reverse-proxy / load-balancer configuration and in-code gRPC channel
|
|
4
|
+
* credentials.
|
|
5
|
+
*
|
|
6
|
+
* A standalone Envoy, Nginx, HAProxy, Traefik, or Caddy config that terminates
|
|
7
|
+
* TLS with an RSA/ECDSA leaf certificate and negotiates an ECDHE key exchange is
|
|
8
|
+
* the canonical harvest-now-decrypt-later (HNDL) surface: an attacker who
|
|
9
|
+
* records today's handshake + ciphertext can decrypt it retroactively once a
|
|
10
|
+
* cryptographically-relevant quantum computer (CRQC) can break the ephemeral
|
|
11
|
+
* (EC)DHE secret. The Kubernetes / service-mesh detectors (`k8s.ts`, `mesh.ts`)
|
|
12
|
+
* cover the CLUSTER-managed cert + mesh-mTLS surfaces; the STANDALONE proxy
|
|
13
|
+
* config FILES and the in-code gRPC credential constructors below are seen by
|
|
14
|
+
* neither them nor the language packs, so they are covered here.
|
|
15
|
+
*
|
|
16
|
+
* Because the config token itself does not name the cert's key algorithm (the
|
|
17
|
+
* key lives in a referenced PEM file, out of band), these findings use
|
|
18
|
+
* `algorithm: "unknown"` — the evidence is "a classical-cert TLS channel is
|
|
19
|
+
* configured HERE", not a specific family. They are `category: "key-exchange"`
|
|
20
|
+
* (the ECDHE handshake is the harvestable primitive) and `hndl: true`.
|
|
21
|
+
* Confidence is `medium`: a config-level indicator of a classical TLS channel,
|
|
22
|
+
* not a matched algorithm string.
|
|
23
|
+
*
|
|
24
|
+
* PRECISION / fast-reject: several of the matched tokens (`ssl_certificate`,
|
|
25
|
+
* `private_key:`, `certificates:`) are generic enough to appear in unrelated
|
|
26
|
+
* YAML/config, so `detect()` bails unless the file carries a distinctive
|
|
27
|
+
* proxy/gRPC marker (see `hasProxyMarker`). This keeps a random YAML that merely
|
|
28
|
+
* has a `private_key:` key, or a Traefik-shaped `certificates:` list that isn't
|
|
29
|
+
* a Traefik config, from firing. Comments are masked (YAML/nginx/HCL `#`, plus
|
|
30
|
+
* `//` and C-style `/* … *\/` blocks) so a commented-out directive can't fire.
|
|
31
|
+
*
|
|
32
|
+
* Covered surfaces:
|
|
33
|
+
* - Envoy — `common_tls_context:` / `DownstreamTlsContext` /
|
|
34
|
+
* `UpstreamTlsContext` with `tls_certificates:` carrying a
|
|
35
|
+
* `certificate_chain:` + `private_key:` (and the
|
|
36
|
+
* `envoy.transport_sockets.tls` transport socket).
|
|
37
|
+
* - Nginx — `ssl_certificate` + `ssl_certificate_key` directives.
|
|
38
|
+
* - HAProxy — `bind … ssl crt …` lines and `crt-store` sections.
|
|
39
|
+
* - Traefik — a `tls:` block's `certificates:` with `certFile:` + `keyFile:`
|
|
40
|
+
* (dynamic config) or a `certResolver` (ACME) reference.
|
|
41
|
+
* - gRPC — in-code TLS channel credentials: `grpc.ssl_channel_credentials(`
|
|
42
|
+
* (Python), `grpc.credentials.createSsl(` (Node),
|
|
43
|
+
* `TlsChannelCredentials` (Java), `credentials.NewTLS(` (Go).
|
|
44
|
+
*/
|
|
45
|
+
import type { Detector } from "../types.js";
|
|
46
|
+
/**
|
|
47
|
+
* Detects classical-cert TLS termination in standalone reverse-proxy /
|
|
48
|
+
* load-balancer config files and in-code gRPC channel credentials. See the
|
|
49
|
+
* module docstring for the covered surfaces and the fast-reject rationale.
|
|
50
|
+
*/
|
|
51
|
+
export declare const proxyDetector: Detector;
|
|
52
|
+
//# sourceMappingURL=proxy.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"proxy.d.ts","sourceRoot":"","sources":["../../src/detectors/proxy.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2CG;AACH,OAAO,KAAK,EAAE,QAAQ,EAAqB,MAAM,aAAa,CAAC;AA2J/D;;;;GAIG;AACH,eAAO,MAAM,aAAa,EAAE,QA+B3B,CAAC"}
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
import { DOC_EXTENSIONS, eachMatch, findingFromRule, hasExtension, maskBlockComments, maskCommentLines, } from "../detect-utils.js";
|
|
2
|
+
import { CWE_BROKEN_CRYPTO } from "../cwe.js";
|
|
3
|
+
// --- Envoy: match ONLY the Envoy-DISTINCTIVE tokens (`common_tls_context:`, the
|
|
4
|
+
// `*TlsContext` type names, `tls_certificates:`, the `envoy.transport_sockets.tls`
|
|
5
|
+
// transport socket). The generic `certificate_chain:` / `private_key:` fields are
|
|
6
|
+
// deliberately NOT matched here — they appear in unrelated YAML and would
|
|
7
|
+
// cross-label a non-Envoy file as Envoy (audit L2). A distinctive token IS the
|
|
8
|
+
// evidence, so no separate marker gate is needed for this rule (audit L1).
|
|
9
|
+
const RE_ENVOY_TLS = /\b(?:DownstreamTlsContext|UpstreamTlsContext|common_tls_context\s*:|tls_certificates\s*:|envoy\.transport_sockets\.tls)/g;
|
|
10
|
+
// --- Nginx: the classic `ssl_certificate` / `ssl_certificate_key` directive
|
|
11
|
+
// pair. `ssl_certificate_key` is matched first-class; `ssl_certificate` requires
|
|
12
|
+
// a following space so it doesn't also swallow `ssl_certificate_key` as a second
|
|
13
|
+
// hit on the same directive (kept as a single rule regardless).
|
|
14
|
+
const RE_NGINX_TLS = /\bssl_certificate(?:_key)?\s+\S/g;
|
|
15
|
+
// --- HAProxy: a `bind` line that terminates TLS (`ssl crt <path>`) or a
|
|
16
|
+
// `crt-store` section (HAProxy 2.8+ certificate store). `ssl` and `crt` may be
|
|
17
|
+
// separated by other bind args, so match them independently on a bind context.
|
|
18
|
+
const RE_HAPROXY_TLS = /\bcrt-store\b|\bbind\b[^\n]*\bssl\b[^\n]*\bcrt\b|\bbind\b[^\n]*\bcrt\b[^\n]*\bssl\b/g;
|
|
19
|
+
// --- Traefik: dynamic-config TLS certificate entries (`certFile:` + `keyFile:`)
|
|
20
|
+
// or an ACME `certResolver` reference. `certFile`/`keyFile` are Traefik-specific
|
|
21
|
+
// enough (paired with the `certResolver`/`tls:` marker gate) to anchor on.
|
|
22
|
+
const RE_TRAEFIK_TLS = /\bcertResolver\b|\b(?:certFile|keyFile)\s*:/g;
|
|
23
|
+
// --- gRPC in-code TLS channel credentials across languages.
|
|
24
|
+
const RE_GRPC_TLS = /grpc\.ssl_channel_credentials\s*\(|grpc\.credentials\.createSsl\s*\(|\bTlsChannelCredentials\b|\bcredentials\.NewTLS\s*\(/g;
|
|
25
|
+
const MESSAGE = (proxy) => `${proxy} terminates TLS with a classical certificate + ECDHE key exchange; the recorded handshake is harvest-now-decrypt-later exposed — verify the cert key algorithm and plan a PQC-hybrid (X25519MLKEM768) TLS migration.`;
|
|
26
|
+
const REMEDIATION = "Enable hybrid PQC key exchange (X25519MLKEM768) once the proxy/gRPC stack supports it; re-key certificates to PQC signatures (ML-DSA) when available.";
|
|
27
|
+
const RULE_ENVOY = {
|
|
28
|
+
id: "proxy-envoy-tls",
|
|
29
|
+
title: "Envoy classical TLS termination",
|
|
30
|
+
description: "Envoy TLS context terminates TLS with a classical certificate + ECDHE key exchange",
|
|
31
|
+
category: "key-exchange",
|
|
32
|
+
severity: "medium",
|
|
33
|
+
confidence: "medium",
|
|
34
|
+
algorithm: "unknown",
|
|
35
|
+
hndl: true,
|
|
36
|
+
cwe: CWE_BROKEN_CRYPTO,
|
|
37
|
+
message: MESSAGE("Envoy"),
|
|
38
|
+
remediation: REMEDIATION,
|
|
39
|
+
};
|
|
40
|
+
const RULE_NGINX = {
|
|
41
|
+
id: "proxy-nginx-tls",
|
|
42
|
+
title: "Nginx classical TLS termination",
|
|
43
|
+
description: "Nginx ssl_certificate/ssl_certificate_key terminates TLS with a classical cert + ECDHE",
|
|
44
|
+
category: "key-exchange",
|
|
45
|
+
severity: "medium",
|
|
46
|
+
confidence: "medium",
|
|
47
|
+
algorithm: "unknown",
|
|
48
|
+
hndl: true,
|
|
49
|
+
cwe: CWE_BROKEN_CRYPTO,
|
|
50
|
+
message: MESSAGE("Nginx"),
|
|
51
|
+
remediation: REMEDIATION,
|
|
52
|
+
};
|
|
53
|
+
const RULE_HAPROXY = {
|
|
54
|
+
id: "proxy-haproxy-tls",
|
|
55
|
+
title: "HAProxy classical TLS termination",
|
|
56
|
+
description: "HAProxy bind ssl crt / crt-store terminates TLS with a classical cert + ECDHE",
|
|
57
|
+
category: "key-exchange",
|
|
58
|
+
severity: "medium",
|
|
59
|
+
confidence: "medium",
|
|
60
|
+
algorithm: "unknown",
|
|
61
|
+
hndl: true,
|
|
62
|
+
cwe: CWE_BROKEN_CRYPTO,
|
|
63
|
+
message: MESSAGE("HAProxy"),
|
|
64
|
+
remediation: REMEDIATION,
|
|
65
|
+
};
|
|
66
|
+
const RULE_TRAEFIK = {
|
|
67
|
+
id: "proxy-traefik-tls",
|
|
68
|
+
title: "Traefik classical TLS termination",
|
|
69
|
+
description: "Traefik tls certificates (certFile/keyFile) or certResolver terminate TLS with a classical cert + ECDHE",
|
|
70
|
+
category: "key-exchange",
|
|
71
|
+
severity: "medium",
|
|
72
|
+
confidence: "medium",
|
|
73
|
+
algorithm: "unknown",
|
|
74
|
+
hndl: true,
|
|
75
|
+
cwe: CWE_BROKEN_CRYPTO,
|
|
76
|
+
message: MESSAGE("Traefik"),
|
|
77
|
+
remediation: REMEDIATION,
|
|
78
|
+
};
|
|
79
|
+
const RULE_GRPC = {
|
|
80
|
+
id: "grpc-tls-credentials",
|
|
81
|
+
title: "gRPC classical TLS channel credentials",
|
|
82
|
+
description: "gRPC channel established with classical TLS credentials (RSA/ECDSA cert + ECDHE)",
|
|
83
|
+
category: "key-exchange",
|
|
84
|
+
severity: "medium",
|
|
85
|
+
confidence: "medium",
|
|
86
|
+
algorithm: "unknown",
|
|
87
|
+
hndl: true,
|
|
88
|
+
cwe: CWE_BROKEN_CRYPTO,
|
|
89
|
+
message: MESSAGE("gRPC"),
|
|
90
|
+
remediation: REMEDIATION,
|
|
91
|
+
};
|
|
92
|
+
/**
|
|
93
|
+
* PERF fast-reject only — a superset of substrings that appear in every rule's
|
|
94
|
+
* matchable input, so it can never drop a file a rule would flag. Correctness is
|
|
95
|
+
* in the per-rule regexes (each is distinctive enough to self-gate); this list
|
|
96
|
+
* exists purely to skip the regex work on files with no proxy/gRPC token at all.
|
|
97
|
+
*/
|
|
98
|
+
const PROXY_FAST_REJECT = [
|
|
99
|
+
// Envoy
|
|
100
|
+
"tls_context",
|
|
101
|
+
"TlsContext",
|
|
102
|
+
"tls_certificates",
|
|
103
|
+
"transport_sockets.tls",
|
|
104
|
+
// Nginx
|
|
105
|
+
"ssl_certificate",
|
|
106
|
+
// HAProxy (`crt-store` and `bind … ssl … crt` both contain "crt")
|
|
107
|
+
"crt",
|
|
108
|
+
// Traefik
|
|
109
|
+
"certResolver",
|
|
110
|
+
"certFile",
|
|
111
|
+
"keyFile",
|
|
112
|
+
// gRPC
|
|
113
|
+
"ssl_channel_credentials",
|
|
114
|
+
"createSsl",
|
|
115
|
+
"TlsChannelCredentials",
|
|
116
|
+
"credentials.NewTLS",
|
|
117
|
+
];
|
|
118
|
+
const PROXY_RULES = [
|
|
119
|
+
{ meta: RULE_ENVOY, re: RE_ENVOY_TLS },
|
|
120
|
+
{ meta: RULE_NGINX, re: RE_NGINX_TLS },
|
|
121
|
+
{ meta: RULE_HAPROXY, re: RE_HAPROXY_TLS },
|
|
122
|
+
{ meta: RULE_TRAEFIK, re: RE_TRAEFIK_TLS },
|
|
123
|
+
{ meta: RULE_GRPC, re: RE_GRPC_TLS },
|
|
124
|
+
];
|
|
125
|
+
/**
|
|
126
|
+
* Detects classical-cert TLS termination in standalone reverse-proxy /
|
|
127
|
+
* load-balancer config files and in-code gRPC channel credentials. See the
|
|
128
|
+
* module docstring for the covered surfaces and the fast-reject rationale.
|
|
129
|
+
*/
|
|
130
|
+
export const proxyDetector = {
|
|
131
|
+
id: "proxy-tls-crypto",
|
|
132
|
+
description: "Classical-cert TLS termination in reverse-proxy / load-balancer config (Envoy, Nginx, HAProxy, Traefik) and gRPC channel credentials",
|
|
133
|
+
scope: "config",
|
|
134
|
+
language: "any",
|
|
135
|
+
rules: PROXY_RULES.map((r) => r.meta),
|
|
136
|
+
// Config + source files are all in scope (gRPC creds live in code); only prose
|
|
137
|
+
// documentation is excluded, so a README mentioning `ssl_certificate` in a
|
|
138
|
+
// sentence can't fire (the marker gate already handles most of this).
|
|
139
|
+
appliesTo: (f) => !hasExtension(f, DOC_EXTENSIONS),
|
|
140
|
+
detect({ file, content }) {
|
|
141
|
+
// Mask comments first so a directive that only appears inside a commented-out
|
|
142
|
+
// block cannot fire.
|
|
143
|
+
const scan = maskCommentLines(maskBlockComments(content), ["#", "//"]);
|
|
144
|
+
// Cheap fast-reject: skip files with none of the distinctive tokens. This is a
|
|
145
|
+
// perf gate only — each rule's regex is distinctive enough to SELF-GATE (a
|
|
146
|
+
// separate too-narrow marker list previously dropped canonical HAProxy/Traefik
|
|
147
|
+
// configs entirely — audit H1/H2), so correctness lives in the per-rule regexes.
|
|
148
|
+
if (!PROXY_FAST_REJECT.some((mk) => scan.includes(mk)))
|
|
149
|
+
return [];
|
|
150
|
+
const findings = [];
|
|
151
|
+
for (const { meta, re } of PROXY_RULES) {
|
|
152
|
+
eachMatch(re, scan, (m) => findings.push(findingFromRule(meta, { file, content, index: m.index, matchLength: m[0].length })));
|
|
153
|
+
}
|
|
154
|
+
return findings;
|
|
155
|
+
},
|
|
156
|
+
};
|
|
157
|
+
//# sourceMappingURL=proxy.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"proxy.js","sourceRoot":"","sources":["../../src/detectors/proxy.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Config detector: classical-certificate TLS termination in standalone\n * reverse-proxy / load-balancer configuration and in-code gRPC channel\n * credentials.\n *\n * A standalone Envoy, Nginx, HAProxy, Traefik, or Caddy config that terminates\n * TLS with an RSA/ECDSA leaf certificate and negotiates an ECDHE key exchange is\n * the canonical harvest-now-decrypt-later (HNDL) surface: an attacker who\n * records today's handshake + ciphertext can decrypt it retroactively once a\n * cryptographically-relevant quantum computer (CRQC) can break the ephemeral\n * (EC)DHE secret. The Kubernetes / service-mesh detectors (`k8s.ts`, `mesh.ts`)\n * cover the CLUSTER-managed cert + mesh-mTLS surfaces; the STANDALONE proxy\n * config FILES and the in-code gRPC credential constructors below are seen by\n * neither them nor the language packs, so they are covered here.\n *\n * Because the config token itself does not name the cert's key algorithm (the\n * key lives in a referenced PEM file, out of band), these findings use\n * `algorithm: \"unknown\"` — the evidence is \"a classical-cert TLS channel is\n * configured HERE\", not a specific family. They are `category: \"key-exchange\"`\n * (the ECDHE handshake is the harvestable primitive) and `hndl: true`.\n * Confidence is `medium`: a config-level indicator of a classical TLS channel,\n * not a matched algorithm string.\n *\n * PRECISION / fast-reject: several of the matched tokens (`ssl_certificate`,\n * `private_key:`, `certificates:`) are generic enough to appear in unrelated\n * YAML/config, so `detect()` bails unless the file carries a distinctive\n * proxy/gRPC marker (see `hasProxyMarker`). This keeps a random YAML that merely\n * has a `private_key:` key, or a Traefik-shaped `certificates:` list that isn't\n * a Traefik config, from firing. Comments are masked (YAML/nginx/HCL `#`, plus\n * `//` and C-style `/* … *\\/` blocks) so a commented-out directive can't fire.\n *\n * Covered surfaces:\n * - Envoy — `common_tls_context:` / `DownstreamTlsContext` /\n * `UpstreamTlsContext` with `tls_certificates:` carrying a\n * `certificate_chain:` + `private_key:` (and the\n * `envoy.transport_sockets.tls` transport socket).\n * - Nginx — `ssl_certificate` + `ssl_certificate_key` directives.\n * - HAProxy — `bind … ssl crt …` lines and `crt-store` sections.\n * - Traefik — a `tls:` block's `certificates:` with `certFile:` + `keyFile:`\n * (dynamic config) or a `certResolver` (ACME) reference.\n * - gRPC — in-code TLS channel credentials: `grpc.ssl_channel_credentials(`\n * (Python), `grpc.credentials.createSsl(` (Node),\n * `TlsChannelCredentials` (Java), `credentials.NewTLS(` (Go).\n */\nimport type { Detector, Finding, RuleMeta } from \"../types.js\";\nimport {\n DOC_EXTENSIONS,\n eachMatch,\n findingFromRule,\n hasExtension,\n maskBlockComments,\n maskCommentLines,\n} from \"../detect-utils.js\";\nimport { CWE_BROKEN_CRYPTO } from \"../cwe.js\";\n\n// --- Envoy: match ONLY the Envoy-DISTINCTIVE tokens (`common_tls_context:`, the\n// `*TlsContext` type names, `tls_certificates:`, the `envoy.transport_sockets.tls`\n// transport socket). The generic `certificate_chain:` / `private_key:` fields are\n// deliberately NOT matched here — they appear in unrelated YAML and would\n// cross-label a non-Envoy file as Envoy (audit L2). A distinctive token IS the\n// evidence, so no separate marker gate is needed for this rule (audit L1).\nconst RE_ENVOY_TLS =\n /\\b(?:DownstreamTlsContext|UpstreamTlsContext|common_tls_context\\s*:|tls_certificates\\s*:|envoy\\.transport_sockets\\.tls)/g;\n\n// --- Nginx: the classic `ssl_certificate` / `ssl_certificate_key` directive\n// pair. `ssl_certificate_key` is matched first-class; `ssl_certificate` requires\n// a following space so it doesn't also swallow `ssl_certificate_key` as a second\n// hit on the same directive (kept as a single rule regardless).\nconst RE_NGINX_TLS = /\\bssl_certificate(?:_key)?\\s+\\S/g;\n\n// --- HAProxy: a `bind` line that terminates TLS (`ssl crt <path>`) or a\n// `crt-store` section (HAProxy 2.8+ certificate store). `ssl` and `crt` may be\n// separated by other bind args, so match them independently on a bind context.\nconst RE_HAPROXY_TLS =\n /\\bcrt-store\\b|\\bbind\\b[^\\n]*\\bssl\\b[^\\n]*\\bcrt\\b|\\bbind\\b[^\\n]*\\bcrt\\b[^\\n]*\\bssl\\b/g;\n\n// --- Traefik: dynamic-config TLS certificate entries (`certFile:` + `keyFile:`)\n// or an ACME `certResolver` reference. `certFile`/`keyFile` are Traefik-specific\n// enough (paired with the `certResolver`/`tls:` marker gate) to anchor on.\nconst RE_TRAEFIK_TLS = /\\bcertResolver\\b|\\b(?:certFile|keyFile)\\s*:/g;\n\n// --- gRPC in-code TLS channel credentials across languages.\nconst RE_GRPC_TLS =\n /grpc\\.ssl_channel_credentials\\s*\\(|grpc\\.credentials\\.createSsl\\s*\\(|\\bTlsChannelCredentials\\b|\\bcredentials\\.NewTLS\\s*\\(/g;\n\nconst MESSAGE = (proxy: string): string =>\n `${proxy} terminates TLS with a classical certificate + ECDHE key exchange; the recorded handshake is harvest-now-decrypt-later exposed — verify the cert key algorithm and plan a PQC-hybrid (X25519MLKEM768) TLS migration.`;\n\nconst REMEDIATION =\n \"Enable hybrid PQC key exchange (X25519MLKEM768) once the proxy/gRPC stack supports it; re-key certificates to PQC signatures (ML-DSA) when available.\";\n\nconst RULE_ENVOY: RuleMeta = {\n id: \"proxy-envoy-tls\",\n title: \"Envoy classical TLS termination\",\n description: \"Envoy TLS context terminates TLS with a classical certificate + ECDHE key exchange\",\n category: \"key-exchange\",\n severity: \"medium\",\n confidence: \"medium\",\n algorithm: \"unknown\",\n hndl: true,\n cwe: CWE_BROKEN_CRYPTO,\n message: MESSAGE(\"Envoy\"),\n remediation: REMEDIATION,\n};\nconst RULE_NGINX: RuleMeta = {\n id: \"proxy-nginx-tls\",\n title: \"Nginx classical TLS termination\",\n description:\n \"Nginx ssl_certificate/ssl_certificate_key terminates TLS with a classical cert + ECDHE\",\n category: \"key-exchange\",\n severity: \"medium\",\n confidence: \"medium\",\n algorithm: \"unknown\",\n hndl: true,\n cwe: CWE_BROKEN_CRYPTO,\n message: MESSAGE(\"Nginx\"),\n remediation: REMEDIATION,\n};\nconst RULE_HAPROXY: RuleMeta = {\n id: \"proxy-haproxy-tls\",\n title: \"HAProxy classical TLS termination\",\n description: \"HAProxy bind ssl crt / crt-store terminates TLS with a classical cert + ECDHE\",\n category: \"key-exchange\",\n severity: \"medium\",\n confidence: \"medium\",\n algorithm: \"unknown\",\n hndl: true,\n cwe: CWE_BROKEN_CRYPTO,\n message: MESSAGE(\"HAProxy\"),\n remediation: REMEDIATION,\n};\nconst RULE_TRAEFIK: RuleMeta = {\n id: \"proxy-traefik-tls\",\n title: \"Traefik classical TLS termination\",\n description:\n \"Traefik tls certificates (certFile/keyFile) or certResolver terminate TLS with a classical cert + ECDHE\",\n category: \"key-exchange\",\n severity: \"medium\",\n confidence: \"medium\",\n algorithm: \"unknown\",\n hndl: true,\n cwe: CWE_BROKEN_CRYPTO,\n message: MESSAGE(\"Traefik\"),\n remediation: REMEDIATION,\n};\nconst RULE_GRPC: RuleMeta = {\n id: \"grpc-tls-credentials\",\n title: \"gRPC classical TLS channel credentials\",\n description: \"gRPC channel established with classical TLS credentials (RSA/ECDSA cert + ECDHE)\",\n category: \"key-exchange\",\n severity: \"medium\",\n confidence: \"medium\",\n algorithm: \"unknown\",\n hndl: true,\n cwe: CWE_BROKEN_CRYPTO,\n message: MESSAGE(\"gRPC\"),\n remediation: REMEDIATION,\n};\n\n/**\n * PERF fast-reject only — a superset of substrings that appear in every rule's\n * matchable input, so it can never drop a file a rule would flag. Correctness is\n * in the per-rule regexes (each is distinctive enough to self-gate); this list\n * exists purely to skip the regex work on files with no proxy/gRPC token at all.\n */\nconst PROXY_FAST_REJECT: readonly string[] = [\n // Envoy\n \"tls_context\",\n \"TlsContext\",\n \"tls_certificates\",\n \"transport_sockets.tls\",\n // Nginx\n \"ssl_certificate\",\n // HAProxy (`crt-store` and `bind … ssl … crt` both contain \"crt\")\n \"crt\",\n // Traefik\n \"certResolver\",\n \"certFile\",\n \"keyFile\",\n // gRPC\n \"ssl_channel_credentials\",\n \"createSsl\",\n \"TlsChannelCredentials\",\n \"credentials.NewTLS\",\n];\n\ninterface ProxyRule {\n meta: RuleMeta;\n re: RegExp;\n}\n\nconst PROXY_RULES: readonly ProxyRule[] = [\n { meta: RULE_ENVOY, re: RE_ENVOY_TLS },\n { meta: RULE_NGINX, re: RE_NGINX_TLS },\n { meta: RULE_HAPROXY, re: RE_HAPROXY_TLS },\n { meta: RULE_TRAEFIK, re: RE_TRAEFIK_TLS },\n { meta: RULE_GRPC, re: RE_GRPC_TLS },\n];\n\n/**\n * Detects classical-cert TLS termination in standalone reverse-proxy /\n * load-balancer config files and in-code gRPC channel credentials. See the\n * module docstring for the covered surfaces and the fast-reject rationale.\n */\nexport const proxyDetector: Detector = {\n id: \"proxy-tls-crypto\",\n description:\n \"Classical-cert TLS termination in reverse-proxy / load-balancer config (Envoy, Nginx, HAProxy, Traefik) and gRPC channel credentials\",\n scope: \"config\",\n language: \"any\",\n rules: PROXY_RULES.map((r) => r.meta),\n // Config + source files are all in scope (gRPC creds live in code); only prose\n // documentation is excluded, so a README mentioning `ssl_certificate` in a\n // sentence can't fire (the marker gate already handles most of this).\n appliesTo: (f) => !hasExtension(f, DOC_EXTENSIONS),\n detect({ file, content }): Finding[] {\n // Mask comments first so a directive that only appears inside a commented-out\n // block cannot fire.\n const scan = maskCommentLines(maskBlockComments(content), [\"#\", \"//\"]);\n // Cheap fast-reject: skip files with none of the distinctive tokens. This is a\n // perf gate only — each rule's regex is distinctive enough to SELF-GATE (a\n // separate too-narrow marker list previously dropped canonical HAProxy/Traefik\n // configs entirely — audit H1/H2), so correctness lives in the per-rule regexes.\n if (!PROXY_FAST_REJECT.some((mk) => scan.includes(mk))) return [];\n\n const findings: Finding[] = [];\n for (const { meta, re } of PROXY_RULES) {\n eachMatch(re, scan, (m) =>\n findings.push(\n findingFromRule(meta, { file, content, index: m.index, matchLength: m[0].length }),\n ),\n );\n }\n return findings;\n },\n};\n"]}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* IaC detector: classical asymmetric keys provisioned by Pulumi's `tls` provider
|
|
3
|
+
* (`@pulumi/tls` / `pulumi_tls` / `pulumi-tls`), across the languages Pulumi programs
|
|
4
|
+
* are written in (TS/JS, Python, Go). A `tls.PrivateKey` resource mints real key
|
|
5
|
+
* material at deploy time — the IaC surface, like Terraform's `tls_private_key`, but
|
|
6
|
+
* expressed as SDK calls in a general-purpose source file rather than HCL.
|
|
7
|
+
*
|
|
8
|
+
* Detection is gated to files that actually use the pulumi-tls provider (a
|
|
9
|
+
* `@pulumi/tls` / `pulumi_tls` / `pulumi-tls` import or a `tls.PrivateKey` /
|
|
10
|
+
* `tls.NewPrivateKey` construction), then classifies each `algorithm` value:
|
|
11
|
+
* - `"RSA"` → RSA key (kem, HNDL)
|
|
12
|
+
* - `"ECDSA"` → EC signing key (signature)
|
|
13
|
+
* - `"ED25519"` → Ed25519 signing key (signature)
|
|
14
|
+
* Comment/string suppression is handled centrally for the host languages.
|
|
15
|
+
*/
|
|
16
|
+
import type { Detector } from "../types.js";
|
|
17
|
+
/** Detects classical asymmetric keys provisioned by Pulumi's tls provider. */
|
|
18
|
+
export declare const pulumiDetector: Detector;
|
|
19
|
+
//# sourceMappingURL=pulumi.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pulumi.d.ts","sourceRoot":"","sources":["../../src/detectors/pulumi.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,OAAO,KAAK,EAAE,QAAQ,EAAqB,MAAM,aAAa,CAAC;AAsE/D,8EAA8E;AAC9E,eAAO,MAAM,cAAc,EAAE,QAmB5B,CAAC"}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { eachMatch, findingFromRule, hasExtension } from "../detect-utils.js";
|
|
2
|
+
import { CWE_BROKEN_CRYPTO } from "../cwe.js";
|
|
3
|
+
const PULUMI_EXTENSIONS = [".ts", ".js", ".mjs", ".cjs", ".py", ".go"];
|
|
4
|
+
// The `algorithm` value of a pulumi-tls PrivateKey, in the TS/Python (`algorithm:
|
|
5
|
+
// "RSA"` / `algorithm="RSA"`) and Go (`Algorithm: pulumi.String("RSA")`) spellings.
|
|
6
|
+
const RE_PULUMI_TLS_ALG = /\b[Aa]lgorithm\s*[:=]\s*(?:pulumi\.String\(\s*)?["'](RSA|ECDSA|ED25519)["']/g;
|
|
7
|
+
/** True when the file actually uses the pulumi-tls provider (gate against FPs). */
|
|
8
|
+
function usesPulumiTls(content) {
|
|
9
|
+
return (content.includes("@pulumi/tls") ||
|
|
10
|
+
content.includes("pulumi_tls") ||
|
|
11
|
+
content.includes("pulumi-tls") ||
|
|
12
|
+
content.includes("tls.PrivateKey") ||
|
|
13
|
+
content.includes("tls.NewPrivateKey"));
|
|
14
|
+
}
|
|
15
|
+
const RULE_PULUMI_RSA = {
|
|
16
|
+
id: "pulumi-tls-rsa",
|
|
17
|
+
title: "Pulumi tls.PrivateKey RSA key",
|
|
18
|
+
description: 'Pulumi tls.PrivateKey with algorithm "RSA"',
|
|
19
|
+
category: "kem",
|
|
20
|
+
severity: "high",
|
|
21
|
+
confidence: "high",
|
|
22
|
+
algorithm: "RSA",
|
|
23
|
+
hndl: true,
|
|
24
|
+
cwe: CWE_BROKEN_CRYPTO,
|
|
25
|
+
message: "Pulumi provisions a classical RSA key (tls.PrivateKey), which is not quantum-safe.",
|
|
26
|
+
remediation: "Plan migration to PQC (ML-KEM-768 for encryption, ML-DSA-65 for signatures).",
|
|
27
|
+
};
|
|
28
|
+
const RULE_PULUMI_ECDSA = {
|
|
29
|
+
id: "pulumi-tls-ecdsa",
|
|
30
|
+
title: "Pulumi tls.PrivateKey ECDSA key",
|
|
31
|
+
description: 'Pulumi tls.PrivateKey with algorithm "ECDSA"',
|
|
32
|
+
category: "signature",
|
|
33
|
+
severity: "high",
|
|
34
|
+
confidence: "high",
|
|
35
|
+
algorithm: "ECDSA",
|
|
36
|
+
hndl: false,
|
|
37
|
+
cwe: CWE_BROKEN_CRYPTO,
|
|
38
|
+
message: "Pulumi provisions a classical ECDSA key (tls.PrivateKey), forgeable by a quantum attacker.",
|
|
39
|
+
remediation: "ML-DSA-65 (FIPS 204) or SLH-DSA (FIPS 205).",
|
|
40
|
+
};
|
|
41
|
+
const RULE_PULUMI_ED25519 = {
|
|
42
|
+
id: "pulumi-tls-ed25519",
|
|
43
|
+
title: "Pulumi tls.PrivateKey Ed25519 key",
|
|
44
|
+
description: 'Pulumi tls.PrivateKey with algorithm "ED25519"',
|
|
45
|
+
category: "signature",
|
|
46
|
+
severity: "medium",
|
|
47
|
+
confidence: "high",
|
|
48
|
+
algorithm: "EdDSA",
|
|
49
|
+
hndl: false,
|
|
50
|
+
cwe: CWE_BROKEN_CRYPTO,
|
|
51
|
+
message: "Pulumi provisions a classical Ed25519 key (tls.PrivateKey), forgeable by a quantum attacker.",
|
|
52
|
+
remediation: "ML-DSA-65 (FIPS 204) or SLH-DSA (FIPS 205).",
|
|
53
|
+
};
|
|
54
|
+
const RULE_BY_ALG = {
|
|
55
|
+
RSA: RULE_PULUMI_RSA,
|
|
56
|
+
ECDSA: RULE_PULUMI_ECDSA,
|
|
57
|
+
ED25519: RULE_PULUMI_ED25519,
|
|
58
|
+
};
|
|
59
|
+
/** Detects classical asymmetric keys provisioned by Pulumi's tls provider. */
|
|
60
|
+
export const pulumiDetector = {
|
|
61
|
+
id: "pulumi-crypto",
|
|
62
|
+
description: "Classical asymmetric keys provisioned by Pulumi's tls provider (IaC)",
|
|
63
|
+
scope: "config",
|
|
64
|
+
language: "any",
|
|
65
|
+
rules: [RULE_PULUMI_RSA, RULE_PULUMI_ECDSA, RULE_PULUMI_ED25519],
|
|
66
|
+
appliesTo: (f) => hasExtension(f, PULUMI_EXTENSIONS),
|
|
67
|
+
detect({ file, content }) {
|
|
68
|
+
if (!usesPulumiTls(content))
|
|
69
|
+
return [];
|
|
70
|
+
const findings = [];
|
|
71
|
+
eachMatch(RE_PULUMI_TLS_ALG, content, (m) => {
|
|
72
|
+
const rule = RULE_BY_ALG[m[1]];
|
|
73
|
+
if (!rule)
|
|
74
|
+
return;
|
|
75
|
+
findings.push(findingFromRule(rule, { file, content, index: m.index, matchLength: m[0].length }));
|
|
76
|
+
});
|
|
77
|
+
return findings;
|
|
78
|
+
},
|
|
79
|
+
};
|
|
80
|
+
//# sourceMappingURL=pulumi.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pulumi.js","sourceRoot":"","sources":["../../src/detectors/pulumi.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * IaC detector: classical asymmetric keys provisioned by Pulumi's `tls` provider\n * (`@pulumi/tls` / `pulumi_tls` / `pulumi-tls`), across the languages Pulumi programs\n * are written in (TS/JS, Python, Go). A `tls.PrivateKey` resource mints real key\n * material at deploy time — the IaC surface, like Terraform's `tls_private_key`, but\n * expressed as SDK calls in a general-purpose source file rather than HCL.\n *\n * Detection is gated to files that actually use the pulumi-tls provider (a\n * `@pulumi/tls` / `pulumi_tls` / `pulumi-tls` import or a `tls.PrivateKey` /\n * `tls.NewPrivateKey` construction), then classifies each `algorithm` value:\n * - `\"RSA\"` → RSA key (kem, HNDL)\n * - `\"ECDSA\"` → EC signing key (signature)\n * - `\"ED25519\"` → Ed25519 signing key (signature)\n * Comment/string suppression is handled centrally for the host languages.\n */\nimport type { Detector, Finding, RuleMeta } from \"../types.js\";\nimport { eachMatch, findingFromRule, hasExtension } from \"../detect-utils.js\";\nimport { CWE_BROKEN_CRYPTO } from \"../cwe.js\";\n\nconst PULUMI_EXTENSIONS: readonly string[] = [\".ts\", \".js\", \".mjs\", \".cjs\", \".py\", \".go\"];\n\n// The `algorithm` value of a pulumi-tls PrivateKey, in the TS/Python (`algorithm:\n// \"RSA\"` / `algorithm=\"RSA\"`) and Go (`Algorithm: pulumi.String(\"RSA\")`) spellings.\nconst RE_PULUMI_TLS_ALG =\n /\\b[Aa]lgorithm\\s*[:=]\\s*(?:pulumi\\.String\\(\\s*)?[\"'](RSA|ECDSA|ED25519)[\"']/g;\n\n/** True when the file actually uses the pulumi-tls provider (gate against FPs). */\nfunction usesPulumiTls(content: string): boolean {\n return (\n content.includes(\"@pulumi/tls\") ||\n content.includes(\"pulumi_tls\") ||\n content.includes(\"pulumi-tls\") ||\n content.includes(\"tls.PrivateKey\") ||\n content.includes(\"tls.NewPrivateKey\")\n );\n}\n\nconst RULE_PULUMI_RSA: RuleMeta = {\n id: \"pulumi-tls-rsa\",\n title: \"Pulumi tls.PrivateKey RSA key\",\n description: 'Pulumi tls.PrivateKey with algorithm \"RSA\"',\n category: \"kem\",\n severity: \"high\",\n confidence: \"high\",\n algorithm: \"RSA\",\n hndl: true,\n cwe: CWE_BROKEN_CRYPTO,\n message: \"Pulumi provisions a classical RSA key (tls.PrivateKey), which is not quantum-safe.\",\n remediation: \"Plan migration to PQC (ML-KEM-768 for encryption, ML-DSA-65 for signatures).\",\n};\nconst RULE_PULUMI_ECDSA: RuleMeta = {\n id: \"pulumi-tls-ecdsa\",\n title: \"Pulumi tls.PrivateKey ECDSA key\",\n description: 'Pulumi tls.PrivateKey with algorithm \"ECDSA\"',\n category: \"signature\",\n severity: \"high\",\n confidence: \"high\",\n algorithm: \"ECDSA\",\n hndl: false,\n cwe: CWE_BROKEN_CRYPTO,\n message:\n \"Pulumi provisions a classical ECDSA key (tls.PrivateKey), forgeable by a quantum attacker.\",\n remediation: \"ML-DSA-65 (FIPS 204) or SLH-DSA (FIPS 205).\",\n};\nconst RULE_PULUMI_ED25519: RuleMeta = {\n id: \"pulumi-tls-ed25519\",\n title: \"Pulumi tls.PrivateKey Ed25519 key\",\n description: 'Pulumi tls.PrivateKey with algorithm \"ED25519\"',\n category: \"signature\",\n severity: \"medium\",\n confidence: \"high\",\n algorithm: \"EdDSA\",\n hndl: false,\n cwe: CWE_BROKEN_CRYPTO,\n message:\n \"Pulumi provisions a classical Ed25519 key (tls.PrivateKey), forgeable by a quantum attacker.\",\n remediation: \"ML-DSA-65 (FIPS 204) or SLH-DSA (FIPS 205).\",\n};\n\nconst RULE_BY_ALG: Record<string, RuleMeta> = {\n RSA: RULE_PULUMI_RSA,\n ECDSA: RULE_PULUMI_ECDSA,\n ED25519: RULE_PULUMI_ED25519,\n};\n\n/** Detects classical asymmetric keys provisioned by Pulumi's tls provider. */\nexport const pulumiDetector: Detector = {\n id: \"pulumi-crypto\",\n description: \"Classical asymmetric keys provisioned by Pulumi's tls provider (IaC)\",\n scope: \"config\",\n language: \"any\",\n rules: [RULE_PULUMI_RSA, RULE_PULUMI_ECDSA, RULE_PULUMI_ED25519],\n appliesTo: (f) => hasExtension(f, PULUMI_EXTENSIONS),\n detect({ file, content }): Finding[] {\n if (!usesPulumiTls(content)) return [];\n const findings: Finding[] = [];\n eachMatch(RE_PULUMI_TLS_ALG, content, (m) => {\n const rule = RULE_BY_ALG[m[1]];\n if (!rule) return;\n findings.push(\n findingFromRule(rule, { file, content, index: m.index, matchLength: m[0].length }),\n );\n });\n return findings;\n },\n};\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rust.d.ts","sourceRoot":"","sources":["../../src/detectors/rust.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,OAAO,KAAK,EAAmB,QAAQ,EAAqB,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"rust.d.ts","sourceRoot":"","sources":["../../src/detectors/rust.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,OAAO,KAAK,EAAmB,QAAQ,EAAqB,MAAM,aAAa,CAAC;AA4ShF,iFAAiF;AACjF,eAAO,MAAM,YAAY,EAAE,QA4E1B,CAAC"}
|