cloud-audit 2.1.0__tar.gz → 2.2.1__tar.gz
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.
- {cloud_audit-2.1.0 → cloud_audit-2.2.1}/CHANGELOG.md +115 -0
- {cloud_audit-2.1.0 → cloud_audit-2.2.1}/PKG-INFO +18 -2
- {cloud_audit-2.1.0 → cloud_audit-2.2.1}/README.md +16 -0
- {cloud_audit-2.1.0 → cloud_audit-2.2.1}/mkdocs.yml +1 -0
- {cloud_audit-2.1.0 → cloud_audit-2.2.1}/pyproject.toml +9 -2
- {cloud_audit-2.1.0 → cloud_audit-2.2.1}/src/cloud_audit/cli.py +139 -0
- {cloud_audit-2.1.0 → cloud_audit-2.2.1}/src/cloud_audit/models.py +9 -0
- {cloud_audit-2.1.0 → cloud_audit-2.2.1}/src/cloud_audit/providers/aws/provider.py +2 -0
- cloud_audit-2.2.1/src/cloud_audit/providers/aws/threat_feed/__init__.py +105 -0
- cloud_audit-2.2.1/src/cloud_audit/providers/aws/threat_feed/cloudtrail_tampering.py +195 -0
- cloud_audit-2.2.1/src/cloud_audit/providers/aws/threat_feed/cryptomining_role.py +193 -0
- cloud_audit-2.2.1/src/cloud_audit/providers/aws/threat_feed/datazone_overgrant.py +173 -0
- cloud_audit-2.2.1/src/cloud_audit/providers/aws/threat_feed/lambda_function_url.py +238 -0
- cloud_audit-2.2.1/src/cloud_audit/providers/aws/threat_feed/mmdsv1_in_use.py +258 -0
- cloud_audit-2.2.1/src/cloud_audit/providers/aws/threat_feed/quarantine_policy.py +191 -0
- cloud_audit-2.2.1/src/cloud_audit/providers/aws/threat_feed/roles_anywhere_abuse.py +166 -0
- cloud_audit-2.2.1/src/cloud_audit/providers/aws/threat_feed/ses_phishing.py +236 -0
- cloud_audit-2.2.1/src/cloud_audit/providers/aws/threat_feed/trufflehog_ua.py +217 -0
- cloud_audit-2.2.1/src/cloud_audit/providers/aws/threat_feed/whoami_confusion.py +230 -0
- cloud_audit-2.2.1/tests/aws/threat_feed/__init__.py +0 -0
- cloud_audit-2.2.1/tests/aws/threat_feed/test_cloudtrail_tampering.py +159 -0
- cloud_audit-2.2.1/tests/aws/threat_feed/test_cryptomining_role.py +169 -0
- cloud_audit-2.2.1/tests/aws/threat_feed/test_datazone_overgrant.py +178 -0
- cloud_audit-2.2.1/tests/aws/threat_feed/test_lambda_function_url.py +275 -0
- cloud_audit-2.2.1/tests/aws/threat_feed/test_mmdsv1_in_use.py +199 -0
- cloud_audit-2.2.1/tests/aws/threat_feed/test_quarantine_policy.py +287 -0
- cloud_audit-2.2.1/tests/aws/threat_feed/test_roles_anywhere_abuse.py +140 -0
- cloud_audit-2.2.1/tests/aws/threat_feed/test_ses_phishing.py +251 -0
- cloud_audit-2.2.1/tests/aws/threat_feed/test_trufflehog_ua.py +156 -0
- cloud_audit-2.2.1/tests/aws/threat_feed/test_whoami_confusion.py +181 -0
- {cloud_audit-2.1.0 → cloud_audit-2.2.1}/.cloud-audit.example.yml +0 -0
- {cloud_audit-2.1.0 → cloud_audit-2.2.1}/.github/FUNDING.yml +0 -0
- {cloud_audit-2.1.0 → cloud_audit-2.2.1}/.github/ISSUE_TEMPLATE/bug_report.yml +0 -0
- {cloud_audit-2.1.0 → cloud_audit-2.2.1}/.github/ISSUE_TEMPLATE/config.yml +0 -0
- {cloud_audit-2.1.0 → cloud_audit-2.2.1}/.github/ISSUE_TEMPLATE/feature_request.yml +0 -0
- {cloud_audit-2.1.0 → cloud_audit-2.2.1}/.github/dependabot.yml +0 -0
- {cloud_audit-2.1.0 → cloud_audit-2.2.1}/.github/workflows/ci.yml +0 -0
- {cloud_audit-2.1.0 → cloud_audit-2.2.1}/.github/workflows/docs.yml +0 -0
- {cloud_audit-2.1.0 → cloud_audit-2.2.1}/.github/workflows/example-scan.yml +0 -0
- {cloud_audit-2.1.0 → cloud_audit-2.2.1}/.github/workflows/release.yml +0 -0
- {cloud_audit-2.1.0 → cloud_audit-2.2.1}/.gitignore +0 -0
- {cloud_audit-2.1.0 → cloud_audit-2.2.1}/.mcp.json +0 -0
- {cloud_audit-2.1.0 → cloud_audit-2.2.1}/.pre-commit-hooks.yaml +0 -0
- {cloud_audit-2.1.0 → cloud_audit-2.2.1}/CODEOWNERS +0 -0
- {cloud_audit-2.1.0 → cloud_audit-2.2.1}/CODE_OF_CONDUCT.md +0 -0
- {cloud_audit-2.1.0 → cloud_audit-2.2.1}/CONTRIBUTING.md +0 -0
- {cloud_audit-2.1.0 → cloud_audit-2.2.1}/Dockerfile +0 -0
- {cloud_audit-2.1.0 → cloud_audit-2.2.1}/LICENSE +0 -0
- {cloud_audit-2.1.0 → cloud_audit-2.2.1}/Makefile +0 -0
- {cloud_audit-2.1.0 → cloud_audit-2.2.1}/ROADMAP.md +0 -0
- {cloud_audit-2.1.0 → cloud_audit-2.2.1}/SECURITY.md +0 -0
- {cloud_audit-2.1.0 → cloud_audit-2.2.1}/action.yml +0 -0
- {cloud_audit-2.1.0 → cloud_audit-2.2.1}/assets/demo.gif +0 -0
- {cloud_audit-2.1.0 → cloud_audit-2.2.1}/assets/logo-nobg.png +0 -0
- {cloud_audit-2.1.0 → cloud_audit-2.2.1}/assets/logo.png +0 -0
- {cloud_audit-2.1.0 → cloud_audit-2.2.1}/assets/report-preview.png +0 -0
- {cloud_audit-2.1.0 → cloud_audit-2.2.1}/assets/social-preview.png +0 -0
- {cloud_audit-2.1.0 → cloud_audit-2.2.1}/examples/daily-scan-with-diff.yml +0 -0
- {cloud_audit-2.1.0 → cloud_audit-2.2.1}/examples/github-actions.yml +0 -0
- {cloud_audit-2.1.0 → cloud_audit-2.2.1}/examples/post-deploy-scan.yml +0 -0
- {cloud_audit-2.1.0 → cloud_audit-2.2.1}/overrides/main.html +0 -0
- {cloud_audit-2.1.0 → cloud_audit-2.2.1}/scripts/generate_demo_gif.py +0 -0
- {cloud_audit-2.1.0 → cloud_audit-2.2.1}/scripts/generate_report_screenshot.py +0 -0
- {cloud_audit-2.1.0 → cloud_audit-2.2.1}/server.json +0 -0
- {cloud_audit-2.1.0 → cloud_audit-2.2.1}/src/cloud_audit/__init__.py +0 -0
- {cloud_audit-2.1.0 → cloud_audit-2.2.1}/src/cloud_audit/__main__.py +0 -0
- {cloud_audit-2.1.0 → cloud_audit-2.2.1}/src/cloud_audit/compliance/__init__.py +0 -0
- {cloud_audit-2.1.0 → cloud_audit-2.2.1}/src/cloud_audit/compliance/engine.py +0 -0
- {cloud_audit-2.1.0 → cloud_audit-2.2.1}/src/cloud_audit/compliance/frameworks/bsi_c5_2020.json +0 -0
- {cloud_audit-2.1.0 → cloud_audit-2.2.1}/src/cloud_audit/compliance/frameworks/cis_aws_v3.json +0 -0
- {cloud_audit-2.1.0 → cloud_audit-2.2.1}/src/cloud_audit/compliance/frameworks/hipaa_security.json +0 -0
- {cloud_audit-2.1.0 → cloud_audit-2.2.1}/src/cloud_audit/compliance/frameworks/iso27001_2022.json +0 -0
- {cloud_audit-2.1.0 → cloud_audit-2.2.1}/src/cloud_audit/compliance/frameworks/nis2_directive.json +0 -0
- {cloud_audit-2.1.0 → cloud_audit-2.2.1}/src/cloud_audit/compliance/frameworks/soc2_type2.json +0 -0
- {cloud_audit-2.1.0 → cloud_audit-2.2.1}/src/cloud_audit/config.py +0 -0
- {cloud_audit-2.1.0 → cloud_audit-2.2.1}/src/cloud_audit/correlate.py +0 -0
- {cloud_audit-2.1.0 → cloud_audit-2.2.1}/src/cloud_audit/cost_model.py +0 -0
- {cloud_audit-2.1.0 → cloud_audit-2.2.1}/src/cloud_audit/diff.py +0 -0
- {cloud_audit-2.1.0 → cloud_audit-2.2.1}/src/cloud_audit/history.py +0 -0
- {cloud_audit-2.1.0 → cloud_audit-2.2.1}/src/cloud_audit/mcp_server.py +0 -0
- {cloud_audit-2.1.0 → cloud_audit-2.2.1}/src/cloud_audit/providers/__init__.py +0 -0
- {cloud_audit-2.1.0 → cloud_audit-2.2.1}/src/cloud_audit/providers/aws/__init__.py +0 -0
- {cloud_audit-2.1.0 → cloud_audit-2.2.1}/src/cloud_audit/providers/aws/checks/__init__.py +0 -0
- {cloud_audit-2.1.0 → cloud_audit-2.2.1}/src/cloud_audit/providers/aws/checks/account.py +0 -0
- {cloud_audit-2.1.0 → cloud_audit-2.2.1}/src/cloud_audit/providers/aws/checks/backup.py +0 -0
- {cloud_audit-2.1.0 → cloud_audit-2.2.1}/src/cloud_audit/providers/aws/checks/bedrock.py +0 -0
- {cloud_audit-2.1.0 → cloud_audit-2.2.1}/src/cloud_audit/providers/aws/checks/cloudtrail.py +0 -0
- {cloud_audit-2.1.0 → cloud_audit-2.2.1}/src/cloud_audit/providers/aws/checks/cloudwatch.py +0 -0
- {cloud_audit-2.1.0 → cloud_audit-2.2.1}/src/cloud_audit/providers/aws/checks/config_.py +0 -0
- {cloud_audit-2.1.0 → cloud_audit-2.2.1}/src/cloud_audit/providers/aws/checks/ec2.py +0 -0
- {cloud_audit-2.1.0 → cloud_audit-2.2.1}/src/cloud_audit/providers/aws/checks/ecs.py +0 -0
- {cloud_audit-2.1.0 → cloud_audit-2.2.1}/src/cloud_audit/providers/aws/checks/efs.py +0 -0
- {cloud_audit-2.1.0 → cloud_audit-2.2.1}/src/cloud_audit/providers/aws/checks/eip.py +0 -0
- {cloud_audit-2.1.0 → cloud_audit-2.2.1}/src/cloud_audit/providers/aws/checks/guardduty.py +0 -0
- {cloud_audit-2.1.0 → cloud_audit-2.2.1}/src/cloud_audit/providers/aws/checks/iam.py +0 -0
- {cloud_audit-2.1.0 → cloud_audit-2.2.1}/src/cloud_audit/providers/aws/checks/inspector.py +0 -0
- {cloud_audit-2.1.0 → cloud_audit-2.2.1}/src/cloud_audit/providers/aws/checks/kms.py +0 -0
- {cloud_audit-2.1.0 → cloud_audit-2.2.1}/src/cloud_audit/providers/aws/checks/lambda_.py +0 -0
- {cloud_audit-2.1.0 → cloud_audit-2.2.1}/src/cloud_audit/providers/aws/checks/rds.py +0 -0
- {cloud_audit-2.1.0 → cloud_audit-2.2.1}/src/cloud_audit/providers/aws/checks/s3.py +0 -0
- {cloud_audit-2.1.0 → cloud_audit-2.2.1}/src/cloud_audit/providers/aws/checks/sagemaker.py +0 -0
- {cloud_audit-2.1.0 → cloud_audit-2.2.1}/src/cloud_audit/providers/aws/checks/secrets.py +0 -0
- {cloud_audit-2.1.0 → cloud_audit-2.2.1}/src/cloud_audit/providers/aws/checks/securityhub.py +0 -0
- {cloud_audit-2.1.0 → cloud_audit-2.2.1}/src/cloud_audit/providers/aws/checks/ssm.py +0 -0
- {cloud_audit-2.1.0 → cloud_audit-2.2.1}/src/cloud_audit/providers/aws/checks/vpc.py +0 -0
- {cloud_audit-2.1.0 → cloud_audit-2.2.1}/src/cloud_audit/providers/aws/checks/waf.py +0 -0
- {cloud_audit-2.1.0 → cloud_audit-2.2.1}/src/cloud_audit/providers/aws/iam_analyzer.py +0 -0
- {cloud_audit-2.1.0 → cloud_audit-2.2.1}/src/cloud_audit/providers/aws/iam_trust_graph.py +0 -0
- {cloud_audit-2.1.0 → cloud_audit-2.2.1}/src/cloud_audit/providers/base.py +0 -0
- {cloud_audit-2.1.0 → cloud_audit-2.2.1}/src/cloud_audit/py.typed +0 -0
- {cloud_audit-2.1.0 → cloud_audit-2.2.1}/src/cloud_audit/reports/__init__.py +0 -0
- {cloud_audit-2.1.0 → cloud_audit-2.2.1}/src/cloud_audit/reports/compliance_html.py +0 -0
- {cloud_audit-2.1.0 → cloud_audit-2.2.1}/src/cloud_audit/reports/compliance_markdown.py +0 -0
- {cloud_audit-2.1.0 → cloud_audit-2.2.1}/src/cloud_audit/reports/diff_markdown.py +0 -0
- {cloud_audit-2.1.0 → cloud_audit-2.2.1}/src/cloud_audit/reports/html.py +0 -0
- {cloud_audit-2.1.0 → cloud_audit-2.2.1}/src/cloud_audit/reports/markdown.py +0 -0
- {cloud_audit-2.1.0 → cloud_audit-2.2.1}/src/cloud_audit/reports/sarif.py +0 -0
- {cloud_audit-2.1.0 → cloud_audit-2.2.1}/src/cloud_audit/reports/templates/report.html.j2 +0 -0
- {cloud_audit-2.1.0 → cloud_audit-2.2.1}/src/cloud_audit/root_cause.py +0 -0
- {cloud_audit-2.1.0 → cloud_audit-2.2.1}/src/cloud_audit/scanner.py +0 -0
- {cloud_audit-2.1.0 → cloud_audit-2.2.1}/src/cloud_audit/simulate.py +0 -0
- {cloud_audit-2.1.0 → cloud_audit-2.2.1}/tests/__init__.py +0 -0
- {cloud_audit-2.1.0 → cloud_audit-2.2.1}/tests/aws/__init__.py +0 -0
- {cloud_audit-2.1.0 → cloud_audit-2.2.1}/tests/aws/test_bedrock.py +0 -0
- {cloud_audit-2.1.0 → cloud_audit-2.2.1}/tests/aws/test_cis_checks.py +0 -0
- {cloud_audit-2.1.0 → cloud_audit-2.2.1}/tests/aws/test_cloudtrail.py +0 -0
- {cloud_audit-2.1.0 → cloud_audit-2.2.1}/tests/aws/test_cloudwatch.py +0 -0
- {cloud_audit-2.1.0 → cloud_audit-2.2.1}/tests/aws/test_config.py +0 -0
- {cloud_audit-2.1.0 → cloud_audit-2.2.1}/tests/aws/test_ec2.py +0 -0
- {cloud_audit-2.1.0 → cloud_audit-2.2.1}/tests/aws/test_ecs.py +0 -0
- {cloud_audit-2.1.0 → cloud_audit-2.2.1}/tests/aws/test_eip.py +0 -0
- {cloud_audit-2.1.0 → cloud_audit-2.2.1}/tests/aws/test_guardduty.py +0 -0
- {cloud_audit-2.1.0 → cloud_audit-2.2.1}/tests/aws/test_iam.py +0 -0
- {cloud_audit-2.1.0 → cloud_audit-2.2.1}/tests/aws/test_iam_analyzer.py +0 -0
- {cloud_audit-2.1.0 → cloud_audit-2.2.1}/tests/aws/test_iam_trust_graph.py +0 -0
- {cloud_audit-2.1.0 → cloud_audit-2.2.1}/tests/aws/test_kms.py +0 -0
- {cloud_audit-2.1.0 → cloud_audit-2.2.1}/tests/aws/test_lambda.py +0 -0
- {cloud_audit-2.1.0 → cloud_audit-2.2.1}/tests/aws/test_rds.py +0 -0
- {cloud_audit-2.1.0 → cloud_audit-2.2.1}/tests/aws/test_s3.py +0 -0
- {cloud_audit-2.1.0 → cloud_audit-2.2.1}/tests/aws/test_sagemaker.py +0 -0
- {cloud_audit-2.1.0 → cloud_audit-2.2.1}/tests/aws/test_secrets.py +0 -0
- {cloud_audit-2.1.0 → cloud_audit-2.2.1}/tests/aws/test_ssm.py +0 -0
- {cloud_audit-2.1.0 → cloud_audit-2.2.1}/tests/aws/test_vpc.py +0 -0
- {cloud_audit-2.1.0 → cloud_audit-2.2.1}/tests/conftest.py +0 -0
- {cloud_audit-2.1.0 → cloud_audit-2.2.1}/tests/test_cli.py +0 -0
- {cloud_audit-2.1.0 → cloud_audit-2.2.1}/tests/test_cli_scan.py +0 -0
- {cloud_audit-2.1.0 → cloud_audit-2.2.1}/tests/test_compliance_frameworks.py +0 -0
- {cloud_audit-2.1.0 → cloud_audit-2.2.1}/tests/test_config.py +0 -0
- {cloud_audit-2.1.0 → cloud_audit-2.2.1}/tests/test_correlate.py +0 -0
- {cloud_audit-2.1.0 → cloud_audit-2.2.1}/tests/test_cost_model.py +0 -0
- {cloud_audit-2.1.0 → cloud_audit-2.2.1}/tests/test_diff.py +0 -0
- {cloud_audit-2.1.0 → cloud_audit-2.2.1}/tests/test_history.py +0 -0
- {cloud_audit-2.1.0 → cloud_audit-2.2.1}/tests/test_html.py +0 -0
- {cloud_audit-2.1.0 → cloud_audit-2.2.1}/tests/test_markdown.py +0 -0
- {cloud_audit-2.1.0 → cloud_audit-2.2.1}/tests/test_mcp_server.py +0 -0
- {cloud_audit-2.1.0 → cloud_audit-2.2.1}/tests/test_models.py +0 -0
- {cloud_audit-2.1.0 → cloud_audit-2.2.1}/tests/test_provider.py +0 -0
- {cloud_audit-2.1.0 → cloud_audit-2.2.1}/tests/test_root_cause.py +0 -0
- {cloud_audit-2.1.0 → cloud_audit-2.2.1}/tests/test_sarif.py +0 -0
- {cloud_audit-2.1.0 → cloud_audit-2.2.1}/tests/test_scanner.py +0 -0
- {cloud_audit-2.1.0 → cloud_audit-2.2.1}/tests/test_simulate.py +0 -0
- {cloud_audit-2.1.0 → cloud_audit-2.2.1}/tests/test_soc2_framework.py +0 -0
|
@@ -7,6 +7,121 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [2.2.1] - 2026-05-12
|
|
11
|
+
|
|
12
|
+
### Changed
|
|
13
|
+
|
|
14
|
+
- **TF-001 (SES phishing setup)** - severity escalation logic rewritten.
|
|
15
|
+
HIGH now requires BOTH out-of-sandbox AND a burst of >=2 recent
|
|
16
|
+
identity verifications in the same account scan. The previous
|
|
17
|
+
"email identity without matching domain" escalation has been removed:
|
|
18
|
+
it modeled the wrong attacker behaviour. Wiz's September 2025 research
|
|
19
|
+
documented attackers *"adding multiple domains as verified identities
|
|
20
|
+
using the CreateEmailIdentity API"* in quick succession - a burst
|
|
21
|
+
pattern, not a single typosquat email. The new logic matches what
|
|
22
|
+
the source incident actually documented.
|
|
23
|
+
|
|
24
|
+
- **TF-004 (leaked-creds scanner UA)** - removed `cloudgrappler` and
|
|
25
|
+
`detention-dodger` from the user-agent signature list. Both are
|
|
26
|
+
Permiso DEFENSIVE tools - their UA appearing in CloudTrail means a
|
|
27
|
+
defender is running them against the account, not that the account
|
|
28
|
+
is under attack. The detector now only matches OFFENSIVE scanner
|
|
29
|
+
signatures (`trufflehog`, `gitleaks`, `noseyparker`, `secretscanner`).
|
|
30
|
+
Module docstring updated with an explicit detection caveat: scanners
|
|
31
|
+
using stock AWS SDK / boto3 / aws-cli default user-agents look
|
|
32
|
+
identical to legitimate traffic and will not trigger this pattern.
|
|
33
|
+
|
|
34
|
+
- **TF-004 references** - replaced a fabricated TruffleHog blog URL in
|
|
35
|
+
the references list with the verified BleepingComputer / Kaspersky
|
|
36
|
+
May 2026 SES abuse coverage and the official TruffleHog GitHub repo.
|
|
37
|
+
|
|
38
|
+
### Tests
|
|
39
|
+
|
|
40
|
+
- 742 -> 747 (+5 net). New regression tests:
|
|
41
|
+
- `test_email_no_matching_domain_does_not_escalate` (TF-001) proves
|
|
42
|
+
the removed typosquat heuristic does not return.
|
|
43
|
+
- `test_burst_out_of_sandbox_escalates_to_high` and
|
|
44
|
+
`test_burst_in_sandbox_stays_medium` cover the new escalation rule.
|
|
45
|
+
- `test_burst_only_counts_recent_identities` verifies the burst
|
|
46
|
+
counter respects the 14-day window.
|
|
47
|
+
- `test_cloudgrappler_ua_not_flagged` and
|
|
48
|
+
`test_detention_dodger_ua_not_flagged` (TF-004) prove defensive
|
|
49
|
+
tools are now excluded.
|
|
50
|
+
|
|
51
|
+
## [2.2.0] - 2026-05-12
|
|
52
|
+
|
|
53
|
+
### Added
|
|
54
|
+
|
|
55
|
+
- **Threat Feed v1** — new `cloud-audit threat-feed` command and a dedicated
|
|
56
|
+
detector pipeline (`providers/aws/threat_feed/`) that flags ACTIVE abuse
|
|
57
|
+
indicators rather than misconfiguration. Each pattern has a versioned
|
|
58
|
+
`TF-XXX` ID, maps to the new `Category.THREAT`, and carries external
|
|
59
|
+
references (research reports, CVE links) on every Finding for credibility.
|
|
60
|
+
Rules pack version: **2026-Q2**.
|
|
61
|
+
|
|
62
|
+
Ten patterns shipped:
|
|
63
|
+
|
|
64
|
+
- `TF-001-ses-phishing-setup` (MEDIUM/HIGH) — SES email/domain identities
|
|
65
|
+
verified within the last 14 days, with severity escalating when an
|
|
66
|
+
out-of-sandbox account hosts a typosquat-style email identity that has
|
|
67
|
+
no matching domain identity. Tracks the Wiz May 2025 + BleepingComputer
|
|
68
|
+
May 2026 SES abuse campaigns.
|
|
69
|
+
- `TF-002-lambda-function-url-persistence` (HIGH/CRITICAL) — Lambda
|
|
70
|
+
functions exposed via `AuthType=NONE` Function URLs, escalating to
|
|
71
|
+
CRITICAL when the execution role grants admin-class permissions
|
|
72
|
+
(matching the role profile of the Nov-Dec 2025 cryptomining campaign).
|
|
73
|
+
- `TF-003-quarantine-policy` (CRITICAL) — IAM principals with
|
|
74
|
+
`AWSCompromisedKeyQuarantineV1/V2/V3` attached. AWS auto-attaches these
|
|
75
|
+
after detecting credential exposure (typically a public GitHub commit).
|
|
76
|
+
- `TF-004-trufflehog-ua-cloudtrail` (CRITICAL) — `sts:GetCallerIdentity`
|
|
77
|
+
calls in the last 24h whose user-agent matches known leaked-credentials
|
|
78
|
+
discovery scanners (TruffleHog, gitleaks, CloudGrappler, DetentionDodger,
|
|
79
|
+
NoseyParker). Confirmed credential validation by an external scanner.
|
|
80
|
+
- `TF-005-cryptomining-role` (HIGH/CRITICAL) — IAM roles created within
|
|
81
|
+
the last 48 hours that carry broad compute managed policies (EC2 Full,
|
|
82
|
+
PowerUser, Admin, ECS Full, Lambda Full). Escalates to CRITICAL when
|
|
83
|
+
the same role also has SES sending permissions (mining + email-spam
|
|
84
|
+
combo from the documented late-2025 campaign cluster).
|
|
85
|
+
- `TF-006-mmdsv1-in-use` (HIGH/CRITICAL) — EC2 instances where
|
|
86
|
+
`HttpTokens != required` (IMDSv1 still callable) and Bedrock AgentCore
|
|
87
|
+
agents on `metadataVersion=v1` (CRITICAL — addresses Unit 42 'Cracks in
|
|
88
|
+
the Bedrock' research and the Feb 2026 MMDSv2 default).
|
|
89
|
+
- `TF-007-whoami-confusion` (MEDIUM) — IAM roles trusted by CI/CD
|
|
90
|
+
identities (codebuild service principals, GitHub OIDC, GitLab OIDC,
|
|
91
|
+
Buildkite federation) that have a broad EC2 managed policy attached —
|
|
92
|
+
the precondition for the Datadog Feb 2025 whoAMI confusion attack.
|
|
93
|
+
- `TF-008-cloudtrail-tampering` (HIGH/CRITICAL) — CloudTrail trails with
|
|
94
|
+
`IsLogging=False` (CRITICAL — canonical post-credential-theft attacker
|
|
95
|
+
behaviour, AiTM phishing follow-on per Datadog March 2026) or with a
|
|
96
|
+
populated `LatestDeliveryError` (HIGH — S3 destination broken).
|
|
97
|
+
- `TF-009-roles-anywhere-abuse` (HIGH/MEDIUM) — IAM Roles Anywhere trust
|
|
98
|
+
anchors with `sourceType=CERTIFICATE_BUNDLE` instead of the recommended
|
|
99
|
+
AWS_ACM_PCA. Anyone able to issue a chain-valid cert can mint AWS
|
|
100
|
+
credentials (fwd:cloudsec 2025 'Let's Encrypt for AWS Console').
|
|
101
|
+
- `TF-010-datazone-overgrant` (HIGH) — `AmazonDataZoneFullAccess` attached
|
|
102
|
+
to non-admin principals (the "easy" onboarding policy that bridges
|
|
103
|
+
identity, Glue catalog, and S3 storage in a single grant).
|
|
104
|
+
|
|
105
|
+
CLI: `cloud-audit threat-feed [--list] [--pattern <id>] [--regions ...]
|
|
106
|
+
[--profile ...] [--threat-feed-version 2026-Q2]`. Exits 1 when CRITICAL
|
|
107
|
+
or HIGH detected (CI gate friendly). Patterns also surface in standard
|
|
108
|
+
`cloud-audit scan --categories threat` output (JSON, SARIF, HTML).
|
|
109
|
+
|
|
110
|
+
### Changed
|
|
111
|
+
|
|
112
|
+
- `Category` enum gains `THREAT` value for active-abuse findings (separate
|
|
113
|
+
from `SECURITY` misconfiguration).
|
|
114
|
+
- `Finding` model gains `threat_pattern_id: str | None` and
|
|
115
|
+
`references: list[str]` for backing research links.
|
|
116
|
+
- 23rd registered AWS check module (`threat_feed`) loaded by `AWSProvider`.
|
|
117
|
+
|
|
118
|
+
### Tests
|
|
119
|
+
|
|
120
|
+
- 638 -> 742 (+104). Each pattern ships 9-12 unit tests covering positive
|
|
121
|
+
detection, negative cases, false-positive guards, severity escalation,
|
|
122
|
+
multi-resource aggregation, AccessDenied resilience, and metadata
|
|
123
|
+
exposure.
|
|
124
|
+
|
|
10
125
|
## [2.1.0] - 2026-04-28
|
|
11
126
|
|
|
12
127
|
### Added
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: cloud-audit
|
|
3
|
-
Version: 2.1
|
|
4
|
-
Summary: Open-source AWS security scanner
|
|
3
|
+
Version: 2.2.1
|
|
4
|
+
Summary: Open-source AWS security scanner. Threat Feed v1 (10 active-abuse patterns from 2025-2026 incidents), 64 IAM escalation methods, What-If simulator, security trends, AI-SPM (Bedrock/SageMaker), 6 compliance frameworks, 31 attack chain rules, breach cost estimation, and MCP server. Every finding includes CLI + Terraform remediation.
|
|
5
5
|
Project-URL: Homepage, https://haitmg.pl/cloud-audit/
|
|
6
6
|
Project-URL: Documentation, https://haitmg.pl/cloud-audit/
|
|
7
7
|
Project-URL: Source, https://github.com/gebalamariusz/cloud-audit
|
|
@@ -83,6 +83,7 @@ Description-Content-Type: text/markdown
|
|
|
83
83
|
<a href="https://haitmg.pl/cloud-audit/compliance/overview/">Compliance</a> -
|
|
84
84
|
<a href="https://haitmg.pl/cloud-audit/features/attack-chains/">Attack Chains</a> -
|
|
85
85
|
<a href="https://haitmg.pl/cloud-audit/features/iam-escalation/">IAM Escalation</a> -
|
|
86
|
+
<a href="https://haitmg.pl/cloud-audit/features/threat-feed/">Threat Feed</a> -
|
|
86
87
|
<a href="https://haitmg.pl/cloud-audit/features/simulate/">Simulator</a> -
|
|
87
88
|
<a href="https://haitmg.pl/cloud-audit/features/mcp-server/">MCP Server</a>
|
|
88
89
|
</p>
|
|
@@ -100,6 +101,21 @@ Uses your default AWS credentials and region. Try without an AWS account:
|
|
|
100
101
|
cloud-audit demo
|
|
101
102
|
```
|
|
102
103
|
|
|
104
|
+
### NEW in v2.2: Threat Feed
|
|
105
|
+
|
|
106
|
+
Detect ACTIVE abuse patterns from 2025-2026 incidents (cryptomining campaigns,
|
|
107
|
+
SES phishing setup, leaked-credential scanner activity, AgentCore CVEs):
|
|
108
|
+
|
|
109
|
+
```bash
|
|
110
|
+
cloud-audit threat-feed # scan all 10 patterns
|
|
111
|
+
cloud-audit threat-feed --list # show registered patterns
|
|
112
|
+
cloud-audit threat-feed --pattern aws-tf-003 # one pattern only
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
Each pattern carries external research references (Wiz, Datadog Security Labs,
|
|
116
|
+
Unit 42, Permiso) on every finding. Exit code 1 when CRITICAL/HIGH detected
|
|
117
|
+
(CI gate friendly). See [Threat Feed docs](https://haitmg.pl/cloud-audit/features/threat-feed/).
|
|
118
|
+
|
|
103
119
|
---
|
|
104
120
|
|
|
105
121
|
## Why It's Different
|
|
@@ -36,6 +36,7 @@
|
|
|
36
36
|
<a href="https://haitmg.pl/cloud-audit/compliance/overview/">Compliance</a> -
|
|
37
37
|
<a href="https://haitmg.pl/cloud-audit/features/attack-chains/">Attack Chains</a> -
|
|
38
38
|
<a href="https://haitmg.pl/cloud-audit/features/iam-escalation/">IAM Escalation</a> -
|
|
39
|
+
<a href="https://haitmg.pl/cloud-audit/features/threat-feed/">Threat Feed</a> -
|
|
39
40
|
<a href="https://haitmg.pl/cloud-audit/features/simulate/">Simulator</a> -
|
|
40
41
|
<a href="https://haitmg.pl/cloud-audit/features/mcp-server/">MCP Server</a>
|
|
41
42
|
</p>
|
|
@@ -53,6 +54,21 @@ Uses your default AWS credentials and region. Try without an AWS account:
|
|
|
53
54
|
cloud-audit demo
|
|
54
55
|
```
|
|
55
56
|
|
|
57
|
+
### NEW in v2.2: Threat Feed
|
|
58
|
+
|
|
59
|
+
Detect ACTIVE abuse patterns from 2025-2026 incidents (cryptomining campaigns,
|
|
60
|
+
SES phishing setup, leaked-credential scanner activity, AgentCore CVEs):
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
cloud-audit threat-feed # scan all 10 patterns
|
|
64
|
+
cloud-audit threat-feed --list # show registered patterns
|
|
65
|
+
cloud-audit threat-feed --pattern aws-tf-003 # one pattern only
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
Each pattern carries external research references (Wiz, Datadog Security Labs,
|
|
69
|
+
Unit 42, Permiso) on every finding. Exit code 1 when CRITICAL/HIGH detected
|
|
70
|
+
(CI gate friendly). See [Threat Feed docs](https://haitmg.pl/cloud-audit/features/threat-feed/).
|
|
71
|
+
|
|
56
72
|
---
|
|
57
73
|
|
|
58
74
|
## Why It's Different
|
|
@@ -60,6 +60,7 @@ nav:
|
|
|
60
60
|
- Features:
|
|
61
61
|
- Attack Chains: features/attack-chains.md
|
|
62
62
|
- IAM Privilege Escalation: features/iam-escalation.md
|
|
63
|
+
- Threat Feed: features/threat-feed.md
|
|
63
64
|
- What-If Simulator: features/simulate.md
|
|
64
65
|
- Security Posture Trend: features/trend.md
|
|
65
66
|
- AI-SPM (Bedrock/SageMaker): features/ai-spm.md
|
|
@@ -4,8 +4,8 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "cloud-audit"
|
|
7
|
-
version = "2.1
|
|
8
|
-
description = "Open-source AWS security scanner
|
|
7
|
+
version = "2.2.1"
|
|
8
|
+
description = "Open-source AWS security scanner. Threat Feed v1 (10 active-abuse patterns from 2025-2026 incidents), 64 IAM escalation methods, What-If simulator, security trends, AI-SPM (Bedrock/SageMaker), 6 compliance frameworks, 31 attack chain rules, breach cost estimation, and MCP server. Every finding includes CLI + Terraform remediation."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = "MIT"
|
|
11
11
|
requires-python = ">=3.10"
|
|
@@ -81,6 +81,13 @@ select = ["E", "F", "I", "N", "W", "UP", "S", "B", "A", "C4", "SIM", "TCH", "RUF
|
|
|
81
81
|
"src/cloud_audit/reports/compliance_markdown.py" = ["E501"]
|
|
82
82
|
"src/cloud_audit/cli.py" = ["TC003"]
|
|
83
83
|
"src/cloud_audit/mcp_server.py" = ["TC003"]
|
|
84
|
+
# Threat-feed modules and tests intentionally use boto3-style CamelCase
|
|
85
|
+
# kwargs (UserName, RoleName, FunctionName, EmailIdentity) to match the
|
|
86
|
+
# AWS API surface they wrap. N803 (lower_snake_case argument) is wrong here.
|
|
87
|
+
# Inner exception classes used to simulate boto3 errors don't need the
|
|
88
|
+
# Error suffix N818 mandates. Lambda/SES helpers also use Optional implicitly.
|
|
89
|
+
"src/cloud_audit/providers/aws/threat_feed/*.py" = ["N803", "N818", "RUF013", "E501", "S112"]
|
|
90
|
+
"tests/aws/threat_feed/*.py" = ["S101", "N803", "N806", "N818", "RUF013", "E501", "TC003", "E402"]
|
|
84
91
|
"tests/**" = ["S101", "TC003", "E402"]
|
|
85
92
|
|
|
86
93
|
[tool.mypy]
|
|
@@ -1448,6 +1448,145 @@ def simulate(
|
|
|
1448
1448
|
console.print()
|
|
1449
1449
|
|
|
1450
1450
|
|
|
1451
|
+
@app.command(name="threat-feed")
|
|
1452
|
+
def threat_feed_cmd(
|
|
1453
|
+
profile: Annotated[str | None, typer.Option("--profile", help="AWS profile")] = None,
|
|
1454
|
+
regions: Annotated[
|
|
1455
|
+
str | None,
|
|
1456
|
+
typer.Option("--regions", "-r", help="Comma-separated regions, or 'all' for every enabled region"),
|
|
1457
|
+
] = None,
|
|
1458
|
+
pattern: Annotated[
|
|
1459
|
+
str | None,
|
|
1460
|
+
typer.Option("--pattern", help="Run only one pattern (e.g. aws-tf-003); default = all"),
|
|
1461
|
+
] = None,
|
|
1462
|
+
list_patterns: Annotated[
|
|
1463
|
+
bool,
|
|
1464
|
+
typer.Option("--list", help="List registered patterns and exit (no scan)"),
|
|
1465
|
+
] = False,
|
|
1466
|
+
threat_feed_version: Annotated[
|
|
1467
|
+
str | None,
|
|
1468
|
+
typer.Option("--threat-feed-version", help="Pin a rules-pack version (informational, default = current)"),
|
|
1469
|
+
] = None,
|
|
1470
|
+
) -> None:
|
|
1471
|
+
"""Detect ACTIVE abuse patterns from 2025-2026 threat reports.
|
|
1472
|
+
|
|
1473
|
+
Distinct from regular `scan`: looks for indicators that an attacker has ALREADY
|
|
1474
|
+
acted on the account (quarantine policies AWS attached after credential leak,
|
|
1475
|
+
public Lambda URLs created as persistence, DataZone over-grants, etc.) rather
|
|
1476
|
+
than mere misconfigurations.
|
|
1477
|
+
|
|
1478
|
+
Examples:
|
|
1479
|
+
cloud-audit threat-feed # scan all patterns
|
|
1480
|
+
cloud-audit threat-feed --pattern aws-tf-003 # one pattern only
|
|
1481
|
+
cloud-audit threat-feed --list # show registered patterns
|
|
1482
|
+
"""
|
|
1483
|
+
from cloud_audit.providers.aws import threat_feed as tf_module
|
|
1484
|
+
|
|
1485
|
+
if list_patterns:
|
|
1486
|
+
table = Table(title=f"Registered threat patterns (rules pack {tf_module.THREAT_FEED_VERSION})")
|
|
1487
|
+
table.add_column("Pattern ID", style="bold")
|
|
1488
|
+
table.add_column("Check ID")
|
|
1489
|
+
table.add_column("Severity")
|
|
1490
|
+
table.add_column("Name")
|
|
1491
|
+
table.add_column("Doc")
|
|
1492
|
+
for p in tf_module.list_patterns():
|
|
1493
|
+
sev_color = SEVERITY_COLORS.get(Severity(p["severity"]), "white")
|
|
1494
|
+
table.add_row(
|
|
1495
|
+
p["pattern_id"],
|
|
1496
|
+
p["check_id"],
|
|
1497
|
+
f"[{sev_color}]{p['severity'].upper()}[/{sev_color}]",
|
|
1498
|
+
p["name"],
|
|
1499
|
+
p["doc_url"],
|
|
1500
|
+
)
|
|
1501
|
+
console.print(table)
|
|
1502
|
+
console.print(f"\n[dim]{len(tf_module.list_patterns())} patterns registered.[/dim]")
|
|
1503
|
+
return
|
|
1504
|
+
|
|
1505
|
+
active_version = threat_feed_version or tf_module.THREAT_FEED_VERSION
|
|
1506
|
+
if threat_feed_version and threat_feed_version != tf_module.THREAT_FEED_VERSION:
|
|
1507
|
+
console.print(
|
|
1508
|
+
f"[yellow]Note: requested rules pack '{threat_feed_version}' differs from installed "
|
|
1509
|
+
f"'{tf_module.THREAT_FEED_VERSION}'. Pinning is informational in this build.[/yellow]"
|
|
1510
|
+
)
|
|
1511
|
+
|
|
1512
|
+
from cloud_audit.providers.aws.provider import AWSProvider
|
|
1513
|
+
|
|
1514
|
+
region_list = None
|
|
1515
|
+
if regions:
|
|
1516
|
+
region_list = ["all"] if regions.strip() == "all" else [r.strip() for r in regions.split(",")]
|
|
1517
|
+
|
|
1518
|
+
try:
|
|
1519
|
+
provider = AWSProvider(profile=profile, regions=region_list)
|
|
1520
|
+
except Exception as exc:
|
|
1521
|
+
console.print(f"[red]Failed to initialize AWS provider: {exc}[/red]")
|
|
1522
|
+
raise typer.Exit(2) from exc
|
|
1523
|
+
|
|
1524
|
+
threat_checks = [c for c in provider.get_checks() if getattr(c, "category", None) and c.category.value == "threat"]
|
|
1525
|
+
if pattern:
|
|
1526
|
+
threat_checks = [c for c in threat_checks if c.check_id == pattern]
|
|
1527
|
+
if not threat_checks:
|
|
1528
|
+
console.print(f"[red]No registered threat pattern with check_id '{pattern}'.[/red]")
|
|
1529
|
+
console.print("Run [cyan]cloud-audit threat-feed --list[/cyan] to see available patterns.")
|
|
1530
|
+
raise typer.Exit(2)
|
|
1531
|
+
|
|
1532
|
+
console.print(
|
|
1533
|
+
Panel(
|
|
1534
|
+
f"Scanning [bold]{len(threat_checks)}[/bold] threat patterns (rules pack [cyan]{active_version}[/cyan])",
|
|
1535
|
+
title="[bold]Threat Feed[/bold]",
|
|
1536
|
+
border_style="magenta",
|
|
1537
|
+
)
|
|
1538
|
+
)
|
|
1539
|
+
|
|
1540
|
+
all_findings: list[Finding] = []
|
|
1541
|
+
errors: list[tuple[str, str]] = []
|
|
1542
|
+
for check in threat_checks:
|
|
1543
|
+
try:
|
|
1544
|
+
result = check()
|
|
1545
|
+
except Exception as exc: # defensive - check should already capture
|
|
1546
|
+
errors.append((check.check_id, str(exc)))
|
|
1547
|
+
continue
|
|
1548
|
+
if result.error:
|
|
1549
|
+
errors.append((check.check_id, result.error))
|
|
1550
|
+
all_findings.extend(result.findings)
|
|
1551
|
+
|
|
1552
|
+
if errors:
|
|
1553
|
+
console.print()
|
|
1554
|
+
for check_id, err in errors:
|
|
1555
|
+
console.print(f"[yellow]! {check_id}: {err}[/yellow]")
|
|
1556
|
+
|
|
1557
|
+
if not all_findings:
|
|
1558
|
+
console.print("\n[green]No active abuse patterns detected.[/green]\n")
|
|
1559
|
+
raise typer.Exit(0)
|
|
1560
|
+
|
|
1561
|
+
table = Table(title=f"Detected threat patterns ({len(all_findings)} findings)", show_lines=True)
|
|
1562
|
+
table.add_column("Severity", no_wrap=True)
|
|
1563
|
+
table.add_column("Pattern", style="bold")
|
|
1564
|
+
table.add_column("Resource")
|
|
1565
|
+
table.add_column("Region")
|
|
1566
|
+
table.add_column("References")
|
|
1567
|
+
|
|
1568
|
+
for f in sorted(all_findings, key=lambda x: list(SEVERITY_COLORS).index(x.severity)):
|
|
1569
|
+
sev_color = SEVERITY_COLORS[f.severity]
|
|
1570
|
+
refs = "\n".join(f.references[:2]) if f.references else "-"
|
|
1571
|
+
table.add_row(
|
|
1572
|
+
f"[{sev_color}]{f.severity.value.upper()}[/{sev_color}]",
|
|
1573
|
+
f"{f.threat_pattern_id or f.check_id}\n[dim]{rich_escape(f.title)}[/dim]",
|
|
1574
|
+
rich_escape(f.resource_id),
|
|
1575
|
+
f.region,
|
|
1576
|
+
refs,
|
|
1577
|
+
)
|
|
1578
|
+
|
|
1579
|
+
console.print(table)
|
|
1580
|
+
console.print(
|
|
1581
|
+
"\n[dim]Use [cyan]cloud-audit scan --categories threat -o json[/cyan] for machine-readable output "
|
|
1582
|
+
"(includes full remediation + references).[/dim]\n"
|
|
1583
|
+
)
|
|
1584
|
+
|
|
1585
|
+
# Exit 1 when CRITICAL/HIGH detected (CI gate)
|
|
1586
|
+
blocking = [f for f in all_findings if f.severity in (Severity.CRITICAL, Severity.HIGH)]
|
|
1587
|
+
raise typer.Exit(1 if blocking else 0)
|
|
1588
|
+
|
|
1589
|
+
|
|
1451
1590
|
@app.command()
|
|
1452
1591
|
def version() -> None:
|
|
1453
1592
|
"""Show version."""
|
|
@@ -22,6 +22,7 @@ class Category(str, Enum):
|
|
|
22
22
|
COST = "cost"
|
|
23
23
|
RELIABILITY = "reliability"
|
|
24
24
|
PERFORMANCE = "performance"
|
|
25
|
+
THREAT = "threat"
|
|
25
26
|
|
|
26
27
|
|
|
27
28
|
class Effort(str, Enum):
|
|
@@ -75,6 +76,14 @@ class Finding(BaseModel):
|
|
|
75
76
|
remediation: Remediation | None = Field(default=None, description="Structured remediation details")
|
|
76
77
|
compliance_refs: list[str] = Field(default_factory=list, description="Compliance references, e.g. ['CIS 1.5']")
|
|
77
78
|
cost_estimate: CostEstimateData | None = Field(default=None, description="Estimated breach cost range")
|
|
79
|
+
threat_pattern_id: str | None = Field(
|
|
80
|
+
default=None,
|
|
81
|
+
description="Threat feed pattern identifier, e.g. 'TF-003-quarantine-policy' (None for regular checks)",
|
|
82
|
+
)
|
|
83
|
+
references: list[str] = Field(
|
|
84
|
+
default_factory=list,
|
|
85
|
+
description="External references (research reports, CVE links, blog posts) backing this finding",
|
|
86
|
+
)
|
|
78
87
|
|
|
79
88
|
|
|
80
89
|
class CheckResult(BaseModel):
|
|
@@ -8,6 +8,7 @@ from typing import TYPE_CHECKING, Any
|
|
|
8
8
|
import boto3
|
|
9
9
|
from botocore.config import Config
|
|
10
10
|
|
|
11
|
+
from cloud_audit.providers.aws import threat_feed
|
|
11
12
|
from cloud_audit.providers.aws.checks import (
|
|
12
13
|
account,
|
|
13
14
|
backup,
|
|
@@ -65,6 +66,7 @@ _CHECK_MODULES = [
|
|
|
65
66
|
waf,
|
|
66
67
|
bedrock,
|
|
67
68
|
sagemaker,
|
|
69
|
+
threat_feed,
|
|
68
70
|
]
|
|
69
71
|
|
|
70
72
|
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
"""Threat Feed - active abuse pattern detectors.
|
|
2
|
+
|
|
3
|
+
Each pattern in this package detects an attack technique observed in real-world
|
|
4
|
+
2025-2026 incidents (cryptomining campaigns, SES phishing, leaked credentials,
|
|
5
|
+
AgentCore vulnerabilities). Patterns are versioned via THREAT_FEED_VERSION so
|
|
6
|
+
operators can pin a known rules pack.
|
|
7
|
+
|
|
8
|
+
Patterns vs regular checks:
|
|
9
|
+
- Regular checks (providers/aws/checks/) detect MISCONFIGURATION
|
|
10
|
+
- Threat patterns (this package) detect ACTIVE ABUSE INDICATORS or
|
|
11
|
+
conditions an attacker exploited in a documented incident.
|
|
12
|
+
|
|
13
|
+
Each pattern produces standard Finding objects with:
|
|
14
|
+
- category=Category.THREAT
|
|
15
|
+
- threat_pattern_id="TF-XXX-name"
|
|
16
|
+
- references=[<URL to research report or CVE>]
|
|
17
|
+
|
|
18
|
+
Adding a new pattern:
|
|
19
|
+
1. Create src/cloud_audit/providers/aws/threat_feed/<name>.py
|
|
20
|
+
2. Implement: def detect(provider) -> CheckResult
|
|
21
|
+
3. Register in _PATTERN_MODULES below
|
|
22
|
+
4. Add tests in tests/aws/threat_feed/test_<name>.py
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
from __future__ import annotations
|
|
26
|
+
|
|
27
|
+
from typing import TYPE_CHECKING
|
|
28
|
+
|
|
29
|
+
from cloud_audit.models import Category
|
|
30
|
+
from cloud_audit.providers.aws.threat_feed import (
|
|
31
|
+
cloudtrail_tampering,
|
|
32
|
+
cryptomining_role,
|
|
33
|
+
datazone_overgrant,
|
|
34
|
+
lambda_function_url,
|
|
35
|
+
mmdsv1_in_use,
|
|
36
|
+
quarantine_policy,
|
|
37
|
+
roles_anywhere_abuse,
|
|
38
|
+
ses_phishing,
|
|
39
|
+
trufflehog_ua,
|
|
40
|
+
whoami_confusion,
|
|
41
|
+
)
|
|
42
|
+
from cloud_audit.providers.base import make_check
|
|
43
|
+
|
|
44
|
+
if TYPE_CHECKING:
|
|
45
|
+
from cloud_audit.providers.aws.provider import AWSProvider
|
|
46
|
+
from cloud_audit.providers.base import CheckFn
|
|
47
|
+
|
|
48
|
+
THREAT_FEED_VERSION = "2026-Q2"
|
|
49
|
+
"""Versioned rules pack identifier - bump when patterns added/removed/changed.
|
|
50
|
+
|
|
51
|
+
Operators pin via `cloud-audit threat-feed --threat-feed-version 2026-Q2`
|
|
52
|
+
to get reproducible scans across releases.
|
|
53
|
+
"""
|
|
54
|
+
|
|
55
|
+
_PATTERN_MODULES = [
|
|
56
|
+
ses_phishing,
|
|
57
|
+
lambda_function_url,
|
|
58
|
+
quarantine_policy,
|
|
59
|
+
trufflehog_ua,
|
|
60
|
+
cryptomining_role,
|
|
61
|
+
mmdsv1_in_use,
|
|
62
|
+
whoami_confusion,
|
|
63
|
+
cloudtrail_tampering,
|
|
64
|
+
roles_anywhere_abuse,
|
|
65
|
+
datazone_overgrant,
|
|
66
|
+
]
|
|
67
|
+
"""Registry of all threat feed pattern modules.
|
|
68
|
+
|
|
69
|
+
Order matters for output stability - keep it deterministic.
|
|
70
|
+
New patterns: append to the end so existing TF-IDs remain stable.
|
|
71
|
+
"""
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def get_checks(provider: AWSProvider) -> list[CheckFn]:
|
|
75
|
+
"""Return all threat feed pattern check functions bound to the provider.
|
|
76
|
+
|
|
77
|
+
Each pattern module exposes:
|
|
78
|
+
- detect(provider) -> CheckResult
|
|
79
|
+
- PATTERN_ID: str (e.g. "TF-003-quarantine-policy")
|
|
80
|
+
- CHECK_ID: str (e.g. "aws-tf-003")
|
|
81
|
+
"""
|
|
82
|
+
checks: list[CheckFn] = []
|
|
83
|
+
for module in _PATTERN_MODULES:
|
|
84
|
+
check = make_check(
|
|
85
|
+
module.detect,
|
|
86
|
+
provider,
|
|
87
|
+
check_id=module.CHECK_ID,
|
|
88
|
+
category=Category.THREAT,
|
|
89
|
+
)
|
|
90
|
+
checks.append(check)
|
|
91
|
+
return checks
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def list_patterns() -> list[dict[str, str]]:
|
|
95
|
+
"""Return metadata for all registered patterns - used by CLI list/help."""
|
|
96
|
+
return [
|
|
97
|
+
{
|
|
98
|
+
"pattern_id": m.PATTERN_ID,
|
|
99
|
+
"check_id": m.CHECK_ID,
|
|
100
|
+
"name": m.PATTERN_NAME,
|
|
101
|
+
"severity": m.PATTERN_SEVERITY.value,
|
|
102
|
+
"doc_url": m.DOC_URL,
|
|
103
|
+
}
|
|
104
|
+
for m in _PATTERN_MODULES
|
|
105
|
+
]
|