@quantakrypto/core 0.4.2 → 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.
- package/LICENSE +201 -0
- package/README.md +38 -10
- package/dist/agent-types.js.map +1 -1
- package/dist/baseline.js.map +1 -1
- package/dist/cache.js.map +1 -1
- package/dist/cbom-merge.d.ts +9 -0
- package/dist/cbom-merge.d.ts.map +1 -0
- package/dist/cbom-merge.js +99 -0
- package/dist/cbom-merge.js.map +1 -0
- package/dist/cbom.js.map +1 -1
- package/dist/changed.js.map +1 -1
- package/dist/codemods/config-toggle.js.map +1 -1
- package/dist/codemods/registry.js.map +1 -1
- package/dist/comments.d.ts +29 -2
- package/dist/comments.d.ts.map +1 -1
- package/dist/comments.js +178 -18
- package/dist/comments.js.map +1 -1
- package/dist/config.js.map +1 -1
- package/dist/cwe.js.map +1 -1
- package/dist/dependencies.d.ts +3 -2
- package/dist/dependencies.d.ts.map +1 -1
- package/dist/dependencies.js +138 -2
- package/dist/dependencies.js.map +1 -1
- package/dist/detect-utils.d.ts +19 -4
- package/dist/detect-utils.d.ts.map +1 -1
- package/dist/detect-utils.js +43 -6
- package/dist/detect-utils.js.map +1 -1
- package/dist/detectors/c.d.ts +4 -2
- package/dist/detectors/c.d.ts.map +1 -1
- package/dist/detectors/c.js +382 -3
- package/dist/detectors/c.js.map +1 -1
- package/dist/detectors/cicd.d.ts +27 -0
- package/dist/detectors/cicd.d.ts.map +1 -0
- package/dist/detectors/cicd.js +116 -0
- package/dist/detectors/cicd.js.map +1 -0
- package/dist/detectors/cloud-kms.d.ts +21 -0
- package/dist/detectors/cloud-kms.d.ts.map +1 -0
- package/dist/detectors/cloud-kms.js +57 -0
- package/dist/detectors/cloud-kms.js.map +1 -0
- package/dist/detectors/cloudformation.d.ts +30 -0
- package/dist/detectors/cloudformation.d.ts.map +1 -0
- package/dist/detectors/cloudformation.js +170 -0
- package/dist/detectors/cloudformation.js.map +1 -0
- package/dist/detectors/csharp.d.ts.map +1 -1
- package/dist/detectors/csharp.js +141 -3
- package/dist/detectors/csharp.js.map +1 -1
- package/dist/detectors/database.d.ts +17 -0
- package/dist/detectors/database.d.ts.map +1 -0
- package/dist/detectors/database.js +60 -0
- package/dist/detectors/database.js.map +1 -0
- package/dist/detectors/dnssec.d.ts +55 -0
- package/dist/detectors/dnssec.d.ts.map +1 -0
- package/dist/detectors/dnssec.js +107 -0
- package/dist/detectors/dnssec.js.map +1 -0
- package/dist/detectors/elixir.d.ts +20 -0
- package/dist/detectors/elixir.d.ts.map +1 -0
- package/dist/detectors/elixir.js +195 -0
- package/dist/detectors/elixir.js.map +1 -0
- package/dist/detectors/go.d.ts +5 -3
- package/dist/detectors/go.d.ts.map +1 -1
- package/dist/detectors/go.js +187 -3
- package/dist/detectors/go.js.map +1 -1
- package/dist/detectors/java.d.ts.map +1 -1
- package/dist/detectors/java.js +169 -4
- package/dist/detectors/java.js.map +1 -1
- package/dist/detectors/jose.d.ts +20 -0
- package/dist/detectors/jose.d.ts.map +1 -0
- package/dist/detectors/jose.js +62 -0
- package/dist/detectors/jose.js.map +1 -0
- package/dist/detectors/jwk.d.ts +25 -0
- package/dist/detectors/jwk.d.ts.map +1 -0
- package/dist/detectors/jwk.js +95 -0
- package/dist/detectors/jwk.js.map +1 -0
- package/dist/detectors/k8s.d.ts +17 -0
- package/dist/detectors/k8s.d.ts.map +1 -0
- package/dist/detectors/k8s.js +85 -0
- package/dist/detectors/k8s.js.map +1 -0
- package/dist/detectors/mesh.d.ts +35 -0
- package/dist/detectors/mesh.d.ts.map +1 -0
- package/dist/detectors/mesh.js +109 -0
- package/dist/detectors/mesh.js.map +1 -0
- package/dist/detectors/messaging.d.ts +16 -0
- package/dist/detectors/messaging.d.ts.map +1 -0
- package/dist/detectors/messaging.js +72 -0
- package/dist/detectors/messaging.js.map +1 -0
- package/dist/detectors/pem.d.ts.map +1 -1
- package/dist/detectors/pem.js +50 -2
- package/dist/detectors/pem.js.map +1 -1
- package/dist/detectors/php.d.ts +24 -0
- package/dist/detectors/php.d.ts.map +1 -0
- package/dist/detectors/php.js +167 -0
- package/dist/detectors/php.js.map +1 -0
- package/dist/detectors/python.d.ts.map +1 -1
- package/dist/detectors/python.js +144 -3
- package/dist/detectors/python.js.map +1 -1
- package/dist/detectors/ruby.d.ts +9 -5
- package/dist/detectors/ruby.d.ts.map +1 -1
- package/dist/detectors/ruby.js +122 -2
- package/dist/detectors/ruby.js.map +1 -1
- package/dist/detectors/rust.d.ts +8 -3
- package/dist/detectors/rust.d.ts.map +1 -1
- package/dist/detectors/rust.js +253 -3
- package/dist/detectors/rust.js.map +1 -1
- package/dist/detectors/secrets.d.ts +21 -0
- package/dist/detectors/secrets.d.ts.map +1 -0
- package/dist/detectors/secrets.js +79 -0
- package/dist/detectors/secrets.js.map +1 -0
- package/dist/detectors/source.d.ts.map +1 -1
- package/dist/detectors/source.js +305 -52
- package/dist/detectors/source.js.map +1 -1
- package/dist/detectors/stateful-hbs.d.ts +22 -0
- package/dist/detectors/stateful-hbs.d.ts.map +1 -0
- package/dist/detectors/stateful-hbs.js +142 -0
- package/dist/detectors/stateful-hbs.js.map +1 -0
- package/dist/detectors/terraform.d.ts +21 -0
- package/dist/detectors/terraform.d.ts.map +1 -0
- package/dist/detectors/terraform.js +123 -0
- package/dist/detectors/terraform.js.map +1 -0
- package/dist/detectors/vpn.d.ts +22 -0
- package/dist/detectors/vpn.d.ts.map +1 -0
- package/dist/detectors/vpn.js +99 -0
- package/dist/detectors/vpn.js.map +1 -0
- package/dist/errors.js.map +1 -1
- package/dist/evidence.d.ts +53 -0
- package/dist/evidence.d.ts.map +1 -0
- package/dist/evidence.js +81 -0
- package/dist/evidence.js.map +1 -0
- package/dist/index.d.ts +9 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +10 -2
- package/dist/index.js.map +1 -1
- package/dist/inventory.d.ts +2 -0
- package/dist/inventory.d.ts.map +1 -1
- package/dist/inventory.js +27 -1
- package/dist/inventory.js.map +1 -1
- package/dist/parallel.js.map +1 -1
- package/dist/patch-policy.js.map +1 -1
- package/dist/policy.d.ts +67 -0
- package/dist/policy.d.ts.map +1 -0
- package/dist/policy.js +115 -0
- package/dist/policy.js.map +1 -0
- package/dist/redact.d.ts.map +1 -1
- package/dist/redact.js +39 -0
- package/dist/redact.js.map +1 -1
- package/dist/registry.d.ts +2 -2
- package/dist/registry.d.ts.map +1 -1
- package/dist/registry.js +34 -2
- package/dist/registry.js.map +1 -1
- package/dist/remediate-pipeline.d.ts +3 -0
- package/dist/remediate-pipeline.d.ts.map +1 -1
- package/dist/remediate-pipeline.js +71 -0
- package/dist/remediate-pipeline.js.map +1 -1
- package/dist/remediate-request.js.map +1 -1
- package/dist/remediation.d.ts +9 -3
- package/dist/remediation.d.ts.map +1 -1
- package/dist/remediation.js +17 -5
- package/dist/remediation.js.map +1 -1
- package/dist/report.d.ts +9 -0
- package/dist/report.d.ts.map +1 -1
- package/dist/report.js +38 -0
- package/dist/report.js.map +1 -1
- package/dist/scan-worker.js.map +1 -1
- package/dist/scan.d.ts.map +1 -1
- package/dist/scan.js +8 -1
- package/dist/scan.js.map +1 -1
- package/dist/severity.js.map +1 -1
- package/dist/standards.d.ts +96 -0
- package/dist/standards.d.ts.map +1 -0
- package/dist/standards.js +107 -0
- package/dist/standards.js.map +1 -0
- package/dist/triage.js.map +1 -1
- package/dist/types.d.ts +2 -2
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/dist/verify.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/version.js.map +1 -1
- package/dist/walk.js.map +1 -1
- package/dist/worktree.js.map +1 -1
- package/package.json +1 -1
- package/src/cbom-merge.ts +116 -0
- package/src/comments.ts +183 -18
- package/src/dependencies.ts +144 -4
- package/src/detect-utils.ts +47 -6
- package/src/detectors/c.ts +411 -5
- package/src/detectors/cicd.ts +158 -0
- package/src/detectors/cloud-kms.ts +87 -0
- package/src/detectors/cloudformation.ts +215 -0
- package/src/detectors/csharp.ts +154 -3
- package/src/detectors/database.ts +88 -0
- package/src/detectors/dnssec.ts +186 -0
- package/src/detectors/elixir.ts +237 -0
- package/src/detectors/go.ts +209 -6
- package/src/detectors/java.ts +191 -4
- package/src/detectors/jose.ts +94 -0
- package/src/detectors/jwk.ts +128 -0
- package/src/detectors/k8s.ts +112 -0
- package/src/detectors/mesh.ts +154 -0
- package/src/detectors/messaging.ts +98 -0
- package/src/detectors/pem.ts +54 -2
- package/src/detectors/php.ts +225 -0
- package/src/detectors/python.ts +153 -3
- package/src/detectors/ruby.ts +138 -7
- package/src/detectors/rust.ts +282 -7
- package/src/detectors/secrets.ts +114 -0
- package/src/detectors/source.ts +382 -72
- package/src/detectors/stateful-hbs.ts +180 -0
- package/src/detectors/terraform.ts +154 -0
- package/src/detectors/vpn.ts +131 -0
- package/src/evidence.ts +135 -0
- package/src/index.ts +15 -1
- package/src/inventory.ts +31 -1
- package/src/policy.ts +180 -0
- package/src/redact.ts +41 -0
- package/src/registry.ts +34 -2
- package/src/remediate-pipeline.ts +78 -0
- package/src/remediation.ts +23 -8
- package/src/report.ts +48 -2
- package/src/scan.ts +13 -1
- package/src/standards.ts +186 -0
- package/src/types.ts +3 -1
- package/src/version.ts +1 -1
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cloud-KMS SDK detector: classical asymmetric keys minted at RUNTIME through a
|
|
3
|
+
* cloud KMS SDK call — the application-code counterpart to the Terraform detector
|
|
4
|
+
* (which covers the infrastructure-as-code path). AWS KMS `CreateKey` /
|
|
5
|
+
* `GenerateDataKeyPair` select the key type with a `KeySpec` / `KeyPairSpec`
|
|
6
|
+
* (legacy `CustomerMasterKeySpec`) field whose value is `RSA_*` or `ECC_*`.
|
|
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.
|
|
13
|
+
*
|
|
14
|
+
* HNDL: an RSA KMS key (encryption/KEM) and an EC KMS key (which AWS KMS can use
|
|
15
|
+
* for ECDH key agreement as well as ECDSA signing) are both harvest-now-decrypt-
|
|
16
|
+
* later exposed → hndl:true.
|
|
17
|
+
*/
|
|
18
|
+
import type { Detector, Finding, RuleMeta } from "../types.js";
|
|
19
|
+
import { eachMatch, findingFromRule } from "../detect-utils.js";
|
|
20
|
+
import { CWE_BROKEN_CRYPTO } from "../cwe.js";
|
|
21
|
+
|
|
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");
|
|
28
|
+
|
|
29
|
+
const RULE_KMS_RSA: RuleMeta = {
|
|
30
|
+
id: "cloud-kms-rsa",
|
|
31
|
+
title: "AWS KMS RSA key",
|
|
32
|
+
description: "AWS KMS CreateKey / GenerateDataKeyPair with an RSA_* key spec",
|
|
33
|
+
category: "kem",
|
|
34
|
+
severity: "high",
|
|
35
|
+
confidence: "high",
|
|
36
|
+
algorithm: "RSA",
|
|
37
|
+
hndl: true,
|
|
38
|
+
cwe: CWE_BROKEN_CRYPTO,
|
|
39
|
+
message:
|
|
40
|
+
"Mints a classical RSA key via the AWS KMS SDK (harvest-now-decrypt-later exposed for encryption).",
|
|
41
|
+
remediation: "Plan migration to PQC as cloud KMS adds ML-KEM / ML-DSA key specs.",
|
|
42
|
+
};
|
|
43
|
+
const RULE_KMS_EC: RuleMeta = {
|
|
44
|
+
id: "cloud-kms-ec",
|
|
45
|
+
title: "AWS KMS EC key",
|
|
46
|
+
description: "AWS KMS CreateKey / GenerateDataKeyPair with an ECC_* key spec",
|
|
47
|
+
category: "key-exchange",
|
|
48
|
+
severity: "high",
|
|
49
|
+
confidence: "high",
|
|
50
|
+
algorithm: "ECDH",
|
|
51
|
+
hndl: true,
|
|
52
|
+
cwe: CWE_BROKEN_CRYPTO,
|
|
53
|
+
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).",
|
|
55
|
+
remediation:
|
|
56
|
+
"For key agreement: hybrid X25519MLKEM768 (ML-KEM-768). For signatures: ML-DSA-65 (FIPS 204).",
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
/** Detects classical asymmetric keys provisioned via a cloud KMS SDK call. */
|
|
60
|
+
export const cloudKmsDetector: Detector = {
|
|
61
|
+
id: "cloud-kms",
|
|
62
|
+
description: "Classical asymmetric keys minted via a cloud KMS SDK (AWS KMS)",
|
|
63
|
+
scope: "config",
|
|
64
|
+
language: "any",
|
|
65
|
+
rules: [RULE_KMS_RSA, RULE_KMS_EC],
|
|
66
|
+
appliesTo: () => true,
|
|
67
|
+
detect({ file, content }): Finding[] {
|
|
68
|
+
// Fast reject: only proceed if a KMS key-spec field name is present.
|
|
69
|
+
if (
|
|
70
|
+
!content.includes("KeySpec") &&
|
|
71
|
+
!content.includes("KeyPairSpec") &&
|
|
72
|
+
!content.includes("CustomerMasterKeySpec")
|
|
73
|
+
) {
|
|
74
|
+
return [];
|
|
75
|
+
}
|
|
76
|
+
const findings: Finding[] = [];
|
|
77
|
+
const add = (re: RegExp, rule: RuleMeta): void =>
|
|
78
|
+
eachMatch(re, content, (m) =>
|
|
79
|
+
findings.push(
|
|
80
|
+
findingFromRule(rule, { file, content, index: m.index, matchLength: m[0].length }),
|
|
81
|
+
),
|
|
82
|
+
);
|
|
83
|
+
add(RE_KMS_RSA, RULE_KMS_RSA);
|
|
84
|
+
add(RE_KMS_EC, RULE_KMS_EC);
|
|
85
|
+
return findings;
|
|
86
|
+
},
|
|
87
|
+
};
|
|
@@ -0,0 +1,215 @@
|
|
|
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, Finding, RuleMeta } from "../types.js";
|
|
25
|
+
import { eachMatch, findingFromRule, hasExtension } from "../detect-utils.js";
|
|
26
|
+
import { CWE_BROKEN_CRYPTO, CWE_WEAK_STRENGTH } from "../cwe.js";
|
|
27
|
+
|
|
28
|
+
const CFN_EXTENSIONS: readonly string[] = [".json", ".yaml", ".yml"];
|
|
29
|
+
|
|
30
|
+
// Fast-reject markers: at least one must appear before any rule regex runs, so
|
|
31
|
+
// this detector never scans arbitrary JSON/YAML that merely happens to share
|
|
32
|
+
// an attribute name (`KeySpec`, `kty`, …) with an unrelated schema.
|
|
33
|
+
const CFN_MARKERS: readonly string[] = [
|
|
34
|
+
"AWS::KMS",
|
|
35
|
+
"AWS::CertificateManager",
|
|
36
|
+
"AWSTemplateFormatVersion",
|
|
37
|
+
"MinimumProtocolVersion",
|
|
38
|
+
"Microsoft.KeyVault",
|
|
39
|
+
'"SslPolicy"',
|
|
40
|
+
];
|
|
41
|
+
|
|
42
|
+
// Each attribute is matched with an optional quote around the key and `:` as
|
|
43
|
+
// the separator (both plain-JSON and YAML-block-scalar forms use `:`). The
|
|
44
|
+
// `(?<![\w"-])` lookbehind stops a longer/prefixed attribute name from
|
|
45
|
+
// 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;
|
|
48
|
+
const RE_CFN_ACM_RSA = /(?<![\w"-])"?KeyAlgorithm"?\s*:\s*"?RSA_\d+"?/g;
|
|
49
|
+
const RE_CFN_ACM_EC = /(?<![\w"-])"?KeyAlgorithm"?\s*:\s*"?EC_[A-Za-z0-9]+"?/g;
|
|
50
|
+
// CloudFront: only the pre-2018 "TLSv1" / "TLSv1_2016" / "TLSv1.1_2016"
|
|
51
|
+
// values are legacy; the lookahead delimiter stops this from matching a
|
|
52
|
+
// "TLSv1.2_2018"-style value that merely starts with the same prefix.
|
|
53
|
+
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).
|
|
56
|
+
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;
|
|
58
|
+
// Azure ARM Microsoft.KeyVault key resource `properties.kty`.
|
|
59
|
+
const RE_CFN_ARM_KV_RSA = /(?<![\w"-])"?kty"?\s*:\s*"?RSA"?(?!\w)/g;
|
|
60
|
+
const RE_CFN_ARM_KV_EC = /(?<![\w"-])"?kty"?\s*:\s*"?EC"?(?!\w)/g;
|
|
61
|
+
|
|
62
|
+
const RULE_CFN_KMS_RSA: RuleMeta = {
|
|
63
|
+
id: "cfn-kms-rsa",
|
|
64
|
+
title: "CloudFormation KMS RSA key",
|
|
65
|
+
description: 'AWS::KMS::Key KeySpec = "RSA_*"',
|
|
66
|
+
category: "kem",
|
|
67
|
+
severity: "high",
|
|
68
|
+
confidence: "high",
|
|
69
|
+
algorithm: "RSA",
|
|
70
|
+
hndl: true,
|
|
71
|
+
cwe: CWE_BROKEN_CRYPTO,
|
|
72
|
+
message:
|
|
73
|
+
"CloudFormation provisions a classical RSA KMS key (harvest-now-decrypt-later exposed for encryption CMKs).",
|
|
74
|
+
remediation: "Plan migration to PQC as cloud KMS adds ML-KEM / ML-DSA key specs.",
|
|
75
|
+
};
|
|
76
|
+
const RULE_CFN_KMS_EC: RuleMeta = {
|
|
77
|
+
id: "cfn-kms-ec",
|
|
78
|
+
title: "CloudFormation KMS EC key",
|
|
79
|
+
description: 'AWS::KMS::Key KeySpec = "ECC_*"',
|
|
80
|
+
category: "key-exchange",
|
|
81
|
+
severity: "high",
|
|
82
|
+
confidence: "high",
|
|
83
|
+
algorithm: "ECDH",
|
|
84
|
+
hndl: true,
|
|
85
|
+
cwe: CWE_BROKEN_CRYPTO,
|
|
86
|
+
message:
|
|
87
|
+
"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).",
|
|
88
|
+
remediation:
|
|
89
|
+
"For key agreement: hybrid X25519MLKEM768 (ML-KEM-768). For signatures: ML-DSA-65 (FIPS 204).",
|
|
90
|
+
};
|
|
91
|
+
const RULE_CFN_ACM_RSA: RuleMeta = {
|
|
92
|
+
id: "cfn-acm-rsa",
|
|
93
|
+
title: "CloudFormation ACM RSA certificate",
|
|
94
|
+
description: 'AWS::CertificateManager::Certificate KeyAlgorithm = "RSA_*"',
|
|
95
|
+
category: "certificate",
|
|
96
|
+
severity: "high",
|
|
97
|
+
confidence: "high",
|
|
98
|
+
algorithm: "RSA",
|
|
99
|
+
hndl: true,
|
|
100
|
+
cwe: CWE_BROKEN_CRYPTO,
|
|
101
|
+
message:
|
|
102
|
+
"CloudFormation provisions an ACM certificate with a classical RSA key, which is not quantum-safe.",
|
|
103
|
+
remediation: "Plan migration to PQC (ML-KEM-768 for encryption, ML-DSA-65 for signatures).",
|
|
104
|
+
};
|
|
105
|
+
const RULE_CFN_ACM_EC: RuleMeta = {
|
|
106
|
+
id: "cfn-acm-ec",
|
|
107
|
+
title: "CloudFormation ACM EC certificate",
|
|
108
|
+
description: 'AWS::CertificateManager::Certificate KeyAlgorithm = "EC_*"',
|
|
109
|
+
category: "certificate",
|
|
110
|
+
severity: "high",
|
|
111
|
+
confidence: "high",
|
|
112
|
+
algorithm: "ECDSA",
|
|
113
|
+
hndl: false,
|
|
114
|
+
cwe: CWE_BROKEN_CRYPTO,
|
|
115
|
+
message:
|
|
116
|
+
"CloudFormation provisions an ACM certificate with a classical EC key, forgeable by a quantum attacker.",
|
|
117
|
+
remediation: "ML-DSA-65 (FIPS 204) or SLH-DSA (FIPS 205).",
|
|
118
|
+
};
|
|
119
|
+
const RULE_CFN_CLOUDFRONT_TLS: RuleMeta = {
|
|
120
|
+
id: "cfn-cloudfront-legacy-tls",
|
|
121
|
+
title: "CloudFormation CloudFront legacy TLS",
|
|
122
|
+
description: 'CloudFront Distribution MinimumProtocolVersion = "TLSv1" / "TLSv1.1"',
|
|
123
|
+
category: "tls",
|
|
124
|
+
severity: "medium",
|
|
125
|
+
confidence: "high",
|
|
126
|
+
hndl: false,
|
|
127
|
+
cwe: CWE_WEAK_STRENGTH,
|
|
128
|
+
message: "CloudFront distribution permits TLS 1.0/1.1, which are deprecated and insecure.",
|
|
129
|
+
remediation:
|
|
130
|
+
"Set MinimumProtocolVersion to TLSv1.2_2021 (or later) and prefer PQC-hybrid key exchange.",
|
|
131
|
+
};
|
|
132
|
+
const RULE_CFN_ELB_TLS: RuleMeta = {
|
|
133
|
+
id: "cfn-elb-legacy-tls",
|
|
134
|
+
title: "CloudFormation ELB/ALB legacy TLS policy",
|
|
135
|
+
description: "Elastic Load Balancer SslPolicy naming a pre-2017 legacy policy",
|
|
136
|
+
category: "tls",
|
|
137
|
+
severity: "medium",
|
|
138
|
+
confidence: "high",
|
|
139
|
+
hndl: false,
|
|
140
|
+
cwe: CWE_WEAK_STRENGTH,
|
|
141
|
+
message: "Load balancer listener uses a legacy SSL negotiation policy permitting TLS 1.0/1.1.",
|
|
142
|
+
remediation: "Use ELBSecurityPolicy-TLS13-1-2-2021-06 (or the latest FS+TLS1.2/1.3 policy).",
|
|
143
|
+
};
|
|
144
|
+
const RULE_CFN_ARM_KV_RSA: RuleMeta = {
|
|
145
|
+
id: "cfn-arm-keyvault-rsa",
|
|
146
|
+
title: "ARM template Key Vault RSA key",
|
|
147
|
+
description: 'Microsoft.KeyVault key resource kty = "RSA"',
|
|
148
|
+
category: "kem",
|
|
149
|
+
severity: "high",
|
|
150
|
+
confidence: "high",
|
|
151
|
+
algorithm: "RSA",
|
|
152
|
+
hndl: true,
|
|
153
|
+
cwe: CWE_BROKEN_CRYPTO,
|
|
154
|
+
message:
|
|
155
|
+
"ARM template provisions a classical RSA Azure Key Vault key, which is not quantum-safe.",
|
|
156
|
+
remediation: "Plan migration to PQC (ML-KEM-768 / ML-DSA-65).",
|
|
157
|
+
};
|
|
158
|
+
const RULE_CFN_ARM_KV_EC: RuleMeta = {
|
|
159
|
+
id: "cfn-arm-keyvault-ec",
|
|
160
|
+
title: "ARM template Key Vault EC key",
|
|
161
|
+
description: 'Microsoft.KeyVault key resource kty = "EC"',
|
|
162
|
+
category: "key-exchange",
|
|
163
|
+
severity: "high",
|
|
164
|
+
confidence: "high",
|
|
165
|
+
algorithm: "ECDH",
|
|
166
|
+
hndl: true,
|
|
167
|
+
cwe: CWE_BROKEN_CRYPTO,
|
|
168
|
+
message:
|
|
169
|
+
"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).",
|
|
170
|
+
remediation:
|
|
171
|
+
"For key agreement: hybrid X25519MLKEM768 (ML-KEM-768). For signatures: ML-DSA-65 (FIPS 204).",
|
|
172
|
+
};
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Detects classical asymmetric crypto and legacy TLS config declared in
|
|
176
|
+
* AWS CloudFormation / Azure ARM (incl. Bicep-compiled JSON) templates.
|
|
177
|
+
*/
|
|
178
|
+
export const cloudformationDetector: Detector = {
|
|
179
|
+
id: "cloudformation-crypto",
|
|
180
|
+
description:
|
|
181
|
+
"Classical asymmetric crypto and legacy TLS config declared in CloudFormation / ARM templates (IaC)",
|
|
182
|
+
scope: "config",
|
|
183
|
+
language: "any",
|
|
184
|
+
rules: [
|
|
185
|
+
RULE_CFN_KMS_RSA,
|
|
186
|
+
RULE_CFN_KMS_EC,
|
|
187
|
+
RULE_CFN_ACM_RSA,
|
|
188
|
+
RULE_CFN_ACM_EC,
|
|
189
|
+
RULE_CFN_CLOUDFRONT_TLS,
|
|
190
|
+
RULE_CFN_ELB_TLS,
|
|
191
|
+
RULE_CFN_ARM_KV_RSA,
|
|
192
|
+
RULE_CFN_ARM_KV_EC,
|
|
193
|
+
],
|
|
194
|
+
appliesTo: (f) => hasExtension(f, CFN_EXTENSIONS),
|
|
195
|
+
detect({ file, content }): Finding[] {
|
|
196
|
+
if (!CFN_MARKERS.some((marker) => content.includes(marker))) return [];
|
|
197
|
+
|
|
198
|
+
const findings: Finding[] = [];
|
|
199
|
+
const add = (re: RegExp, rule: RuleMeta) =>
|
|
200
|
+
eachMatch(re, content, (m) =>
|
|
201
|
+
findings.push(
|
|
202
|
+
findingFromRule(rule, { file, content, index: m.index, matchLength: m[0].length }),
|
|
203
|
+
),
|
|
204
|
+
);
|
|
205
|
+
add(RE_CFN_KMS_RSA, RULE_CFN_KMS_RSA);
|
|
206
|
+
add(RE_CFN_KMS_EC, RULE_CFN_KMS_EC);
|
|
207
|
+
add(RE_CFN_ACM_RSA, RULE_CFN_ACM_RSA);
|
|
208
|
+
add(RE_CFN_ACM_EC, RULE_CFN_ACM_EC);
|
|
209
|
+
add(RE_CFN_CLOUDFRONT_TLS, RULE_CFN_CLOUDFRONT_TLS);
|
|
210
|
+
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);
|
|
213
|
+
return findings;
|
|
214
|
+
},
|
|
215
|
+
};
|
package/src/detectors/csharp.ts
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
*/
|
|
12
12
|
import type { Detector, Finding, RuleMeta } from "../types.js";
|
|
13
13
|
import { CSHARP_EXTENSIONS, eachMatch, findingFromRule, hasExtension } from "../detect-utils.js";
|
|
14
|
-
import { CWE_BROKEN_CRYPTO } from "../cwe.js";
|
|
14
|
+
import { CWE_BROKEN_CRYPTO, CWE_CERT_VALIDATION, CWE_WEAK_STRENGTH } from "../cwe.js";
|
|
15
15
|
|
|
16
16
|
const RE_CS_RSA =
|
|
17
17
|
/\bRSA\.Create\s*\(|\bnew\s+RSACryptoServiceProvider\s*\(|\bnew\s+RSACng\s*\(|\bnew\s+RSAOpenSsl\s*\(/g;
|
|
@@ -19,6 +19,37 @@ const RE_CS_ECDSA = /\bECDsa\.Create\s*\(|\bnew\s+ECDsaCng\s*\(|\bnew\s+ECDsaOpe
|
|
|
19
19
|
const RE_CS_ECDH =
|
|
20
20
|
/\bECDiffieHellman\.Create\s*\(|\bnew\s+ECDiffieHellmanCng\s*\(|\bnew\s+ECDiffieHellmanOpenSsl\s*\(/g;
|
|
21
21
|
const RE_CS_DSA = /\bDSA\.Create\s*\(|\bnew\s+DSACryptoServiceProvider\s*\(|\bnew\s+DSACng\s*\(/g;
|
|
22
|
+
// Insecure TLS configuration (.NET). Mirrors source.ts's tlsDetector split of a
|
|
23
|
+
// cert-verification-disabled rule (tls-reject-unauthorized) and a legacy-version
|
|
24
|
+
// rule (tls-legacy-version) for the equivalent C# idioms:
|
|
25
|
+
// - DangerousAcceptAnyServerCertificateValidator / a custom
|
|
26
|
+
// ServerCertificateCustomValidationCallback → accepts any cert (MITM).
|
|
27
|
+
// - SslProtocols.Ssl3 / .Tls / .Tls11 → deprecated SSL 3.0 / TLS 1.0 / 1.1.
|
|
28
|
+
// `SslProtocols.Tls` (no suffix) is the legacy TLS 1.0 constant; `Tls12`/`Tls13`
|
|
29
|
+
// are excluded by the trailing \b (the following digit is a word char).
|
|
30
|
+
const RE_CS_TLS_CERT_VALIDATION =
|
|
31
|
+
/\bDangerousAcceptAnyServerCertificateValidator\b|ServerCertificateCustomValidationCallback\s*=/g;
|
|
32
|
+
const RE_CS_TLS_LEGACY_VERSION = /\bSslProtocols\.(?:Tls|Tls11|Ssl3)\b/g;
|
|
33
|
+
// Identifier-form JWT/JOSE signature algorithms (audit F7). The quoted-string
|
|
34
|
+
// alg token ("RS256") is caught by the language-agnostic jwt-jose detector, but
|
|
35
|
+
// 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;
|
|
38
|
+
// BouncyCastle (Org.BouncyCastle) modern-curve and finite-field DH primitives,
|
|
39
|
+
// which System.Security.Cryptography does not expose directly. Each regex is
|
|
40
|
+
// anchored to the distinctive BouncyCastle class names for one family so it
|
|
41
|
+
// can't misfire on unrelated identifiers:
|
|
42
|
+
// - Ed25519KeyPairGenerator / Ed25519Signer / Ed25519PrivateKeyParameters → EdDSA
|
|
43
|
+
// - X25519KeyPairGenerator / X25519Agreement / X25519PrivateKeyParameters → X25519
|
|
44
|
+
// - X448KeyPairGenerator / X448Agreement / X448PrivateKeyParameters → X448
|
|
45
|
+
// - DHParametersGenerator / DHBasicAgreement / DH(Basic)KeyPairGenerator / DHParameters → DH
|
|
46
|
+
// The trailing \b keeps each token whole (e.g. Ed25519KeyGenerationParameters,
|
|
47
|
+
// X25519PublicKeyParameters and DHKeyGenerationParameters are NOT matched).
|
|
48
|
+
const RE_CS_BC_EDDSA = /\bEd25519(?:KeyPairGenerator|Signer|PrivateKeyParameters)\b/g;
|
|
49
|
+
const RE_CS_BC_X25519 = /\bX25519(?:KeyPairGenerator|Agreement|PrivateKeyParameters)\b/g;
|
|
50
|
+
const RE_CS_BC_X448 = /\bX448(?:KeyPairGenerator|Agreement|PrivateKeyParameters)\b/g;
|
|
51
|
+
const RE_CS_BC_DH =
|
|
52
|
+
/\bDH(?:ParametersGenerator|BasicAgreement|BasicKeyPairGenerator|KeyPairGenerator|Parameters)\b/g;
|
|
22
53
|
|
|
23
54
|
const RULE_CS_RSA: RuleMeta = {
|
|
24
55
|
id: "csharp-rsa",
|
|
@@ -71,14 +102,123 @@ const RULE_CS_DSA: RuleMeta = {
|
|
|
71
102
|
message: "Classical DSA (.NET) is deprecated and forgeable by a quantum attacker.",
|
|
72
103
|
remediation: "Rotate off DSA and migrate to ML-DSA-65 (FIPS 204).",
|
|
73
104
|
};
|
|
105
|
+
const RULE_CS_TLS_CERT: RuleMeta = {
|
|
106
|
+
id: "csharp-tls-cert-validation",
|
|
107
|
+
title: "C# TLS certificate verification disabled",
|
|
108
|
+
description:
|
|
109
|
+
"DangerousAcceptAnyServerCertificateValidator / ServerCertificateCustomValidationCallback override",
|
|
110
|
+
category: "tls",
|
|
111
|
+
severity: "high",
|
|
112
|
+
confidence: "high",
|
|
113
|
+
hndl: false,
|
|
114
|
+
cwe: CWE_CERT_VALIDATION,
|
|
115
|
+
message: "Accepting any server certificate disables TLS certificate verification (MITM risk).",
|
|
116
|
+
remediation:
|
|
117
|
+
"Remove the custom validator and verify certificates properly; prefer PQC-hybrid key exchange.",
|
|
118
|
+
};
|
|
119
|
+
const RULE_CS_TLS_LEGACY: RuleMeta = {
|
|
120
|
+
id: "csharp-tls-legacy-version",
|
|
121
|
+
title: "C# legacy TLS/SSL version pinned",
|
|
122
|
+
description: "SslProtocols pinned to Ssl3 / TLS 1.0 / TLS 1.1",
|
|
123
|
+
category: "tls",
|
|
124
|
+
severity: "medium",
|
|
125
|
+
confidence: "high",
|
|
126
|
+
hndl: false,
|
|
127
|
+
cwe: CWE_WEAK_STRENGTH,
|
|
128
|
+
message:
|
|
129
|
+
"SSL 3.0 / TLS 1.0 / TLS 1.1 are deprecated and insecure; require TLS 1.2+ (prefer 1.3).",
|
|
130
|
+
remediation: "Use SslProtocols.Tls13 (or Tls12) and prefer PQC-hybrid key exchange.",
|
|
131
|
+
};
|
|
132
|
+
const RULE_CS_JWT_ALG: RuleMeta = {
|
|
133
|
+
id: "csharp-jwt-alg",
|
|
134
|
+
title: "C# identifier-form JWT/JOSE algorithm",
|
|
135
|
+
description: "Microsoft.IdentityModel SecurityAlgorithms.RsaSha* / EcdsaSha*",
|
|
136
|
+
category: "signature",
|
|
137
|
+
severity: "high",
|
|
138
|
+
confidence: "high",
|
|
139
|
+
algorithm: "unknown",
|
|
140
|
+
hndl: false,
|
|
141
|
+
cwe: CWE_BROKEN_CRYPTO,
|
|
142
|
+
message:
|
|
143
|
+
"A classical JWT/JOSE signature algorithm (.NET, identifier form) is used, forgeable by a quantum attacker.",
|
|
144
|
+
remediation: "ML-DSA-65 (FIPS 204); track IETF PQC JOSE/COSE algorithms",
|
|
145
|
+
};
|
|
146
|
+
const RULE_CS_BC_EDDSA: RuleMeta = {
|
|
147
|
+
id: "csharp-bouncycastle-eddsa",
|
|
148
|
+
title: "C# Ed25519 signature (BouncyCastle)",
|
|
149
|
+
description:
|
|
150
|
+
"Org.BouncyCastle Ed25519KeyPairGenerator / Ed25519Signer / Ed25519PrivateKeyParameters",
|
|
151
|
+
category: "signature",
|
|
152
|
+
severity: "high",
|
|
153
|
+
confidence: "high",
|
|
154
|
+
algorithm: "EdDSA",
|
|
155
|
+
hndl: false,
|
|
156
|
+
cwe: CWE_BROKEN_CRYPTO,
|
|
157
|
+
message: "Classical Ed25519 signing (BouncyCastle) is forgeable by a quantum attacker.",
|
|
158
|
+
remediation: "ML-DSA-65 (FIPS 204) or SLH-DSA (FIPS 205)",
|
|
159
|
+
};
|
|
160
|
+
const RULE_CS_BC_X25519: RuleMeta = {
|
|
161
|
+
id: "csharp-bouncycastle-x25519",
|
|
162
|
+
title: "C# X25519 key agreement (BouncyCastle)",
|
|
163
|
+
description:
|
|
164
|
+
"Org.BouncyCastle X25519KeyPairGenerator / X25519Agreement / X25519PrivateKeyParameters",
|
|
165
|
+
category: "key-exchange",
|
|
166
|
+
severity: "high",
|
|
167
|
+
confidence: "high",
|
|
168
|
+
algorithm: "X25519",
|
|
169
|
+
hndl: true,
|
|
170
|
+
cwe: CWE_BROKEN_CRYPTO,
|
|
171
|
+
message:
|
|
172
|
+
"X25519 Diffie-Hellman key agreement (BouncyCastle) is broken by Shor's algorithm (harvest-now-decrypt-later).",
|
|
173
|
+
};
|
|
174
|
+
const RULE_CS_BC_X448: RuleMeta = {
|
|
175
|
+
id: "csharp-bouncycastle-x448",
|
|
176
|
+
title: "C# X448 key agreement (BouncyCastle)",
|
|
177
|
+
description: "Org.BouncyCastle X448KeyPairGenerator / X448Agreement / X448PrivateKeyParameters",
|
|
178
|
+
category: "key-exchange",
|
|
179
|
+
severity: "high",
|
|
180
|
+
confidence: "high",
|
|
181
|
+
algorithm: "X448",
|
|
182
|
+
hndl: true,
|
|
183
|
+
cwe: CWE_BROKEN_CRYPTO,
|
|
184
|
+
message:
|
|
185
|
+
"X448 Diffie-Hellman key agreement (BouncyCastle) is broken by Shor's algorithm (harvest-now-decrypt-later).",
|
|
186
|
+
};
|
|
187
|
+
const RULE_CS_BC_DH: RuleMeta = {
|
|
188
|
+
id: "csharp-bouncycastle-dh",
|
|
189
|
+
title: "C# finite-field Diffie-Hellman (BouncyCastle)",
|
|
190
|
+
description:
|
|
191
|
+
"Org.BouncyCastle DHParametersGenerator / DHBasicAgreement / DHKeyPairGenerator / DHParameters",
|
|
192
|
+
category: "key-exchange",
|
|
193
|
+
severity: "high",
|
|
194
|
+
confidence: "high",
|
|
195
|
+
algorithm: "DH",
|
|
196
|
+
hndl: true,
|
|
197
|
+
cwe: CWE_BROKEN_CRYPTO,
|
|
198
|
+
message:
|
|
199
|
+
"Finite-field Diffie-Hellman (BouncyCastle) is broken by Shor's algorithm (harvest-now-decrypt-later).",
|
|
200
|
+
};
|
|
74
201
|
|
|
75
202
|
/** Detects classical asymmetric crypto in C# (System.Security.Cryptography). */
|
|
76
203
|
export const csharpDetector: Detector = {
|
|
77
204
|
id: "csharp-crypto",
|
|
78
|
-
description:
|
|
205
|
+
description:
|
|
206
|
+
"Classical asymmetric crypto (System.Security.Cryptography) and insecure TLS config in C#/.NET",
|
|
79
207
|
scope: "source",
|
|
80
208
|
language: "csharp",
|
|
81
|
-
rules: [
|
|
209
|
+
rules: [
|
|
210
|
+
RULE_CS_RSA,
|
|
211
|
+
RULE_CS_ECDSA,
|
|
212
|
+
RULE_CS_ECDH,
|
|
213
|
+
RULE_CS_DSA,
|
|
214
|
+
RULE_CS_TLS_CERT,
|
|
215
|
+
RULE_CS_TLS_LEGACY,
|
|
216
|
+
RULE_CS_JWT_ALG,
|
|
217
|
+
RULE_CS_BC_EDDSA,
|
|
218
|
+
RULE_CS_BC_X25519,
|
|
219
|
+
RULE_CS_BC_X448,
|
|
220
|
+
RULE_CS_BC_DH,
|
|
221
|
+
],
|
|
82
222
|
appliesTo: (f) => hasExtension(f, CSHARP_EXTENSIONS),
|
|
83
223
|
detect({ file, content }): Finding[] {
|
|
84
224
|
const findings: Finding[] = [];
|
|
@@ -93,6 +233,17 @@ export const csharpDetector: Detector = {
|
|
|
93
233
|
add(RE_CS_ECDH, RULE_CS_ECDH);
|
|
94
234
|
add(RE_CS_RSA, RULE_CS_RSA);
|
|
95
235
|
add(RE_CS_DSA, RULE_CS_DSA);
|
|
236
|
+
// Insecure TLS configuration (disjoint from the crypto factories above).
|
|
237
|
+
add(RE_CS_TLS_CERT_VALIDATION, RULE_CS_TLS_CERT);
|
|
238
|
+
add(RE_CS_TLS_LEGACY_VERSION, RULE_CS_TLS_LEGACY);
|
|
239
|
+
// Identifier-form JWT/JOSE signature algorithms (Microsoft.IdentityModel).
|
|
240
|
+
add(RE_CS_JWT_ALG, RULE_CS_JWT_ALG);
|
|
241
|
+
// BouncyCastle (Org.BouncyCastle) curve / finite-field DH primitives that
|
|
242
|
+
// System.Security.Cryptography does not expose directly.
|
|
243
|
+
add(RE_CS_BC_EDDSA, RULE_CS_BC_EDDSA);
|
|
244
|
+
add(RE_CS_BC_X25519, RULE_CS_BC_X25519);
|
|
245
|
+
add(RE_CS_BC_X448, RULE_CS_BC_X448);
|
|
246
|
+
add(RE_CS_BC_DH, RULE_CS_BC_DH);
|
|
96
247
|
return findings;
|
|
97
248
|
},
|
|
98
249
|
};
|
|
@@ -0,0 +1,88 @@
|
|
|
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, Finding, RuleMeta } from "../types.js";
|
|
15
|
+
import { DOC_EXTENSIONS, eachMatch, findingFromRule, hasExtension } from "../detect-utils.js";
|
|
16
|
+
import { CWE_BROKEN_CRYPTO, CWE_CERT_VALIDATION } from "../cwe.js";
|
|
17
|
+
|
|
18
|
+
const RE_PGCRYPTO = /\bpgp_pub_(?:encrypt|decrypt)\b/g;
|
|
19
|
+
const RE_WEAK_SSLMODE = /\bsslmode\s*=\s*["']?(?:disable|allow|prefer|require)\b/gi;
|
|
20
|
+
|
|
21
|
+
const RULE_PGCRYPTO: RuleMeta = {
|
|
22
|
+
id: "db-pgcrypto-pubkey",
|
|
23
|
+
title: "pgcrypto public-key encryption",
|
|
24
|
+
description: "Postgres pgcrypto pgp_pub_encrypt / pgp_pub_decrypt (RSA/ElGamal) on stored data",
|
|
25
|
+
category: "kem",
|
|
26
|
+
severity: "high",
|
|
27
|
+
confidence: "high",
|
|
28
|
+
algorithm: "RSA",
|
|
29
|
+
hndl: true,
|
|
30
|
+
cwe: CWE_BROKEN_CRYPTO,
|
|
31
|
+
message:
|
|
32
|
+
"Column data is encrypted with pgcrypto public-key crypto (classical RSA/ElGamal); stored ciphertext is harvest-now-decrypt-later exposed.",
|
|
33
|
+
remediation:
|
|
34
|
+
"Plan migration to a post-quantum KEM (ML-KEM-768) envelope for at-rest data; re-encrypt long-lived rows.",
|
|
35
|
+
};
|
|
36
|
+
const RULE_WEAK_SSLMODE: RuleMeta = {
|
|
37
|
+
id: "db-weak-sslmode",
|
|
38
|
+
title: "Database sslmode without verification",
|
|
39
|
+
description: "libpq sslmode is disable/allow/prefer/require (no certificate verification)",
|
|
40
|
+
category: "tls",
|
|
41
|
+
severity: "medium",
|
|
42
|
+
confidence: "high",
|
|
43
|
+
hndl: false,
|
|
44
|
+
cwe: CWE_CERT_VALIDATION,
|
|
45
|
+
message:
|
|
46
|
+
"Database sslmode does not verify the server certificate; the classical TLS session is MITM-able and its key exchange is harvestable.",
|
|
47
|
+
remediation:
|
|
48
|
+
"Use sslmode=verify-full and TLS 1.3; track PQC-hybrid KEX (X25519MLKEM768) for database transport.",
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
/** Detects classical database at-rest / transport crypto. */
|
|
52
|
+
export const databaseDetector: Detector = {
|
|
53
|
+
id: "database-crypto",
|
|
54
|
+
description: "Classical crypto in database usage (pgcrypto public-key, weak client sslmode)",
|
|
55
|
+
scope: "config",
|
|
56
|
+
language: "any",
|
|
57
|
+
rules: [RULE_PGCRYPTO, RULE_WEAK_SSLMODE],
|
|
58
|
+
// Skip prose/docs: a README showing `sslmode=require` is not a live connection string.
|
|
59
|
+
appliesTo: (f) => !hasExtension(f, DOC_EXTENSIONS),
|
|
60
|
+
detect({ file, content }): Finding[] {
|
|
61
|
+
const findings: Finding[] = [];
|
|
62
|
+
if (file.toLowerCase().endsWith(".sql") && content.includes("pgp_pub_")) {
|
|
63
|
+
eachMatch(RE_PGCRYPTO, content, (m) =>
|
|
64
|
+
findings.push(
|
|
65
|
+
findingFromRule(RULE_PGCRYPTO, {
|
|
66
|
+
file,
|
|
67
|
+
content,
|
|
68
|
+
index: m.index,
|
|
69
|
+
matchLength: m[0].length,
|
|
70
|
+
}),
|
|
71
|
+
),
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
if (content.includes("sslmode")) {
|
|
75
|
+
eachMatch(RE_WEAK_SSLMODE, content, (m) =>
|
|
76
|
+
findings.push(
|
|
77
|
+
findingFromRule(RULE_WEAK_SSLMODE, {
|
|
78
|
+
file,
|
|
79
|
+
content,
|
|
80
|
+
index: m.index,
|
|
81
|
+
matchLength: m[0].length,
|
|
82
|
+
}),
|
|
83
|
+
),
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
return findings;
|
|
87
|
+
},
|
|
88
|
+
};
|