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