@quantakrypto/core 0.4.4 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +76 -27
- package/dist/cbom-merge.d.ts.map +1 -1
- package/dist/cbom-merge.js +30 -8
- package/dist/cbom-merge.js.map +1 -1
- package/dist/cbom.d.ts +5 -0
- package/dist/cbom.d.ts.map +1 -1
- package/dist/cbom.js +132 -37
- package/dist/cbom.js.map +1 -1
- package/dist/comments.d.ts +2 -23
- package/dist/comments.d.ts.map +1 -1
- package/dist/comments.js +24 -5
- package/dist/comments.js.map +1 -1
- package/dist/crypto-agility.d.ts +158 -0
- package/dist/crypto-agility.d.ts.map +1 -0
- package/dist/crypto-agility.js +285 -0
- package/dist/crypto-agility.js.map +1 -0
- package/dist/dependencies.d.ts.map +1 -1
- package/dist/dependencies.js +180 -18
- package/dist/dependencies.js.map +1 -1
- package/dist/detect-utils.d.ts +55 -21
- package/dist/detect-utils.d.ts.map +1 -1
- package/dist/detect-utils.js +133 -7
- package/dist/detect-utils.js.map +1 -1
- package/dist/detectors/age.d.ts +16 -0
- package/dist/detectors/age.d.ts.map +1 -0
- package/dist/detectors/age.js +40 -0
- package/dist/detectors/age.js.map +1 -0
- package/dist/detectors/ansible.d.ts +13 -0
- package/dist/detectors/ansible.d.ts.map +1 -0
- package/dist/detectors/ansible.js +108 -0
- package/dist/detectors/ansible.js.map +1 -0
- package/dist/detectors/bicep.d.ts +20 -0
- package/dist/detectors/bicep.d.ts.map +1 -0
- package/dist/detectors/bicep.js +74 -0
- package/dist/detectors/bicep.js.map +1 -0
- package/dist/detectors/c.d.ts.map +1 -1
- package/dist/detectors/c.js +30 -3
- package/dist/detectors/c.js.map +1 -1
- package/dist/detectors/cicd.d.ts.map +1 -1
- package/dist/detectors/cicd.js +18 -5
- package/dist/detectors/cicd.js.map +1 -1
- package/dist/detectors/cloud-kms.d.ts +8 -5
- package/dist/detectors/cloud-kms.d.ts.map +1 -1
- package/dist/detectors/cloud-kms.js +62 -20
- package/dist/detectors/cloud-kms.js.map +1 -1
- package/dist/detectors/cloudformation.d.ts +8 -0
- package/dist/detectors/cloudformation.d.ts.map +1 -1
- package/dist/detectors/cloudformation.js +41 -10
- package/dist/detectors/cloudformation.js.map +1 -1
- package/dist/detectors/codesign.d.ts +50 -0
- package/dist/detectors/codesign.d.ts.map +1 -0
- package/dist/detectors/codesign.js +143 -0
- package/dist/detectors/codesign.js.map +1 -0
- package/dist/detectors/csharp.d.ts.map +1 -1
- package/dist/detectors/csharp.js +13 -3
- package/dist/detectors/csharp.js.map +1 -1
- package/dist/detectors/dart.d.ts +35 -0
- package/dist/detectors/dart.d.ts.map +1 -0
- package/dist/detectors/dart.js +177 -0
- package/dist/detectors/dart.js.map +1 -0
- package/dist/detectors/database.d.ts +5 -3
- package/dist/detectors/database.d.ts.map +1 -1
- package/dist/detectors/database.js +45 -8
- package/dist/detectors/database.js.map +1 -1
- package/dist/detectors/dkim.d.ts +54 -0
- package/dist/detectors/dkim.d.ts.map +1 -0
- package/dist/detectors/dkim.js +92 -0
- package/dist/detectors/dkim.js.map +1 -0
- package/dist/detectors/dnssec.d.ts.map +1 -1
- package/dist/detectors/dnssec.js +19 -10
- package/dist/detectors/dnssec.js.map +1 -1
- package/dist/detectors/elixir.d.ts.map +1 -1
- package/dist/detectors/elixir.js +34 -2
- package/dist/detectors/elixir.js.map +1 -1
- package/dist/detectors/java.d.ts.map +1 -1
- package/dist/detectors/java.js +33 -3
- package/dist/detectors/java.js.map +1 -1
- package/dist/detectors/jose.d.ts.map +1 -1
- package/dist/detectors/jose.js +13 -3
- package/dist/detectors/jose.js.map +1 -1
- package/dist/detectors/jwk.d.ts +7 -7
- package/dist/detectors/jwk.d.ts.map +1 -1
- package/dist/detectors/jwk.js +44 -8
- package/dist/detectors/jwk.js.map +1 -1
- package/dist/detectors/k8s.d.ts.map +1 -1
- package/dist/detectors/k8s.js +5 -2
- package/dist/detectors/k8s.js.map +1 -1
- package/dist/detectors/keystore.d.ts +19 -0
- package/dist/detectors/keystore.d.ts.map +1 -0
- package/dist/detectors/keystore.js +76 -0
- package/dist/detectors/keystore.js.map +1 -0
- package/dist/detectors/mesh.d.ts +7 -8
- package/dist/detectors/mesh.d.ts.map +1 -1
- package/dist/detectors/mesh.js +17 -35
- package/dist/detectors/mesh.js.map +1 -1
- package/dist/detectors/messaging.d.ts +2 -2
- package/dist/detectors/messaging.d.ts.map +1 -1
- package/dist/detectors/messaging.js +19 -12
- package/dist/detectors/messaging.js.map +1 -1
- package/dist/detectors/objc.d.ts +41 -0
- package/dist/detectors/objc.d.ts.map +1 -0
- package/dist/detectors/objc.js +146 -0
- package/dist/detectors/objc.js.map +1 -0
- package/dist/detectors/openpgp.d.ts +20 -0
- package/dist/detectors/openpgp.d.ts.map +1 -0
- package/dist/detectors/openpgp.js +151 -0
- package/dist/detectors/openpgp.js.map +1 -0
- package/dist/detectors/pem.d.ts.map +1 -1
- package/dist/detectors/pem.js +32 -0
- package/dist/detectors/pem.js.map +1 -1
- package/dist/detectors/php.d.ts +2 -1
- package/dist/detectors/php.d.ts.map +1 -1
- package/dist/detectors/php.js +7 -3
- package/dist/detectors/php.js.map +1 -1
- package/dist/detectors/pkcs11.d.ts +17 -0
- package/dist/detectors/pkcs11.d.ts.map +1 -0
- package/dist/detectors/pkcs11.js +87 -0
- package/dist/detectors/pkcs11.js.map +1 -0
- package/dist/detectors/proxy.d.ts +52 -0
- package/dist/detectors/proxy.d.ts.map +1 -0
- package/dist/detectors/proxy.js +157 -0
- package/dist/detectors/proxy.js.map +1 -0
- package/dist/detectors/pulumi.d.ts +19 -0
- package/dist/detectors/pulumi.d.ts.map +1 -0
- package/dist/detectors/pulumi.js +80 -0
- package/dist/detectors/pulumi.js.map +1 -0
- package/dist/detectors/rust.d.ts.map +1 -1
- package/dist/detectors/rust.js +5 -0
- package/dist/detectors/rust.js.map +1 -1
- package/dist/detectors/secrets.d.ts +4 -1
- package/dist/detectors/secrets.d.ts.map +1 -1
- package/dist/detectors/secrets.js +5 -21
- package/dist/detectors/secrets.js.map +1 -1
- package/dist/detectors/solidity.d.ts +50 -0
- package/dist/detectors/solidity.d.ts.map +1 -0
- package/dist/detectors/solidity.js +108 -0
- package/dist/detectors/solidity.js.map +1 -0
- package/dist/detectors/source.d.ts.map +1 -1
- package/dist/detectors/source.js +105 -6
- package/dist/detectors/source.js.map +1 -1
- package/dist/detectors/spire.d.ts +49 -0
- package/dist/detectors/spire.d.ts.map +1 -0
- package/dist/detectors/spire.js +77 -0
- package/dist/detectors/spire.js.map +1 -0
- package/dist/detectors/ssh-ca.d.ts +72 -0
- package/dist/detectors/ssh-ca.d.ts.map +1 -0
- package/dist/detectors/ssh-ca.js +119 -0
- package/dist/detectors/ssh-ca.js.map +1 -0
- package/dist/detectors/supply-chain.d.ts +17 -0
- package/dist/detectors/supply-chain.d.ts.map +1 -0
- package/dist/detectors/supply-chain.js +89 -0
- package/dist/detectors/supply-chain.js.map +1 -0
- package/dist/detectors/swift.d.ts +22 -0
- package/dist/detectors/swift.d.ts.map +1 -0
- package/dist/detectors/swift.js +120 -0
- package/dist/detectors/swift.js.map +1 -0
- package/dist/detectors/terraform.d.ts.map +1 -1
- package/dist/detectors/terraform.js +35 -8
- package/dist/detectors/terraform.js.map +1 -1
- package/dist/detectors/vault.d.ts +15 -0
- package/dist/detectors/vault.d.ts.map +1 -0
- package/dist/detectors/vault.js +97 -0
- package/dist/detectors/vault.js.map +1 -0
- package/dist/detectors/vpn.d.ts +8 -8
- package/dist/detectors/vpn.d.ts.map +1 -1
- package/dist/detectors/vpn.js +12 -38
- package/dist/detectors/vpn.js.map +1 -1
- package/dist/detectors/weak-hash.d.ts +71 -0
- package/dist/detectors/weak-hash.d.ts.map +1 -0
- package/dist/detectors/weak-hash.js +116 -0
- package/dist/detectors/weak-hash.js.map +1 -0
- package/dist/detectors/webauthn.d.ts +61 -0
- package/dist/detectors/webauthn.d.ts.map +1 -0
- package/dist/detectors/webauthn.js +109 -0
- package/dist/detectors/webauthn.js.map +1 -0
- package/dist/detectors/xmldsig.d.ts +23 -0
- package/dist/detectors/xmldsig.d.ts.map +1 -0
- package/dist/detectors/xmldsig.js +84 -0
- package/dist/detectors/xmldsig.js.map +1 -0
- package/dist/evidence.d.ts +74 -4
- package/dist/evidence.d.ts.map +1 -1
- package/dist/evidence.js +71 -0
- package/dist/evidence.js.map +1 -1
- package/dist/hndl.d.ts +241 -0
- package/dist/hndl.d.ts.map +1 -0
- package/dist/hndl.js +752 -0
- package/dist/hndl.js.map +1 -0
- package/dist/index.d.ts +18 -11
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +21 -11
- package/dist/index.js.map +1 -1
- package/dist/inventory.d.ts +0 -2
- package/dist/inventory.d.ts.map +1 -1
- package/dist/inventory.js +15 -6
- package/dist/inventory.js.map +1 -1
- package/dist/parallel.d.ts.map +1 -1
- package/dist/parallel.js +22 -23
- package/dist/parallel.js.map +1 -1
- package/dist/registry.d.ts +9 -4
- package/dist/registry.d.ts.map +1 -1
- package/dist/registry.js +51 -4
- package/dist/registry.js.map +1 -1
- package/dist/remediate-pipeline.d.ts +0 -3
- package/dist/remediate-pipeline.d.ts.map +1 -1
- package/dist/remediate-pipeline.js +1 -1
- package/dist/remediate-pipeline.js.map +1 -1
- package/dist/remediation.d.ts +10 -0
- package/dist/remediation.d.ts.map +1 -1
- package/dist/remediation.js +32 -0
- package/dist/remediation.js.map +1 -1
- package/dist/report.d.ts +17 -0
- package/dist/report.d.ts.map +1 -1
- package/dist/report.js +106 -20
- package/dist/report.js.map +1 -1
- package/dist/scan-worker.js +5 -3
- package/dist/scan-worker.js.map +1 -1
- package/dist/scan.d.ts.map +1 -1
- package/dist/scan.js +35 -6
- package/dist/scan.js.map +1 -1
- package/dist/standards-profiles.d.ts +58 -0
- package/dist/standards-profiles.d.ts.map +1 -0
- package/dist/standards-profiles.js +100 -0
- package/dist/standards-profiles.js.map +1 -0
- package/dist/types.d.ts +7 -6
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/version.js.map +1 -1
- package/dist/vex.d.ts +39 -0
- package/dist/vex.d.ts.map +1 -0
- package/dist/vex.js +87 -0
- package/dist/vex.js.map +1 -0
- package/dist/walk.d.ts +39 -4
- package/dist/walk.d.ts.map +1 -1
- package/dist/walk.js +62 -10
- package/dist/walk.js.map +1 -1
- package/package.json +1 -1
- package/src/cbom-merge.ts +38 -8
- package/src/cbom.ts +153 -39
- package/src/comments.ts +25 -6
- package/src/crypto-agility.ts +407 -0
- package/src/dependencies.ts +182 -16
- package/src/detect-utils.ts +133 -11
- package/src/detectors/age.ts +60 -0
- package/src/detectors/ansible.ts +140 -0
- package/src/detectors/bicep.ts +107 -0
- package/src/detectors/c.ts +31 -3
- package/src/detectors/cicd.ts +18 -5
- package/src/detectors/cloud-kms.ts +79 -25
- package/src/detectors/cloudformation.ts +45 -10
- package/src/detectors/codesign.ts +214 -0
- package/src/detectors/csharp.ts +13 -3
- package/src/detectors/dart.ts +237 -0
- package/src/detectors/database.ts +64 -11
- package/src/detectors/dkim.ts +172 -0
- package/src/detectors/dnssec.ts +19 -10
- package/src/detectors/elixir.ts +33 -2
- package/src/detectors/java.ts +37 -3
- package/src/detectors/jose.ts +19 -3
- package/src/detectors/jwk.ts +59 -17
- package/src/detectors/k8s.ts +5 -2
- package/src/detectors/keystore.ts +109 -0
- package/src/detectors/mesh.ts +21 -45
- package/src/detectors/messaging.ts +22 -14
- package/src/detectors/objc.ts +209 -0
- package/src/detectors/openpgp.ts +194 -0
- package/src/detectors/pem.ts +30 -0
- package/src/detectors/php.ts +9 -4
- package/src/detectors/pkcs11.ts +113 -0
- package/src/detectors/proxy.ts +236 -0
- package/src/detectors/pulumi.ts +106 -0
- package/src/detectors/rust.ts +5 -0
- package/src/detectors/secrets.ts +15 -26
- package/src/detectors/solidity.ts +176 -0
- package/src/detectors/source.ts +110 -5
- package/src/detectors/spire.ts +143 -0
- package/src/detectors/ssh-ca.ts +215 -0
- package/src/detectors/supply-chain.ts +117 -0
- package/src/detectors/swift.ts +149 -0
- package/src/detectors/terraform.ts +42 -8
- package/src/detectors/vault.ts +120 -0
- package/src/detectors/vpn.ts +20 -48
- package/src/detectors/weak-hash.ts +217 -0
- package/src/detectors/webauthn.ts +205 -0
- package/src/detectors/xmldsig.ts +115 -0
- package/src/evidence.ts +127 -4
- package/src/hndl.ts +1012 -0
- package/src/index.ts +93 -13
- package/src/inventory.ts +15 -7
- package/src/parallel.ts +21 -20
- package/src/registry.ts +51 -4
- package/src/remediate-pipeline.ts +1 -1
- package/src/remediation.ts +39 -0
- package/src/report.ts +120 -20
- package/src/scan-worker.ts +5 -3
- package/src/scan.ts +41 -6
- package/src/standards-profiles.ts +136 -0
- package/src/types.ts +18 -5
- package/src/version.ts +1 -1
- package/src/vex.ts +122 -0
- package/src/walk.ts +80 -12
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"messaging.js","sourceRoot":"","sources":["../../src/detectors/messaging.ts"],"names":[],"mappings":"AAaA,OAAO,EAAE,SAAS,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"messaging.js","sourceRoot":"","sources":["../../src/detectors/messaging.ts"],"names":[],"mappings":"AAaA,OAAO,EAAE,SAAS,EAAE,eAAe,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAChG,OAAO,EAAE,mBAAmB,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAC;AAEnE,MAAM,aAAa,GAAsB,CAAC,aAAa,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;AAOlF,MAAM,QAAQ,GAAa;IACzB;QACE,2EAA2E;QAC3E,sEAAsE;QACtE,EAAE,EAAE,8EAA8E;QAClF,IAAI,EAAE;YACJ,EAAE,EAAE,qBAAqB;YACzB,KAAK,EAAE,2BAA2B;YAClC,WAAW,EAAE,kEAAkE;YAC/E,QAAQ,EAAE,KAAK;YACf,QAAQ,EAAE,QAAQ;YAClB,UAAU,EAAE,MAAM;YAClB,IAAI,EAAE,KAAK;YACX,GAAG,EAAE,mBAAmB;YACxB,OAAO,EACL,0KAA0K;YAC5K,WAAW,EAAE,4DAA4D;SAC1E;KACF;IACD;QACE,EAAE,EAAE,0CAA0C;QAC9C,IAAI,EAAE;YACJ,EAAE,EAAE,oBAAoB;YACxB,KAAK,EAAE,yBAAyB;YAChC,WAAW,EAAE,kDAAkD;YAC/D,QAAQ,EAAE,KAAK;YACf,QAAQ,EAAE,QAAQ;YAClB,UAAU,EAAE,MAAM;YAClB,IAAI,EAAE,KAAK;YACX,GAAG,EAAE,mBAAmB;YACxB,OAAO,EACL,gJAAgJ;YAClJ,WAAW,EAAE,6DAA6D;SAC3E;KACF;IACD;QACE,gFAAgF;QAChF,kFAAkF;QAClF,qFAAqF;QACrF,EAAE,EAAE,2DAA2D;QAC/D,IAAI,EAAE;YACJ,EAAE,EAAE,sBAAsB;YAC1B,KAAK,EAAE,wCAAwC;YAC/C,WAAW,EACT,iFAAiF;YACnF,QAAQ,EAAE,KAAK;YACf,SAAS,EAAE,KAAK;YAChB,QAAQ,EAAE,QAAQ;YAClB,UAAU,EAAE,MAAM;YAClB,IAAI,EAAE,IAAI;YACV,GAAG,EAAE,iBAAiB;YACtB,OAAO,EACL,0KAA0K;YAC5K,WAAW,EACT,+FAA+F;SAClG;KACF;CACF,CAAC;AAEF,kFAAkF;AAClF,MAAM,CAAC,MAAM,iBAAiB,GAAa;IACzC,EAAE,EAAE,qBAAqB;IACzB,WAAW,EAAE,6EAA6E;IAC1F,KAAK,EAAE,QAAQ;IACf,QAAQ,EAAE,KAAK;IACf,KAAK,EAAE,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;IAClC,SAAS,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,YAAY,CAAC,CAAC,EAAE,aAAa,CAAC;IAChD,MAAM,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE;QACtB,MAAM,QAAQ,GAAc,EAAE,CAAC;QAC/B,2EAA2E;QAC3E,iFAAiF;QACjF,gEAAgE;QAChE,MAAM,IAAI,GAAG,gBAAgB,CAAC,OAAO,EAAE,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC,CAAC;QACxD,KAAK,MAAM,IAAI,IAAI,QAAQ,EAAE,CAAC;YAC5B,SAAS,CAAC,IAAI,CAAC,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,EAAE;gBAC7B,QAAQ,CAAC,IAAI,CACX,eAAe,CAAC,IAAI,CAAC,IAAI,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC,CAAC,KAAK,EAAE,WAAW,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CACxF,CAAC;YACJ,CAAC,CAAC,CAAC;QACL,CAAC;QACD,OAAO,QAAQ,CAAC;IAClB,CAAC;CACF,CAAC","sourcesContent":["/**\n * Config detector: classical transport crypto in message brokers / event streams\n * (Kafka, RabbitMQ, MQTT/Mosquitto, NATS). Broker traffic is \"communication\n * between things\"; a legacy TLS floor or a classical (EC)DHE cipher suite makes\n * every message in flight harvest-now-decrypt-later exposed.\n *\n * Covered in broker config files (`.properties`, `.conf`, `.cfg`, `.ini`):\n * - Kafka `ssl.protocol` / `ssl.enabled.protocols = TLSv1 | TLSv1.1` (legacy).\n * - MQTT/Mosquitto `tls_version tlsv1 | tlsv1.1` (legacy).\n * - Kafka `ssl.cipher.suites` naming a static-RSA `TLS_RSA_WITH_*` key-transport\n * suite (the ECDHE/DHE suites are owned by source.ts's `tls-classical-kex`).\n */\nimport type { Detector, Finding, RuleMeta } from \"../types.js\";\nimport { eachMatch, findingFromRule, hasExtension, maskCommentLines } from \"../detect-utils.js\";\nimport { CWE_RISKY_PRIMITIVE, CWE_BROKEN_CRYPTO } from \"../cwe.js\";\n\nconst MQ_EXTENSIONS: readonly string[] = [\".properties\", \".conf\", \".cfg\", \".ini\"];\n\ninterface MqRule {\n re: RegExp;\n meta: RuleMeta;\n}\n\nconst MQ_RULES: MqRule[] = [\n {\n // Match TLSv1 (=1.0) and TLSv1.1 but never TLSv1.2 / TLSv1.3: the negative\n // lookahead stops \"TLSv1\" from matching the \"TLSv1\" inside \"TLSv1.3\".\n re: /\\bssl\\.(?:enabled\\.)?protocols?\\s*=\\s*[^\\n]{0,80}?\\bTLSv1(?:\\.1)?(?![.\\d])/gi,\n meta: {\n id: \"mq-kafka-legacy-tls\",\n title: \"Kafka legacy TLS protocol\",\n description: \"Kafka ssl.protocol / ssl.enabled.protocols permits TLS 1.0 / 1.1\",\n category: \"tls\",\n severity: \"medium\",\n confidence: \"high\",\n hndl: false,\n cwe: CWE_RISKY_PRIMITIVE,\n message:\n \"Kafka broker permits legacy TLS 1.0/1.1, an obsolete protocol; require TLS 1.3 (the harvestable classical key exchange is reported separately by the cipher-suite rule).\",\n remediation: \"Require TLS 1.3 and track PQC-hybrid KEX (X25519MLKEM768).\",\n },\n },\n {\n re: /\\btls_version\\s+tlsv1(?:\\.1)?(?![.\\d])/gi,\n meta: {\n id: \"mq-mqtt-legacy-tls\",\n title: \"MQTT legacy TLS version\",\n description: \"Mosquitto/MQTT tls_version permits TLS 1.0 / 1.1\",\n category: \"tls\",\n severity: \"medium\",\n confidence: \"high\",\n hndl: false,\n cwe: CWE_RISKY_PRIMITIVE,\n message:\n \"MQTT broker permits legacy TLS 1.0/1.1, an obsolete protocol; require TLS 1.3 (the harvestable classical key exchange is reported separately).\",\n remediation: \"Require TLS 1.3 and track PQC-hybrid KEX for device fleets.\",\n },\n },\n {\n // Only static-RSA key transport (`TLS_RSA_WITH_…`) is flagged here: the ECDHE /\n // DHE suites are owned by source.ts's language-agnostic `tls-classical-kex` token\n // rule (which fires on `.properties` too), so flagging them here would double-count.\n re: /\\bssl\\.cipher\\.suites\\s*=\\s*[^\\n]{0,200}?\\bTLS_RSA_WITH_/g,\n meta: {\n id: \"mq-rsa-key-transport\",\n title: \"Broker static-RSA key transport cipher\",\n description:\n \"Kafka ssl.cipher.suites names a static-RSA (TLS_RSA_WITH_*) key-transport suite\",\n category: \"kem\",\n algorithm: \"RSA\",\n severity: \"medium\",\n confidence: \"high\",\n hndl: true,\n cwe: CWE_BROKEN_CRYPTO,\n message:\n \"Broker TLS is pinned to a static-RSA (TLS_RSA_WITH_*) key-transport suite; the wrapped session key is harvest-now-decrypt-later exposed (and it has no forward secrecy).\",\n remediation:\n \"Move to TLS 1.3 with a PQC-hybrid group (X25519MLKEM768) once the broker/runtime supports it.\",\n },\n },\n];\n\n/** Detects classical transport crypto in message-broker / event-stream config. */\nexport const messagingDetector: Detector = {\n id: \"messaging-transport\",\n description: \"Classical transport crypto in message brokers (Kafka, MQTT, RabbitMQ, NATS)\",\n scope: \"config\",\n language: \"any\",\n rules: MQ_RULES.map((r) => r.meta),\n appliesTo: (f) => hasExtension(f, MQ_EXTENSIONS),\n detect({ file, content }): Finding[] {\n const findings: Finding[] = [];\n // Broker config files ship large commented-out example blocks; a commented\n // directive is not active. Match over comment-masked content (offsets preserved,\n // so the snippet from the original `content` is still correct).\n const scan = maskCommentLines(content, [\"#\", \"!\", \";\"]);\n for (const rule of MQ_RULES) {\n eachMatch(rule.re, scan, (m) => {\n findings.push(\n findingFromRule(rule.meta, { file, content, index: m.index, matchLength: m[0].length }),\n );\n });\n }\n return findings;\n },\n};\n"]}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Source detector for classical asymmetric cryptography in Objective-C on Apple
|
|
3
|
+
* platforms (iOS / macOS / tvOS / watchOS), via the Security framework's
|
|
4
|
+
* `SecKey*` API (and, underneath it, CommonCrypto). This is the Objective-C
|
|
5
|
+
* sibling of the Swift pack: the same Security-framework surface, reached from
|
|
6
|
+
* `.m` / `.mm` translation units instead of `.swift`. Lexical, same strategy as
|
|
7
|
+
* the other language packs — the Security-framework identifiers
|
|
8
|
+
* (`kSecAttrKeyType*`, `kSecKeyAlgorithm*`) are distinctive enough to keep the
|
|
9
|
+
* false-positive risk low.
|
|
10
|
+
*
|
|
11
|
+
* WHY a dedicated Objective-C pack (and not the C/C++ pack):
|
|
12
|
+
* - The C/C++ pack (`c.ts`) matches OpenSSL / libsodium / Mbed TLS / wolfSSL
|
|
13
|
+
* symbols and is gated to `.c/.h/.cc/.cpp/...` (which INCLUDES `.h`). Apple's
|
|
14
|
+
* Security framework is a different API surface entirely, so a separate pack
|
|
15
|
+
* is the clean home for it.
|
|
16
|
+
* - Objective-C headers are `.h` — shared with C/C++. To avoid double-scanning
|
|
17
|
+
* a header the C pack already owns, this detector is gated to `.m` and `.mm`
|
|
18
|
+
* ONLY (implementation files). Declarations of `SecKey*` usage live in the
|
|
19
|
+
* implementation, not the interface, so no real coverage is lost.
|
|
20
|
+
*
|
|
21
|
+
* Security-framework surface mapped here:
|
|
22
|
+
* - Key generation attributes (`SecKeyCreateRandomKey` / `SecKeyGeneratePair`):
|
|
23
|
+
* `kSecAttrKeyTypeRSA` → RSA keygen (kem, hndl:true)
|
|
24
|
+
* `kSecAttrKeyTypeEC` / `…ECSECPrimeRandom` → EC keygen (ECDSA, signature,
|
|
25
|
+
* hndl:false — EC keys default
|
|
26
|
+
* to signing; conservative)
|
|
27
|
+
* - Signing / verifying (`SecKeyCreateSignature` / `SecKeyVerifySignature`):
|
|
28
|
+
* `kSecKeyAlgorithmRSASignature*` → RSA signature (hndl:false)
|
|
29
|
+
* `kSecKeyAlgorithmECDSASignature*` → ECDSA signature (hndl:false)
|
|
30
|
+
* - Encryption / key agreement (`SecKeyCreateEncryptedData` / `SecKeyCopyKeyExchangeResult`):
|
|
31
|
+
* `kSecKeyAlgorithmRSAEncryption*` → RSA encryption (kem, hndl:true)
|
|
32
|
+
* `kSecKeyAlgorithmECDHKeyExchange*` → ECDH key agreement (hndl:true)
|
|
33
|
+
*
|
|
34
|
+
* Comment suppression is handled inline here (Objective-C is a C-style comment
|
|
35
|
+
* language): C-style block comments and `//` line comments are masked before the
|
|
36
|
+
* regexes run, so commented-out crypto never fires.
|
|
37
|
+
*/
|
|
38
|
+
import type { Detector } from "../types.js";
|
|
39
|
+
/** Detects classical asymmetric crypto in Objective-C (Apple Security framework). */
|
|
40
|
+
export declare const objcDetector: Detector;
|
|
41
|
+
//# sourceMappingURL=objc.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"objc.d.ts","sourceRoot":"","sources":["../../src/detectors/objc.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AACH,OAAO,KAAK,EAAE,QAAQ,EAAqB,MAAM,aAAa,CAAC;AAuH/D,qFAAqF;AACrF,eAAO,MAAM,YAAY,EAAE,QAmD1B,CAAC"}
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
import { eachMatch, findingFromRule, hasExtension, maskBlockComments, maskCommentLines, OBJC_EXTENSIONS, } from "../detect-utils.js";
|
|
2
|
+
import { CWE_BROKEN_CRYPTO } from "../cwe.js";
|
|
3
|
+
// Security-framework key-type attributes (used with SecKeyCreateRandomKey /
|
|
4
|
+
// SecKeyGeneratePair). RSA vs EC. The EC form covers both the modern
|
|
5
|
+
// `kSecAttrKeyTypeECSECPrimeRandom` and the legacy `kSecAttrKeyTypeEC` alias.
|
|
6
|
+
const RE_OBJC_SEC_RSA = /\bkSecAttrKeyTypeRSA\b/g;
|
|
7
|
+
const RE_OBJC_SEC_EC = /\bkSecAttrKeyType(?:ECSECPrimeRandom|EC)\b/g;
|
|
8
|
+
// SecKey algorithm constants for signing / encryption / key agreement. The `\w*`
|
|
9
|
+
// tail captures the full family suffix (…PKCS1v15SHA256, …OAEPSHA256, …Standard,
|
|
10
|
+
// …CofactorX963SHA256, …) without needing to enumerate every variant.
|
|
11
|
+
const RE_OBJC_RSA_SIGN = /\bkSecKeyAlgorithmRSASignature\w*/g;
|
|
12
|
+
const RE_OBJC_RSA_ENCRYPT = /\bkSecKeyAlgorithmRSAEncryption\w*/g;
|
|
13
|
+
const RE_OBJC_ECDSA_SIGN = /\bkSecKeyAlgorithmECDSASignature\w*/g;
|
|
14
|
+
const RE_OBJC_ECDH = /\bkSecKeyAlgorithmECDHKeyExchange\w*/g;
|
|
15
|
+
const RULE_OBJC_SEC_RSA = {
|
|
16
|
+
id: "objc-seckey-rsa",
|
|
17
|
+
title: "Objective-C Security-framework RSA key",
|
|
18
|
+
description: "SecKeyCreateRandomKey / SecKeyGeneratePair with kSecAttrKeyTypeRSA",
|
|
19
|
+
category: "kem",
|
|
20
|
+
severity: "high",
|
|
21
|
+
confidence: "high",
|
|
22
|
+
algorithm: "RSA",
|
|
23
|
+
hndl: true,
|
|
24
|
+
cwe: CWE_BROKEN_CRYPTO,
|
|
25
|
+
message: "Security-framework RSA key (Objective-C) is classical and not quantum-safe.",
|
|
26
|
+
remediation: "Migrate to PQC as Apple's CryptoKit / Security add support: ML-KEM for encryption/key-agreement, ML-DSA for signatures.",
|
|
27
|
+
};
|
|
28
|
+
const RULE_OBJC_SEC_EC = {
|
|
29
|
+
id: "objc-seckey-ec",
|
|
30
|
+
title: "Objective-C Security-framework EC key",
|
|
31
|
+
description: "SecKeyCreateRandomKey / SecKeyGeneratePair with kSecAttrKeyTypeECSECPrimeRandom",
|
|
32
|
+
// An EC key at generation is AMBIGUOUS (it can feed ECDSA signing OR ECDH key
|
|
33
|
+
// agreement). The fleet convention (java-ec-keygen, python-ec-keygen, swift-sec-ec,
|
|
34
|
+
// cloud-kms-ec, …) classifies ambiguous EC keygen as key-exchange/ECDH/hndl:true —
|
|
35
|
+
// the HNDL-SAFE choice, so a possible key-agreement use is never under-reported.
|
|
36
|
+
category: "key-exchange",
|
|
37
|
+
severity: "high",
|
|
38
|
+
confidence: "high",
|
|
39
|
+
algorithm: "ECDH",
|
|
40
|
+
hndl: true,
|
|
41
|
+
cwe: CWE_BROKEN_CRYPTO,
|
|
42
|
+
message: "Security-framework EC key (Objective-C); EC keys feed ECDH key agreement (harvest-now-decrypt-later exposed) as well as ECDSA signatures, and are not quantum-safe.",
|
|
43
|
+
remediation: "Migrate to PQC as Apple's CryptoKit / Security add support: ML-KEM for the ECDH key-agreement path, ML-DSA for signatures.",
|
|
44
|
+
};
|
|
45
|
+
const RULE_OBJC_RSA_SIGN = {
|
|
46
|
+
id: "objc-rsa-sign",
|
|
47
|
+
title: "Objective-C RSA signature",
|
|
48
|
+
description: "SecKeyCreateSignature / SecKeyVerifySignature with kSecKeyAlgorithmRSASignature*",
|
|
49
|
+
category: "signature",
|
|
50
|
+
// `high`, consistent with every sibling pack's signature severity (swift-rsa,
|
|
51
|
+
// go-rsa-sign, java-rsa-sign) — the same primitive must not flip CI exit codes
|
|
52
|
+
// based on which language wrote it.
|
|
53
|
+
severity: "high",
|
|
54
|
+
confidence: "high",
|
|
55
|
+
algorithm: "RSA",
|
|
56
|
+
hndl: false,
|
|
57
|
+
cwe: CWE_BROKEN_CRYPTO,
|
|
58
|
+
message: "Classical RSA signing (Objective-C, Security framework) is forgeable by a quantum attacker.",
|
|
59
|
+
remediation: "Migrate to ML-DSA (FIPS 204) as Apple's CryptoKit / Security add PQC support.",
|
|
60
|
+
};
|
|
61
|
+
const RULE_OBJC_RSA_ENCRYPT = {
|
|
62
|
+
id: "objc-rsa-encrypt",
|
|
63
|
+
title: "Objective-C RSA encryption",
|
|
64
|
+
description: "SecKeyCreateEncryptedData with kSecKeyAlgorithmRSAEncryption*",
|
|
65
|
+
category: "kem",
|
|
66
|
+
severity: "high",
|
|
67
|
+
confidence: "high",
|
|
68
|
+
algorithm: "RSA",
|
|
69
|
+
hndl: true,
|
|
70
|
+
cwe: CWE_BROKEN_CRYPTO,
|
|
71
|
+
message: "Classical RSA encryption (Objective-C, Security framework) is harvest-now-decrypt-later exposed.",
|
|
72
|
+
remediation: "Migrate to ML-KEM (FIPS 203) as Apple's CryptoKit / Security add PQC support.",
|
|
73
|
+
};
|
|
74
|
+
const RULE_OBJC_ECDSA_SIGN = {
|
|
75
|
+
id: "objc-ecdsa-sign",
|
|
76
|
+
title: "Objective-C ECDSA signature",
|
|
77
|
+
description: "SecKeyCreateSignature / SecKeyVerifySignature with kSecKeyAlgorithmECDSASignature*",
|
|
78
|
+
category: "signature",
|
|
79
|
+
// `high`, consistent with swift-ecdsa / go-ecdsa / java-ecdsa-sign / dart-ecdsa.
|
|
80
|
+
severity: "high",
|
|
81
|
+
confidence: "high",
|
|
82
|
+
algorithm: "ECDSA",
|
|
83
|
+
hndl: false,
|
|
84
|
+
cwe: CWE_BROKEN_CRYPTO,
|
|
85
|
+
message: "Classical ECDSA signing (Objective-C, Security framework) is forgeable by a quantum attacker.",
|
|
86
|
+
remediation: "Migrate to ML-DSA (FIPS 204) as Apple's CryptoKit / Security add PQC support.",
|
|
87
|
+
};
|
|
88
|
+
const RULE_OBJC_ECDH = {
|
|
89
|
+
id: "objc-ecdh",
|
|
90
|
+
title: "Objective-C ECDH key agreement",
|
|
91
|
+
description: "SecKeyCopyKeyExchangeResult with kSecKeyAlgorithmECDHKeyExchange*",
|
|
92
|
+
category: "key-exchange",
|
|
93
|
+
severity: "high",
|
|
94
|
+
confidence: "high",
|
|
95
|
+
algorithm: "ECDH",
|
|
96
|
+
hndl: true,
|
|
97
|
+
cwe: CWE_BROKEN_CRYPTO,
|
|
98
|
+
message: "Elliptic-curve Diffie-Hellman (Objective-C, Security framework) is broken by Shor's algorithm (harvest-now-decrypt-later).",
|
|
99
|
+
remediation: "Migrate key agreement to ML-KEM (FIPS 203) as Apple's CryptoKit / Security add PQC support.",
|
|
100
|
+
};
|
|
101
|
+
/** Detects classical asymmetric crypto in Objective-C (Apple Security framework). */
|
|
102
|
+
export const objcDetector = {
|
|
103
|
+
id: "objc-crypto",
|
|
104
|
+
description: "Classical asymmetric crypto in Objective-C (Apple Security framework / SecKey)",
|
|
105
|
+
scope: "source",
|
|
106
|
+
language: "objc",
|
|
107
|
+
rules: [
|
|
108
|
+
RULE_OBJC_SEC_RSA,
|
|
109
|
+
RULE_OBJC_SEC_EC,
|
|
110
|
+
RULE_OBJC_RSA_SIGN,
|
|
111
|
+
RULE_OBJC_RSA_ENCRYPT,
|
|
112
|
+
RULE_OBJC_ECDSA_SIGN,
|
|
113
|
+
RULE_OBJC_ECDH,
|
|
114
|
+
],
|
|
115
|
+
appliesTo: (f) => hasExtension(f, OBJC_EXTENSIONS),
|
|
116
|
+
detect({ file, content }) {
|
|
117
|
+
// Fast reject: none of the Security-framework surface can appear without one
|
|
118
|
+
// of these substrings, so most Objective-C files bail before any regex runs.
|
|
119
|
+
if (!content.includes("SecKey") &&
|
|
120
|
+
!content.includes("kSecAttrKeyType") &&
|
|
121
|
+
!content.includes("kSecKeyAlgorithm")) {
|
|
122
|
+
return [];
|
|
123
|
+
}
|
|
124
|
+
// Mask C-style block comments then `//` line comments so commented-out crypto
|
|
125
|
+
// does not fire. Both maskers preserve byte offsets, so finding line/column
|
|
126
|
+
// stay exact.
|
|
127
|
+
const masked = maskCommentLines(maskBlockComments(content), ["//"]);
|
|
128
|
+
const findings = [];
|
|
129
|
+
// Scan the masked text, but build the finding (its snippet) from the ORIGINAL
|
|
130
|
+
// `content` so a line with a trailing comment renders live, not blanked.
|
|
131
|
+
const add = (re, rule) => eachMatch(re, masked, (m) => findings.push(findingFromRule(rule, {
|
|
132
|
+
file,
|
|
133
|
+
content,
|
|
134
|
+
index: m.index,
|
|
135
|
+
matchLength: m[0].length,
|
|
136
|
+
})));
|
|
137
|
+
add(RE_OBJC_SEC_RSA, RULE_OBJC_SEC_RSA);
|
|
138
|
+
add(RE_OBJC_SEC_EC, RULE_OBJC_SEC_EC);
|
|
139
|
+
add(RE_OBJC_RSA_SIGN, RULE_OBJC_RSA_SIGN);
|
|
140
|
+
add(RE_OBJC_RSA_ENCRYPT, RULE_OBJC_RSA_ENCRYPT);
|
|
141
|
+
add(RE_OBJC_ECDSA_SIGN, RULE_OBJC_ECDSA_SIGN);
|
|
142
|
+
add(RE_OBJC_ECDH, RULE_OBJC_ECDH);
|
|
143
|
+
return findings;
|
|
144
|
+
},
|
|
145
|
+
};
|
|
146
|
+
//# sourceMappingURL=objc.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"objc.js","sourceRoot":"","sources":["../../src/detectors/objc.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Source detector for classical asymmetric cryptography in Objective-C on Apple\n * platforms (iOS / macOS / tvOS / watchOS), via the Security framework's\n * `SecKey*` API (and, underneath it, CommonCrypto). This is the Objective-C\n * sibling of the Swift pack: the same Security-framework surface, reached from\n * `.m` / `.mm` translation units instead of `.swift`. Lexical, same strategy as\n * the other language packs — the Security-framework identifiers\n * (`kSecAttrKeyType*`, `kSecKeyAlgorithm*`) are distinctive enough to keep the\n * false-positive risk low.\n *\n * WHY a dedicated Objective-C pack (and not the C/C++ pack):\n * - The C/C++ pack (`c.ts`) matches OpenSSL / libsodium / Mbed TLS / wolfSSL\n * symbols and is gated to `.c/.h/.cc/.cpp/...` (which INCLUDES `.h`). Apple's\n * Security framework is a different API surface entirely, so a separate pack\n * is the clean home for it.\n * - Objective-C headers are `.h` — shared with C/C++. To avoid double-scanning\n * a header the C pack already owns, this detector is gated to `.m` and `.mm`\n * ONLY (implementation files). Declarations of `SecKey*` usage live in the\n * implementation, not the interface, so no real coverage is lost.\n *\n * Security-framework surface mapped here:\n * - Key generation attributes (`SecKeyCreateRandomKey` / `SecKeyGeneratePair`):\n * `kSecAttrKeyTypeRSA` → RSA keygen (kem, hndl:true)\n * `kSecAttrKeyTypeEC` / `…ECSECPrimeRandom` → EC keygen (ECDSA, signature,\n * hndl:false — EC keys default\n * to signing; conservative)\n * - Signing / verifying (`SecKeyCreateSignature` / `SecKeyVerifySignature`):\n * `kSecKeyAlgorithmRSASignature*` → RSA signature (hndl:false)\n * `kSecKeyAlgorithmECDSASignature*` → ECDSA signature (hndl:false)\n * - Encryption / key agreement (`SecKeyCreateEncryptedData` / `SecKeyCopyKeyExchangeResult`):\n * `kSecKeyAlgorithmRSAEncryption*` → RSA encryption (kem, hndl:true)\n * `kSecKeyAlgorithmECDHKeyExchange*` → ECDH key agreement (hndl:true)\n *\n * Comment suppression is handled inline here (Objective-C is a C-style comment\n * language): C-style block comments and `//` line comments are masked before the\n * regexes run, so commented-out crypto never fires.\n */\nimport type { Detector, Finding, RuleMeta } from \"../types.js\";\nimport {\n eachMatch,\n findingFromRule,\n hasExtension,\n maskBlockComments,\n maskCommentLines,\n OBJC_EXTENSIONS,\n} from \"../detect-utils.js\";\nimport { CWE_BROKEN_CRYPTO } from \"../cwe.js\";\n\n// Security-framework key-type attributes (used with SecKeyCreateRandomKey /\n// SecKeyGeneratePair). RSA vs EC. The EC form covers both the modern\n// `kSecAttrKeyTypeECSECPrimeRandom` and the legacy `kSecAttrKeyTypeEC` alias.\nconst RE_OBJC_SEC_RSA = /\\bkSecAttrKeyTypeRSA\\b/g;\nconst RE_OBJC_SEC_EC = /\\bkSecAttrKeyType(?:ECSECPrimeRandom|EC)\\b/g;\n// SecKey algorithm constants for signing / encryption / key agreement. The `\\w*`\n// tail captures the full family suffix (…PKCS1v15SHA256, …OAEPSHA256, …Standard,\n// …CofactorX963SHA256, …) without needing to enumerate every variant.\nconst RE_OBJC_RSA_SIGN = /\\bkSecKeyAlgorithmRSASignature\\w*/g;\nconst RE_OBJC_RSA_ENCRYPT = /\\bkSecKeyAlgorithmRSAEncryption\\w*/g;\nconst RE_OBJC_ECDSA_SIGN = /\\bkSecKeyAlgorithmECDSASignature\\w*/g;\nconst RE_OBJC_ECDH = /\\bkSecKeyAlgorithmECDHKeyExchange\\w*/g;\n\nconst RULE_OBJC_SEC_RSA: RuleMeta = {\n id: \"objc-seckey-rsa\",\n title: \"Objective-C Security-framework RSA key\",\n description: \"SecKeyCreateRandomKey / SecKeyGeneratePair with kSecAttrKeyTypeRSA\",\n category: \"kem\",\n severity: \"high\",\n confidence: \"high\",\n algorithm: \"RSA\",\n hndl: true,\n cwe: CWE_BROKEN_CRYPTO,\n message: \"Security-framework RSA key (Objective-C) is classical and not quantum-safe.\",\n remediation:\n \"Migrate to PQC as Apple's CryptoKit / Security add support: ML-KEM for encryption/key-agreement, ML-DSA for signatures.\",\n};\nconst RULE_OBJC_SEC_EC: RuleMeta = {\n id: \"objc-seckey-ec\",\n title: \"Objective-C Security-framework EC key\",\n description: \"SecKeyCreateRandomKey / SecKeyGeneratePair with kSecAttrKeyTypeECSECPrimeRandom\",\n // An EC key at generation is AMBIGUOUS (it can feed ECDSA signing OR ECDH key\n // agreement). The fleet convention (java-ec-keygen, python-ec-keygen, swift-sec-ec,\n // cloud-kms-ec, …) classifies ambiguous EC keygen as key-exchange/ECDH/hndl:true —\n // the HNDL-SAFE choice, so a possible key-agreement use is never under-reported.\n category: \"key-exchange\",\n severity: \"high\",\n confidence: \"high\",\n algorithm: \"ECDH\",\n hndl: true,\n cwe: CWE_BROKEN_CRYPTO,\n message:\n \"Security-framework EC key (Objective-C); EC keys feed ECDH key agreement (harvest-now-decrypt-later exposed) as well as ECDSA signatures, and are not quantum-safe.\",\n remediation:\n \"Migrate to PQC as Apple's CryptoKit / Security add support: ML-KEM for the ECDH key-agreement path, ML-DSA for signatures.\",\n};\nconst RULE_OBJC_RSA_SIGN: RuleMeta = {\n id: \"objc-rsa-sign\",\n title: \"Objective-C RSA signature\",\n description: \"SecKeyCreateSignature / SecKeyVerifySignature with kSecKeyAlgorithmRSASignature*\",\n category: \"signature\",\n // `high`, consistent with every sibling pack's signature severity (swift-rsa,\n // go-rsa-sign, java-rsa-sign) — the same primitive must not flip CI exit codes\n // based on which language wrote it.\n severity: \"high\",\n confidence: \"high\",\n algorithm: \"RSA\",\n hndl: false,\n cwe: CWE_BROKEN_CRYPTO,\n message:\n \"Classical RSA signing (Objective-C, Security framework) is forgeable by a quantum attacker.\",\n remediation: \"Migrate to ML-DSA (FIPS 204) as Apple's CryptoKit / Security add PQC support.\",\n};\nconst RULE_OBJC_RSA_ENCRYPT: RuleMeta = {\n id: \"objc-rsa-encrypt\",\n title: \"Objective-C RSA encryption\",\n description: \"SecKeyCreateEncryptedData with kSecKeyAlgorithmRSAEncryption*\",\n category: \"kem\",\n severity: \"high\",\n confidence: \"high\",\n algorithm: \"RSA\",\n hndl: true,\n cwe: CWE_BROKEN_CRYPTO,\n message:\n \"Classical RSA encryption (Objective-C, Security framework) is harvest-now-decrypt-later exposed.\",\n remediation: \"Migrate to ML-KEM (FIPS 203) as Apple's CryptoKit / Security add PQC support.\",\n};\nconst RULE_OBJC_ECDSA_SIGN: RuleMeta = {\n id: \"objc-ecdsa-sign\",\n title: \"Objective-C ECDSA signature\",\n description: \"SecKeyCreateSignature / SecKeyVerifySignature with kSecKeyAlgorithmECDSASignature*\",\n category: \"signature\",\n // `high`, consistent with swift-ecdsa / go-ecdsa / java-ecdsa-sign / dart-ecdsa.\n severity: \"high\",\n confidence: \"high\",\n algorithm: \"ECDSA\",\n hndl: false,\n cwe: CWE_BROKEN_CRYPTO,\n message:\n \"Classical ECDSA signing (Objective-C, Security framework) is forgeable by a quantum attacker.\",\n remediation: \"Migrate to ML-DSA (FIPS 204) as Apple's CryptoKit / Security add PQC support.\",\n};\nconst RULE_OBJC_ECDH: RuleMeta = {\n id: \"objc-ecdh\",\n title: \"Objective-C ECDH key agreement\",\n description: \"SecKeyCopyKeyExchangeResult with kSecKeyAlgorithmECDHKeyExchange*\",\n category: \"key-exchange\",\n severity: \"high\",\n confidence: \"high\",\n algorithm: \"ECDH\",\n hndl: true,\n cwe: CWE_BROKEN_CRYPTO,\n message:\n \"Elliptic-curve Diffie-Hellman (Objective-C, Security framework) is broken by Shor's algorithm (harvest-now-decrypt-later).\",\n remediation:\n \"Migrate key agreement to ML-KEM (FIPS 203) as Apple's CryptoKit / Security add PQC support.\",\n};\n\n/** Detects classical asymmetric crypto in Objective-C (Apple Security framework). */\nexport const objcDetector: Detector = {\n id: \"objc-crypto\",\n description: \"Classical asymmetric crypto in Objective-C (Apple Security framework / SecKey)\",\n scope: \"source\",\n language: \"objc\",\n rules: [\n RULE_OBJC_SEC_RSA,\n RULE_OBJC_SEC_EC,\n RULE_OBJC_RSA_SIGN,\n RULE_OBJC_RSA_ENCRYPT,\n RULE_OBJC_ECDSA_SIGN,\n RULE_OBJC_ECDH,\n ],\n appliesTo: (f) => hasExtension(f, OBJC_EXTENSIONS),\n detect({ file, content }): Finding[] {\n // Fast reject: none of the Security-framework surface can appear without one\n // of these substrings, so most Objective-C files bail before any regex runs.\n if (\n !content.includes(\"SecKey\") &&\n !content.includes(\"kSecAttrKeyType\") &&\n !content.includes(\"kSecKeyAlgorithm\")\n ) {\n return [];\n }\n // Mask C-style block comments then `//` line comments so commented-out crypto\n // does not fire. Both maskers preserve byte offsets, so finding line/column\n // stay exact.\n const masked = maskCommentLines(maskBlockComments(content), [\"//\"]);\n\n const findings: Finding[] = [];\n // Scan the masked text, but build the finding (its snippet) from the ORIGINAL\n // `content` so a line with a trailing comment renders live, not blanked.\n const add = (re: RegExp, rule: RuleMeta) =>\n eachMatch(re, masked, (m) =>\n findings.push(\n findingFromRule(rule, {\n file,\n content,\n index: m.index,\n matchLength: m[0].length,\n }),\n ),\n );\n add(RE_OBJC_SEC_RSA, RULE_OBJC_SEC_RSA);\n add(RE_OBJC_SEC_EC, RULE_OBJC_SEC_EC);\n add(RE_OBJC_RSA_SIGN, RULE_OBJC_RSA_SIGN);\n add(RE_OBJC_RSA_ENCRYPT, RULE_OBJC_RSA_ENCRYPT);\n add(RE_OBJC_ECDSA_SIGN, RULE_OBJC_ECDSA_SIGN);\n add(RE_OBJC_ECDH, RULE_OBJC_ECDH);\n return findings;\n },\n};\n"]}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Config detector: BINARY OpenPGP key material and encrypted messages committed to
|
|
3
|
+
* a repository — `secring.gpg` / `pubring.gpg` / `.gpg` / `.pgp` / `.kbx`. Armored
|
|
4
|
+
* blocks (`-----BEGIN PGP …-----`, base64 text) are handled by the PEM/secrets
|
|
5
|
+
* detectors; this covers the BINARY packet form, which the scan pipeline now reads
|
|
6
|
+
* byte-preserving (latin1; see {@link ../walk.ts isKeystorePath}) so we can read
|
|
7
|
+
* packet tags and the public-key algorithm id.
|
|
8
|
+
*
|
|
9
|
+
* A committed OpenPGP SECRET key is the sharp finding: a classical private key
|
|
10
|
+
* (RSA/DSA/ElGamal/ECDSA/EdDSA/ECDH) in version control is exposed and
|
|
11
|
+
* retroactively un-fixable. A binary PGP-encrypted message (PKESK) is
|
|
12
|
+
* harvest-now-decrypt-later exposed. Secret-key matches are sensitive material, so
|
|
13
|
+
* reporters drop the (binary) snippet.
|
|
14
|
+
*
|
|
15
|
+
* Packet framing per RFC 4880/9580. The parser is pure and bounds-checked (fuzzed).
|
|
16
|
+
*/
|
|
17
|
+
import type { Detector } from "../types.js";
|
|
18
|
+
/** Detects binary OpenPGP key material / messages and GnuPG keyboxes. */
|
|
19
|
+
export declare const openpgpDetector: Detector;
|
|
20
|
+
//# sourceMappingURL=openpgp.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"openpgp.d.ts","sourceRoot":"","sources":["../../src/detectors/openpgp.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AACH,OAAO,KAAK,EAAmB,QAAQ,EAAqB,MAAM,aAAa,CAAC;AAyIhF,yEAAyE;AACzE,eAAO,MAAM,eAAe,EAAE,QAuC7B,CAAC"}
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import { findingFromRule, hasExtension } from "../detect-utils.js";
|
|
2
|
+
import { CWE_HARDCODED_KEY, CWE_BROKEN_CRYPTO } from "../cwe.js";
|
|
3
|
+
const OPENPGP_EXTENSIONS = [".gpg", ".pgp"];
|
|
4
|
+
// Packet tags we care about.
|
|
5
|
+
const TAG_PKESK = 1; // public-key encrypted session key (an encrypted message)
|
|
6
|
+
const TAG_SECRET_KEY = 5;
|
|
7
|
+
const TAG_PUBLIC_KEY = 6;
|
|
8
|
+
const TAG_SECRET_SUBKEY = 7;
|
|
9
|
+
const TAG_PUBLIC_SUBKEY = 14;
|
|
10
|
+
/** Parse the FIRST OpenPGP packet header; return its tag and body offset. */
|
|
11
|
+
function firstPacket(content) {
|
|
12
|
+
if (content.length < 2)
|
|
13
|
+
return undefined;
|
|
14
|
+
const b0 = content.charCodeAt(0);
|
|
15
|
+
if ((b0 & 0x80) === 0)
|
|
16
|
+
return undefined; // high bit always set on a packet tag
|
|
17
|
+
if (b0 & 0x40) {
|
|
18
|
+
// New-format packet: tag is the low 6 bits; length octet(s) follow.
|
|
19
|
+
const tag = b0 & 0x3f;
|
|
20
|
+
const l = content.charCodeAt(1);
|
|
21
|
+
let bodyOffset;
|
|
22
|
+
if (l < 192)
|
|
23
|
+
bodyOffset = 2;
|
|
24
|
+
else if (l < 224)
|
|
25
|
+
bodyOffset = 3;
|
|
26
|
+
else if (l === 255)
|
|
27
|
+
bodyOffset = 6;
|
|
28
|
+
else
|
|
29
|
+
bodyOffset = 2; // partial-body length
|
|
30
|
+
return { tag, bodyOffset };
|
|
31
|
+
}
|
|
32
|
+
// Old-format packet: tag in bits 5-2; length-type in the low 2 bits.
|
|
33
|
+
const tag = (b0 >> 2) & 0x0f;
|
|
34
|
+
const lt = b0 & 0x03;
|
|
35
|
+
const bodyOffset = lt === 0 ? 2 : lt === 1 ? 3 : lt === 2 ? 5 : 1;
|
|
36
|
+
return { tag, bodyOffset };
|
|
37
|
+
}
|
|
38
|
+
/** Map an OpenPGP public-key algorithm id to a family + HNDL (confidentiality) flag. */
|
|
39
|
+
function algo(id) {
|
|
40
|
+
switch (id) {
|
|
41
|
+
case 1:
|
|
42
|
+
case 2:
|
|
43
|
+
case 3:
|
|
44
|
+
return { family: "RSA", hndl: true }; // RSA (encrypt-capable)
|
|
45
|
+
case 16:
|
|
46
|
+
case 20:
|
|
47
|
+
return { family: "unknown", hndl: true }; // ElGamal (encrypt)
|
|
48
|
+
case 17:
|
|
49
|
+
return { family: "DSA", hndl: false }; // DSA (sign only)
|
|
50
|
+
case 18:
|
|
51
|
+
return { family: "ECDH", hndl: true }; // ECDH (encrypt)
|
|
52
|
+
case 19:
|
|
53
|
+
return { family: "ECDSA", hndl: false }; // ECDSA (sign only)
|
|
54
|
+
case 22:
|
|
55
|
+
return { family: "EdDSA", hndl: false }; // EdDSA (sign only)
|
|
56
|
+
// RFC 9580 (crypto-refresh) v6 algorithm ids.
|
|
57
|
+
case 25:
|
|
58
|
+
return { family: "X25519", hndl: true }; // X25519 (encrypt / key agreement)
|
|
59
|
+
case 26:
|
|
60
|
+
return { family: "X448", hndl: true }; // X448 (encrypt / key agreement)
|
|
61
|
+
case 27:
|
|
62
|
+
return { family: "EdDSA", hndl: false }; // Ed25519 (sign only)
|
|
63
|
+
case 28:
|
|
64
|
+
return { family: "EdDSA", hndl: false }; // Ed448 (sign only)
|
|
65
|
+
default:
|
|
66
|
+
return undefined;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
/** Read the public-key algorithm id from a key packet body, or undefined. */
|
|
70
|
+
function keyPacketAlgo(content, bodyOffset) {
|
|
71
|
+
if (bodyOffset >= content.length)
|
|
72
|
+
return undefined;
|
|
73
|
+
const version = content.charCodeAt(bodyOffset);
|
|
74
|
+
// v2/v3: version(1) created(4) validity(2) algo; v4/v5/v6: version(1) created(4) algo.
|
|
75
|
+
const algoOffset = version === 2 || version === 3 ? bodyOffset + 7 : bodyOffset + 5;
|
|
76
|
+
if (algoOffset >= content.length)
|
|
77
|
+
return undefined;
|
|
78
|
+
return content.charCodeAt(algoOffset);
|
|
79
|
+
}
|
|
80
|
+
/** Read the wrapping algorithm from a PKESK (encrypted-message) packet body. */
|
|
81
|
+
function pkeskAlgo(content, bodyOffset) {
|
|
82
|
+
if (bodyOffset >= content.length)
|
|
83
|
+
return undefined;
|
|
84
|
+
const version = content.charCodeAt(bodyOffset);
|
|
85
|
+
// v3: version(1) keyid(8) algo. (v6 differs; v3 dominates on-disk.)
|
|
86
|
+
if (version !== 3)
|
|
87
|
+
return undefined;
|
|
88
|
+
const algoOffset = bodyOffset + 9;
|
|
89
|
+
if (algoOffset >= content.length)
|
|
90
|
+
return undefined;
|
|
91
|
+
return content.charCodeAt(algoOffset);
|
|
92
|
+
}
|
|
93
|
+
function rule(id, title, message, opts) {
|
|
94
|
+
return {
|
|
95
|
+
id,
|
|
96
|
+
title,
|
|
97
|
+
description: message,
|
|
98
|
+
category: "certificate",
|
|
99
|
+
severity: "high",
|
|
100
|
+
confidence: "high",
|
|
101
|
+
hndl: false,
|
|
102
|
+
cwe: CWE_HARDCODED_KEY,
|
|
103
|
+
message,
|
|
104
|
+
remediation: "Remove key material from version control and rotate it; store keys in a secret manager / HSM. Plan re-issuance with PQC keys (ML-DSA-65 / ML-KEM-768) as OpenPGP tooling supports them.",
|
|
105
|
+
...opts,
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
const RULE_SECRET = rule("openpgp-secret-key", "OpenPGP secret key (binary)", "A binary OpenPGP SECRET key is committed to version control — a classical private key that is exposed and retroactively un-fixable.", { sensitive: true });
|
|
109
|
+
const RULE_PUBLIC = rule("openpgp-public-key", "OpenPGP public key (binary)", "A binary OpenPGP public key uses classical asymmetric crypto (RSA/DSA/EC); plan a PQC migration path.", { severity: "medium" });
|
|
110
|
+
const RULE_PKESK = rule("openpgp-encrypted-message", "OpenPGP-encrypted message (binary)", "A binary OpenPGP-encrypted message wraps its session key with classical asymmetric crypto (RSA/ElGamal/ECDH); harvest-now-decrypt-later exposed.", { category: "kem", hndl: true, cwe: CWE_BROKEN_CRYPTO });
|
|
111
|
+
const RULE_KEYBOX = rule("openpgp-keybox", "GnuPG keybox (.kbx)", "A GnuPG keybox (.kbx) is a database of classical OpenPGP/X.509 keys and certificates.", { severity: "medium", cwe: CWE_BROKEN_CRYPTO });
|
|
112
|
+
/** Detects binary OpenPGP key material / messages and GnuPG keyboxes. */
|
|
113
|
+
export const openpgpDetector = {
|
|
114
|
+
id: "openpgp-material",
|
|
115
|
+
description: "Binary OpenPGP key material / encrypted messages and GnuPG keyboxes",
|
|
116
|
+
scope: "config",
|
|
117
|
+
language: "any",
|
|
118
|
+
rules: [RULE_SECRET, RULE_PUBLIC, RULE_PKESK, RULE_KEYBOX],
|
|
119
|
+
appliesTo: (f) => hasExtension(f, OPENPGP_EXTENSIONS) || f.toLowerCase().endsWith(".kbx"),
|
|
120
|
+
detect({ file, content }) {
|
|
121
|
+
const at = { file, content, index: 0, matchLength: Math.min(4, content.length) };
|
|
122
|
+
// GnuPG keybox: a key/cert database, identified by the "KBXf" magic in its
|
|
123
|
+
// first blob header (so a garbage/empty .kbx does not fire).
|
|
124
|
+
if (file.toLowerCase().endsWith(".kbx")) {
|
|
125
|
+
return content.includes("KBXf") ? [findingFromRule(RULE_KEYBOX, at)] : [];
|
|
126
|
+
}
|
|
127
|
+
const pkt = firstPacket(content);
|
|
128
|
+
if (!pkt)
|
|
129
|
+
return [];
|
|
130
|
+
const { tag, bodyOffset } = pkt;
|
|
131
|
+
if (tag === TAG_SECRET_KEY || tag === TAG_SECRET_SUBKEY) {
|
|
132
|
+
const id = keyPacketAlgo(content, bodyOffset);
|
|
133
|
+
const a = id !== undefined ? algo(id) : undefined;
|
|
134
|
+
return [
|
|
135
|
+
findingFromRule(RULE_SECRET, at, a ? { algorithm: a.family, hndl: a.hndl } : undefined),
|
|
136
|
+
];
|
|
137
|
+
}
|
|
138
|
+
if (tag === TAG_PUBLIC_KEY || tag === TAG_PUBLIC_SUBKEY) {
|
|
139
|
+
const id = keyPacketAlgo(content, bodyOffset);
|
|
140
|
+
const a = id !== undefined ? algo(id) : undefined;
|
|
141
|
+
return [findingFromRule(RULE_PUBLIC, at, a ? { algorithm: a.family } : undefined)];
|
|
142
|
+
}
|
|
143
|
+
if (tag === TAG_PKESK) {
|
|
144
|
+
const id = pkeskAlgo(content, bodyOffset);
|
|
145
|
+
const a = id !== undefined ? algo(id) : undefined;
|
|
146
|
+
return [findingFromRule(RULE_PKESK, at, a ? { algorithm: a.family } : undefined)];
|
|
147
|
+
}
|
|
148
|
+
return [];
|
|
149
|
+
},
|
|
150
|
+
};
|
|
151
|
+
//# sourceMappingURL=openpgp.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"openpgp.js","sourceRoot":"","sources":["../../src/detectors/openpgp.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Config detector: BINARY OpenPGP key material and encrypted messages committed to\n * a repository — `secring.gpg` / `pubring.gpg` / `.gpg` / `.pgp` / `.kbx`. Armored\n * blocks (`-----BEGIN PGP …-----`, base64 text) are handled by the PEM/secrets\n * detectors; this covers the BINARY packet form, which the scan pipeline now reads\n * byte-preserving (latin1; see {@link ../walk.ts isKeystorePath}) so we can read\n * packet tags and the public-key algorithm id.\n *\n * A committed OpenPGP SECRET key is the sharp finding: a classical private key\n * (RSA/DSA/ElGamal/ECDSA/EdDSA/ECDH) in version control is exposed and\n * retroactively un-fixable. A binary PGP-encrypted message (PKESK) is\n * harvest-now-decrypt-later exposed. Secret-key matches are sensitive material, so\n * reporters drop the (binary) snippet.\n *\n * Packet framing per RFC 4880/9580. The parser is pure and bounds-checked (fuzzed).\n */\nimport type { AlgorithmFamily, Detector, Finding, RuleMeta } from \"../types.js\";\nimport { findingFromRule, hasExtension } from \"../detect-utils.js\";\nimport { CWE_HARDCODED_KEY, CWE_BROKEN_CRYPTO } from \"../cwe.js\";\n\nconst OPENPGP_EXTENSIONS: readonly string[] = [\".gpg\", \".pgp\"];\n\n// Packet tags we care about.\nconst TAG_PKESK = 1; // public-key encrypted session key (an encrypted message)\nconst TAG_SECRET_KEY = 5;\nconst TAG_PUBLIC_KEY = 6;\nconst TAG_SECRET_SUBKEY = 7;\nconst TAG_PUBLIC_SUBKEY = 14;\n\ninterface Packet {\n tag: number;\n /** Offset where the packet body begins (after the length octets). */\n bodyOffset: number;\n}\n\n/** Parse the FIRST OpenPGP packet header; return its tag and body offset. */\nfunction firstPacket(content: string): Packet | undefined {\n if (content.length < 2) return undefined;\n const b0 = content.charCodeAt(0);\n if ((b0 & 0x80) === 0) return undefined; // high bit always set on a packet tag\n if (b0 & 0x40) {\n // New-format packet: tag is the low 6 bits; length octet(s) follow.\n const tag = b0 & 0x3f;\n const l = content.charCodeAt(1);\n let bodyOffset: number;\n if (l < 192) bodyOffset = 2;\n else if (l < 224) bodyOffset = 3;\n else if (l === 255) bodyOffset = 6;\n else bodyOffset = 2; // partial-body length\n return { tag, bodyOffset };\n }\n // Old-format packet: tag in bits 5-2; length-type in the low 2 bits.\n const tag = (b0 >> 2) & 0x0f;\n const lt = b0 & 0x03;\n const bodyOffset = lt === 0 ? 2 : lt === 1 ? 3 : lt === 2 ? 5 : 1;\n return { tag, bodyOffset };\n}\n\n/** Map an OpenPGP public-key algorithm id to a family + HNDL (confidentiality) flag. */\nfunction algo(id: number): { family: AlgorithmFamily; hndl: boolean } | undefined {\n switch (id) {\n case 1:\n case 2:\n case 3:\n return { family: \"RSA\", hndl: true }; // RSA (encrypt-capable)\n case 16:\n case 20:\n return { family: \"unknown\", hndl: true }; // ElGamal (encrypt)\n case 17:\n return { family: \"DSA\", hndl: false }; // DSA (sign only)\n case 18:\n return { family: \"ECDH\", hndl: true }; // ECDH (encrypt)\n case 19:\n return { family: \"ECDSA\", hndl: false }; // ECDSA (sign only)\n case 22:\n return { family: \"EdDSA\", hndl: false }; // EdDSA (sign only)\n // RFC 9580 (crypto-refresh) v6 algorithm ids.\n case 25:\n return { family: \"X25519\", hndl: true }; // X25519 (encrypt / key agreement)\n case 26:\n return { family: \"X448\", hndl: true }; // X448 (encrypt / key agreement)\n case 27:\n return { family: \"EdDSA\", hndl: false }; // Ed25519 (sign only)\n case 28:\n return { family: \"EdDSA\", hndl: false }; // Ed448 (sign only)\n default:\n return undefined;\n }\n}\n\n/** Read the public-key algorithm id from a key packet body, or undefined. */\nfunction keyPacketAlgo(content: string, bodyOffset: number): number | undefined {\n if (bodyOffset >= content.length) return undefined;\n const version = content.charCodeAt(bodyOffset);\n // v2/v3: version(1) created(4) validity(2) algo; v4/v5/v6: version(1) created(4) algo.\n const algoOffset = version === 2 || version === 3 ? bodyOffset + 7 : bodyOffset + 5;\n if (algoOffset >= content.length) return undefined;\n return content.charCodeAt(algoOffset);\n}\n\n/** Read the wrapping algorithm from a PKESK (encrypted-message) packet body. */\nfunction pkeskAlgo(content: string, bodyOffset: number): number | undefined {\n if (bodyOffset >= content.length) return undefined;\n const version = content.charCodeAt(bodyOffset);\n // v3: version(1) keyid(8) algo. (v6 differs; v3 dominates on-disk.)\n if (version !== 3) return undefined;\n const algoOffset = bodyOffset + 9;\n if (algoOffset >= content.length) return undefined;\n return content.charCodeAt(algoOffset);\n}\n\nfunction rule(id: string, title: string, message: string, opts: Partial<RuleMeta>): RuleMeta {\n return {\n id,\n title,\n description: message,\n category: \"certificate\",\n severity: \"high\",\n confidence: \"high\",\n hndl: false,\n cwe: CWE_HARDCODED_KEY,\n message,\n remediation:\n \"Remove key material from version control and rotate it; store keys in a secret manager / HSM. Plan re-issuance with PQC keys (ML-DSA-65 / ML-KEM-768) as OpenPGP tooling supports them.\",\n ...opts,\n };\n}\n\nconst RULE_SECRET = rule(\n \"openpgp-secret-key\",\n \"OpenPGP secret key (binary)\",\n \"A binary OpenPGP SECRET key is committed to version control — a classical private key that is exposed and retroactively un-fixable.\",\n { sensitive: true },\n);\nconst RULE_PUBLIC = rule(\n \"openpgp-public-key\",\n \"OpenPGP public key (binary)\",\n \"A binary OpenPGP public key uses classical asymmetric crypto (RSA/DSA/EC); plan a PQC migration path.\",\n { severity: \"medium\" },\n);\nconst RULE_PKESK = rule(\n \"openpgp-encrypted-message\",\n \"OpenPGP-encrypted message (binary)\",\n \"A binary OpenPGP-encrypted message wraps its session key with classical asymmetric crypto (RSA/ElGamal/ECDH); harvest-now-decrypt-later exposed.\",\n { category: \"kem\", hndl: true, cwe: CWE_BROKEN_CRYPTO },\n);\nconst RULE_KEYBOX = rule(\n \"openpgp-keybox\",\n \"GnuPG keybox (.kbx)\",\n \"A GnuPG keybox (.kbx) is a database of classical OpenPGP/X.509 keys and certificates.\",\n { severity: \"medium\", cwe: CWE_BROKEN_CRYPTO },\n);\n\n/** Detects binary OpenPGP key material / messages and GnuPG keyboxes. */\nexport const openpgpDetector: Detector = {\n id: \"openpgp-material\",\n description: \"Binary OpenPGP key material / encrypted messages and GnuPG keyboxes\",\n scope: \"config\",\n language: \"any\",\n rules: [RULE_SECRET, RULE_PUBLIC, RULE_PKESK, RULE_KEYBOX],\n appliesTo: (f) => hasExtension(f, OPENPGP_EXTENSIONS) || f.toLowerCase().endsWith(\".kbx\"),\n detect({ file, content }): Finding[] {\n const at = { file, content, index: 0, matchLength: Math.min(4, content.length) };\n\n // GnuPG keybox: a key/cert database, identified by the \"KBXf\" magic in its\n // first blob header (so a garbage/empty .kbx does not fire).\n if (file.toLowerCase().endsWith(\".kbx\")) {\n return content.includes(\"KBXf\") ? [findingFromRule(RULE_KEYBOX, at)] : [];\n }\n\n const pkt = firstPacket(content);\n if (!pkt) return [];\n const { tag, bodyOffset } = pkt;\n\n if (tag === TAG_SECRET_KEY || tag === TAG_SECRET_SUBKEY) {\n const id = keyPacketAlgo(content, bodyOffset);\n const a = id !== undefined ? algo(id) : undefined;\n return [\n findingFromRule(RULE_SECRET, at, a ? { algorithm: a.family, hndl: a.hndl } : undefined),\n ];\n }\n if (tag === TAG_PUBLIC_KEY || tag === TAG_PUBLIC_SUBKEY) {\n const id = keyPacketAlgo(content, bodyOffset);\n const a = id !== undefined ? algo(id) : undefined;\n return [findingFromRule(RULE_PUBLIC, at, a ? { algorithm: a.family } : undefined)];\n }\n if (tag === TAG_PKESK) {\n const id = pkeskAlgo(content, bodyOffset);\n const a = id !== undefined ? algo(id) : undefined;\n return [findingFromRule(RULE_PKESK, at, a ? { algorithm: a.family } : undefined)];\n }\n return [];\n },\n};\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pem.d.ts","sourceRoot":"","sources":["../../src/detectors/pem.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,KAAK,EAAE,QAAQ,EAAqB,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"pem.d.ts","sourceRoot":"","sources":["../../src/detectors/pem.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,KAAK,EAAE,QAAQ,EAAqB,MAAM,aAAa,CAAC;AAqO/D,+DAA+D;AAC/D,eAAO,MAAM,WAAW,EAAE,QAgCzB,CAAC"}
|
package/dist/detectors/pem.js
CHANGED
|
@@ -184,6 +184,32 @@ const PEM_RULES = [
|
|
|
184
184
|
},
|
|
185
185
|
},
|
|
186
186
|
];
|
|
187
|
+
/**
|
|
188
|
+
* True when what follows the begin marker looks like a REAL PEM block rather than a
|
|
189
|
+
* `-----BEGIN …-----` header STRING LITERAL (as appears in PEM parsers, tests, and
|
|
190
|
+
* i18n messages). `from` points just past the begin marker. Three accepting signals:
|
|
191
|
+
*
|
|
192
|
+
* (a) a full LINE of base64 (≥24 chars, optional `=` padding) — real multi-line PEM
|
|
193
|
+
* bodies are line-wrapped base64; requiring the whole line to be base64 stops a
|
|
194
|
+
* long camelCase identifier on a code line from validating a bare header constant.
|
|
195
|
+
* (b) an ESCAPED-newline base64 run (`\n<base64>`, a LITERAL backslash-n) — a key
|
|
196
|
+
* embedded in a single-line JSON/.env string (GCP service accounts, `PRIVATE_KEY=
|
|
197
|
+
* "…\n…"`), whose body can run past the window before its `-----END`. The literal
|
|
198
|
+
* `\n` escape distinguishes it from a source constant, which has a REAL newline.
|
|
199
|
+
* (c) a matching `-----END …-----` marker with NO double-quote between it and the
|
|
200
|
+
* begin marker. This admits short/placeholder bodies (`x`, `abc`, `MIIB` in tests)
|
|
201
|
+
* while rejecting a parser's PAIRED header/footer constants, whose intervening
|
|
202
|
+
* `";` … `= "` always contains a `"` — a char a real PEM body never holds.
|
|
203
|
+
*/
|
|
204
|
+
function hasBase64Body(content, from) {
|
|
205
|
+
const window = content.slice(from, from + 800);
|
|
206
|
+
if (/^[ \t]*[A-Za-z0-9+/]{24,}={0,2}[ \t]*$/m.test(window))
|
|
207
|
+
return true;
|
|
208
|
+
if (/\\n[A-Za-z0-9+/]{24,}/.test(window))
|
|
209
|
+
return true;
|
|
210
|
+
const end = window.search(/-----END [A-Z0-9 ]+-----/);
|
|
211
|
+
return end >= 0 && !window.slice(0, end).includes('"');
|
|
212
|
+
}
|
|
187
213
|
/** Detects PEM key/certificate material in arbitrary files. */
|
|
188
214
|
export const pemDetector = {
|
|
189
215
|
id: "pem-material",
|
|
@@ -200,6 +226,12 @@ export const pemDetector = {
|
|
|
200
226
|
const findings = [];
|
|
201
227
|
for (const rule of PEM_RULES) {
|
|
202
228
|
eachMatch(rule.re, content, (m) => {
|
|
229
|
+
// Require an actual base64 body after the marker, so a bare
|
|
230
|
+
// `-----BEGIN RSA PRIVATE KEY-----` string literal in a PEM parser / test /
|
|
231
|
+
// i18n message does NOT get reported as an embedded key. A real PEM block has
|
|
232
|
+
// a run of base64 within the next few hundred chars.
|
|
233
|
+
if (!hasBase64Body(content, m.index + m[0].length))
|
|
234
|
+
return;
|
|
203
235
|
findings.push(findingFromRule(rule.meta, {
|
|
204
236
|
file,
|
|
205
237
|
content,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pem.js","sourceRoot":"","sources":["../../src/detectors/pem.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Config / certificate detector: finds PEM-encoded cryptographic material in\n * any text file (source, config, .pem, .key, .crt, .env, …). This catches\n * embedded private keys and X.509 certificates regardless of language.\n *\n * Every rule's metadata lives in the {@link RuleMeta} declaration below (the\n * catalog entry); `detect()` builds findings straight from it via\n * `findingFromRule`. All PEM findings are high-confidence exact-marker matches.\n */\nimport type { Detector, Finding, RuleMeta } from \"../types.js\";\nimport { eachMatch, findingFromRule } from \"../detect-utils.js\";\nimport { CWE_BROKEN_CRYPTO, CWE_HARDCODED_KEY } from \"../cwe.js\";\n\n/** A PEM rule: its catalog metadata plus the begin-marker regex that triggers it. */\ninterface PemRule {\n /** Regex matching the PEM begin marker. */\n re: RegExp;\n meta: RuleMeta;\n}\n\nconst PEM_RULES: PemRule[] = [\n {\n re: /-----BEGIN RSA PRIVATE KEY-----/g,\n meta: {\n id: \"pem-rsa-private-key\",\n title: \"RSA private key (PEM)\",\n description: \"PKCS#1 RSA private key block\",\n category: \"certificate\",\n severity: \"critical\",\n confidence: \"high\",\n algorithm: \"RSA\",\n hndl: true,\n cwe: CWE_HARDCODED_KEY,\n sensitive: true,\n message: \"Embedded RSA private key (PKCS#1 PEM); classical and not quantum-safe.\",\n remediation: \"Migrate to ML-DSA / ML-KEM keys and remove embedded private keys from source.\",\n },\n },\n {\n re: /-----BEGIN EC PRIVATE KEY-----/g,\n meta: {\n id: \"pem-ec-private-key\",\n title: \"EC private key (PEM)\",\n description: \"SEC1 EC private key block\",\n category: \"certificate\",\n severity: \"critical\",\n confidence: \"high\",\n algorithm: \"ECDSA\",\n hndl: true,\n cwe: CWE_HARDCODED_KEY,\n sensitive: true,\n message: \"Embedded EC private key (SEC1 PEM); classical ECDSA/ECDH key, not quantum-safe.\",\n remediation:\n \"Migrate to ML-DSA (FIPS 204) for signatures or hybrid X25519MLKEM768 for key agreement; remove embedded private keys from source.\",\n },\n },\n {\n re: /-----BEGIN DSA PRIVATE KEY-----/g,\n meta: {\n id: \"pem-dsa-private-key\",\n title: \"DSA private key (PEM)\",\n description: \"DSA private key block\",\n category: \"certificate\",\n severity: \"critical\",\n confidence: \"high\",\n algorithm: \"DSA\",\n hndl: false,\n cwe: CWE_HARDCODED_KEY,\n sensitive: true,\n message:\n \"Embedded DSA private key (PEM); classical, already deprecated, and not quantum-safe.\",\n remediation: \"Rotate immediately (DSA is deprecated) and migrate to ML-DSA-65 (FIPS 204).\",\n },\n },\n {\n re: /-----BEGIN OPENSSH PRIVATE KEY-----/g,\n meta: {\n id: \"pem-openssh-private-key\",\n title: \"OpenSSH private key\",\n description: \"OpenSSH private key block\",\n category: \"certificate\",\n severity: \"critical\",\n confidence: \"high\",\n algorithm: \"unknown\",\n hndl: true,\n cwe: CWE_HARDCODED_KEY,\n sensitive: true,\n message: \"Embedded OpenSSH private key (RSA/ECDSA/Ed25519); classical and not quantum-safe.\",\n remediation:\n \"Rotate the key; plan migration to PQC-capable SSH (prefer the mlkem768x25519-sha256 KEX, OpenSSH 10's default since Apr 2025).\",\n },\n },\n {\n re: /-----BEGIN PGP PRIVATE KEY BLOCK-----/g,\n meta: {\n id: \"pem-pgp-private-key\",\n title: \"PGP/GPG private key block\",\n description: \"OpenPGP private key block\",\n category: \"certificate\",\n severity: \"critical\",\n confidence: \"high\",\n algorithm: \"unknown\",\n hndl: true,\n cwe: CWE_HARDCODED_KEY,\n sensitive: true,\n message:\n \"Embedded PGP/GPG private key block (RSA/ECDSA/EdDSA/ElGamal); classical and not quantum-safe.\",\n remediation: \"Rotate the key; track OpenPGP PQC drafts for migration.\",\n },\n },\n {\n re: /-----BEGIN PGP MESSAGE-----/g,\n meta: {\n id: \"pem-pgp-message\",\n title: \"PGP/GPG encrypted message\",\n description: \"OpenPGP encrypted message block\",\n category: \"certificate\",\n severity: \"low\",\n confidence: \"high\",\n algorithm: \"unknown\",\n hndl: true,\n cwe: CWE_BROKEN_CRYPTO,\n message:\n \"Embedded PGP/GPG message; likely encrypted with classical RSA/ElGamal (harvest-now-decrypt-later).\",\n remediation: \"Re-encrypt with PQC-capable tooling as OpenPGP PQC profiles mature.\",\n },\n },\n {\n re: /-----BEGIN (?:ENCRYPTED )?PRIVATE KEY-----/g,\n meta: {\n id: \"pem-pkcs8-private-key\",\n title: \"Private key (PKCS#8 PEM)\",\n description: \"PKCS#8 private key block\",\n category: \"certificate\",\n severity: \"critical\",\n confidence: \"high\",\n algorithm: \"unknown\",\n hndl: true,\n cwe: CWE_HARDCODED_KEY,\n sensitive: true,\n message: \"Embedded PKCS#8 private key; likely classical RSA/EC, not quantum-safe.\",\n remediation: \"Migrate to PQC keys and remove embedded private keys from source.\",\n },\n },\n {\n re: /-----BEGIN CERTIFICATE-----/g,\n meta: {\n id: \"pem-certificate\",\n title: \"X.509 certificate (PEM)\",\n description: \"X.509 certificate block\",\n category: \"certificate\",\n severity: \"low\",\n confidence: \"high\",\n algorithm: \"unknown\",\n hndl: false,\n cwe: CWE_BROKEN_CRYPTO,\n message: \"Embedded X.509 certificate; almost certainly signed with classical RSA/ECDSA.\",\n remediation: \"Plan re-issuance with PQC-capable CAs as ML-DSA certificate profiles mature.\",\n },\n },\n {\n re: /-----BEGIN (?:RSA )?PUBLIC KEY-----/g,\n meta: {\n id: \"pem-public-key\",\n title: \"Classical public key (PEM)\",\n description: \"SubjectPublicKeyInfo / PKCS#1 RSA public key block\",\n category: \"certificate\",\n severity: \"low\",\n confidence: \"high\",\n algorithm: \"unknown\",\n hndl: false,\n cwe: CWE_BROKEN_CRYPTO,\n message:\n \"Embedded classical public key (RSA/EC/DSA); its key pair is not quantum-safe — forgeable signatures or classical key exchange.\",\n remediation: \"Re-issue with PQC keys (ML-DSA / ML-KEM) as the ecosystem adopts them.\",\n },\n },\n {\n re: /-----BEGIN DH PARAMETERS-----/g,\n meta: {\n id: \"pem-dh-parameters\",\n title: \"Diffie-Hellman parameters (PEM)\",\n description: \"Finite-field DH group parameters block\",\n category: \"key-exchange\",\n severity: \"medium\",\n confidence: \"high\",\n algorithm: \"DH\",\n hndl: true,\n cwe: CWE_BROKEN_CRYPTO,\n message:\n \"Embedded finite-field Diffie-Hellman parameters; classical DH key exchange is harvest-now-decrypt-later exposed.\",\n remediation: \"Migrate key exchange to hybrid X25519MLKEM768 (ML-KEM-768).\",\n },\n },\n {\n re: /-----BEGIN (?:NEW )?CERTIFICATE REQUEST-----/g,\n meta: {\n id: \"pem-cert-request\",\n title: \"Certificate signing request (PEM)\",\n description: \"PKCS#10 certificate request block\",\n category: \"certificate\",\n severity: \"low\",\n confidence: \"high\",\n algorithm: \"unknown\",\n hndl: false,\n cwe: CWE_BROKEN_CRYPTO,\n message:\n \"Embedded PKCS#10 CSR; carries a classical public key and will be signed with classical crypto.\",\n remediation: \"Re-generate with PQC keys as PQC-capable CAs mature.\",\n },\n },\n];\n\n/** Detects PEM key/certificate material in arbitrary files. */\nexport const pemDetector: Detector = {\n id: \"pem-material\",\n description: \"PEM-encoded private keys and X.509 certificates in any file\",\n scope: \"config\",\n language: \"any\",\n rules: PEM_RULES.map((r) => r.meta),\n // Applies to every text file; the walker already filters out binaries.\n appliesTo: () => true,\n detect({ file, content }): Finding[] {\n // Fast reject: only proceed if a PEM header is present at all.\n if (!content.includes(\"-----BEGIN \")) return [];\n\n const findings: Finding[] = [];\n for (const rule of PEM_RULES) {\n eachMatch(rule.re, content, (m) => {\n findings.push(\n findingFromRule(rule.meta, {\n file,\n content,\n index: m.index,\n matchLength: m[0].length,\n }),\n );\n });\n }\n return findings;\n },\n};\n"]}
|
|
1
|
+
{"version":3,"file":"pem.js","sourceRoot":"","sources":["../../src/detectors/pem.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Config / certificate detector: finds PEM-encoded cryptographic material in\n * any text file (source, config, .pem, .key, .crt, .env, …). This catches\n * embedded private keys and X.509 certificates regardless of language.\n *\n * Every rule's metadata lives in the {@link RuleMeta} declaration below (the\n * catalog entry); `detect()` builds findings straight from it via\n * `findingFromRule`. All PEM findings are high-confidence exact-marker matches.\n */\nimport type { Detector, Finding, RuleMeta } from \"../types.js\";\nimport { eachMatch, findingFromRule } from \"../detect-utils.js\";\nimport { CWE_BROKEN_CRYPTO, CWE_HARDCODED_KEY } from \"../cwe.js\";\n\n/** A PEM rule: its catalog metadata plus the begin-marker regex that triggers it. */\ninterface PemRule {\n /** Regex matching the PEM begin marker. */\n re: RegExp;\n meta: RuleMeta;\n}\n\nconst PEM_RULES: PemRule[] = [\n {\n re: /-----BEGIN RSA PRIVATE KEY-----/g,\n meta: {\n id: \"pem-rsa-private-key\",\n title: \"RSA private key (PEM)\",\n description: \"PKCS#1 RSA private key block\",\n category: \"certificate\",\n severity: \"critical\",\n confidence: \"high\",\n algorithm: \"RSA\",\n hndl: true,\n cwe: CWE_HARDCODED_KEY,\n sensitive: true,\n message: \"Embedded RSA private key (PKCS#1 PEM); classical and not quantum-safe.\",\n remediation: \"Migrate to ML-DSA / ML-KEM keys and remove embedded private keys from source.\",\n },\n },\n {\n re: /-----BEGIN EC PRIVATE KEY-----/g,\n meta: {\n id: \"pem-ec-private-key\",\n title: \"EC private key (PEM)\",\n description: \"SEC1 EC private key block\",\n category: \"certificate\",\n severity: \"critical\",\n confidence: \"high\",\n algorithm: \"ECDSA\",\n hndl: true,\n cwe: CWE_HARDCODED_KEY,\n sensitive: true,\n message: \"Embedded EC private key (SEC1 PEM); classical ECDSA/ECDH key, not quantum-safe.\",\n remediation:\n \"Migrate to ML-DSA (FIPS 204) for signatures or hybrid X25519MLKEM768 for key agreement; remove embedded private keys from source.\",\n },\n },\n {\n re: /-----BEGIN DSA PRIVATE KEY-----/g,\n meta: {\n id: \"pem-dsa-private-key\",\n title: \"DSA private key (PEM)\",\n description: \"DSA private key block\",\n category: \"certificate\",\n severity: \"critical\",\n confidence: \"high\",\n algorithm: \"DSA\",\n hndl: false,\n cwe: CWE_HARDCODED_KEY,\n sensitive: true,\n message:\n \"Embedded DSA private key (PEM); classical, already deprecated, and not quantum-safe.\",\n remediation: \"Rotate immediately (DSA is deprecated) and migrate to ML-DSA-65 (FIPS 204).\",\n },\n },\n {\n re: /-----BEGIN OPENSSH PRIVATE KEY-----/g,\n meta: {\n id: \"pem-openssh-private-key\",\n title: \"OpenSSH private key\",\n description: \"OpenSSH private key block\",\n category: \"certificate\",\n severity: \"critical\",\n confidence: \"high\",\n algorithm: \"unknown\",\n hndl: true,\n cwe: CWE_HARDCODED_KEY,\n sensitive: true,\n message: \"Embedded OpenSSH private key (RSA/ECDSA/Ed25519); classical and not quantum-safe.\",\n remediation:\n \"Rotate the key; plan migration to PQC-capable SSH (prefer the mlkem768x25519-sha256 KEX, OpenSSH 10's default since Apr 2025).\",\n },\n },\n {\n re: /-----BEGIN PGP PRIVATE KEY BLOCK-----/g,\n meta: {\n id: \"pem-pgp-private-key\",\n title: \"PGP/GPG private key block\",\n description: \"OpenPGP private key block\",\n category: \"certificate\",\n severity: \"critical\",\n confidence: \"high\",\n algorithm: \"unknown\",\n hndl: true,\n cwe: CWE_HARDCODED_KEY,\n sensitive: true,\n message:\n \"Embedded PGP/GPG private key block (RSA/ECDSA/EdDSA/ElGamal); classical and not quantum-safe.\",\n remediation: \"Rotate the key; track OpenPGP PQC drafts for migration.\",\n },\n },\n {\n re: /-----BEGIN PGP MESSAGE-----/g,\n meta: {\n id: \"pem-pgp-message\",\n title: \"PGP/GPG encrypted message\",\n description: \"OpenPGP encrypted message block\",\n category: \"certificate\",\n severity: \"low\",\n confidence: \"high\",\n algorithm: \"unknown\",\n hndl: true,\n cwe: CWE_BROKEN_CRYPTO,\n message:\n \"Embedded PGP/GPG message; likely encrypted with classical RSA/ElGamal (harvest-now-decrypt-later).\",\n remediation: \"Re-encrypt with PQC-capable tooling as OpenPGP PQC profiles mature.\",\n },\n },\n {\n re: /-----BEGIN (?:ENCRYPTED )?PRIVATE KEY-----/g,\n meta: {\n id: \"pem-pkcs8-private-key\",\n title: \"Private key (PKCS#8 PEM)\",\n description: \"PKCS#8 private key block\",\n category: \"certificate\",\n severity: \"critical\",\n confidence: \"high\",\n algorithm: \"unknown\",\n hndl: true,\n cwe: CWE_HARDCODED_KEY,\n sensitive: true,\n message: \"Embedded PKCS#8 private key; likely classical RSA/EC, not quantum-safe.\",\n remediation: \"Migrate to PQC keys and remove embedded private keys from source.\",\n },\n },\n {\n re: /-----BEGIN CERTIFICATE-----/g,\n meta: {\n id: \"pem-certificate\",\n title: \"X.509 certificate (PEM)\",\n description: \"X.509 certificate block\",\n category: \"certificate\",\n severity: \"low\",\n confidence: \"high\",\n algorithm: \"unknown\",\n hndl: false,\n cwe: CWE_BROKEN_CRYPTO,\n message: \"Embedded X.509 certificate; almost certainly signed with classical RSA/ECDSA.\",\n remediation: \"Plan re-issuance with PQC-capable CAs as ML-DSA certificate profiles mature.\",\n },\n },\n {\n re: /-----BEGIN (?:RSA )?PUBLIC KEY-----/g,\n meta: {\n id: \"pem-public-key\",\n title: \"Classical public key (PEM)\",\n description: \"SubjectPublicKeyInfo / PKCS#1 RSA public key block\",\n category: \"certificate\",\n severity: \"low\",\n confidence: \"high\",\n algorithm: \"unknown\",\n hndl: false,\n cwe: CWE_BROKEN_CRYPTO,\n message:\n \"Embedded classical public key (RSA/EC/DSA); its key pair is not quantum-safe — forgeable signatures or classical key exchange.\",\n remediation: \"Re-issue with PQC keys (ML-DSA / ML-KEM) as the ecosystem adopts them.\",\n },\n },\n {\n re: /-----BEGIN DH PARAMETERS-----/g,\n meta: {\n id: \"pem-dh-parameters\",\n title: \"Diffie-Hellman parameters (PEM)\",\n description: \"Finite-field DH group parameters block\",\n category: \"key-exchange\",\n severity: \"medium\",\n confidence: \"high\",\n algorithm: \"DH\",\n hndl: true,\n cwe: CWE_BROKEN_CRYPTO,\n message:\n \"Embedded finite-field Diffie-Hellman parameters; classical DH key exchange is harvest-now-decrypt-later exposed.\",\n remediation: \"Migrate key exchange to hybrid X25519MLKEM768 (ML-KEM-768).\",\n },\n },\n {\n re: /-----BEGIN (?:NEW )?CERTIFICATE REQUEST-----/g,\n meta: {\n id: \"pem-cert-request\",\n title: \"Certificate signing request (PEM)\",\n description: \"PKCS#10 certificate request block\",\n category: \"certificate\",\n severity: \"low\",\n confidence: \"high\",\n algorithm: \"unknown\",\n hndl: false,\n cwe: CWE_BROKEN_CRYPTO,\n message:\n \"Embedded PKCS#10 CSR; carries a classical public key and will be signed with classical crypto.\",\n remediation: \"Re-generate with PQC keys as PQC-capable CAs mature.\",\n },\n },\n];\n\n/**\n * True when what follows the begin marker looks like a REAL PEM block rather than a\n * `-----BEGIN …-----` header STRING LITERAL (as appears in PEM parsers, tests, and\n * i18n messages). `from` points just past the begin marker. Three accepting signals:\n *\n * (a) a full LINE of base64 (≥24 chars, optional `=` padding) — real multi-line PEM\n * bodies are line-wrapped base64; requiring the whole line to be base64 stops a\n * long camelCase identifier on a code line from validating a bare header constant.\n * (b) an ESCAPED-newline base64 run (`\\n<base64>`, a LITERAL backslash-n) — a key\n * embedded in a single-line JSON/.env string (GCP service accounts, `PRIVATE_KEY=\n * \"…\\n…\"`), whose body can run past the window before its `-----END`. The literal\n * `\\n` escape distinguishes it from a source constant, which has a REAL newline.\n * (c) a matching `-----END …-----` marker with NO double-quote between it and the\n * begin marker. This admits short/placeholder bodies (`x`, `abc`, `MIIB` in tests)\n * while rejecting a parser's PAIRED header/footer constants, whose intervening\n * `\";` … `= \"` always contains a `\"` — a char a real PEM body never holds.\n */\nfunction hasBase64Body(content: string, from: number): boolean {\n const window = content.slice(from, from + 800);\n if (/^[ \\t]*[A-Za-z0-9+/]{24,}={0,2}[ \\t]*$/m.test(window)) return true;\n if (/\\\\n[A-Za-z0-9+/]{24,}/.test(window)) return true;\n const end = window.search(/-----END [A-Z0-9 ]+-----/);\n return end >= 0 && !window.slice(0, end).includes('\"');\n}\n\n/** Detects PEM key/certificate material in arbitrary files. */\nexport const pemDetector: Detector = {\n id: \"pem-material\",\n description: \"PEM-encoded private keys and X.509 certificates in any file\",\n scope: \"config\",\n language: \"any\",\n rules: PEM_RULES.map((r) => r.meta),\n // Applies to every text file; the walker already filters out binaries.\n appliesTo: () => true,\n detect({ file, content }): Finding[] {\n // Fast reject: only proceed if a PEM header is present at all.\n if (!content.includes(\"-----BEGIN \")) return [];\n\n const findings: Finding[] = [];\n for (const rule of PEM_RULES) {\n eachMatch(rule.re, content, (m) => {\n // Require an actual base64 body after the marker, so a bare\n // `-----BEGIN RSA PRIVATE KEY-----` string literal in a PEM parser / test /\n // i18n message does NOT get reported as an embedded key. A real PEM block has\n // a run of base64 within the next few hundred chars.\n if (!hasBase64Body(content, m.index + m[0].length)) return;\n findings.push(\n findingFromRule(rule.meta, {\n file,\n content,\n index: m.index,\n matchLength: m[0].length,\n }),\n );\n });\n }\n return findings;\n },\n};\n"]}
|
package/dist/detectors/php.d.ts
CHANGED
|
@@ -5,7 +5,8 @@
|
|
|
5
5
|
*
|
|
6
6
|
* - **ext/openssl** — `openssl_pkey_new()` (classified by its
|
|
7
7
|
* `OPENSSL_KEYTYPE_*` config, defaulting to RSA), `openssl_public_encrypt` /
|
|
8
|
-
* `openssl_private_decrypt`
|
|
8
|
+
* `openssl_private_decrypt` / `openssl_seal` / `openssl_open` (RSA), and
|
|
9
|
+
* `openssl_sign` / `openssl_verify`.
|
|
9
10
|
* - **phpseclib3** — `RSA::createKey()` / `EC::createKey()` / `DSA::createKey()`.
|
|
10
11
|
* - **libsodium** — `sodium_crypto_box`/`kx` keypairs (X25519) and
|
|
11
12
|
* `sodium_crypto_sign` keypairs (Ed25519).
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"php.d.ts","sourceRoot":"","sources":["../../src/detectors/php.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"php.d.ts","sourceRoot":"","sources":["../../src/detectors/php.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,OAAO,KAAK,EAAE,QAAQ,EAAqB,MAAM,aAAa,CAAC;AAyI/D,kFAAkF;AAClF,eAAO,MAAM,WAAW,EAAE,QAsEzB,CAAC"}
|