cloudg 0.3.0__py3-none-any.whl
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.
- cloudg/__init__.py +21 -0
- cloudg/api.py +613 -0
- cloudg/cli.py +934 -0
- cloudg/collectors/__init__.py +5 -0
- cloudg/collectors/aws.py +830 -0
- cloudg/collectors/azure.py +362 -0
- cloudg/collectors/base.py +44 -0
- cloudg/collectors/gcp.py +170 -0
- cloudg/collectors/multi.py +329 -0
- cloudg/config.py +261 -0
- cloudg/coverage.py +93 -0
- cloudg/credentials.py +451 -0
- cloudg/graph/__init__.py +6 -0
- cloudg/graph/builder.py +336 -0
- cloudg/graph/ontology.py +914 -0
- cloudg/graph/rag_export.py +533 -0
- cloudg/graph/reachability.py +237 -0
- cloudg/normaliser.py +390 -0
- cloudg/policies/custodian-aws-security.yml +139 -0
- cloudg/policies/custodian-azure.yml +95 -0
- cloudg/policies/custodian-gcp.yml +87 -0
- cloudg/policies/custodian.yml +131 -0
- cloudg/region_discovery.py +330 -0
- cloudg/registry.py +142 -0
- cloudg/renderers/__init__.py +1 -0
- cloudg/renderers/html_report.py +430 -0
- cloudg/renderers/json_export.py +65 -0
- cloudg/renderers/svg.py +689 -0
- cloudg/renderers/terraform_export.py +666 -0
- cloudg/retry.py +103 -0
- cloudg/rules/cis_aws_v3.yaml +148 -0
- cloudg/rules/cis_azure_v2.yaml +142 -0
- cloudg/rules/cis_gcp_v3.yaml +133 -0
- cloudg/rules/frameworks/aws_foundational_security_best_practices_aws.yaml +1286 -0
- cloudg/rules/frameworks/cis_5.0_aws.yaml +280 -0
- cloudg/rules/frameworks/cis_5.0_azure.yaml +475 -0
- cloudg/rules/frameworks/cis_5.0_gcp.yaml +327 -0
- cloudg/rules/frameworks/gdpr_aws.yaml +95 -0
- cloudg/rules/frameworks/hipaa_aws.yaml +461 -0
- cloudg/rules/frameworks/hipaa_azure.yaml +504 -0
- cloudg/rules/frameworks/hipaa_gcp.yaml +211 -0
- cloudg/rules/frameworks/iso27001_2022_aws.yaml +789 -0
- cloudg/rules/frameworks/iso27001_2022_azure.yaml +477 -0
- cloudg/rules/frameworks/iso27001_2022_gcp.yaml +240 -0
- cloudg/rules/frameworks/mitre_attack_aws.yaml +596 -0
- cloudg/rules/frameworks/mitre_attack_azure.yaml +418 -0
- cloudg/rules/frameworks/mitre_attack_gcp.yaml +300 -0
- cloudg/rules/frameworks/nist_800_53_revision_5_aws.yaml +3234 -0
- cloudg/rules/frameworks/nist_csf_2.0_aws.yaml +828 -0
- cloudg/rules/frameworks/pci_4.0_aws.yaml +7108 -0
- cloudg/rules/frameworks/pci_4.0_azure.yaml +4696 -0
- cloudg/rules/frameworks/pci_4.0_gcp.yaml +5405 -0
- cloudg/rules/frameworks/soc2_aws.yaml +421 -0
- cloudg/rules/frameworks/soc2_azure.yaml +477 -0
- cloudg/rules/frameworks/soc2_gcp.yaml +342 -0
- cloudg/rules/gdpr.yaml +56 -0
- cloudg/rules/hipaa_security_rule.yaml +83 -0
- cloudg/rules/iso_27001_2022.yaml +88 -0
- cloudg/rules/nist_800_53.yaml +134 -0
- cloudg/rules/pci_dss_v4.yaml +108 -0
- cloudg/rules/soc2_tsc.yaml +93 -0
- cloudg/scanners/__init__.py +1 -0
- cloudg/scanners/checkov.py +174 -0
- cloudg/scanners/iam_linter.py +200 -0
- cloudg/scanners/prowler.py +237 -0
- cloudg/scanners/scoutsuite.py +163 -0
- cloudg/scanners/trivy.py +339 -0
- cloudg/schema/__init__.py +25 -0
- cloudg/schema/models.py +260 -0
- cloudg/templates/report.html.j2 +828 -0
- cloudg-0.3.0.dist-info/METADATA +330 -0
- cloudg-0.3.0.dist-info/RECORD +75 -0
- cloudg-0.3.0.dist-info/WHEEL +4 -0
- cloudg-0.3.0.dist-info/entry_points.txt +14 -0
- cloudg-0.3.0.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
# ─────────────────────────────────────────────────────────────────
|
|
2
|
+
# NIST 800-53 Rev 5 — Control Family Mappings
|
|
3
|
+
# Source: NIST SP 800-53 Rev 5 (public domain, U.S. government)
|
|
4
|
+
#
|
|
5
|
+
# FALLBACK rulesets — used only for findings that arrive
|
|
6
|
+
# without scanner-native compliance control IDs.
|
|
7
|
+
# ─────────────────────────────────────────────────────────────────
|
|
8
|
+
|
|
9
|
+
framework: NIST-800-53
|
|
10
|
+
version: "Rev5"
|
|
11
|
+
source: "NIST SP 800-53 Revision 5"
|
|
12
|
+
|
|
13
|
+
controls:
|
|
14
|
+
# AC — Access Control
|
|
15
|
+
- id: "AC-2"
|
|
16
|
+
title: "Account Management"
|
|
17
|
+
patterns:
|
|
18
|
+
- "iam.*user"
|
|
19
|
+
- "account.*manage"
|
|
20
|
+
- "access.*control"
|
|
21
|
+
- "unused.*account"
|
|
22
|
+
severity: MEDIUM
|
|
23
|
+
|
|
24
|
+
- id: "AC-3"
|
|
25
|
+
title: "Access Enforcement"
|
|
26
|
+
patterns:
|
|
27
|
+
- "permission"
|
|
28
|
+
- "authorization"
|
|
29
|
+
- "access.*denied"
|
|
30
|
+
severity: HIGH
|
|
31
|
+
|
|
32
|
+
- id: "AC-6"
|
|
33
|
+
title: "Least Privilege"
|
|
34
|
+
patterns:
|
|
35
|
+
- "wildcard"
|
|
36
|
+
- "admin.*access"
|
|
37
|
+
- "overly.*permissive"
|
|
38
|
+
- "Action.*\\*"
|
|
39
|
+
- "Resource.*\\*"
|
|
40
|
+
severity: HIGH
|
|
41
|
+
|
|
42
|
+
# AU — Audit and Accountability
|
|
43
|
+
- id: "AU-2"
|
|
44
|
+
title: "Event Logging"
|
|
45
|
+
patterns:
|
|
46
|
+
- "logging"
|
|
47
|
+
- "cloudtrail"
|
|
48
|
+
- "audit.*trail"
|
|
49
|
+
- "flow.*log"
|
|
50
|
+
severity: MEDIUM
|
|
51
|
+
|
|
52
|
+
- id: "AU-6"
|
|
53
|
+
title: "Audit Record Review, Analysis, and Reporting"
|
|
54
|
+
patterns:
|
|
55
|
+
- "log.*review"
|
|
56
|
+
- "monitor"
|
|
57
|
+
- "alarm"
|
|
58
|
+
severity: MEDIUM
|
|
59
|
+
|
|
60
|
+
# CM — Configuration Management
|
|
61
|
+
- id: "CM-6"
|
|
62
|
+
title: "Configuration Settings"
|
|
63
|
+
patterns:
|
|
64
|
+
- "default.*config"
|
|
65
|
+
- "misconfigur"
|
|
66
|
+
- "hardening"
|
|
67
|
+
severity: MEDIUM
|
|
68
|
+
|
|
69
|
+
# IA — Identification and Authentication
|
|
70
|
+
- id: "IA-2"
|
|
71
|
+
title: "Identification and Authentication"
|
|
72
|
+
patterns:
|
|
73
|
+
- "mfa"
|
|
74
|
+
- "multi.factor"
|
|
75
|
+
- "authentication"
|
|
76
|
+
- "password.*policy"
|
|
77
|
+
severity: HIGH
|
|
78
|
+
|
|
79
|
+
- id: "IA-5"
|
|
80
|
+
title: "Authenticator Management"
|
|
81
|
+
patterns:
|
|
82
|
+
- "key.*rotat"
|
|
83
|
+
- "credential.*rotat"
|
|
84
|
+
- "password.*expir"
|
|
85
|
+
severity: MEDIUM
|
|
86
|
+
|
|
87
|
+
# SC — System and Communications Protection
|
|
88
|
+
- id: "SC-7"
|
|
89
|
+
title: "Boundary Protection"
|
|
90
|
+
patterns:
|
|
91
|
+
- "security.*group"
|
|
92
|
+
- "firewall"
|
|
93
|
+
- "network.*acl"
|
|
94
|
+
- "nacl"
|
|
95
|
+
- "internet.*exposed"
|
|
96
|
+
- "0\\.0\\.0\\.0"
|
|
97
|
+
severity: HIGH
|
|
98
|
+
|
|
99
|
+
- id: "SC-8"
|
|
100
|
+
title: "Transmission Confidentiality and Integrity"
|
|
101
|
+
patterns:
|
|
102
|
+
- "ssl"
|
|
103
|
+
- "tls"
|
|
104
|
+
- "https"
|
|
105
|
+
- "transit.*encrypt"
|
|
106
|
+
severity: MEDIUM
|
|
107
|
+
|
|
108
|
+
- id: "SC-28"
|
|
109
|
+
title: "Protection of Information at Rest"
|
|
110
|
+
patterns:
|
|
111
|
+
- "encrypt.*rest"
|
|
112
|
+
- "encrypt.*enabl"
|
|
113
|
+
- "storage.*encrypt"
|
|
114
|
+
- "kms"
|
|
115
|
+
severity: HIGH
|
|
116
|
+
|
|
117
|
+
# SI — System and Information Integrity
|
|
118
|
+
- id: "SI-2"
|
|
119
|
+
title: "Flaw Remediation"
|
|
120
|
+
patterns:
|
|
121
|
+
- "vulnerability"
|
|
122
|
+
- "cve"
|
|
123
|
+
- "patch"
|
|
124
|
+
- "update.*required"
|
|
125
|
+
severity: HIGH
|
|
126
|
+
|
|
127
|
+
- id: "SI-4"
|
|
128
|
+
title: "System Monitoring"
|
|
129
|
+
patterns:
|
|
130
|
+
- "guardduty"
|
|
131
|
+
- "security.*hub"
|
|
132
|
+
- "intrusion"
|
|
133
|
+
- "detect"
|
|
134
|
+
severity: MEDIUM
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
# ─────────────────────────────────────────────────────────────────
|
|
2
|
+
# PCI DSS v4.0 — Requirement Mappings
|
|
3
|
+
# Source: PCI Security Standards Council, PCI DSS v4.0 (public summary
|
|
4
|
+
# of requirements; the full standard is published by the PCI SSC)
|
|
5
|
+
#
|
|
6
|
+
# Fallback mappings for findings that arrive without scanner-native
|
|
7
|
+
# compliance control IDs.
|
|
8
|
+
# ─────────────────────────────────────────────────────────────────
|
|
9
|
+
|
|
10
|
+
framework: PCI-DSS
|
|
11
|
+
version: "4.0"
|
|
12
|
+
source: "PCI DSS v4.0"
|
|
13
|
+
|
|
14
|
+
controls:
|
|
15
|
+
- id: "PCI-1.2"
|
|
16
|
+
title: "Network security controls (NSCs) are configured and maintained"
|
|
17
|
+
patterns:
|
|
18
|
+
- "security.*group"
|
|
19
|
+
- "firewall"
|
|
20
|
+
- "network.*acl"
|
|
21
|
+
- "nsg"
|
|
22
|
+
severity: HIGH
|
|
23
|
+
|
|
24
|
+
- id: "PCI-1.3"
|
|
25
|
+
title: "Network access to and from the cardholder data environment is restricted"
|
|
26
|
+
patterns:
|
|
27
|
+
- "0\\.0\\.0\\.0"
|
|
28
|
+
- "internet.*exposed"
|
|
29
|
+
- "publicly.*accessible"
|
|
30
|
+
- "open.*to.*the.*world"
|
|
31
|
+
severity: CRITICAL
|
|
32
|
+
|
|
33
|
+
- id: "PCI-2.2"
|
|
34
|
+
title: "System components are configured and managed securely"
|
|
35
|
+
patterns:
|
|
36
|
+
- "default.*config"
|
|
37
|
+
- "default.*password"
|
|
38
|
+
- "hardening"
|
|
39
|
+
- "misconfigur"
|
|
40
|
+
severity: MEDIUM
|
|
41
|
+
|
|
42
|
+
- id: "PCI-3.5"
|
|
43
|
+
title: "Primary account number (PAN) is secured wherever it is stored"
|
|
44
|
+
patterns:
|
|
45
|
+
- "encrypt.*rest"
|
|
46
|
+
- "storage.*encrypt"
|
|
47
|
+
- "unencrypted"
|
|
48
|
+
severity: HIGH
|
|
49
|
+
|
|
50
|
+
- id: "PCI-4.2"
|
|
51
|
+
title: "PAN is protected with strong cryptography during transmission"
|
|
52
|
+
patterns:
|
|
53
|
+
- "tls"
|
|
54
|
+
- "ssl"
|
|
55
|
+
- "https"
|
|
56
|
+
- "transit.*encrypt"
|
|
57
|
+
- "secure.*transfer"
|
|
58
|
+
severity: HIGH
|
|
59
|
+
|
|
60
|
+
- id: "PCI-7.2"
|
|
61
|
+
title: "Access to system components and data is appropriately defined and assigned"
|
|
62
|
+
patterns:
|
|
63
|
+
- "least.*privilege"
|
|
64
|
+
- "overly.*permissive"
|
|
65
|
+
- "wildcard"
|
|
66
|
+
- "admin.*access"
|
|
67
|
+
severity: HIGH
|
|
68
|
+
|
|
69
|
+
- id: "PCI-8.3"
|
|
70
|
+
title: "Strong authentication for users and administrators is established and managed"
|
|
71
|
+
patterns:
|
|
72
|
+
- "mfa"
|
|
73
|
+
- "multi.factor"
|
|
74
|
+
- "password.*policy"
|
|
75
|
+
severity: HIGH
|
|
76
|
+
|
|
77
|
+
- id: "PCI-8.6"
|
|
78
|
+
title: "Use of application and system accounts and associated authentication factors is strictly managed"
|
|
79
|
+
patterns:
|
|
80
|
+
- "access.*key.*rotat"
|
|
81
|
+
- "credential.*rotat"
|
|
82
|
+
- "stale.*credential"
|
|
83
|
+
severity: MEDIUM
|
|
84
|
+
|
|
85
|
+
- id: "PCI-10.2"
|
|
86
|
+
title: "Audit logs are implemented to support the detection of anomalies"
|
|
87
|
+
patterns:
|
|
88
|
+
- "logging"
|
|
89
|
+
- "cloudtrail"
|
|
90
|
+
- "audit.*log"
|
|
91
|
+
- "flow.*log"
|
|
92
|
+
severity: MEDIUM
|
|
93
|
+
|
|
94
|
+
- id: "PCI-10.4"
|
|
95
|
+
title: "Audit logs are reviewed to identify anomalies or suspicious activity"
|
|
96
|
+
patterns:
|
|
97
|
+
- "monitor"
|
|
98
|
+
- "alarm"
|
|
99
|
+
- "alert"
|
|
100
|
+
severity: MEDIUM
|
|
101
|
+
|
|
102
|
+
- id: "PCI-11.3"
|
|
103
|
+
title: "External and internal vulnerabilities are regularly identified and addressed"
|
|
104
|
+
patterns:
|
|
105
|
+
- "vulnerability"
|
|
106
|
+
- "cve"
|
|
107
|
+
- "patch"
|
|
108
|
+
severity: HIGH
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
# ─────────────────────────────────────────────────────────────────
|
|
2
|
+
# SOC 2 — Trust Services Criteria (2017, revised 2022) Mappings
|
|
3
|
+
# Source: AICPA Trust Services Criteria (Common Criteria series)
|
|
4
|
+
#
|
|
5
|
+
# Fallback mappings for findings that arrive without scanner-native
|
|
6
|
+
# compliance control IDs.
|
|
7
|
+
# ─────────────────────────────────────────────────────────────────
|
|
8
|
+
|
|
9
|
+
framework: SOC2
|
|
10
|
+
version: "2017 (2022 revision)"
|
|
11
|
+
source: "AICPA Trust Services Criteria"
|
|
12
|
+
|
|
13
|
+
controls:
|
|
14
|
+
- id: "SOC2-CC6.1"
|
|
15
|
+
title: "Logical access security software, infrastructure, and architectures are implemented"
|
|
16
|
+
patterns:
|
|
17
|
+
- "access.*control"
|
|
18
|
+
- "authorization"
|
|
19
|
+
- "iam.*polic"
|
|
20
|
+
- "least.*privilege"
|
|
21
|
+
severity: HIGH
|
|
22
|
+
|
|
23
|
+
- id: "SOC2-CC6.2"
|
|
24
|
+
title: "New internal and external users are registered and authorized before access is granted"
|
|
25
|
+
patterns:
|
|
26
|
+
- "unused.*account"
|
|
27
|
+
- "stale.*user"
|
|
28
|
+
- "account.*manage"
|
|
29
|
+
severity: MEDIUM
|
|
30
|
+
|
|
31
|
+
- id: "SOC2-CC6.3"
|
|
32
|
+
title: "Access to data and software is removed or modified when no longer required"
|
|
33
|
+
patterns:
|
|
34
|
+
- "access.*key.*rotat"
|
|
35
|
+
- "credential.*rotat"
|
|
36
|
+
- "inactive.*credential"
|
|
37
|
+
severity: MEDIUM
|
|
38
|
+
|
|
39
|
+
- id: "SOC2-CC6.6"
|
|
40
|
+
title: "Logical access security measures protect against threats from outside system boundaries"
|
|
41
|
+
patterns:
|
|
42
|
+
- "internet.*exposed"
|
|
43
|
+
- "publicly.*accessible"
|
|
44
|
+
- "0\\.0\\.0\\.0"
|
|
45
|
+
- "firewall"
|
|
46
|
+
- "security.*group"
|
|
47
|
+
severity: CRITICAL
|
|
48
|
+
|
|
49
|
+
- id: "SOC2-CC6.7"
|
|
50
|
+
title: "Transmission, movement, and removal of information is restricted and protected"
|
|
51
|
+
patterns:
|
|
52
|
+
- "encrypt"
|
|
53
|
+
- "tls"
|
|
54
|
+
- "ssl"
|
|
55
|
+
- "https"
|
|
56
|
+
severity: HIGH
|
|
57
|
+
|
|
58
|
+
- id: "SOC2-CC6.8"
|
|
59
|
+
title: "Controls prevent or detect the introduction of unauthorized or malicious software"
|
|
60
|
+
patterns:
|
|
61
|
+
- "vulnerability"
|
|
62
|
+
- "cve"
|
|
63
|
+
- "malware"
|
|
64
|
+
- "patch"
|
|
65
|
+
severity: HIGH
|
|
66
|
+
|
|
67
|
+
- id: "SOC2-CC7.1"
|
|
68
|
+
title: "Vulnerabilities are identified and monitored through detection and monitoring procedures"
|
|
69
|
+
patterns:
|
|
70
|
+
- "scan"
|
|
71
|
+
- "detect"
|
|
72
|
+
- "guardduty"
|
|
73
|
+
- "security.*hub"
|
|
74
|
+
severity: MEDIUM
|
|
75
|
+
|
|
76
|
+
- id: "SOC2-CC7.2"
|
|
77
|
+
title: "System components are monitored for anomalies indicative of malicious acts or errors"
|
|
78
|
+
patterns:
|
|
79
|
+
- "monitor"
|
|
80
|
+
- "logging"
|
|
81
|
+
- "audit"
|
|
82
|
+
- "alarm"
|
|
83
|
+
- "cloudtrail"
|
|
84
|
+
severity: MEDIUM
|
|
85
|
+
|
|
86
|
+
- id: "SOC2-A1.2"
|
|
87
|
+
title: "Environmental protections, backup, and recovery infrastructure are maintained"
|
|
88
|
+
patterns:
|
|
89
|
+
- "backup"
|
|
90
|
+
- "snapshot"
|
|
91
|
+
- "multi.?az"
|
|
92
|
+
- "replication"
|
|
93
|
+
severity: MEDIUM
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Security scanners package."""
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
"""Checkov IaC static analysis scanner wrapper."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import json
|
|
6
|
+
import logging
|
|
7
|
+
import shutil
|
|
8
|
+
import subprocess
|
|
9
|
+
from typing import Any
|
|
10
|
+
|
|
11
|
+
from cloudg.schema.models import Finding, Severity
|
|
12
|
+
|
|
13
|
+
logger = logging.getLogger(__name__)
|
|
14
|
+
|
|
15
|
+
_SEVERITY_MAP = {
|
|
16
|
+
"CRITICAL": Severity.CRITICAL,
|
|
17
|
+
"HIGH": Severity.HIGH,
|
|
18
|
+
"MEDIUM": Severity.MEDIUM,
|
|
19
|
+
"LOW": Severity.LOW,
|
|
20
|
+
"INFO": Severity.INFO,
|
|
21
|
+
"UNKNOWN": Severity.MEDIUM,
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class CheckovScanner:
|
|
26
|
+
"""Wraps Checkov CLI to scan IaC directories for misconfigurations.
|
|
27
|
+
|
|
28
|
+
Supports Terraform, CloudFormation, ARM templates, Kubernetes manifests.
|
|
29
|
+
"""
|
|
30
|
+
|
|
31
|
+
def __init__(
|
|
32
|
+
self,
|
|
33
|
+
target_dir: str = ".",
|
|
34
|
+
frameworks: list[str] | None = None,
|
|
35
|
+
extra_args: list[str] | None = None,
|
|
36
|
+
) -> None:
|
|
37
|
+
self._target_dir = target_dir
|
|
38
|
+
self._frameworks = frameworks or []
|
|
39
|
+
self._extra_args = extra_args or []
|
|
40
|
+
|
|
41
|
+
@staticmethod
|
|
42
|
+
def is_available() -> bool:
|
|
43
|
+
"""Check if Checkov is installed."""
|
|
44
|
+
return shutil.which("checkov") is not None
|
|
45
|
+
|
|
46
|
+
def run(self) -> list[Finding]:
|
|
47
|
+
"""Execute Checkov scan and return normalised findings.
|
|
48
|
+
|
|
49
|
+
If no frameworks are configured, Checkov auto-detects all applicable
|
|
50
|
+
frameworks (terraform, cloudformation, arm, kubernetes, etc.) in the
|
|
51
|
+
target directory — which is the correct behaviour for cloud
|
|
52
|
+
infrastructure scanning.
|
|
53
|
+
"""
|
|
54
|
+
if not self.is_available():
|
|
55
|
+
logger.warning(
|
|
56
|
+
"Checkov is not installed. Install with: pip install checkov. "
|
|
57
|
+
"Skipping Checkov scan."
|
|
58
|
+
)
|
|
59
|
+
return []
|
|
60
|
+
|
|
61
|
+
cmd = [
|
|
62
|
+
"checkov",
|
|
63
|
+
"-d",
|
|
64
|
+
self._target_dir,
|
|
65
|
+
"--output",
|
|
66
|
+
"json",
|
|
67
|
+
"--quiet",
|
|
68
|
+
]
|
|
69
|
+
|
|
70
|
+
# Pass all frameworks in one shot; omit flag entirely for auto-detect
|
|
71
|
+
if self._frameworks:
|
|
72
|
+
cmd.extend(["--framework", *self._frameworks])
|
|
73
|
+
|
|
74
|
+
cmd.extend(self._extra_args)
|
|
75
|
+
|
|
76
|
+
logger.info("Running Checkov: %s", " ".join(cmd))
|
|
77
|
+
|
|
78
|
+
try:
|
|
79
|
+
result = subprocess.run(
|
|
80
|
+
cmd,
|
|
81
|
+
capture_output=True,
|
|
82
|
+
text=True,
|
|
83
|
+
timeout=1800,
|
|
84
|
+
check=False, # Checkov returns non-zero on failures found
|
|
85
|
+
)
|
|
86
|
+
except subprocess.TimeoutExpired:
|
|
87
|
+
logger.error("Checkov scan timed out")
|
|
88
|
+
return []
|
|
89
|
+
except Exception as exc:
|
|
90
|
+
logger.error("Failed to run Checkov: %s", exc)
|
|
91
|
+
return []
|
|
92
|
+
|
|
93
|
+
return self._parse_output(result.stdout)
|
|
94
|
+
|
|
95
|
+
def _parse_output(self, stdout: str) -> list[Finding]:
|
|
96
|
+
"""Parse Checkov JSON output from stdout."""
|
|
97
|
+
findings: list[Finding] = []
|
|
98
|
+
|
|
99
|
+
if not stdout.strip():
|
|
100
|
+
logger.warning("Checkov produced no output")
|
|
101
|
+
return []
|
|
102
|
+
|
|
103
|
+
try:
|
|
104
|
+
data = json.loads(stdout)
|
|
105
|
+
except json.JSONDecodeError as exc:
|
|
106
|
+
logger.warning("Failed to parse Checkov JSON output: %s", exc)
|
|
107
|
+
return []
|
|
108
|
+
|
|
109
|
+
# Checkov output can be a list (multiple frameworks) or single dict
|
|
110
|
+
if isinstance(data, list):
|
|
111
|
+
for framework_result in data:
|
|
112
|
+
findings.extend(self._parse_framework_result(framework_result))
|
|
113
|
+
elif isinstance(data, dict):
|
|
114
|
+
findings.extend(self._parse_framework_result(data))
|
|
115
|
+
|
|
116
|
+
logger.info("Parsed %d findings from Checkov", len(findings))
|
|
117
|
+
return findings
|
|
118
|
+
|
|
119
|
+
def _parse_framework_result(self, data: dict[str, Any]) -> list[Finding]:
|
|
120
|
+
"""Parse a single framework result block."""
|
|
121
|
+
findings: list[Finding] = []
|
|
122
|
+
|
|
123
|
+
check_type = data.get("check_type", "unknown")
|
|
124
|
+
failed_checks = data.get("results", {}).get("failed_checks", [])
|
|
125
|
+
|
|
126
|
+
for check in failed_checks:
|
|
127
|
+
severity_str = check.get("severity", "MEDIUM")
|
|
128
|
+
if isinstance(severity_str, str):
|
|
129
|
+
severity = _SEVERITY_MAP.get(severity_str.upper(), Severity.MEDIUM)
|
|
130
|
+
else:
|
|
131
|
+
severity = Severity.MEDIUM
|
|
132
|
+
|
|
133
|
+
resource = check.get("resource", "")
|
|
134
|
+
file_path = check.get("file_path", "")
|
|
135
|
+
file_line_range = check.get("file_line_range", [])
|
|
136
|
+
|
|
137
|
+
evidence = f"File: {file_path}"
|
|
138
|
+
if file_line_range:
|
|
139
|
+
evidence += f", Lines: {file_line_range}"
|
|
140
|
+
|
|
141
|
+
guideline = check.get("guideline", "")
|
|
142
|
+
check_id = check.get("check_id", "")
|
|
143
|
+
|
|
144
|
+
findings.append(
|
|
145
|
+
Finding(
|
|
146
|
+
resource_id=resource,
|
|
147
|
+
resource_arn=resource,
|
|
148
|
+
severity=severity,
|
|
149
|
+
title=f"[Checkov/{check_type}] {check.get('check_name', check_id)}",
|
|
150
|
+
description=check.get("check_name", ""),
|
|
151
|
+
evidence=evidence,
|
|
152
|
+
remediation=guideline if guideline else f"See Checkov check {check_id}",
|
|
153
|
+
source_tool="checkov",
|
|
154
|
+
source_finding_id=check_id,
|
|
155
|
+
compliance_frameworks=self._map_compliance(check_id),
|
|
156
|
+
)
|
|
157
|
+
)
|
|
158
|
+
|
|
159
|
+
return findings
|
|
160
|
+
|
|
161
|
+
def _map_compliance(self, check_id: str) -> list[str]:
|
|
162
|
+
"""Map Checkov check ID to compliance frameworks."""
|
|
163
|
+
# Checkov checks often map to CIS/NIST
|
|
164
|
+
frameworks = []
|
|
165
|
+
check_lower = check_id.lower()
|
|
166
|
+
if "cis" in check_lower:
|
|
167
|
+
frameworks.append("CIS")
|
|
168
|
+
if "nist" in check_lower:
|
|
169
|
+
frameworks.append("NIST-800-53")
|
|
170
|
+
if "pci" in check_lower:
|
|
171
|
+
frameworks.append("PCI-DSS")
|
|
172
|
+
if not frameworks:
|
|
173
|
+
frameworks.append("CIS") # Default for IaC misconfigs
|
|
174
|
+
return frameworks
|