@quantakrypto/core 0.4.3 → 0.4.4

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 (163) hide show
  1. package/dist/cbom-merge.d.ts +9 -0
  2. package/dist/cbom-merge.d.ts.map +1 -0
  3. package/dist/cbom-merge.js +99 -0
  4. package/dist/cbom-merge.js.map +1 -0
  5. package/dist/comments.d.ts +4 -3
  6. package/dist/comments.d.ts.map +1 -1
  7. package/dist/comments.js +53 -33
  8. package/dist/comments.js.map +1 -1
  9. package/dist/dependencies.d.ts.map +1 -1
  10. package/dist/dependencies.js +1 -1
  11. package/dist/dependencies.js.map +1 -1
  12. package/dist/detect-utils.d.ts +5 -1
  13. package/dist/detect-utils.d.ts.map +1 -1
  14. package/dist/detect-utils.js +11 -2
  15. package/dist/detect-utils.js.map +1 -1
  16. package/dist/detectors/c.d.ts +4 -2
  17. package/dist/detectors/c.d.ts.map +1 -1
  18. package/dist/detectors/c.js +192 -1
  19. package/dist/detectors/c.js.map +1 -1
  20. package/dist/detectors/cicd.d.ts +27 -0
  21. package/dist/detectors/cicd.d.ts.map +1 -0
  22. package/dist/detectors/cicd.js +116 -0
  23. package/dist/detectors/cicd.js.map +1 -0
  24. package/dist/detectors/cloud-kms.d.ts +21 -0
  25. package/dist/detectors/cloud-kms.d.ts.map +1 -0
  26. package/dist/detectors/cloud-kms.js +57 -0
  27. package/dist/detectors/cloud-kms.js.map +1 -0
  28. package/dist/detectors/cloudformation.d.ts +30 -0
  29. package/dist/detectors/cloudformation.d.ts.map +1 -0
  30. package/dist/detectors/cloudformation.js +170 -0
  31. package/dist/detectors/cloudformation.js.map +1 -0
  32. package/dist/detectors/database.d.ts +17 -0
  33. package/dist/detectors/database.d.ts.map +1 -0
  34. package/dist/detectors/database.js +60 -0
  35. package/dist/detectors/database.js.map +1 -0
  36. package/dist/detectors/dnssec.d.ts +55 -0
  37. package/dist/detectors/dnssec.d.ts.map +1 -0
  38. package/dist/detectors/dnssec.js +107 -0
  39. package/dist/detectors/dnssec.js.map +1 -0
  40. package/dist/detectors/elixir.d.ts +20 -0
  41. package/dist/detectors/elixir.d.ts.map +1 -0
  42. package/dist/detectors/elixir.js +195 -0
  43. package/dist/detectors/elixir.js.map +1 -0
  44. package/dist/detectors/java.d.ts.map +1 -1
  45. package/dist/detectors/java.js +1 -1
  46. package/dist/detectors/java.js.map +1 -1
  47. package/dist/detectors/jose.d.ts +20 -0
  48. package/dist/detectors/jose.d.ts.map +1 -0
  49. package/dist/detectors/jose.js +62 -0
  50. package/dist/detectors/jose.js.map +1 -0
  51. package/dist/detectors/jwk.d.ts +25 -0
  52. package/dist/detectors/jwk.d.ts.map +1 -0
  53. package/dist/detectors/jwk.js +95 -0
  54. package/dist/detectors/jwk.js.map +1 -0
  55. package/dist/detectors/k8s.d.ts +17 -0
  56. package/dist/detectors/k8s.d.ts.map +1 -0
  57. package/dist/detectors/k8s.js +85 -0
  58. package/dist/detectors/k8s.js.map +1 -0
  59. package/dist/detectors/mesh.d.ts +35 -0
  60. package/dist/detectors/mesh.d.ts.map +1 -0
  61. package/dist/detectors/mesh.js +109 -0
  62. package/dist/detectors/mesh.js.map +1 -0
  63. package/dist/detectors/messaging.d.ts +16 -0
  64. package/dist/detectors/messaging.d.ts.map +1 -0
  65. package/dist/detectors/messaging.js +72 -0
  66. package/dist/detectors/messaging.js.map +1 -0
  67. package/dist/detectors/pem.js +1 -1
  68. package/dist/detectors/pem.js.map +1 -1
  69. package/dist/detectors/php.d.ts +24 -0
  70. package/dist/detectors/php.d.ts.map +1 -0
  71. package/dist/detectors/php.js +167 -0
  72. package/dist/detectors/php.js.map +1 -0
  73. package/dist/detectors/python.d.ts.map +1 -1
  74. package/dist/detectors/python.js +70 -0
  75. package/dist/detectors/python.js.map +1 -1
  76. package/dist/detectors/rust.d.ts.map +1 -1
  77. package/dist/detectors/rust.js +68 -0
  78. package/dist/detectors/rust.js.map +1 -1
  79. package/dist/detectors/secrets.d.ts +21 -0
  80. package/dist/detectors/secrets.d.ts.map +1 -0
  81. package/dist/detectors/secrets.js +79 -0
  82. package/dist/detectors/secrets.js.map +1 -0
  83. package/dist/detectors/source.d.ts.map +1 -1
  84. package/dist/detectors/source.js +190 -47
  85. package/dist/detectors/source.js.map +1 -1
  86. package/dist/detectors/stateful-hbs.d.ts.map +1 -1
  87. package/dist/detectors/stateful-hbs.js +9 -6
  88. package/dist/detectors/stateful-hbs.js.map +1 -1
  89. package/dist/detectors/terraform.d.ts +21 -0
  90. package/dist/detectors/terraform.d.ts.map +1 -0
  91. package/dist/detectors/terraform.js +123 -0
  92. package/dist/detectors/terraform.js.map +1 -0
  93. package/dist/detectors/vpn.d.ts +22 -0
  94. package/dist/detectors/vpn.d.ts.map +1 -0
  95. package/dist/detectors/vpn.js +99 -0
  96. package/dist/detectors/vpn.js.map +1 -0
  97. package/dist/evidence.d.ts +5 -0
  98. package/dist/evidence.d.ts.map +1 -1
  99. package/dist/evidence.js +5 -0
  100. package/dist/evidence.js.map +1 -1
  101. package/dist/index.d.ts +5 -0
  102. package/dist/index.d.ts.map +1 -1
  103. package/dist/index.js +6 -0
  104. package/dist/index.js.map +1 -1
  105. package/dist/policy.d.ts +67 -0
  106. package/dist/policy.d.ts.map +1 -0
  107. package/dist/policy.js +115 -0
  108. package/dist/policy.js.map +1 -0
  109. package/dist/registry.d.ts.map +1 -1
  110. package/dist/registry.js +30 -0
  111. package/dist/registry.js.map +1 -1
  112. package/dist/remediate-pipeline.d.ts.map +1 -1
  113. package/dist/remediate-pipeline.js +22 -1
  114. package/dist/remediate-pipeline.js.map +1 -1
  115. package/dist/report.d.ts.map +1 -1
  116. package/dist/report.js +7 -0
  117. package/dist/report.js.map +1 -1
  118. package/dist/standards.d.ts +96 -0
  119. package/dist/standards.d.ts.map +1 -0
  120. package/dist/standards.js +107 -0
  121. package/dist/standards.js.map +1 -0
  122. package/dist/types.d.ts +1 -1
  123. package/dist/types.d.ts.map +1 -1
  124. package/dist/types.js.map +1 -1
  125. package/dist/version.d.ts +1 -1
  126. package/dist/version.js +1 -1
  127. package/dist/version.js.map +1 -1
  128. package/package.json +1 -1
  129. package/src/cbom-merge.ts +116 -0
  130. package/src/comments.ts +59 -33
  131. package/src/dependencies.ts +2 -1
  132. package/src/detect-utils.ts +14 -2
  133. package/src/detectors/c.ts +209 -3
  134. package/src/detectors/cicd.ts +158 -0
  135. package/src/detectors/cloud-kms.ts +87 -0
  136. package/src/detectors/cloudformation.ts +215 -0
  137. package/src/detectors/database.ts +88 -0
  138. package/src/detectors/dnssec.ts +186 -0
  139. package/src/detectors/elixir.ts +237 -0
  140. package/src/detectors/java.ts +2 -1
  141. package/src/detectors/jose.ts +94 -0
  142. package/src/detectors/jwk.ts +128 -0
  143. package/src/detectors/k8s.ts +112 -0
  144. package/src/detectors/mesh.ts +154 -0
  145. package/src/detectors/messaging.ts +98 -0
  146. package/src/detectors/pem.ts +1 -1
  147. package/src/detectors/php.ts +225 -0
  148. package/src/detectors/python.ts +71 -0
  149. package/src/detectors/rust.ts +71 -0
  150. package/src/detectors/secrets.ts +114 -0
  151. package/src/detectors/source.ts +235 -68
  152. package/src/detectors/stateful-hbs.ts +9 -6
  153. package/src/detectors/terraform.ts +154 -0
  154. package/src/detectors/vpn.ts +131 -0
  155. package/src/evidence.ts +11 -0
  156. package/src/index.ts +10 -0
  157. package/src/policy.ts +180 -0
  158. package/src/registry.ts +30 -0
  159. package/src/remediate-pipeline.ts +25 -2
  160. package/src/report.ts +7 -0
  161. package/src/standards.ts +186 -0
  162. package/src/types.ts +2 -0
  163. package/src/version.ts +1 -1
@@ -0,0 +1,30 @@
1
+ /**
2
+ * IaC detector: classical asymmetric cryptography (and legacy TLS config)
3
+ * declared in AWS CloudFormation / Azure ARM (incl. Bicep-compiled JSON)
4
+ * templates — plus CDK `cdk synth` output, which is plain CloudFormation
5
+ * JSON. This is a distinct surface from `terraform.ts`: CloudFormation/ARM
6
+ * use PascalCase JSON/YAML property names, never Terraform's snake_case HCL
7
+ * attributes, so the two detectors never collide on the same file.
8
+ *
9
+ * Covered:
10
+ * - AWS::KMS::Key `KeySpec`: `"RSA_…"` (kem) / `"ECC_…"` (key-exchange)
11
+ * - AWS::CertificateManager::Certificate
12
+ * `KeyAlgorithm`: `"RSA_…"` / `"EC_…"` (certificate)
13
+ * - CloudFront distribution `MinimumProtocolVersion`: legacy `TLSv1` / `TLSv1.1` (tls)
14
+ * - ELB/ALB listener `SslPolicy`: legacy `ELBSecurityPolicy-2016-08` /
15
+ * `-TLS-1-0-…` / `-TLS-1-1-…` (tls)
16
+ * - Azure ARM `Microsoft.KeyVault` key `kty`: `"RSA"` (kem) / `"EC"` (key-exchange)
17
+ *
18
+ * `KeySpec` is PascalCase and case-sensitive, so it never matches Terraform's
19
+ * snake_case `customer_master_key_spec` (same underlying AWS API, different
20
+ * authoring surface). A fast-reject on a CFN/ARM marker keeps this detector
21
+ * from ever firing on arbitrary JSON/YAML that happens to contain one of
22
+ * these property names in an unrelated context.
23
+ */
24
+ import type { Detector } from "../types.js";
25
+ /**
26
+ * Detects classical asymmetric crypto and legacy TLS config declared in
27
+ * AWS CloudFormation / Azure ARM (incl. Bicep-compiled JSON) templates.
28
+ */
29
+ export declare const cloudformationDetector: Detector;
30
+ //# sourceMappingURL=cloudformation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cloudformation.d.ts","sourceRoot":"","sources":["../../src/detectors/cloudformation.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,OAAO,KAAK,EAAE,QAAQ,EAAqB,MAAM,aAAa,CAAC;AAsJ/D;;;GAGG;AACH,eAAO,MAAM,sBAAsB,EAAE,QAqCpC,CAAC"}
@@ -0,0 +1,170 @@
1
+ import { eachMatch, findingFromRule, hasExtension } from "../detect-utils.js";
2
+ import { CWE_BROKEN_CRYPTO, CWE_WEAK_STRENGTH } from "../cwe.js";
3
+ const CFN_EXTENSIONS = [".json", ".yaml", ".yml"];
4
+ // Fast-reject markers: at least one must appear before any rule regex runs, so
5
+ // this detector never scans arbitrary JSON/YAML that merely happens to share
6
+ // an attribute name (`KeySpec`, `kty`, …) with an unrelated schema.
7
+ const CFN_MARKERS = [
8
+ "AWS::KMS",
9
+ "AWS::CertificateManager",
10
+ "AWSTemplateFormatVersion",
11
+ "MinimumProtocolVersion",
12
+ "Microsoft.KeyVault",
13
+ '"SslPolicy"',
14
+ ];
15
+ // Each attribute is matched with an optional quote around the key and `:` as
16
+ // the separator (both plain-JSON and YAML-block-scalar forms use `:`). The
17
+ // `(?<![\w"-])` lookbehind stops a longer/prefixed attribute name from
18
+ // matching on its suffix.
19
+ const RE_CFN_KMS_RSA = /(?<![\w"-])"?KeySpec"?\s*:\s*"?RSA_\d+"?/g;
20
+ const RE_CFN_KMS_EC = /(?<![\w"-])"?KeySpec"?\s*:\s*"?ECC_[A-Z0-9_]+"?/g;
21
+ const RE_CFN_ACM_RSA = /(?<![\w"-])"?KeyAlgorithm"?\s*:\s*"?RSA_\d+"?/g;
22
+ const RE_CFN_ACM_EC = /(?<![\w"-])"?KeyAlgorithm"?\s*:\s*"?EC_[A-Za-z0-9]+"?/g;
23
+ // CloudFront: only the pre-2018 "TLSv1" / "TLSv1_2016" / "TLSv1.1_2016"
24
+ // values are legacy; the lookahead delimiter stops this from matching a
25
+ // "TLSv1.2_2018"-style value that merely starts with the same prefix.
26
+ const RE_CFN_CLOUDFRONT_TLS = /(?<![\w"-])"?MinimumProtocolVersion"?\s*:\s*"?(?:TLSv1(?:\.1)?_2016|TLSv1)(?=["'\s,}]|$)/gm;
27
+ // ELB/ALB legacy SSL negotiation policies (named, dated policy strings).
28
+ const RE_CFN_ELB_TLS = /(?<![\w"-])"?SslPolicy"?\s*:\s*"ELBSecurityPolicy-(?:2016-08|TLS-1-0-\d{4}-\d{2}|TLS-1-1-\d{4}-\d{2})"/g;
29
+ // Azure ARM Microsoft.KeyVault key resource `properties.kty`.
30
+ const RE_CFN_ARM_KV_RSA = /(?<![\w"-])"?kty"?\s*:\s*"?RSA"?(?!\w)/g;
31
+ const RE_CFN_ARM_KV_EC = /(?<![\w"-])"?kty"?\s*:\s*"?EC"?(?!\w)/g;
32
+ const RULE_CFN_KMS_RSA = {
33
+ id: "cfn-kms-rsa",
34
+ title: "CloudFormation KMS RSA key",
35
+ description: 'AWS::KMS::Key KeySpec = "RSA_*"',
36
+ category: "kem",
37
+ severity: "high",
38
+ confidence: "high",
39
+ algorithm: "RSA",
40
+ hndl: true,
41
+ cwe: CWE_BROKEN_CRYPTO,
42
+ message: "CloudFormation provisions a classical RSA KMS key (harvest-now-decrypt-later exposed for encryption CMKs).",
43
+ remediation: "Plan migration to PQC as cloud KMS adds ML-KEM / ML-DSA key specs.",
44
+ };
45
+ const RULE_CFN_KMS_EC = {
46
+ id: "cfn-kms-ec",
47
+ title: "CloudFormation KMS EC key",
48
+ description: 'AWS::KMS::Key KeySpec = "ECC_*"',
49
+ category: "key-exchange",
50
+ severity: "high",
51
+ confidence: "high",
52
+ algorithm: "ECDH",
53
+ hndl: true,
54
+ cwe: CWE_BROKEN_CRYPTO,
55
+ message: "CloudFormation provisions a classical EC KMS key; EC keys feed ECDSA signatures and ECDH key agreement (the ECDH path is harvest-now-decrypt-later exposed).",
56
+ remediation: "For key agreement: hybrid X25519MLKEM768 (ML-KEM-768). For signatures: ML-DSA-65 (FIPS 204).",
57
+ };
58
+ const RULE_CFN_ACM_RSA = {
59
+ id: "cfn-acm-rsa",
60
+ title: "CloudFormation ACM RSA certificate",
61
+ description: 'AWS::CertificateManager::Certificate KeyAlgorithm = "RSA_*"',
62
+ category: "certificate",
63
+ severity: "high",
64
+ confidence: "high",
65
+ algorithm: "RSA",
66
+ hndl: true,
67
+ cwe: CWE_BROKEN_CRYPTO,
68
+ message: "CloudFormation provisions an ACM certificate with a classical RSA key, which is not quantum-safe.",
69
+ remediation: "Plan migration to PQC (ML-KEM-768 for encryption, ML-DSA-65 for signatures).",
70
+ };
71
+ const RULE_CFN_ACM_EC = {
72
+ id: "cfn-acm-ec",
73
+ title: "CloudFormation ACM EC certificate",
74
+ description: 'AWS::CertificateManager::Certificate KeyAlgorithm = "EC_*"',
75
+ category: "certificate",
76
+ severity: "high",
77
+ confidence: "high",
78
+ algorithm: "ECDSA",
79
+ hndl: false,
80
+ cwe: CWE_BROKEN_CRYPTO,
81
+ message: "CloudFormation provisions an ACM certificate with a classical EC key, forgeable by a quantum attacker.",
82
+ remediation: "ML-DSA-65 (FIPS 204) or SLH-DSA (FIPS 205).",
83
+ };
84
+ const RULE_CFN_CLOUDFRONT_TLS = {
85
+ id: "cfn-cloudfront-legacy-tls",
86
+ title: "CloudFormation CloudFront legacy TLS",
87
+ description: 'CloudFront Distribution MinimumProtocolVersion = "TLSv1" / "TLSv1.1"',
88
+ category: "tls",
89
+ severity: "medium",
90
+ confidence: "high",
91
+ hndl: false,
92
+ cwe: CWE_WEAK_STRENGTH,
93
+ message: "CloudFront distribution permits TLS 1.0/1.1, which are deprecated and insecure.",
94
+ remediation: "Set MinimumProtocolVersion to TLSv1.2_2021 (or later) and prefer PQC-hybrid key exchange.",
95
+ };
96
+ const RULE_CFN_ELB_TLS = {
97
+ id: "cfn-elb-legacy-tls",
98
+ title: "CloudFormation ELB/ALB legacy TLS policy",
99
+ description: "Elastic Load Balancer SslPolicy naming a pre-2017 legacy policy",
100
+ category: "tls",
101
+ severity: "medium",
102
+ confidence: "high",
103
+ hndl: false,
104
+ cwe: CWE_WEAK_STRENGTH,
105
+ message: "Load balancer listener uses a legacy SSL negotiation policy permitting TLS 1.0/1.1.",
106
+ remediation: "Use ELBSecurityPolicy-TLS13-1-2-2021-06 (or the latest FS+TLS1.2/1.3 policy).",
107
+ };
108
+ const RULE_CFN_ARM_KV_RSA = {
109
+ id: "cfn-arm-keyvault-rsa",
110
+ title: "ARM template Key Vault RSA key",
111
+ description: 'Microsoft.KeyVault key resource kty = "RSA"',
112
+ category: "kem",
113
+ severity: "high",
114
+ confidence: "high",
115
+ algorithm: "RSA",
116
+ hndl: true,
117
+ cwe: CWE_BROKEN_CRYPTO,
118
+ message: "ARM template provisions a classical RSA Azure Key Vault key, which is not quantum-safe.",
119
+ remediation: "Plan migration to PQC (ML-KEM-768 / ML-DSA-65).",
120
+ };
121
+ const RULE_CFN_ARM_KV_EC = {
122
+ id: "cfn-arm-keyvault-ec",
123
+ title: "ARM template Key Vault EC key",
124
+ description: 'Microsoft.KeyVault key resource kty = "EC"',
125
+ category: "key-exchange",
126
+ severity: "high",
127
+ confidence: "high",
128
+ algorithm: "ECDH",
129
+ hndl: true,
130
+ cwe: CWE_BROKEN_CRYPTO,
131
+ message: "ARM template provisions a classical EC Azure Key Vault key; EC keys feed ECDSA signatures and ECDH key agreement (the ECDH path is harvest-now-decrypt-later exposed).",
132
+ remediation: "For key agreement: hybrid X25519MLKEM768 (ML-KEM-768). For signatures: ML-DSA-65 (FIPS 204).",
133
+ };
134
+ /**
135
+ * Detects classical asymmetric crypto and legacy TLS config declared in
136
+ * AWS CloudFormation / Azure ARM (incl. Bicep-compiled JSON) templates.
137
+ */
138
+ export const cloudformationDetector = {
139
+ id: "cloudformation-crypto",
140
+ description: "Classical asymmetric crypto and legacy TLS config declared in CloudFormation / ARM templates (IaC)",
141
+ scope: "config",
142
+ language: "any",
143
+ rules: [
144
+ RULE_CFN_KMS_RSA,
145
+ RULE_CFN_KMS_EC,
146
+ RULE_CFN_ACM_RSA,
147
+ RULE_CFN_ACM_EC,
148
+ RULE_CFN_CLOUDFRONT_TLS,
149
+ RULE_CFN_ELB_TLS,
150
+ RULE_CFN_ARM_KV_RSA,
151
+ RULE_CFN_ARM_KV_EC,
152
+ ],
153
+ appliesTo: (f) => hasExtension(f, CFN_EXTENSIONS),
154
+ detect({ file, content }) {
155
+ if (!CFN_MARKERS.some((marker) => content.includes(marker)))
156
+ return [];
157
+ const findings = [];
158
+ const add = (re, rule) => eachMatch(re, content, (m) => findings.push(findingFromRule(rule, { file, content, index: m.index, matchLength: m[0].length })));
159
+ add(RE_CFN_KMS_RSA, RULE_CFN_KMS_RSA);
160
+ add(RE_CFN_KMS_EC, RULE_CFN_KMS_EC);
161
+ add(RE_CFN_ACM_RSA, RULE_CFN_ACM_RSA);
162
+ add(RE_CFN_ACM_EC, RULE_CFN_ACM_EC);
163
+ add(RE_CFN_CLOUDFRONT_TLS, RULE_CFN_CLOUDFRONT_TLS);
164
+ add(RE_CFN_ELB_TLS, RULE_CFN_ELB_TLS);
165
+ add(RE_CFN_ARM_KV_RSA, RULE_CFN_ARM_KV_RSA);
166
+ add(RE_CFN_ARM_KV_EC, RULE_CFN_ARM_KV_EC);
167
+ return findings;
168
+ },
169
+ };
170
+ //# sourceMappingURL=cloudformation.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cloudformation.js","sourceRoot":"","sources":["../../src/detectors/cloudformation.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * IaC detector: classical asymmetric cryptography (and legacy TLS config)\n * declared in AWS CloudFormation / Azure ARM (incl. Bicep-compiled JSON)\n * templates — plus CDK `cdk synth` output, which is plain CloudFormation\n * JSON. This is a distinct surface from `terraform.ts`: CloudFormation/ARM\n * use PascalCase JSON/YAML property names, never Terraform's snake_case HCL\n * attributes, so the two detectors never collide on the same file.\n *\n * Covered:\n * - AWS::KMS::Key `KeySpec`: `\"RSA_…\"` (kem) / `\"ECC_…\"` (key-exchange)\n * - AWS::CertificateManager::Certificate\n * `KeyAlgorithm`: `\"RSA_…\"` / `\"EC_…\"` (certificate)\n * - CloudFront distribution `MinimumProtocolVersion`: legacy `TLSv1` / `TLSv1.1` (tls)\n * - ELB/ALB listener `SslPolicy`: legacy `ELBSecurityPolicy-2016-08` /\n * `-TLS-1-0-…` / `-TLS-1-1-…` (tls)\n * - Azure ARM `Microsoft.KeyVault` key `kty`: `\"RSA\"` (kem) / `\"EC\"` (key-exchange)\n *\n * `KeySpec` is PascalCase and case-sensitive, so it never matches Terraform's\n * snake_case `customer_master_key_spec` (same underlying AWS API, different\n * authoring surface). A fast-reject on a CFN/ARM marker keeps this detector\n * from ever firing on arbitrary JSON/YAML that happens to contain one of\n * these property names in an unrelated context.\n */\nimport type { Detector, Finding, RuleMeta } from \"../types.js\";\nimport { eachMatch, findingFromRule, hasExtension } from \"../detect-utils.js\";\nimport { CWE_BROKEN_CRYPTO, CWE_WEAK_STRENGTH } from \"../cwe.js\";\n\nconst CFN_EXTENSIONS: readonly string[] = [\".json\", \".yaml\", \".yml\"];\n\n// Fast-reject markers: at least one must appear before any rule regex runs, so\n// this detector never scans arbitrary JSON/YAML that merely happens to share\n// an attribute name (`KeySpec`, `kty`, …) with an unrelated schema.\nconst CFN_MARKERS: readonly string[] = [\n \"AWS::KMS\",\n \"AWS::CertificateManager\",\n \"AWSTemplateFormatVersion\",\n \"MinimumProtocolVersion\",\n \"Microsoft.KeyVault\",\n '\"SslPolicy\"',\n];\n\n// Each attribute is matched with an optional quote around the key and `:` as\n// the separator (both plain-JSON and YAML-block-scalar forms use `:`). The\n// `(?<![\\w\"-])` lookbehind stops a longer/prefixed attribute name from\n// matching on its suffix.\nconst RE_CFN_KMS_RSA = /(?<![\\w\"-])\"?KeySpec\"?\\s*:\\s*\"?RSA_\\d+\"?/g;\nconst RE_CFN_KMS_EC = /(?<![\\w\"-])\"?KeySpec\"?\\s*:\\s*\"?ECC_[A-Z0-9_]+\"?/g;\nconst RE_CFN_ACM_RSA = /(?<![\\w\"-])\"?KeyAlgorithm\"?\\s*:\\s*\"?RSA_\\d+\"?/g;\nconst RE_CFN_ACM_EC = /(?<![\\w\"-])\"?KeyAlgorithm\"?\\s*:\\s*\"?EC_[A-Za-z0-9]+\"?/g;\n// CloudFront: only the pre-2018 \"TLSv1\" / \"TLSv1_2016\" / \"TLSv1.1_2016\"\n// values are legacy; the lookahead delimiter stops this from matching a\n// \"TLSv1.2_2018\"-style value that merely starts with the same prefix.\nconst RE_CFN_CLOUDFRONT_TLS =\n /(?<![\\w\"-])\"?MinimumProtocolVersion\"?\\s*:\\s*\"?(?:TLSv1(?:\\.1)?_2016|TLSv1)(?=[\"'\\s,}]|$)/gm;\n// ELB/ALB legacy SSL negotiation policies (named, dated policy strings).\nconst RE_CFN_ELB_TLS =\n /(?<![\\w\"-])\"?SslPolicy\"?\\s*:\\s*\"ELBSecurityPolicy-(?:2016-08|TLS-1-0-\\d{4}-\\d{2}|TLS-1-1-\\d{4}-\\d{2})\"/g;\n// Azure ARM Microsoft.KeyVault key resource `properties.kty`.\nconst RE_CFN_ARM_KV_RSA = /(?<![\\w\"-])\"?kty\"?\\s*:\\s*\"?RSA\"?(?!\\w)/g;\nconst RE_CFN_ARM_KV_EC = /(?<![\\w\"-])\"?kty\"?\\s*:\\s*\"?EC\"?(?!\\w)/g;\n\nconst RULE_CFN_KMS_RSA: RuleMeta = {\n id: \"cfn-kms-rsa\",\n title: \"CloudFormation KMS RSA key\",\n description: 'AWS::KMS::Key KeySpec = \"RSA_*\"',\n category: \"kem\",\n severity: \"high\",\n confidence: \"high\",\n algorithm: \"RSA\",\n hndl: true,\n cwe: CWE_BROKEN_CRYPTO,\n message:\n \"CloudFormation provisions a classical RSA KMS key (harvest-now-decrypt-later exposed for encryption CMKs).\",\n remediation: \"Plan migration to PQC as cloud KMS adds ML-KEM / ML-DSA key specs.\",\n};\nconst RULE_CFN_KMS_EC: RuleMeta = {\n id: \"cfn-kms-ec\",\n title: \"CloudFormation KMS EC key\",\n description: 'AWS::KMS::Key KeySpec = \"ECC_*\"',\n category: \"key-exchange\",\n severity: \"high\",\n confidence: \"high\",\n algorithm: \"ECDH\",\n hndl: true,\n cwe: CWE_BROKEN_CRYPTO,\n message:\n \"CloudFormation provisions a classical EC KMS key; EC keys feed ECDSA signatures and ECDH key agreement (the ECDH path is harvest-now-decrypt-later exposed).\",\n remediation:\n \"For key agreement: hybrid X25519MLKEM768 (ML-KEM-768). For signatures: ML-DSA-65 (FIPS 204).\",\n};\nconst RULE_CFN_ACM_RSA: RuleMeta = {\n id: \"cfn-acm-rsa\",\n title: \"CloudFormation ACM RSA certificate\",\n description: 'AWS::CertificateManager::Certificate KeyAlgorithm = \"RSA_*\"',\n category: \"certificate\",\n severity: \"high\",\n confidence: \"high\",\n algorithm: \"RSA\",\n hndl: true,\n cwe: CWE_BROKEN_CRYPTO,\n message:\n \"CloudFormation provisions an ACM certificate with a classical RSA key, which is not quantum-safe.\",\n remediation: \"Plan migration to PQC (ML-KEM-768 for encryption, ML-DSA-65 for signatures).\",\n};\nconst RULE_CFN_ACM_EC: RuleMeta = {\n id: \"cfn-acm-ec\",\n title: \"CloudFormation ACM EC certificate\",\n description: 'AWS::CertificateManager::Certificate KeyAlgorithm = \"EC_*\"',\n category: \"certificate\",\n severity: \"high\",\n confidence: \"high\",\n algorithm: \"ECDSA\",\n hndl: false,\n cwe: CWE_BROKEN_CRYPTO,\n message:\n \"CloudFormation provisions an ACM certificate with a classical EC key, forgeable by a quantum attacker.\",\n remediation: \"ML-DSA-65 (FIPS 204) or SLH-DSA (FIPS 205).\",\n};\nconst RULE_CFN_CLOUDFRONT_TLS: RuleMeta = {\n id: \"cfn-cloudfront-legacy-tls\",\n title: \"CloudFormation CloudFront legacy TLS\",\n description: 'CloudFront Distribution MinimumProtocolVersion = \"TLSv1\" / \"TLSv1.1\"',\n category: \"tls\",\n severity: \"medium\",\n confidence: \"high\",\n hndl: false,\n cwe: CWE_WEAK_STRENGTH,\n message: \"CloudFront distribution permits TLS 1.0/1.1, which are deprecated and insecure.\",\n remediation:\n \"Set MinimumProtocolVersion to TLSv1.2_2021 (or later) and prefer PQC-hybrid key exchange.\",\n};\nconst RULE_CFN_ELB_TLS: RuleMeta = {\n id: \"cfn-elb-legacy-tls\",\n title: \"CloudFormation ELB/ALB legacy TLS policy\",\n description: \"Elastic Load Balancer SslPolicy naming a pre-2017 legacy policy\",\n category: \"tls\",\n severity: \"medium\",\n confidence: \"high\",\n hndl: false,\n cwe: CWE_WEAK_STRENGTH,\n message: \"Load balancer listener uses a legacy SSL negotiation policy permitting TLS 1.0/1.1.\",\n remediation: \"Use ELBSecurityPolicy-TLS13-1-2-2021-06 (or the latest FS+TLS1.2/1.3 policy).\",\n};\nconst RULE_CFN_ARM_KV_RSA: RuleMeta = {\n id: \"cfn-arm-keyvault-rsa\",\n title: \"ARM template Key Vault RSA key\",\n description: 'Microsoft.KeyVault key resource kty = \"RSA\"',\n category: \"kem\",\n severity: \"high\",\n confidence: \"high\",\n algorithm: \"RSA\",\n hndl: true,\n cwe: CWE_BROKEN_CRYPTO,\n message:\n \"ARM template provisions a classical RSA Azure Key Vault key, which is not quantum-safe.\",\n remediation: \"Plan migration to PQC (ML-KEM-768 / ML-DSA-65).\",\n};\nconst RULE_CFN_ARM_KV_EC: RuleMeta = {\n id: \"cfn-arm-keyvault-ec\",\n title: \"ARM template Key Vault EC key\",\n description: 'Microsoft.KeyVault key resource kty = \"EC\"',\n category: \"key-exchange\",\n severity: \"high\",\n confidence: \"high\",\n algorithm: \"ECDH\",\n hndl: true,\n cwe: CWE_BROKEN_CRYPTO,\n message:\n \"ARM template provisions a classical EC Azure Key Vault key; EC keys feed ECDSA signatures and ECDH key agreement (the ECDH path is harvest-now-decrypt-later exposed).\",\n remediation:\n \"For key agreement: hybrid X25519MLKEM768 (ML-KEM-768). For signatures: ML-DSA-65 (FIPS 204).\",\n};\n\n/**\n * Detects classical asymmetric crypto and legacy TLS config declared in\n * AWS CloudFormation / Azure ARM (incl. Bicep-compiled JSON) templates.\n */\nexport const cloudformationDetector: Detector = {\n id: \"cloudformation-crypto\",\n description:\n \"Classical asymmetric crypto and legacy TLS config declared in CloudFormation / ARM templates (IaC)\",\n scope: \"config\",\n language: \"any\",\n rules: [\n RULE_CFN_KMS_RSA,\n RULE_CFN_KMS_EC,\n RULE_CFN_ACM_RSA,\n RULE_CFN_ACM_EC,\n RULE_CFN_CLOUDFRONT_TLS,\n RULE_CFN_ELB_TLS,\n RULE_CFN_ARM_KV_RSA,\n RULE_CFN_ARM_KV_EC,\n ],\n appliesTo: (f) => hasExtension(f, CFN_EXTENSIONS),\n detect({ file, content }): Finding[] {\n if (!CFN_MARKERS.some((marker) => content.includes(marker))) return [];\n\n const findings: Finding[] = [];\n const add = (re: RegExp, rule: RuleMeta) =>\n eachMatch(re, content, (m) =>\n findings.push(\n findingFromRule(rule, { file, content, index: m.index, matchLength: m[0].length }),\n ),\n );\n add(RE_CFN_KMS_RSA, RULE_CFN_KMS_RSA);\n add(RE_CFN_KMS_EC, RULE_CFN_KMS_EC);\n add(RE_CFN_ACM_RSA, RULE_CFN_ACM_RSA);\n add(RE_CFN_ACM_EC, RULE_CFN_ACM_EC);\n add(RE_CFN_CLOUDFRONT_TLS, RULE_CFN_CLOUDFRONT_TLS);\n add(RE_CFN_ELB_TLS, RULE_CFN_ELB_TLS);\n add(RE_CFN_ARM_KV_RSA, RULE_CFN_ARM_KV_RSA);\n add(RE_CFN_ARM_KV_EC, RULE_CFN_ARM_KV_EC);\n return findings;\n },\n};\n"]}
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Config detector: classical crypto in database usage — public-key encryption of
3
+ * stored data (pgcrypto) and weak client TLS posture. Data-at-rest is the sharpest
4
+ * harvest-now-decrypt-later target: a column encrypted to an RSA key today is a
5
+ * time capsule an adversary can hold until key recovery is cheap.
6
+ *
7
+ * Covered:
8
+ * - pgcrypto `pgp_pub_encrypt` / `pgp_pub_decrypt` in `.sql` (RSA/ElGamal public-key
9
+ * encryption of column data) → `hndl:true`.
10
+ * - libpq `sslmode = disable | allow | prefer | require` in any config/connection
11
+ * file — a mode that does NOT verify the server certificate, so the classical
12
+ * TLS session it does (or doesn't) negotiate is both MITM-able and harvestable.
13
+ */
14
+ import type { Detector } from "../types.js";
15
+ /** Detects classical database at-rest / transport crypto. */
16
+ export declare const databaseDetector: Detector;
17
+ //# sourceMappingURL=database.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"database.d.ts","sourceRoot":"","sources":["../../src/detectors/database.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,OAAO,KAAK,EAAE,QAAQ,EAAqB,MAAM,aAAa,CAAC;AAqC/D,6DAA6D;AAC7D,eAAO,MAAM,gBAAgB,EAAE,QAoC9B,CAAC"}
@@ -0,0 +1,60 @@
1
+ import { DOC_EXTENSIONS, eachMatch, findingFromRule, hasExtension } from "../detect-utils.js";
2
+ import { CWE_BROKEN_CRYPTO, CWE_CERT_VALIDATION } from "../cwe.js";
3
+ const RE_PGCRYPTO = /\bpgp_pub_(?:encrypt|decrypt)\b/g;
4
+ const RE_WEAK_SSLMODE = /\bsslmode\s*=\s*["']?(?:disable|allow|prefer|require)\b/gi;
5
+ const RULE_PGCRYPTO = {
6
+ id: "db-pgcrypto-pubkey",
7
+ title: "pgcrypto public-key encryption",
8
+ description: "Postgres pgcrypto pgp_pub_encrypt / pgp_pub_decrypt (RSA/ElGamal) on stored data",
9
+ category: "kem",
10
+ severity: "high",
11
+ confidence: "high",
12
+ algorithm: "RSA",
13
+ hndl: true,
14
+ cwe: CWE_BROKEN_CRYPTO,
15
+ message: "Column data is encrypted with pgcrypto public-key crypto (classical RSA/ElGamal); stored ciphertext is harvest-now-decrypt-later exposed.",
16
+ remediation: "Plan migration to a post-quantum KEM (ML-KEM-768) envelope for at-rest data; re-encrypt long-lived rows.",
17
+ };
18
+ const RULE_WEAK_SSLMODE = {
19
+ id: "db-weak-sslmode",
20
+ title: "Database sslmode without verification",
21
+ description: "libpq sslmode is disable/allow/prefer/require (no certificate verification)",
22
+ category: "tls",
23
+ severity: "medium",
24
+ confidence: "high",
25
+ hndl: false,
26
+ cwe: CWE_CERT_VALIDATION,
27
+ message: "Database sslmode does not verify the server certificate; the classical TLS session is MITM-able and its key exchange is harvestable.",
28
+ remediation: "Use sslmode=verify-full and TLS 1.3; track PQC-hybrid KEX (X25519MLKEM768) for database transport.",
29
+ };
30
+ /** Detects classical database at-rest / transport crypto. */
31
+ export const databaseDetector = {
32
+ id: "database-crypto",
33
+ description: "Classical crypto in database usage (pgcrypto public-key, weak client sslmode)",
34
+ scope: "config",
35
+ language: "any",
36
+ rules: [RULE_PGCRYPTO, RULE_WEAK_SSLMODE],
37
+ // Skip prose/docs: a README showing `sslmode=require` is not a live connection string.
38
+ appliesTo: (f) => !hasExtension(f, DOC_EXTENSIONS),
39
+ detect({ file, content }) {
40
+ const findings = [];
41
+ if (file.toLowerCase().endsWith(".sql") && content.includes("pgp_pub_")) {
42
+ eachMatch(RE_PGCRYPTO, content, (m) => findings.push(findingFromRule(RULE_PGCRYPTO, {
43
+ file,
44
+ content,
45
+ index: m.index,
46
+ matchLength: m[0].length,
47
+ })));
48
+ }
49
+ if (content.includes("sslmode")) {
50
+ eachMatch(RE_WEAK_SSLMODE, content, (m) => findings.push(findingFromRule(RULE_WEAK_SSLMODE, {
51
+ file,
52
+ content,
53
+ index: m.index,
54
+ matchLength: m[0].length,
55
+ })));
56
+ }
57
+ return findings;
58
+ },
59
+ };
60
+ //# sourceMappingURL=database.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"database.js","sourceRoot":"","sources":["../../src/detectors/database.ts"],"names":[],"mappings":"AAcA,OAAO,EAAE,cAAc,EAAE,SAAS,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAC9F,OAAO,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,MAAM,WAAW,CAAC;AAEnE,MAAM,WAAW,GAAG,kCAAkC,CAAC;AACvD,MAAM,eAAe,GAAG,2DAA2D,CAAC;AAEpF,MAAM,aAAa,GAAa;IAC9B,EAAE,EAAE,oBAAoB;IACxB,KAAK,EAAE,gCAAgC;IACvC,WAAW,EAAE,kFAAkF;IAC/F,QAAQ,EAAE,KAAK;IACf,QAAQ,EAAE,MAAM;IAChB,UAAU,EAAE,MAAM;IAClB,SAAS,EAAE,KAAK;IAChB,IAAI,EAAE,IAAI;IACV,GAAG,EAAE,iBAAiB;IACtB,OAAO,EACL,2IAA2I;IAC7I,WAAW,EACT,0GAA0G;CAC7G,CAAC;AACF,MAAM,iBAAiB,GAAa;IAClC,EAAE,EAAE,iBAAiB;IACrB,KAAK,EAAE,uCAAuC;IAC9C,WAAW,EAAE,6EAA6E;IAC1F,QAAQ,EAAE,KAAK;IACf,QAAQ,EAAE,QAAQ;IAClB,UAAU,EAAE,MAAM;IAClB,IAAI,EAAE,KAAK;IACX,GAAG,EAAE,mBAAmB;IACxB,OAAO,EACL,sIAAsI;IACxI,WAAW,EACT,oGAAoG;CACvG,CAAC;AAEF,6DAA6D;AAC7D,MAAM,CAAC,MAAM,gBAAgB,GAAa;IACxC,EAAE,EAAE,iBAAiB;IACrB,WAAW,EAAE,+EAA+E;IAC5F,KAAK,EAAE,QAAQ;IACf,QAAQ,EAAE,KAAK;IACf,KAAK,EAAE,CAAC,aAAa,EAAE,iBAAiB,CAAC;IACzC,uFAAuF;IACvF,SAAS,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,YAAY,CAAC,CAAC,EAAE,cAAc,CAAC;IAClD,MAAM,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE;QACtB,MAAM,QAAQ,GAAc,EAAE,CAAC;QAC/B,IAAI,IAAI,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAC,EAAE,CAAC;YACxE,SAAS,CAAC,WAAW,EAAE,OAAO,EAAE,CAAC,CAAC,EAAE,EAAE,CACpC,QAAQ,CAAC,IAAI,CACX,eAAe,CAAC,aAAa,EAAE;gBAC7B,IAAI;gBACJ,OAAO;gBACP,KAAK,EAAE,CAAC,CAAC,KAAK;gBACd,WAAW,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM;aACzB,CAAC,CACH,CACF,CAAC;QACJ,CAAC;QACD,IAAI,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC;YAChC,SAAS,CAAC,eAAe,EAAE,OAAO,EAAE,CAAC,CAAC,EAAE,EAAE,CACxC,QAAQ,CAAC,IAAI,CACX,eAAe,CAAC,iBAAiB,EAAE;gBACjC,IAAI;gBACJ,OAAO;gBACP,KAAK,EAAE,CAAC,CAAC,KAAK;gBACd,WAAW,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM;aACzB,CAAC,CACH,CACF,CAAC;QACJ,CAAC;QACD,OAAO,QAAQ,CAAC;IAClB,CAAC;CACF,CAAC","sourcesContent":["/**\n * Config detector: classical crypto in database usage — public-key encryption of\n * stored data (pgcrypto) and weak client TLS posture. Data-at-rest is the sharpest\n * harvest-now-decrypt-later target: a column encrypted to an RSA key today is a\n * time capsule an adversary can hold until key recovery is cheap.\n *\n * Covered:\n * - pgcrypto `pgp_pub_encrypt` / `pgp_pub_decrypt` in `.sql` (RSA/ElGamal public-key\n * encryption of column data) → `hndl:true`.\n * - libpq `sslmode = disable | allow | prefer | require` in any config/connection\n * file — a mode that does NOT verify the server certificate, so the classical\n * TLS session it does (or doesn't) negotiate is both MITM-able and harvestable.\n */\nimport type { Detector, Finding, RuleMeta } from \"../types.js\";\nimport { DOC_EXTENSIONS, eachMatch, findingFromRule, hasExtension } from \"../detect-utils.js\";\nimport { CWE_BROKEN_CRYPTO, CWE_CERT_VALIDATION } from \"../cwe.js\";\n\nconst RE_PGCRYPTO = /\\bpgp_pub_(?:encrypt|decrypt)\\b/g;\nconst RE_WEAK_SSLMODE = /\\bsslmode\\s*=\\s*[\"']?(?:disable|allow|prefer|require)\\b/gi;\n\nconst RULE_PGCRYPTO: RuleMeta = {\n id: \"db-pgcrypto-pubkey\",\n title: \"pgcrypto public-key encryption\",\n description: \"Postgres pgcrypto pgp_pub_encrypt / pgp_pub_decrypt (RSA/ElGamal) on stored data\",\n category: \"kem\",\n severity: \"high\",\n confidence: \"high\",\n algorithm: \"RSA\",\n hndl: true,\n cwe: CWE_BROKEN_CRYPTO,\n message:\n \"Column data is encrypted with pgcrypto public-key crypto (classical RSA/ElGamal); stored ciphertext is harvest-now-decrypt-later exposed.\",\n remediation:\n \"Plan migration to a post-quantum KEM (ML-KEM-768) envelope for at-rest data; re-encrypt long-lived rows.\",\n};\nconst RULE_WEAK_SSLMODE: RuleMeta = {\n id: \"db-weak-sslmode\",\n title: \"Database sslmode without verification\",\n description: \"libpq sslmode is disable/allow/prefer/require (no certificate verification)\",\n category: \"tls\",\n severity: \"medium\",\n confidence: \"high\",\n hndl: false,\n cwe: CWE_CERT_VALIDATION,\n message:\n \"Database sslmode does not verify the server certificate; the classical TLS session is MITM-able and its key exchange is harvestable.\",\n remediation:\n \"Use sslmode=verify-full and TLS 1.3; track PQC-hybrid KEX (X25519MLKEM768) for database transport.\",\n};\n\n/** Detects classical database at-rest / transport crypto. */\nexport const databaseDetector: Detector = {\n id: \"database-crypto\",\n description: \"Classical crypto in database usage (pgcrypto public-key, weak client sslmode)\",\n scope: \"config\",\n language: \"any\",\n rules: [RULE_PGCRYPTO, RULE_WEAK_SSLMODE],\n // Skip prose/docs: a README showing `sslmode=require` is not a live connection string.\n appliesTo: (f) => !hasExtension(f, DOC_EXTENSIONS),\n detect({ file, content }): Finding[] {\n const findings: Finding[] = [];\n if (file.toLowerCase().endsWith(\".sql\") && content.includes(\"pgp_pub_\")) {\n eachMatch(RE_PGCRYPTO, content, (m) =>\n findings.push(\n findingFromRule(RULE_PGCRYPTO, {\n file,\n content,\n index: m.index,\n matchLength: m[0].length,\n }),\n ),\n );\n }\n if (content.includes(\"sslmode\")) {\n eachMatch(RE_WEAK_SSLMODE, content, (m) =>\n findings.push(\n findingFromRule(RULE_WEAK_SSLMODE, {\n file,\n content,\n index: m.index,\n matchLength: m[0].length,\n }),\n ),\n );\n }\n return findings;\n },\n};\n"]}
@@ -0,0 +1,55 @@
1
+ /**
2
+ * Config detector: classical DNSSEC signing algorithms in zone files and
3
+ * signer/resolver config (BIND, Knot DNS, PowerDNS, `ldns-signzone` /
4
+ * `dnssec-signzone` invocations recorded in ops config).
5
+ *
6
+ * DNSSEC is "communication between things" infrastructure: resolvers verify
7
+ * DNSKEY/RRSIG chains to authenticate zone data. Unlike a KEM or key-exchange
8
+ * finding, a DNSSEC signing key is not harvest-now-decrypt-later exposed —
9
+ * there is no confidentiality to harvest — but every classical signature it
10
+ * produces becomes FORGEABLE the moment a cryptographically-relevant quantum
11
+ * computer (CRQC) exists. A forged DNSKEY/RRSIG chain lets an attacker spoof
12
+ * DNSSEC-validated records (cache poisoning that a validating resolver would
13
+ * otherwise reject). So these findings are `category: "signature"`,
14
+ * `hndl: false`.
15
+ *
16
+ * RFC 8624 / IANA "DNSSEC Algorithm Numbers" families covered, mapped to
17
+ * {@link AlgorithmFamily}:
18
+ * - RSASHA1 (5), RSASHA1-NSEC3-SHA1 (7), RSASHA256 (8), RSASHA512 (10) → RSA
19
+ * - ECDSAP256SHA256 (13), ECDSAP384SHA384 (14) → ECDSA
20
+ * - ED25519 (15), ED448 (16) → EdDSA
21
+ * - DSA (3), DSA-NSEC3-SHA1 (6) → DSA
22
+ *
23
+ * Two independent match shapes per family, both gated to `.zone` / `.db` /
24
+ * `.conf` files AND to a file-level DNSSEC marker (see `hasDnssecMarker`
25
+ * below) so the algorithm tokens can't fire on unrelated config:
26
+ *
27
+ * 1. NAMED form — the algorithm's mnemonic name, as written in signer/policy
28
+ * config (Knot `algorithm RSASHA256;`, a `dnssec-policy` block, BIND
29
+ * `algorithm: ecdsap256sha256;`) or on an `ldns-signzone -a ECDSAP256SHA256`
30
+ * / `dnssec-signzone -a ECDSAP256SHA256` command line. These mnemonics
31
+ * (`RSASHA256`, `ECDSAP384SHA384`, `ED25519`, …) are distinctive enough to
32
+ * match bare, EXCEPT plain `DSA`, which is too generic a token on its own
33
+ * (used well outside DNSSEC); that one is only matched immediately after
34
+ * an `algorithm` keyword.
35
+ * 2. STRUCTURAL form — the DNSKEY presentation-format RDATA, e.g.
36
+ * `example.com. IN DNSKEY 257 3 8 AwEAAd...`, where the fields after the
37
+ * `DNSKEY` keyword are `flags protocol algorithm public-key` (RFC 4034
38
+ * §2). Anchoring on the literal `DNSKEY` keyword AND the literal
39
+ * protocol value `3` (always 3 for DNSSEC) before a restricted algorithm
40
+ * number keeps this precise despite being numeric. Bare algorithm
41
+ * numbers with no such anchor are deliberately NOT matched anywhere —
42
+ * `algorithm 8;` alone is far too ambiguous to key off of.
43
+ *
44
+ * Fast reject: `detect()` bails unless the file contains `DNSKEY`, `RRSIG`,
45
+ * a case-insensitive `dnssec` substring (present in `dnssec-policy`,
46
+ * `dnssec-signing: on`, etc. in any real signer config), or an
47
+ * `ldns-signzone` / `dnssec-signzone` invocation. This stops the DSA/EdDSA
48
+ * named-form tokens — which are individually generic outside DNSSEC — from
49
+ * firing on, say, an unrelated SSH or TLS `.conf` file that happens to
50
+ * mention `ED25519` or `DSA` for its own reasons.
51
+ */
52
+ import type { Detector } from "../types.js";
53
+ /** Detects classical DNSSEC signing algorithms in zone files and signer config. */
54
+ export declare const dnssecDetector: Detector;
55
+ //# sourceMappingURL=dnssec.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dnssec.d.ts","sourceRoot":"","sources":["../../src/detectors/dnssec.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkDG;AACH,OAAO,KAAK,EAAE,QAAQ,EAAqB,MAAM,aAAa,CAAC;AA+G/D,mFAAmF;AACnF,eAAO,MAAM,cAAc,EAAE,QAsB5B,CAAC"}
@@ -0,0 +1,107 @@
1
+ import { eachMatch, findingFromRule, hasExtension } from "../detect-utils.js";
2
+ import { CWE_BROKEN_CRYPTO } from "../cwe.js";
3
+ const DNSSEC_EXTENSIONS = [".zone", ".db", ".conf"];
4
+ // IANA DNSSEC algorithm numbers, grouped by family.
5
+ const NUM_RSA = "5|7|8|10"; // RSASHA1, RSASHA1-NSEC3-SHA1, RSASHA256, RSASHA512
6
+ const NUM_ECDSA = "13|14"; // ECDSAP256SHA256, ECDSAP384SHA384
7
+ const NUM_EDDSA = "15|16"; // ED25519, ED448
8
+ const NUM_DSA = "3|6"; // DSA, DSA-NSEC3-SHA1
9
+ // --- NAMED form: mnemonic algorithm names in signer/policy config or CLI args. ---
10
+ const RE_NAMED_RSA = /\bRSASHA(?:256|512|1(?:-NSEC3-SHA1)?)\b/g;
11
+ const RE_NAMED_ECDSA = /\bECDSAP(?:256SHA256|384SHA384)\b/g;
12
+ const RE_NAMED_EDDSA = /\bED(?:25519|448)\b/g;
13
+ // Bare "DSA" is too generic to key off alone; require it right after an
14
+ // `algorithm` keyword (Knot/BIND config / dnssec-policy style), optionally
15
+ // quoted or separated by `:`/`=`.
16
+ const RE_NAMED_DSA = /\balgorithm\s*[:=]?\s*"?DSA(?:-NSEC3-SHA1)?"?\b/gi;
17
+ // --- STRUCTURAL form: `DNSKEY <flags> 3 <algorithm>` presentation RDATA (RFC 4034 §2). ---
18
+ const RE_DNSKEY_RSA = new RegExp(`\\bDNSKEY\\s+\\d+\\s+3\\s+(?:${NUM_RSA})\\b`, "g");
19
+ const RE_DNSKEY_ECDSA = new RegExp(`\\bDNSKEY\\s+\\d+\\s+3\\s+(?:${NUM_ECDSA})\\b`, "g");
20
+ const RE_DNSKEY_EDDSA = new RegExp(`\\bDNSKEY\\s+\\d+\\s+3\\s+(?:${NUM_EDDSA})\\b`, "g");
21
+ const RE_DNSKEY_DSA = new RegExp(`\\bDNSKEY\\s+\\d+\\s+3\\s+(?:${NUM_DSA})\\b`, "g");
22
+ const RULE_DNSSEC_RSA = {
23
+ id: "dnssec-rsa-sig",
24
+ title: "DNSSEC RSA signing algorithm",
25
+ description: "DNSSEC zone signed with a classical RSA algorithm (RSASHA1/256/512)",
26
+ category: "signature",
27
+ severity: "medium",
28
+ confidence: "high",
29
+ algorithm: "RSA",
30
+ hndl: false,
31
+ cwe: CWE_BROKEN_CRYPTO,
32
+ message: "DNSSEC zone is signed with a classical RSA algorithm (RSASHA1/RSASHA1-NSEC3-SHA1/RSASHA256/RSASHA512); DNSKEY/RRSIG signatures become forgeable once a CRQC exists.",
33
+ remediation: "Track IETF dnsop post-quantum DNSSEC signing work (ML-DSA); plan re-signing with a PQC algorithm once assigned an IANA DNSSEC algorithm number.",
34
+ };
35
+ const RULE_DNSSEC_ECDSA = {
36
+ id: "dnssec-ecdsa-sig",
37
+ title: "DNSSEC ECDSA signing algorithm",
38
+ description: "DNSSEC zone signed with a classical ECDSA algorithm (ECDSAP256SHA256/384SHA384)",
39
+ category: "signature",
40
+ severity: "medium",
41
+ confidence: "high",
42
+ algorithm: "ECDSA",
43
+ hndl: false,
44
+ cwe: CWE_BROKEN_CRYPTO,
45
+ message: "DNSSEC zone is signed with a classical ECDSA algorithm (ECDSAP256SHA256/ECDSAP384SHA384); DNSKEY/RRSIG signatures become forgeable once a CRQC exists.",
46
+ remediation: "Track IETF dnsop post-quantum DNSSEC signing work (ML-DSA); plan re-signing with a PQC algorithm once assigned an IANA DNSSEC algorithm number.",
47
+ };
48
+ const RULE_DNSSEC_EDDSA = {
49
+ id: "dnssec-eddsa-sig",
50
+ title: "DNSSEC EdDSA signing algorithm",
51
+ description: "DNSSEC zone signed with a classical EdDSA algorithm (ED25519/ED448)",
52
+ category: "signature",
53
+ severity: "medium",
54
+ confidence: "high",
55
+ algorithm: "EdDSA",
56
+ hndl: false,
57
+ cwe: CWE_BROKEN_CRYPTO,
58
+ message: "DNSSEC zone is signed with a classical EdDSA algorithm (ED25519/ED448); modern but still classical — DNSKEY/RRSIG signatures become forgeable once a CRQC exists.",
59
+ remediation: "Track IETF dnsop post-quantum DNSSEC signing work (ML-DSA); plan re-signing with a PQC algorithm once assigned an IANA DNSSEC algorithm number.",
60
+ };
61
+ const RULE_DNSSEC_DSA = {
62
+ id: "dnssec-dsa-sig",
63
+ title: "DNSSEC DSA signing algorithm (deprecated)",
64
+ description: "DNSSEC zone signed with the deprecated classical DSA algorithm (DSA/DSA-NSEC3-SHA1)",
65
+ category: "signature",
66
+ severity: "medium",
67
+ confidence: "high",
68
+ algorithm: "DSA",
69
+ hndl: false,
70
+ cwe: CWE_BROKEN_CRYPTO,
71
+ message: "DNSSEC zone is signed with DSA/DSA-NSEC3-SHA1 — deprecated by RFC 8624 (MUST NOT sign) and, independent of that, forgeable once a CRQC exists.",
72
+ remediation: "Re-sign with a non-deprecated algorithm today (RFC 8624); track IETF dnsop post-quantum DNSSEC signing work (ML-DSA) for the eventual PQC migration.",
73
+ };
74
+ /** True when `content` carries some DNSSEC-specific marker (not just a bare algorithm token). */
75
+ function hasDnssecMarker(content) {
76
+ return (content.includes("DNSKEY") ||
77
+ content.includes("RRSIG") ||
78
+ /dnssec/i.test(content) ||
79
+ /ldns-signzone|dnssec-signzone/.test(content));
80
+ }
81
+ const DNSSEC_RULES = [
82
+ { meta: RULE_DNSSEC_RSA, res: [RE_NAMED_RSA, RE_DNSKEY_RSA] },
83
+ { meta: RULE_DNSSEC_ECDSA, res: [RE_NAMED_ECDSA, RE_DNSKEY_ECDSA] },
84
+ { meta: RULE_DNSSEC_EDDSA, res: [RE_NAMED_EDDSA, RE_DNSKEY_EDDSA] },
85
+ { meta: RULE_DNSSEC_DSA, res: [RE_NAMED_DSA, RE_DNSKEY_DSA] },
86
+ ];
87
+ /** Detects classical DNSSEC signing algorithms in zone files and signer config. */
88
+ export const dnssecDetector = {
89
+ id: "dnssec-crypto",
90
+ description: "Classical DNSSEC signing algorithms in zone files / signer config",
91
+ scope: "config",
92
+ language: "any",
93
+ rules: DNSSEC_RULES.map((r) => r.meta),
94
+ appliesTo: (f) => hasExtension(f, DNSSEC_EXTENSIONS),
95
+ detect({ file, content }) {
96
+ if (!hasDnssecMarker(content))
97
+ return [];
98
+ const findings = [];
99
+ for (const { meta, res } of DNSSEC_RULES) {
100
+ for (const re of res) {
101
+ eachMatch(re, content, (m) => findings.push(findingFromRule(meta, { file, content, index: m.index, matchLength: m[0].length })));
102
+ }
103
+ }
104
+ return findings;
105
+ },
106
+ };
107
+ //# sourceMappingURL=dnssec.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dnssec.js","sourceRoot":"","sources":["../../src/detectors/dnssec.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Config detector: classical DNSSEC signing algorithms in zone files and\n * signer/resolver config (BIND, Knot DNS, PowerDNS, `ldns-signzone` /\n * `dnssec-signzone` invocations recorded in ops config).\n *\n * DNSSEC is \"communication between things\" infrastructure: resolvers verify\n * DNSKEY/RRSIG chains to authenticate zone data. Unlike a KEM or key-exchange\n * finding, a DNSSEC signing key is not harvest-now-decrypt-later exposed —\n * there is no confidentiality to harvest — but every classical signature it\n * produces becomes FORGEABLE the moment a cryptographically-relevant quantum\n * computer (CRQC) exists. A forged DNSKEY/RRSIG chain lets an attacker spoof\n * DNSSEC-validated records (cache poisoning that a validating resolver would\n * otherwise reject). So these findings are `category: \"signature\"`,\n * `hndl: false`.\n *\n * RFC 8624 / IANA \"DNSSEC Algorithm Numbers\" families covered, mapped to\n * {@link AlgorithmFamily}:\n * - RSASHA1 (5), RSASHA1-NSEC3-SHA1 (7), RSASHA256 (8), RSASHA512 (10) → RSA\n * - ECDSAP256SHA256 (13), ECDSAP384SHA384 (14) → ECDSA\n * - ED25519 (15), ED448 (16) → EdDSA\n * - DSA (3), DSA-NSEC3-SHA1 (6) → DSA\n *\n * Two independent match shapes per family, both gated to `.zone` / `.db` /\n * `.conf` files AND to a file-level DNSSEC marker (see `hasDnssecMarker`\n * below) so the algorithm tokens can't fire on unrelated config:\n *\n * 1. NAMED form — the algorithm's mnemonic name, as written in signer/policy\n * config (Knot `algorithm RSASHA256;`, a `dnssec-policy` block, BIND\n * `algorithm: ecdsap256sha256;`) or on an `ldns-signzone -a ECDSAP256SHA256`\n * / `dnssec-signzone -a ECDSAP256SHA256` command line. These mnemonics\n * (`RSASHA256`, `ECDSAP384SHA384`, `ED25519`, …) are distinctive enough to\n * match bare, EXCEPT plain `DSA`, which is too generic a token on its own\n * (used well outside DNSSEC); that one is only matched immediately after\n * an `algorithm` keyword.\n * 2. STRUCTURAL form — the DNSKEY presentation-format RDATA, e.g.\n * `example.com. IN DNSKEY 257 3 8 AwEAAd...`, where the fields after the\n * `DNSKEY` keyword are `flags protocol algorithm public-key` (RFC 4034\n * §2). Anchoring on the literal `DNSKEY` keyword AND the literal\n * protocol value `3` (always 3 for DNSSEC) before a restricted algorithm\n * number keeps this precise despite being numeric. Bare algorithm\n * numbers with no such anchor are deliberately NOT matched anywhere —\n * `algorithm 8;` alone is far too ambiguous to key off of.\n *\n * Fast reject: `detect()` bails unless the file contains `DNSKEY`, `RRSIG`,\n * a case-insensitive `dnssec` substring (present in `dnssec-policy`,\n * `dnssec-signing: on`, etc. in any real signer config), or an\n * `ldns-signzone` / `dnssec-signzone` invocation. This stops the DSA/EdDSA\n * named-form tokens — which are individually generic outside DNSSEC — from\n * firing on, say, an unrelated SSH or TLS `.conf` file that happens to\n * mention `ED25519` or `DSA` for its own reasons.\n */\nimport type { Detector, Finding, RuleMeta } from \"../types.js\";\nimport { eachMatch, findingFromRule, hasExtension } from \"../detect-utils.js\";\nimport { CWE_BROKEN_CRYPTO } from \"../cwe.js\";\n\nconst DNSSEC_EXTENSIONS: readonly string[] = [\".zone\", \".db\", \".conf\"];\n\n// IANA DNSSEC algorithm numbers, grouped by family.\nconst NUM_RSA = \"5|7|8|10\"; // RSASHA1, RSASHA1-NSEC3-SHA1, RSASHA256, RSASHA512\nconst NUM_ECDSA = \"13|14\"; // ECDSAP256SHA256, ECDSAP384SHA384\nconst NUM_EDDSA = \"15|16\"; // ED25519, ED448\nconst NUM_DSA = \"3|6\"; // DSA, DSA-NSEC3-SHA1\n\n// --- NAMED form: mnemonic algorithm names in signer/policy config or CLI args. ---\nconst RE_NAMED_RSA = /\\bRSASHA(?:256|512|1(?:-NSEC3-SHA1)?)\\b/g;\nconst RE_NAMED_ECDSA = /\\bECDSAP(?:256SHA256|384SHA384)\\b/g;\nconst RE_NAMED_EDDSA = /\\bED(?:25519|448)\\b/g;\n// Bare \"DSA\" is too generic to key off alone; require it right after an\n// `algorithm` keyword (Knot/BIND config / dnssec-policy style), optionally\n// quoted or separated by `:`/`=`.\nconst RE_NAMED_DSA = /\\balgorithm\\s*[:=]?\\s*\"?DSA(?:-NSEC3-SHA1)?\"?\\b/gi;\n\n// --- STRUCTURAL form: `DNSKEY <flags> 3 <algorithm>` presentation RDATA (RFC 4034 §2). ---\nconst RE_DNSKEY_RSA = new RegExp(`\\\\bDNSKEY\\\\s+\\\\d+\\\\s+3\\\\s+(?:${NUM_RSA})\\\\b`, \"g\");\nconst RE_DNSKEY_ECDSA = new RegExp(`\\\\bDNSKEY\\\\s+\\\\d+\\\\s+3\\\\s+(?:${NUM_ECDSA})\\\\b`, \"g\");\nconst RE_DNSKEY_EDDSA = new RegExp(`\\\\bDNSKEY\\\\s+\\\\d+\\\\s+3\\\\s+(?:${NUM_EDDSA})\\\\b`, \"g\");\nconst RE_DNSKEY_DSA = new RegExp(`\\\\bDNSKEY\\\\s+\\\\d+\\\\s+3\\\\s+(?:${NUM_DSA})\\\\b`, \"g\");\n\nconst RULE_DNSSEC_RSA: RuleMeta = {\n id: \"dnssec-rsa-sig\",\n title: \"DNSSEC RSA signing algorithm\",\n description: \"DNSSEC zone signed with a classical RSA algorithm (RSASHA1/256/512)\",\n category: \"signature\",\n severity: \"medium\",\n confidence: \"high\",\n algorithm: \"RSA\",\n hndl: false,\n cwe: CWE_BROKEN_CRYPTO,\n message:\n \"DNSSEC zone is signed with a classical RSA algorithm (RSASHA1/RSASHA1-NSEC3-SHA1/RSASHA256/RSASHA512); DNSKEY/RRSIG signatures become forgeable once a CRQC exists.\",\n remediation:\n \"Track IETF dnsop post-quantum DNSSEC signing work (ML-DSA); plan re-signing with a PQC algorithm once assigned an IANA DNSSEC algorithm number.\",\n};\nconst RULE_DNSSEC_ECDSA: RuleMeta = {\n id: \"dnssec-ecdsa-sig\",\n title: \"DNSSEC ECDSA signing algorithm\",\n description: \"DNSSEC zone signed with a classical ECDSA algorithm (ECDSAP256SHA256/384SHA384)\",\n category: \"signature\",\n severity: \"medium\",\n confidence: \"high\",\n algorithm: \"ECDSA\",\n hndl: false,\n cwe: CWE_BROKEN_CRYPTO,\n message:\n \"DNSSEC zone is signed with a classical ECDSA algorithm (ECDSAP256SHA256/ECDSAP384SHA384); DNSKEY/RRSIG signatures become forgeable once a CRQC exists.\",\n remediation:\n \"Track IETF dnsop post-quantum DNSSEC signing work (ML-DSA); plan re-signing with a PQC algorithm once assigned an IANA DNSSEC algorithm number.\",\n};\nconst RULE_DNSSEC_EDDSA: RuleMeta = {\n id: \"dnssec-eddsa-sig\",\n title: \"DNSSEC EdDSA signing algorithm\",\n description: \"DNSSEC zone signed with a classical EdDSA algorithm (ED25519/ED448)\",\n category: \"signature\",\n severity: \"medium\",\n confidence: \"high\",\n algorithm: \"EdDSA\",\n hndl: false,\n cwe: CWE_BROKEN_CRYPTO,\n message:\n \"DNSSEC zone is signed with a classical EdDSA algorithm (ED25519/ED448); modern but still classical — DNSKEY/RRSIG signatures become forgeable once a CRQC exists.\",\n remediation:\n \"Track IETF dnsop post-quantum DNSSEC signing work (ML-DSA); plan re-signing with a PQC algorithm once assigned an IANA DNSSEC algorithm number.\",\n};\nconst RULE_DNSSEC_DSA: RuleMeta = {\n id: \"dnssec-dsa-sig\",\n title: \"DNSSEC DSA signing algorithm (deprecated)\",\n description:\n \"DNSSEC zone signed with the deprecated classical DSA algorithm (DSA/DSA-NSEC3-SHA1)\",\n category: \"signature\",\n severity: \"medium\",\n confidence: \"high\",\n algorithm: \"DSA\",\n hndl: false,\n cwe: CWE_BROKEN_CRYPTO,\n message:\n \"DNSSEC zone is signed with DSA/DSA-NSEC3-SHA1 — deprecated by RFC 8624 (MUST NOT sign) and, independent of that, forgeable once a CRQC exists.\",\n remediation:\n \"Re-sign with a non-deprecated algorithm today (RFC 8624); track IETF dnsop post-quantum DNSSEC signing work (ML-DSA) for the eventual PQC migration.\",\n};\n\n/** True when `content` carries some DNSSEC-specific marker (not just a bare algorithm token). */\nfunction hasDnssecMarker(content: string): boolean {\n return (\n content.includes(\"DNSKEY\") ||\n content.includes(\"RRSIG\") ||\n /dnssec/i.test(content) ||\n /ldns-signzone|dnssec-signzone/.test(content)\n );\n}\n\ninterface DnssecRule {\n meta: RuleMeta;\n res: readonly RegExp[];\n}\n\nconst DNSSEC_RULES: readonly DnssecRule[] = [\n { meta: RULE_DNSSEC_RSA, res: [RE_NAMED_RSA, RE_DNSKEY_RSA] },\n { meta: RULE_DNSSEC_ECDSA, res: [RE_NAMED_ECDSA, RE_DNSKEY_ECDSA] },\n { meta: RULE_DNSSEC_EDDSA, res: [RE_NAMED_EDDSA, RE_DNSKEY_EDDSA] },\n { meta: RULE_DNSSEC_DSA, res: [RE_NAMED_DSA, RE_DNSKEY_DSA] },\n];\n\n/** Detects classical DNSSEC signing algorithms in zone files and signer config. */\nexport const dnssecDetector: Detector = {\n id: \"dnssec-crypto\",\n description: \"Classical DNSSEC signing algorithms in zone files / signer config\",\n scope: \"config\",\n language: \"any\",\n rules: DNSSEC_RULES.map((r) => r.meta),\n appliesTo: (f) => hasExtension(f, DNSSEC_EXTENSIONS),\n detect({ file, content }): Finding[] {\n if (!hasDnssecMarker(content)) return [];\n\n const findings: Finding[] = [];\n for (const { meta, res } of DNSSEC_RULES) {\n for (const re of res) {\n eachMatch(re, content, (m) =>\n findings.push(\n findingFromRule(meta, { file, content, index: m.index, matchLength: m[0].length }),\n ),\n );\n }\n }\n return findings;\n },\n};\n"]}
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Source-code detector for classical asymmetric cryptography in Elixir (the BEAM
3
+ * / Phoenix ecosystem), previously uncovered. Handles the three common surfaces:
4
+ *
5
+ * - **Erlang `:crypto`** — `:crypto.generate_key(:rsa|:ecdh|:dh|:eddsa, …)`
6
+ * (the curve atom disambiguates X25519/X448 from a generic ECDH curve) and
7
+ * `:crypto.sign/:crypto.verify(:rsa|:ecdsa|:eddsa, …)`.
8
+ * - **`X509`** (the `x509` hex package) — `X509.PrivateKey.new_rsa` / `new_ec`.
9
+ * - **`JOSE`** (erlang-jose) — `JOSE.JWK.generate_key({:rsa|:ec|:okp, …})`.
10
+ *
11
+ * Lexical, like the other packs; the `:crypto.` atom-module syntax and the
12
+ * `X509.` / `JOSE.` module paths are distinctive, so the false-positive rate is low.
13
+ *
14
+ * HNDL: RSA and (EC/X)DH key agreement are harvest-now-decrypt-later exposed
15
+ * (hndl:true); ECDSA / EdDSA signatures are hndl:false but forgeable.
16
+ */
17
+ import type { Detector } from "../types.js";
18
+ /** Detects classical asymmetric crypto in Elixir (:crypto, X509, JOSE). */
19
+ export declare const elixirDetector: Detector;
20
+ //# sourceMappingURL=elixir.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"elixir.d.ts","sourceRoot":"","sources":["../../src/detectors/elixir.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AACH,OAAO,KAAK,EAAmB,QAAQ,EAAqB,MAAM,aAAa,CAAC;AAuIhF,2EAA2E;AAC3E,eAAO,MAAM,cAAc,EAAE,QAoF5B,CAAC"}