@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.
Files changed (284) hide show
  1. package/README.md +76 -27
  2. package/dist/cbom-merge.d.ts.map +1 -1
  3. package/dist/cbom-merge.js +30 -8
  4. package/dist/cbom-merge.js.map +1 -1
  5. package/dist/cbom.d.ts +5 -0
  6. package/dist/cbom.d.ts.map +1 -1
  7. package/dist/cbom.js +132 -37
  8. package/dist/cbom.js.map +1 -1
  9. package/dist/comments.d.ts +2 -23
  10. package/dist/comments.d.ts.map +1 -1
  11. package/dist/comments.js +24 -5
  12. package/dist/comments.js.map +1 -1
  13. package/dist/detect-utils.d.ts +55 -21
  14. package/dist/detect-utils.d.ts.map +1 -1
  15. package/dist/detect-utils.js +133 -7
  16. package/dist/detect-utils.js.map +1 -1
  17. package/dist/detectors/age.d.ts +16 -0
  18. package/dist/detectors/age.d.ts.map +1 -0
  19. package/dist/detectors/age.js +40 -0
  20. package/dist/detectors/age.js.map +1 -0
  21. package/dist/detectors/ansible.d.ts +13 -0
  22. package/dist/detectors/ansible.d.ts.map +1 -0
  23. package/dist/detectors/ansible.js +108 -0
  24. package/dist/detectors/ansible.js.map +1 -0
  25. package/dist/detectors/bicep.d.ts +20 -0
  26. package/dist/detectors/bicep.d.ts.map +1 -0
  27. package/dist/detectors/bicep.js +74 -0
  28. package/dist/detectors/bicep.js.map +1 -0
  29. package/dist/detectors/c.d.ts.map +1 -1
  30. package/dist/detectors/c.js +30 -3
  31. package/dist/detectors/c.js.map +1 -1
  32. package/dist/detectors/cicd.d.ts.map +1 -1
  33. package/dist/detectors/cicd.js +18 -5
  34. package/dist/detectors/cicd.js.map +1 -1
  35. package/dist/detectors/cloud-kms.d.ts +8 -5
  36. package/dist/detectors/cloud-kms.d.ts.map +1 -1
  37. package/dist/detectors/cloud-kms.js +62 -20
  38. package/dist/detectors/cloud-kms.js.map +1 -1
  39. package/dist/detectors/cloudformation.d.ts +8 -0
  40. package/dist/detectors/cloudformation.d.ts.map +1 -1
  41. package/dist/detectors/cloudformation.js +41 -10
  42. package/dist/detectors/cloudformation.js.map +1 -1
  43. package/dist/detectors/codesign.d.ts +50 -0
  44. package/dist/detectors/codesign.d.ts.map +1 -0
  45. package/dist/detectors/codesign.js +143 -0
  46. package/dist/detectors/codesign.js.map +1 -0
  47. package/dist/detectors/csharp.d.ts.map +1 -1
  48. package/dist/detectors/csharp.js +13 -3
  49. package/dist/detectors/csharp.js.map +1 -1
  50. package/dist/detectors/dart.d.ts +35 -0
  51. package/dist/detectors/dart.d.ts.map +1 -0
  52. package/dist/detectors/dart.js +177 -0
  53. package/dist/detectors/dart.js.map +1 -0
  54. package/dist/detectors/database.d.ts +5 -3
  55. package/dist/detectors/database.d.ts.map +1 -1
  56. package/dist/detectors/database.js +45 -8
  57. package/dist/detectors/database.js.map +1 -1
  58. package/dist/detectors/dkim.d.ts +54 -0
  59. package/dist/detectors/dkim.d.ts.map +1 -0
  60. package/dist/detectors/dkim.js +92 -0
  61. package/dist/detectors/dkim.js.map +1 -0
  62. package/dist/detectors/dnssec.d.ts.map +1 -1
  63. package/dist/detectors/dnssec.js +19 -10
  64. package/dist/detectors/dnssec.js.map +1 -1
  65. package/dist/detectors/elixir.d.ts.map +1 -1
  66. package/dist/detectors/elixir.js +34 -2
  67. package/dist/detectors/elixir.js.map +1 -1
  68. package/dist/detectors/java.d.ts.map +1 -1
  69. package/dist/detectors/java.js +33 -3
  70. package/dist/detectors/java.js.map +1 -1
  71. package/dist/detectors/jose.d.ts.map +1 -1
  72. package/dist/detectors/jose.js +13 -3
  73. package/dist/detectors/jose.js.map +1 -1
  74. package/dist/detectors/jwk.d.ts +7 -7
  75. package/dist/detectors/jwk.d.ts.map +1 -1
  76. package/dist/detectors/jwk.js +44 -8
  77. package/dist/detectors/jwk.js.map +1 -1
  78. package/dist/detectors/k8s.d.ts.map +1 -1
  79. package/dist/detectors/k8s.js +5 -2
  80. package/dist/detectors/k8s.js.map +1 -1
  81. package/dist/detectors/keystore.d.ts +19 -0
  82. package/dist/detectors/keystore.d.ts.map +1 -0
  83. package/dist/detectors/keystore.js +76 -0
  84. package/dist/detectors/keystore.js.map +1 -0
  85. package/dist/detectors/mesh.d.ts +7 -8
  86. package/dist/detectors/mesh.d.ts.map +1 -1
  87. package/dist/detectors/mesh.js +17 -35
  88. package/dist/detectors/mesh.js.map +1 -1
  89. package/dist/detectors/messaging.d.ts +2 -2
  90. package/dist/detectors/messaging.d.ts.map +1 -1
  91. package/dist/detectors/messaging.js +19 -12
  92. package/dist/detectors/messaging.js.map +1 -1
  93. package/dist/detectors/objc.d.ts +41 -0
  94. package/dist/detectors/objc.d.ts.map +1 -0
  95. package/dist/detectors/objc.js +146 -0
  96. package/dist/detectors/objc.js.map +1 -0
  97. package/dist/detectors/openpgp.d.ts +20 -0
  98. package/dist/detectors/openpgp.d.ts.map +1 -0
  99. package/dist/detectors/openpgp.js +151 -0
  100. package/dist/detectors/openpgp.js.map +1 -0
  101. package/dist/detectors/pem.d.ts.map +1 -1
  102. package/dist/detectors/pem.js +32 -0
  103. package/dist/detectors/pem.js.map +1 -1
  104. package/dist/detectors/php.d.ts +2 -1
  105. package/dist/detectors/php.d.ts.map +1 -1
  106. package/dist/detectors/php.js +7 -3
  107. package/dist/detectors/php.js.map +1 -1
  108. package/dist/detectors/pkcs11.d.ts +17 -0
  109. package/dist/detectors/pkcs11.d.ts.map +1 -0
  110. package/dist/detectors/pkcs11.js +87 -0
  111. package/dist/detectors/pkcs11.js.map +1 -0
  112. package/dist/detectors/proxy.d.ts +52 -0
  113. package/dist/detectors/proxy.d.ts.map +1 -0
  114. package/dist/detectors/proxy.js +157 -0
  115. package/dist/detectors/proxy.js.map +1 -0
  116. package/dist/detectors/pulumi.d.ts +19 -0
  117. package/dist/detectors/pulumi.d.ts.map +1 -0
  118. package/dist/detectors/pulumi.js +80 -0
  119. package/dist/detectors/pulumi.js.map +1 -0
  120. package/dist/detectors/rust.d.ts.map +1 -1
  121. package/dist/detectors/rust.js +5 -0
  122. package/dist/detectors/rust.js.map +1 -1
  123. package/dist/detectors/secrets.d.ts +4 -1
  124. package/dist/detectors/secrets.d.ts.map +1 -1
  125. package/dist/detectors/secrets.js +5 -21
  126. package/dist/detectors/secrets.js.map +1 -1
  127. package/dist/detectors/solidity.d.ts +50 -0
  128. package/dist/detectors/solidity.d.ts.map +1 -0
  129. package/dist/detectors/solidity.js +108 -0
  130. package/dist/detectors/solidity.js.map +1 -0
  131. package/dist/detectors/source.d.ts.map +1 -1
  132. package/dist/detectors/source.js +53 -6
  133. package/dist/detectors/source.js.map +1 -1
  134. package/dist/detectors/spire.d.ts +49 -0
  135. package/dist/detectors/spire.d.ts.map +1 -0
  136. package/dist/detectors/spire.js +77 -0
  137. package/dist/detectors/spire.js.map +1 -0
  138. package/dist/detectors/ssh-ca.d.ts +72 -0
  139. package/dist/detectors/ssh-ca.d.ts.map +1 -0
  140. package/dist/detectors/ssh-ca.js +119 -0
  141. package/dist/detectors/ssh-ca.js.map +1 -0
  142. package/dist/detectors/supply-chain.d.ts +17 -0
  143. package/dist/detectors/supply-chain.d.ts.map +1 -0
  144. package/dist/detectors/supply-chain.js +89 -0
  145. package/dist/detectors/supply-chain.js.map +1 -0
  146. package/dist/detectors/swift.d.ts +22 -0
  147. package/dist/detectors/swift.d.ts.map +1 -0
  148. package/dist/detectors/swift.js +120 -0
  149. package/dist/detectors/swift.js.map +1 -0
  150. package/dist/detectors/terraform.d.ts.map +1 -1
  151. package/dist/detectors/terraform.js +35 -8
  152. package/dist/detectors/terraform.js.map +1 -1
  153. package/dist/detectors/vault.d.ts +15 -0
  154. package/dist/detectors/vault.d.ts.map +1 -0
  155. package/dist/detectors/vault.js +97 -0
  156. package/dist/detectors/vault.js.map +1 -0
  157. package/dist/detectors/vpn.d.ts +8 -8
  158. package/dist/detectors/vpn.d.ts.map +1 -1
  159. package/dist/detectors/vpn.js +12 -38
  160. package/dist/detectors/vpn.js.map +1 -1
  161. package/dist/detectors/weak-hash.d.ts +71 -0
  162. package/dist/detectors/weak-hash.d.ts.map +1 -0
  163. package/dist/detectors/weak-hash.js +116 -0
  164. package/dist/detectors/weak-hash.js.map +1 -0
  165. package/dist/detectors/webauthn.d.ts +61 -0
  166. package/dist/detectors/webauthn.d.ts.map +1 -0
  167. package/dist/detectors/webauthn.js +109 -0
  168. package/dist/detectors/webauthn.js.map +1 -0
  169. package/dist/detectors/xmldsig.d.ts +23 -0
  170. package/dist/detectors/xmldsig.d.ts.map +1 -0
  171. package/dist/detectors/xmldsig.js +84 -0
  172. package/dist/detectors/xmldsig.js.map +1 -0
  173. package/dist/evidence.d.ts +74 -4
  174. package/dist/evidence.d.ts.map +1 -1
  175. package/dist/evidence.js +71 -0
  176. package/dist/evidence.js.map +1 -1
  177. package/dist/index.d.ts +14 -11
  178. package/dist/index.d.ts.map +1 -1
  179. package/dist/index.js +17 -11
  180. package/dist/index.js.map +1 -1
  181. package/dist/inventory.d.ts +0 -2
  182. package/dist/inventory.d.ts.map +1 -1
  183. package/dist/inventory.js +15 -6
  184. package/dist/inventory.js.map +1 -1
  185. package/dist/registry.d.ts +9 -4
  186. package/dist/registry.d.ts.map +1 -1
  187. package/dist/registry.js +51 -4
  188. package/dist/registry.js.map +1 -1
  189. package/dist/remediate-pipeline.d.ts +0 -3
  190. package/dist/remediate-pipeline.d.ts.map +1 -1
  191. package/dist/remediate-pipeline.js +1 -1
  192. package/dist/remediate-pipeline.js.map +1 -1
  193. package/dist/remediation.d.ts +10 -0
  194. package/dist/remediation.d.ts.map +1 -1
  195. package/dist/remediation.js +32 -0
  196. package/dist/remediation.js.map +1 -1
  197. package/dist/report.d.ts +9 -0
  198. package/dist/report.d.ts.map +1 -1
  199. package/dist/report.js +31 -1
  200. package/dist/report.js.map +1 -1
  201. package/dist/scan-worker.js +5 -3
  202. package/dist/scan-worker.js.map +1 -1
  203. package/dist/scan.d.ts.map +1 -1
  204. package/dist/scan.js +35 -6
  205. package/dist/scan.js.map +1 -1
  206. package/dist/standards-profiles.d.ts +58 -0
  207. package/dist/standards-profiles.d.ts.map +1 -0
  208. package/dist/standards-profiles.js +100 -0
  209. package/dist/standards-profiles.js.map +1 -0
  210. package/dist/types.d.ts +6 -5
  211. package/dist/types.d.ts.map +1 -1
  212. package/dist/types.js.map +1 -1
  213. package/dist/version.d.ts +1 -1
  214. package/dist/version.js +1 -1
  215. package/dist/version.js.map +1 -1
  216. package/dist/vex.d.ts +39 -0
  217. package/dist/vex.d.ts.map +1 -0
  218. package/dist/vex.js +87 -0
  219. package/dist/vex.js.map +1 -0
  220. package/dist/walk.d.ts +8 -3
  221. package/dist/walk.d.ts.map +1 -1
  222. package/dist/walk.js +32 -1
  223. package/dist/walk.js.map +1 -1
  224. package/package.json +1 -1
  225. package/src/cbom-merge.ts +38 -8
  226. package/src/cbom.ts +153 -39
  227. package/src/comments.ts +25 -6
  228. package/src/detect-utils.ts +133 -11
  229. package/src/detectors/age.ts +60 -0
  230. package/src/detectors/ansible.ts +140 -0
  231. package/src/detectors/bicep.ts +107 -0
  232. package/src/detectors/c.ts +31 -3
  233. package/src/detectors/cicd.ts +18 -5
  234. package/src/detectors/cloud-kms.ts +79 -25
  235. package/src/detectors/cloudformation.ts +45 -10
  236. package/src/detectors/codesign.ts +214 -0
  237. package/src/detectors/csharp.ts +13 -3
  238. package/src/detectors/dart.ts +237 -0
  239. package/src/detectors/database.ts +64 -11
  240. package/src/detectors/dkim.ts +172 -0
  241. package/src/detectors/dnssec.ts +19 -10
  242. package/src/detectors/elixir.ts +33 -2
  243. package/src/detectors/java.ts +37 -3
  244. package/src/detectors/jose.ts +19 -3
  245. package/src/detectors/jwk.ts +59 -17
  246. package/src/detectors/k8s.ts +5 -2
  247. package/src/detectors/keystore.ts +109 -0
  248. package/src/detectors/mesh.ts +21 -45
  249. package/src/detectors/messaging.ts +22 -14
  250. package/src/detectors/objc.ts +209 -0
  251. package/src/detectors/openpgp.ts +194 -0
  252. package/src/detectors/pem.ts +30 -0
  253. package/src/detectors/php.ts +9 -4
  254. package/src/detectors/pkcs11.ts +113 -0
  255. package/src/detectors/proxy.ts +236 -0
  256. package/src/detectors/pulumi.ts +106 -0
  257. package/src/detectors/rust.ts +5 -0
  258. package/src/detectors/secrets.ts +15 -26
  259. package/src/detectors/solidity.ts +176 -0
  260. package/src/detectors/source.ts +48 -5
  261. package/src/detectors/spire.ts +143 -0
  262. package/src/detectors/ssh-ca.ts +215 -0
  263. package/src/detectors/supply-chain.ts +117 -0
  264. package/src/detectors/swift.ts +149 -0
  265. package/src/detectors/terraform.ts +42 -8
  266. package/src/detectors/vault.ts +120 -0
  267. package/src/detectors/vpn.ts +20 -48
  268. package/src/detectors/weak-hash.ts +217 -0
  269. package/src/detectors/webauthn.ts +205 -0
  270. package/src/detectors/xmldsig.ts +115 -0
  271. package/src/evidence.ts +127 -4
  272. package/src/index.ts +41 -13
  273. package/src/inventory.ts +15 -7
  274. package/src/registry.ts +51 -4
  275. package/src/remediate-pipeline.ts +1 -1
  276. package/src/remediation.ts +39 -0
  277. package/src/report.ts +36 -1
  278. package/src/scan-worker.ts +5 -3
  279. package/src/scan.ts +41 -6
  280. package/src/standards-profiles.ts +136 -0
  281. package/src/types.ts +9 -4
  282. package/src/version.ts +1 -1
  283. package/src/vex.ts +122 -0
  284. package/src/walk.ts +33 -2
@@ -5,31 +5,61 @@
5
5
  * `GenerateDataKeyPair` select the key type with a `KeySpec` / `KeyPairSpec`
6
6
  * (legacy `CustomerMasterKeySpec`) field whose value is `RSA_*` or `ECC_*`.
7
7
  *
8
- * These PascalCase field names + `RSA_2048` / `ECC_NIST_P256` values are specific
9
- * to the AWS KMS API across every SDK language (JS/TS, Python/boto3, Java, Go,
10
- * the CLI, JSON policies), so a single lexical rule catches them all with a very
11
- * low false-positive rate. Terraform uses the snake_case `customer_master_key_spec`
12
- * instead, so this never double-counts with the IaC detector.
8
+ * These field names + `RSA_2048` / `ECC_NIST_P256` values are specific to the AWS
9
+ * KMS/ACM API across every SDK language (JS/TS, Python/boto3, Java, Go, the CLI, JSON
10
+ * policies), so a single lexical rule catches them at very low false-positive rate.
11
+ * Covered forms: the quoted-value SDK/JSON form (`KeySpec: "RSA_2048"`), the camelCase
12
+ * CDK/Pulumi prop form (`customerMasterKeySpec: "RSA_2048"`), and the AWS CDK ENUM
13
+ * form (`kms.KeySpec.RSA_2048`, `KeyAlgorithm.EC_prime256v1`). Terraform uses the
14
+ * snake_case `customer_master_key_spec`, so this never double-counts with the IaC
15
+ * detector.
13
16
  *
14
17
  * HNDL: an RSA KMS key (encryption/KEM) and an EC KMS key (which AWS KMS can use
15
18
  * for ECDH key agreement as well as ECDSA signing) are both harvest-now-decrypt-
16
19
  * later exposed → hndl:true.
17
20
  */
18
21
  import type { Detector, Finding, RuleMeta } from "../types.js";
19
- import { eachMatch, findingFromRule } from "../detect-utils.js";
22
+ import {
23
+ DOC_EXTENSIONS,
24
+ eachMatch,
25
+ findingFromRule,
26
+ hasExtension,
27
+ maskBlockComments,
28
+ maskCommentLines,
29
+ } from "../detect-utils.js";
30
+ import { isCloudTemplateFile } from "./cloudformation.js";
20
31
  import { CWE_BROKEN_CRYPTO } from "../cwe.js";
21
32
 
22
- // The AWS KMS key-spec fields (CreateKey / GenerateDataKeyPair / legacy CMK). The
23
- // optional `"?` after the field name accepts both the JS/HCL form (`KeySpec:`) and
24
- // the JSON form where the key is quoted (`"KeySpec":`).
25
- const SPEC_KEYS = "KeySpec|KeyPairSpec|CustomerMasterKeySpec";
26
- const RE_KMS_RSA = new RegExp(`\\b(?:${SPEC_KEYS})"?\\s*[:=]\\s*['"]RSA_\\d+['"]`, "g");
27
- const RE_KMS_EC = new RegExp(`\\b(?:${SPEC_KEYS})"?\\s*[:=]\\s*['"]ECC_[A-Z0-9_]+['"]`, "g");
33
+ // The AWS KMS / ACM key-spec field names. Both PascalCase (SDK / boto3 / JSON) and
34
+ // camelCase (AWS CDK / Pulumi props) leading letters are accepted. `KeyAlgorithm` is
35
+ // the ACM certificate key spec.
36
+ const SPEC_KEYS = "[Kk]eySpec|[Kk]eyPairSpec|[Cc]ustomerMasterKeySpec|[Kk]eyAlgorithm";
37
+ // (a) The QUOTED-VALUE form: `KeySpec: "RSA_2048"` / `"KeySpec": "RSA_2048"` /
38
+ // `customerMasterKeySpec: "RSA_2048"` (SDK, boto3, JSON, Pulumi props).
39
+ const RE_KMS_RSA = new RegExp(`\\b(?:${SPEC_KEYS})"?\\s*[:=]\\s*['"](?:RSA_\\d+)['"]`, "g");
40
+ const RE_KMS_EC = new RegExp(
41
+ `\\b(?:${SPEC_KEYS})"?\\s*[:=]\\s*['"](?:ECC_[A-Z0-9_]+|EC_[A-Za-z0-9]+)['"]`,
42
+ "g",
43
+ );
44
+ // (b) The ENUM-MEMBER form used by AWS CDK: `kms.KeySpec.RSA_2048`,
45
+ // `KeySpec.ECC_NIST_P256`, `KeyAlgorithm.RSA_2048`, `KeyAlgorithm.EC_prime256v1`
46
+ // — an enum reference, no quoted value, so (a) never matches it.
47
+ const RE_KMS_RSA_ENUM = /\b(?:KeySpec|KeyAlgorithm)\.RSA_\d+\b/g;
48
+ const RE_KMS_EC_ENUM = /\b(?:KeySpec|KeyAlgorithm)\.(?:ECC_[A-Z0-9_]+|EC_[A-Za-z0-9]+)\b/g;
49
+ // (c) GCP Cloud KMS: the CryptoKeyVersion algorithm — `RSA_SIGN_PSS_2048_SHA256`,
50
+ // `RSA_DECRYPT_OAEP_*`, `EC_SIGN_P256_SHA256` (enum or quoted string). The
51
+ // `RSA_SIGN`/`RSA_DECRYPT`/`EC_SIGN` prefixes are GCP-KMS-specific.
52
+ const RE_GCP_KMS_RSA = /\bRSA_(?:SIGN|DECRYPT)_[A-Z0-9_]+/g;
53
+ const RE_GCP_KMS_EC = /\bEC_SIGN_[A-Z0-9_]+/g;
54
+ // (d) Azure Key Vault SDK: `createRsaKey` / `createEcKey` (+ `Create*KeyOptions`) and
55
+ // `KeyType.Rsa` / `KeyType.Ec`.
56
+ const RE_AZURE_KV_RSA = /\b[Cc]reateRsaKey(?:Options)?\b|\bKeyType\.Rsa\b/g;
57
+ const RE_AZURE_KV_EC = /\b[Cc]reateEcKey(?:Options)?\b|\bKeyType\.Ec\b/g;
28
58
 
29
59
  const RULE_KMS_RSA: RuleMeta = {
30
60
  id: "cloud-kms-rsa",
31
- title: "AWS KMS RSA key",
32
- description: "AWS KMS CreateKey / GenerateDataKeyPair with an RSA_* key spec",
61
+ title: "Cloud KMS RSA key",
62
+ description: "AWS/GCP/Azure KMS RSA key spec (KeySpec / RSA_SIGN_* / createRsaKey)",
33
63
  category: "kem",
34
64
  severity: "high",
35
65
  confidence: "high",
@@ -37,13 +67,13 @@ const RULE_KMS_RSA: RuleMeta = {
37
67
  hndl: true,
38
68
  cwe: CWE_BROKEN_CRYPTO,
39
69
  message:
40
- "Mints a classical RSA key via the AWS KMS SDK (harvest-now-decrypt-later exposed for encryption).",
70
+ "Mints a classical RSA key via a cloud KMS SDK (AWS/GCP/Azure); harvest-now-decrypt-later exposed for encryption.",
41
71
  remediation: "Plan migration to PQC as cloud KMS adds ML-KEM / ML-DSA key specs.",
42
72
  };
43
73
  const RULE_KMS_EC: RuleMeta = {
44
74
  id: "cloud-kms-ec",
45
- title: "AWS KMS EC key",
46
- description: "AWS KMS CreateKey / GenerateDataKeyPair with an ECC_* key spec",
75
+ title: "Cloud KMS EC key",
76
+ description: "AWS/GCP/Azure KMS EC key spec (ECC_* / EC_SIGN_* / createEcKey)",
47
77
  category: "key-exchange",
48
78
  severity: "high",
49
79
  confidence: "high",
@@ -51,7 +81,7 @@ const RULE_KMS_EC: RuleMeta = {
51
81
  hndl: true,
52
82
  cwe: CWE_BROKEN_CRYPTO,
53
83
  message:
54
- "Mints a classical EC key via the AWS KMS SDK; EC keys feed ECDSA signatures and ECDH key agreement (the ECDH path is harvest-now-decrypt-later exposed).",
84
+ "Mints a classical EC key via a cloud KMS SDK (AWS/GCP/Azure); EC keys feed ECDSA signatures and ECDH key agreement (the ECDH path is harvest-now-decrypt-later exposed).",
55
85
  remediation:
56
86
  "For key agreement: hybrid X25519MLKEM768 (ML-KEM-768). For signatures: ML-DSA-65 (FIPS 204).",
57
87
  };
@@ -59,29 +89,53 @@ const RULE_KMS_EC: RuleMeta = {
59
89
  /** Detects classical asymmetric keys provisioned via a cloud KMS SDK call. */
60
90
  export const cloudKmsDetector: Detector = {
61
91
  id: "cloud-kms",
62
- description: "Classical asymmetric keys minted via a cloud KMS SDK (AWS KMS)",
92
+ description: "Classical asymmetric keys minted via a cloud KMS/ACM SDK, AWS CDK, or Pulumi",
63
93
  scope: "config",
64
94
  language: "any",
65
95
  rules: [RULE_KMS_RSA, RULE_KMS_EC],
66
- appliesTo: () => true,
96
+ // Skip prose/docs: a README or tutorial showing `KeySpec: "RSA_2048"` to describe
97
+ // the KMS API is not a live key-minting call.
98
+ appliesTo: (f) => !hasExtension(f, DOC_EXTENSIONS),
67
99
  detect({ file, content }): Finding[] {
68
- // Fast reject: only proceed if a KMS key-spec field name is present.
100
+ // Fast reject: only proceed if an AWS / GCP / Azure KMS key-spec marker is present.
101
+ const lc = content.toLowerCase();
69
102
  if (
70
- !content.includes("KeySpec") &&
71
- !content.includes("KeyPairSpec") &&
72
- !content.includes("CustomerMasterKeySpec")
103
+ !lc.includes("keyspec") &&
104
+ !lc.includes("keypairspec") &&
105
+ !lc.includes("keyalgorithm") &&
106
+ !lc.includes("rsa_sign") &&
107
+ !lc.includes("rsa_decrypt") &&
108
+ !lc.includes("ec_sign") &&
109
+ !lc.includes("creatersakey") &&
110
+ !lc.includes("createeckey") &&
111
+ !lc.includes("keytype.")
73
112
  ) {
74
113
  return [];
75
114
  }
115
+ // Inside a CloudFormation / ARM template FILE, the cloudformation detector owns
116
+ // the KMS key specs — defer so a KeySpec line is not counted twice. Gated to the
117
+ // template extensions it scans, so an SDK call in a `.ts` stays covered here.
118
+ if (isCloudTemplateFile(file, content)) return [];
119
+ // Mask comments before scanning so a commented-out `# KeySpec: "RSA_2048"` in a
120
+ // YAML/JSON/HCL config (which the central source-comment stripper doesn't cover)
121
+ // can't fire. Offsets are preserved, so the finding's snippet — built from the
122
+ // original `content` — stays exact.
123
+ const scan = maskCommentLines(maskBlockComments(content), ["#", "//"]);
76
124
  const findings: Finding[] = [];
77
125
  const add = (re: RegExp, rule: RuleMeta): void =>
78
- eachMatch(re, content, (m) =>
126
+ eachMatch(re, scan, (m) =>
79
127
  findings.push(
80
128
  findingFromRule(rule, { file, content, index: m.index, matchLength: m[0].length }),
81
129
  ),
82
130
  );
83
131
  add(RE_KMS_RSA, RULE_KMS_RSA);
84
132
  add(RE_KMS_EC, RULE_KMS_EC);
133
+ add(RE_KMS_RSA_ENUM, RULE_KMS_RSA);
134
+ add(RE_KMS_EC_ENUM, RULE_KMS_EC);
135
+ add(RE_GCP_KMS_RSA, RULE_KMS_RSA);
136
+ add(RE_GCP_KMS_EC, RULE_KMS_EC);
137
+ add(RE_AZURE_KV_RSA, RULE_KMS_RSA);
138
+ add(RE_AZURE_KV_EC, RULE_KMS_EC);
85
139
  return findings;
86
140
  },
87
141
  };
@@ -22,7 +22,7 @@
22
22
  * these property names in an unrelated context.
23
23
  */
24
24
  import type { Detector, Finding, RuleMeta } from "../types.js";
25
- import { eachMatch, findingFromRule, hasExtension } from "../detect-utils.js";
25
+ import { eachMatch, findingFromRule, hasExtension, maskCommentLines } from "../detect-utils.js";
26
26
  import { CWE_BROKEN_CRYPTO, CWE_WEAK_STRENGTH } from "../cwe.js";
27
27
 
28
28
  const CFN_EXTENSIONS: readonly string[] = [".json", ".yaml", ".yml"];
@@ -36,25 +36,51 @@ const CFN_MARKERS: readonly string[] = [
36
36
  "AWSTemplateFormatVersion",
37
37
  "MinimumProtocolVersion",
38
38
  "Microsoft.KeyVault",
39
- '"SslPolicy"',
39
+ "SslPolicy", // unquoted so a YAML `SslPolicy:` key also gates the file in, not just JSON
40
40
  ];
41
41
 
42
+ /**
43
+ * True when the content looks like a CloudFormation / ARM deployment template (one
44
+ * of the {@link CFN_MARKERS} is present).
45
+ */
46
+ function isCloudTemplate(content: string): boolean {
47
+ return CFN_MARKERS.some((marker) => content.includes(marker));
48
+ }
49
+
50
+ /**
51
+ * True when a file is one THIS detector actually scans (a `.json`/`.yaml`/`.yml`
52
+ * template). The `cloud-kms` and `jwk` detectors defer to this detector ONLY on
53
+ * such files — deferring on any extension that merely contains a marker string
54
+ * (e.g. a `.ts` with an `AWS::KMS` comment) would drop a real finding, since this
55
+ * detector never runs there.
56
+ */
57
+ export function isCloudTemplateFile(file: string, content: string): boolean {
58
+ return hasExtension(file, CFN_EXTENSIONS) && isCloudTemplate(content);
59
+ }
60
+
42
61
  // Each attribute is matched with an optional quote around the key and `:` as
43
62
  // the separator (both plain-JSON and YAML-block-scalar forms use `:`). The
44
63
  // `(?<![\w"-])` lookbehind stops a longer/prefixed attribute name from
45
64
  // matching on its suffix.
46
- const RE_CFN_KMS_RSA = /(?<![\w"-])"?KeySpec"?\s*:\s*"?RSA_\d+"?/g;
47
- const RE_CFN_KMS_EC = /(?<![\w"-])"?KeySpec"?\s*:\s*"?ECC_[A-Z0-9_]+"?/g;
65
+ // Match all three AWS KMS spec-key spellings so this detector fully owns KMS keys
66
+ // inside a template (KeySpec, the KeyPair variant, and the legacy CustomerMasterKeySpec).
67
+ const RE_CFN_KMS_RSA =
68
+ /(?<![\w"-])"?(?:KeySpec|KeyPairSpec|CustomerMasterKeySpec)"?\s*:\s*"?RSA_\d+"?/g;
69
+ const RE_CFN_KMS_EC =
70
+ /(?<![\w"-])"?(?:KeySpec|KeyPairSpec|CustomerMasterKeySpec)"?\s*:\s*"?ECC_[A-Z0-9_]+"?/g;
48
71
  const RE_CFN_ACM_RSA = /(?<![\w"-])"?KeyAlgorithm"?\s*:\s*"?RSA_\d+"?/g;
49
72
  const RE_CFN_ACM_EC = /(?<![\w"-])"?KeyAlgorithm"?\s*:\s*"?EC_[A-Za-z0-9]+"?/g;
50
73
  // CloudFront: only the pre-2018 "TLSv1" / "TLSv1_2016" / "TLSv1.1_2016"
51
74
  // values are legacy; the lookahead delimiter stops this from matching a
52
75
  // "TLSv1.2_2018"-style value that merely starts with the same prefix.
53
76
  const RE_CFN_CLOUDFRONT_TLS =
54
- /(?<![\w"-])"?MinimumProtocolVersion"?\s*:\s*"?(?:TLSv1(?:\.1)?_2016|TLSv1)(?=["'\s,}]|$)/gm;
55
- // ELB/ALB legacy SSL negotiation policies (named, dated policy strings).
77
+ /(?<![\w"-])"?MinimumProtocolVersion"?\s*:\s*"?(?:TLSv1(?:\.1)?_2016|TLSv1|SSLv3)(?=["'\s,}]|$)/gm;
78
+ // ELB/ALB legacy SSL negotiation policies (named, dated policy strings). The value
79
+ // quote is optional (`"?`) so YAML block scalars (`SslPolicy: ELBSecurityPolicy-...`)
80
+ // match too, with a delimiter lookahead so a longer/newer policy name that merely
81
+ // shares the prefix is not matched.
56
82
  const RE_CFN_ELB_TLS =
57
- /(?<![\w"-])"?SslPolicy"?\s*:\s*"ELBSecurityPolicy-(?:2016-08|TLS-1-0-\d{4}-\d{2}|TLS-1-1-\d{4}-\d{2})"/g;
83
+ /(?<![\w"-])"?SslPolicy"?\s*:\s*"?ELBSecurityPolicy-(?:2016-08|TLS-1-0-\d{4}-\d{2}|TLS-1-1-\d{4}-\d{2})(?=["'\s,}]|$)/g;
58
84
  // Azure ARM Microsoft.KeyVault key resource `properties.kty`.
59
85
  const RE_CFN_ARM_KV_RSA = /(?<![\w"-])"?kty"?\s*:\s*"?RSA"?(?!\w)/g;
60
86
  const RE_CFN_ARM_KV_EC = /(?<![\w"-])"?kty"?\s*:\s*"?EC"?(?!\w)/g;
@@ -195,9 +221,13 @@ export const cloudformationDetector: Detector = {
195
221
  detect({ file, content }): Finding[] {
196
222
  if (!CFN_MARKERS.some((marker) => content.includes(marker))) return [];
197
223
 
224
+ // Mask whole `#` comment lines (YAML CFN/ARM templates ship large commented-out
225
+ // blocks); harmless for JSON, which has no comments. Offsets preserved so finding
226
+ // locations stay exact. `.yaml`/`.json` are not centrally comment-stripped.
227
+ const scan = maskCommentLines(content, ["#"]);
198
228
  const findings: Finding[] = [];
199
229
  const add = (re: RegExp, rule: RuleMeta) =>
200
- eachMatch(re, content, (m) =>
230
+ eachMatch(re, scan, (m) =>
201
231
  findings.push(
202
232
  findingFromRule(rule, { file, content, index: m.index, matchLength: m[0].length }),
203
233
  ),
@@ -208,8 +238,13 @@ export const cloudformationDetector: Detector = {
208
238
  add(RE_CFN_ACM_EC, RULE_CFN_ACM_EC);
209
239
  add(RE_CFN_CLOUDFRONT_TLS, RULE_CFN_CLOUDFRONT_TLS);
210
240
  add(RE_CFN_ELB_TLS, RULE_CFN_ELB_TLS);
211
- add(RE_CFN_ARM_KV_RSA, RULE_CFN_ARM_KV_RSA);
212
- add(RE_CFN_ARM_KV_EC, RULE_CFN_ARM_KV_EC);
241
+ // The bare `kty` attribute is generic (every JWK has it), so only treat it as
242
+ // an ARM Key Vault key when the ARM resource type is actually present — otherwise
243
+ // a JWK in a file gated in by another marker (e.g. SslPolicy) would misfire here.
244
+ if (content.includes("Microsoft.KeyVault")) {
245
+ add(RE_CFN_ARM_KV_RSA, RULE_CFN_ARM_KV_RSA);
246
+ add(RE_CFN_ARM_KV_EC, RULE_CFN_ARM_KV_EC);
247
+ }
213
248
  return findings;
214
249
  },
215
250
  };
@@ -0,0 +1,214 @@
1
+ /**
2
+ * Config detector: classical **code-signing CLI** invocations in build scripts —
3
+ * the tools that sign long-lived, distributable artifacts (Windows installers,
4
+ * Android APKs, RPM/deb packages, NuGet packages, macOS apps) with a classical
5
+ * RSA/ECDSA signing identity.
6
+ *
7
+ * Why this is quantum-migration debt: a code signature produced today keeps
8
+ * verifying against its classical public key for the life of the artifact
9
+ * (installers and packages linger on mirrors and machines for years). Once a
10
+ * cryptographically-relevant quantum computer (CRQC) can recover the signing
11
+ * key from that public key, every such signature becomes *forgeable* — an
12
+ * attacker can mint a validly-signed trojaned installer. This is the
13
+ * signature-side analogue of harvest-now-decrypt-later: there is no
14
+ * confidentiality to harvest, so findings are `category:"signature"`,
15
+ * `hndl:false`, but they are real debt to inventory now.
16
+ *
17
+ * How this differs from the two neighbouring signing detectors — kept
18
+ * deliberately non-overlapping:
19
+ *
20
+ * - `cicd.ts` (cicdDetector) is gated to CI *pipeline* files
21
+ * (`.github/workflows/*.yml`, `.gitlab-ci.yml`, Jenkinsfile, azure-pipelines,
22
+ * CircleCI) and matches the release-automation signers there
23
+ * (cosign / gpg / jarsigner / codesign / minisign). It intentionally does
24
+ * NOT fire on a plain `build.sh`, `build.ps1`, `build.gradle`, or Makefile.
25
+ * - `supply-chain.ts` (supplyChainDetector) covers container/artifact signing
26
+ * (Docker Content Trust, CNCF Notation, in-toto) in CI files, Dockerfiles,
27
+ * and shell scripts.
28
+ *
29
+ * This detector fills the gap: the *native platform* code-signing CLIs
30
+ * (`signtool`, `osslsigncode`, `Set-AuthenticodeSignature`, `apksigner`, Gradle
31
+ * `signingConfigs`, `rpmsign`/`dpkg-sig`, `nuget sign`, `notarytool`) as they
32
+ * appear in a build script of ANY extension — because that is where developers
33
+ * actually run them. It applies everywhere except documentation
34
+ * (a README showing `signtool sign` is prose, not a build step). One token
35
+ * (`codesign`) can also be matched by `cicd.ts` when it appears inside a CI
36
+ * file; a duplicate finding across the two detectors is acceptable, and keeping
37
+ * `codesign` here means a `codesign --sign` in a hand-written `build.sh` — which
38
+ * `cicd.ts`'s CI-file gate deliberately skips — is still caught.
39
+ *
40
+ * Precision comes from two gates, mirroring `dnssec.ts`: a cheap file-level
41
+ * fast-reject (bail unless some signing-command marker is present) and
42
+ * comment-line masking (`#` for shell/PowerShell/Makefile/Gradle, `//` for
43
+ * Gradle/Groovy) so a commented-out signing step never fires. The rule regexes
44
+ * themselves key on distinctive `<tool> sign` command shapes, so the residual
45
+ * false-positive risk is low and confidence is `high`.
46
+ */
47
+ import type { Detector, Finding, RuleMeta } from "../types.js";
48
+ import {
49
+ eachMatch,
50
+ findingFromRule,
51
+ hasExtension,
52
+ maskCommentLines,
53
+ DOC_EXTENSIONS,
54
+ } from "../detect-utils.js";
55
+ import { CWE_BROKEN_CRYPTO } from "../cwe.js";
56
+
57
+ /** Shared one-line explanation, parameterised by the signing tool named in the finding. */
58
+ function signMessage(tool: string): string {
59
+ return (
60
+ `${tool} signs an artifact with a classical RSA/ECDSA key; the signature is ` +
61
+ `forgeable once a CRQC can recover the signing key — inventory the signing ` +
62
+ `identity and plan PQC migration.`
63
+ );
64
+ }
65
+
66
+ /** Shared remediation: no PQC code-signing format is broadly standardized yet. */
67
+ const REMEDIATION =
68
+ "No PQC code-signing format is broadly standardized yet; track platform roadmaps " +
69
+ "(e.g. Sigstore/PQC, Authenticode); keep signing-key rotation ready.";
70
+
71
+ /**
72
+ * Distinctive markers for the file-level fast-reject. Kept as a SUPERSET of the
73
+ * rule tokens below so the cheap gate can never exclude a file one of the rules
74
+ * would match: it includes every rule's trigger token (`--addsign`, `%_gpg_name`,
75
+ * `codesign`, `nuget`, …), not only the headline command names.
76
+ */
77
+ const MARKER_RE =
78
+ /signtool|osslsigncode|AuthenticodeSignature|apksigner|signingConfigs|rpmsign|--addsign|%_gpg_name|dpkg-sig|nuget|notarytool|codesign/;
79
+
80
+ interface CsRule {
81
+ re: RegExp;
82
+ meta: RuleMeta;
83
+ }
84
+
85
+ const CS_RULES: CsRule[] = [
86
+ {
87
+ // Windows Authenticode: `signtool sign …` (optionally `signtool.exe`),
88
+ // `osslsigncode sign …`, or the PowerShell `Set-AuthenticodeSignature` cmdlet.
89
+ re: /\bsigntool(?:\.exe)?\s+sign\b|\bosslsigncode\s+sign\b|\bSet-AuthenticodeSignature\b/g,
90
+ meta: {
91
+ id: "codesign-authenticode",
92
+ title: "Windows Authenticode code signing (RSA)",
93
+ description: "signtool / osslsigncode / Set-AuthenticodeSignature Authenticode signing",
94
+ category: "signature",
95
+ severity: "medium",
96
+ confidence: "high",
97
+ algorithm: "RSA",
98
+ hndl: false,
99
+ cwe: CWE_BROKEN_CRYPTO,
100
+ message: signMessage(
101
+ "Windows Authenticode (signtool/osslsigncode/Set-AuthenticodeSignature)",
102
+ ),
103
+ remediation: REMEDIATION,
104
+ },
105
+ },
106
+ {
107
+ // Android: `apksigner sign …` (covers `--ks` / `--ks-key-alias` forms), or a
108
+ // Gradle `signingConfigs { … }` block (which carries `storeFile`/`keyAlias`).
109
+ re: /\bapksigner\s+sign\b|\bsigningConfigs\s*\{/g,
110
+ meta: {
111
+ id: "codesign-apk",
112
+ title: "Android APK signing (RSA)",
113
+ description: "apksigner / Gradle signingConfigs APK signing",
114
+ category: "signature",
115
+ severity: "medium",
116
+ confidence: "high",
117
+ algorithm: "RSA",
118
+ hndl: false,
119
+ cwe: CWE_BROKEN_CRYPTO,
120
+ message: signMessage("Android APK signing (apksigner/Gradle signingConfigs)"),
121
+ remediation: REMEDIATION,
122
+ },
123
+ },
124
+ {
125
+ // RPM/deb: `rpmsign --addsign`, `rpm --addsign`, the `%_gpg_name` rpmmacro, or
126
+ // `dpkg-sig --sign`. All ultimately GPG (classically RSA) signatures.
127
+ re: /\brpmsign\s+--addsign\b|\brpm\s+--addsign\b|%_gpg_name\b|\bdpkg-sig\s+--sign\b/g,
128
+ meta: {
129
+ id: "codesign-rpm",
130
+ title: "RPM/deb package signing (RSA)",
131
+ description: "rpmsign / rpm --addsign / %_gpg_name / dpkg-sig package signing",
132
+ category: "signature",
133
+ severity: "medium",
134
+ confidence: "high",
135
+ algorithm: "RSA",
136
+ hndl: false,
137
+ cwe: CWE_BROKEN_CRYPTO,
138
+ message: signMessage("RPM/deb package signing (rpmsign/rpm --addsign/dpkg-sig, GPG)"),
139
+ remediation: REMEDIATION,
140
+ },
141
+ },
142
+ {
143
+ // NuGet: `nuget sign …` — also matches `dotnet nuget sign …` (the `nuget sign`
144
+ // token is a substring of it), typically paired with `--certificate-*` flags.
145
+ re: /\bnuget\s+sign\b/g,
146
+ meta: {
147
+ id: "codesign-nuget",
148
+ title: "NuGet package signing (RSA)",
149
+ description: "nuget sign / dotnet nuget sign package signing",
150
+ category: "signature",
151
+ severity: "medium",
152
+ confidence: "high",
153
+ algorithm: "RSA",
154
+ hndl: false,
155
+ cwe: CWE_BROKEN_CRYPTO,
156
+ message: signMessage("NuGet package signing (nuget sign/dotnet nuget sign)"),
157
+ remediation: REMEDIATION,
158
+ },
159
+ },
160
+ {
161
+ // Apple: `xcrun notarytool submit …`, or `codesign --sign` / `codesign -s`.
162
+ // Allow intervening flags before `--sign`/`-s`, bounded to the codesign
163
+ // invocation so it can't latch onto a later command's flag across `&&`/`|`/`;`
164
+ // (same shape as cicd.ts's codesign rule). Algorithm is "unknown": an Apple
165
+ // signing identity may be RSA or ECDSA.
166
+ re: /\bnotarytool\s+submit\b|\bcodesign\b[^\n&|;]{0,120}?\s(?:-s\b|--sign\b)/g,
167
+ meta: {
168
+ id: "codesign-apple",
169
+ title: "Apple code signing / notarization",
170
+ description: "codesign --sign / xcrun notarytool submit macOS signing",
171
+ category: "signature",
172
+ severity: "medium",
173
+ confidence: "high",
174
+ algorithm: "unknown",
175
+ hndl: false,
176
+ cwe: CWE_BROKEN_CRYPTO,
177
+ message: signMessage("Apple code signing (codesign/notarytool)"),
178
+ remediation: REMEDIATION,
179
+ },
180
+ },
181
+ ];
182
+
183
+ /** Detects classical code-signing CLI invocations in build scripts. */
184
+ export const codesignDetector: Detector = {
185
+ id: "codesign-signing",
186
+ description:
187
+ "Classical code-signing CLIs in build scripts (Authenticode, APK, RPM, NuGet, Apple)",
188
+ scope: "config",
189
+ language: "any",
190
+ rules: CS_RULES.map((r) => r.meta),
191
+ // Applies to build scripts of any extension EXCEPT documentation: a README that
192
+ // shows `signtool sign` in an example is prose, not a build step.
193
+ appliesTo: (f) => !hasExtension(f, DOC_EXTENSIONS),
194
+ detect({ file, content }): Finding[] {
195
+ // Cheap fast-reject: bail unless some signing-command marker is present, so
196
+ // the per-rule regexes never run on the overwhelming majority of files.
197
+ if (!MARKER_RE.test(content)) return [];
198
+
199
+ // Mask whole comment lines so a commented-out signing step can't fire.
200
+ // `#` covers shell / PowerShell / Makefile / Gradle(Groovy & Kotlin DSL);
201
+ // `//` covers Gradle/Groovy. Offsets are preserved so finding locations stay
202
+ // exact (see maskCommentLines).
203
+ const scan = maskCommentLines(content, ["#", "//"]);
204
+ const findings: Finding[] = [];
205
+ for (const rule of CS_RULES) {
206
+ eachMatch(rule.re, scan, (m) => {
207
+ findings.push(
208
+ findingFromRule(rule.meta, { file, content, index: m.index, matchLength: m[0].length }),
209
+ );
210
+ });
211
+ }
212
+ return findings;
213
+ },
214
+ };
@@ -27,14 +27,24 @@ const RE_CS_DSA = /\bDSA\.Create\s*\(|\bnew\s+DSACryptoServiceProvider\s*\(|\bne
27
27
  // - SslProtocols.Ssl3 / .Tls / .Tls11 → deprecated SSL 3.0 / TLS 1.0 / 1.1.
28
28
  // `SslProtocols.Tls` (no suffix) is the legacy TLS 1.0 constant; `Tls12`/`Tls13`
29
29
  // are excluded by the trailing \b (the following digit is a word char).
30
+ // Only the genuinely-permissive forms: the built-in accept-any validator, or a
31
+ // callback assigned an always-`true` lambda. A callback assigned a NAMED validator
32
+ // method is typically certificate PINNING (stricter than default) and must NOT fire.
33
+ // An accept-any server-certificate callback: the BouncyCastle helper, or a
34
+ // ServerCertificateCustomValidationCallback that unconditionally yields `true` — as an
35
+ // expression lambda (`=> true`), a block-body lambda (`=> { return true; }`), or a
36
+ // `delegate { return true; }`. Requiring a literal `true` (not just any `=>`) keeps
37
+ // certificate PINNING — which returns a comparison, `=> cert.Thumbprint == pinned` —
38
+ // from being flagged as a disabled validator.
30
39
  const RE_CS_TLS_CERT_VALIDATION =
31
- /\bDangerousAcceptAnyServerCertificateValidator\b|ServerCertificateCustomValidationCallback\s*=/g;
40
+ /\bDangerousAcceptAnyServerCertificateValidator\b|ServerCertificateCustomValidationCallback\s*=\s*(?:[^;\n]{0,80}=>\s*(?:true\b|\{\s*return\s+true\b)|delegate\s*(?:\([^)]{0,80}\))?\s*\{\s*return\s+true\b)/g;
32
41
  const RE_CS_TLS_LEGACY_VERSION = /\bSslProtocols\.(?:Tls|Tls11|Ssl3)\b/g;
33
42
  // Identifier-form JWT/JOSE signature algorithms (audit F7). The quoted-string
34
43
  // alg token ("RS256") is caught by the language-agnostic jwt-jose detector, but
35
44
  // Microsoft.IdentityModel passes the alg as an IDENTIFIER, not a string literal:
36
- // SecurityAlgorithms.RsaSha256 / SecurityAlgorithms.EcdsaSha256.
37
- const RE_CS_JWT_ALG = /\bSecurityAlgorithms\.(?:Rsa|Ecdsa)Sha(?:256|384|512)\b/g;
45
+ // SecurityAlgorithms.RsaSha256 / SecurityAlgorithms.EcdsaSha256, and the
46
+ // `…Signature`-suffixed constants used in SigningCredentials (RsaSha256Signature).
47
+ const RE_CS_JWT_ALG = /\bSecurityAlgorithms\.(?:Rsa|Ecdsa)Sha(?:256|384|512)(?:Signature)?\b/g;
38
48
  // BouncyCastle (Org.BouncyCastle) modern-curve and finite-field DH primitives,
39
49
  // which System.Security.Cryptography does not expose directly. Each regex is
40
50
  // anchored to the distinctive BouncyCastle class names for one family so it