Skip to content

Commit 214accc

Browse files
feat(phase-2): real AWS mode, 4 new S3 checks, tests, CI
- AWS real-mode is default; --simulated flag for explicit opt-in - 4 new S3 checks: PUBLIC-ACL, PUBLIC-POLICY, NO-LOGGING, NO-MFA-DELETE - Fix XSS in HTML reporter (html.escape) - Fix i18n (simulated findings now in English) - Fix exit codes (1 on findings, 2 on usage error, 3 on runtime) - Fix STS lazy init (no longer crashes in __init__) - Fix Account BPA empty-dict logic - Wire YAML rules to AWS scanner via RuleRegistry - 36 tests with moto (92.44% coverage) - GitHub Actions CI (pytest + ruff on Python 3.9-3.12) - pyproject.toml for pip install -e . - CLI: --output, --severity, --simulated flags
1 parent b2e134d commit 214accc

26 files changed

Lines changed: 2201 additions & 436 deletions

.github/workflows/ci.yml

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
name: CI
2+
3+
on:
4+
push:
5+
branches: [main, master]
6+
pull_request:
7+
branches: [main, master]
8+
9+
permissions:
10+
contents: read
11+
12+
jobs:
13+
test:
14+
name: Test (Python ${{ matrix.python-version }})
15+
runs-on: ubuntu-latest
16+
strategy:
17+
fail-fast: false
18+
matrix:
19+
python-version: ["3.9", "3.10", "3.11", "3.12"]
20+
21+
steps:
22+
- name: Checkout
23+
uses: actions/checkout@v4
24+
25+
- name: Set up Python ${{ matrix.python-version }}
26+
uses: actions/setup-python@v5
27+
with:
28+
python-version: ${{ matrix.python-version }}
29+
cache: pip
30+
cache-dependency-path: |
31+
requirements.txt
32+
requirements-dev.txt
33+
34+
- name: Install dependencies
35+
run: |
36+
python -m pip install --upgrade pip
37+
pip install -r requirements.txt
38+
pip install -r requirements-dev.txt
39+
pip install -e .
40+
41+
- name: Lint with ruff
42+
run: ruff check cms tests
43+
44+
- name: Run tests with coverage
45+
run: pytest -v
46+
47+
- name: Upload coverage to artifacts
48+
if: always()
49+
uses: actions/upload-artifact@v4
50+
with:
51+
name: coverage-${{ matrix.python-version }}
52+
path: |
53+
.coverage
54+
htmlcov/
55+
if-no-files-found: ignore

CONTRIBUTING.md

Lines changed: 28 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -28,30 +28,45 @@ Thanks for your interest in improving cloud security! This tool aims to be a lig
2828
git clone https://github.com/frangelbarrera/Cloud-Misconfig-Scanner.git
2929
cd Cloud-Misconfig-Scanner
3030
pip install -r requirements.txt
31-
python cms.py --provider aws --simulated # verify it runs
31+
pip install -r requirements-dev.txt
32+
pip install -e .
33+
34+
# Run tests with coverage (>=80% enforced)
35+
pytest -v
36+
37+
# Lint
38+
ruff check cms tests
39+
40+
# Verify the CLI works
41+
python cms.py --provider aws --simulated # simulated mode
3242
```
3343

3444
## Code Style
35-
- **Python**: PEP 8, type hints encouraged
36-
- **Imports**: stdlib → third-party → local (isort)
45+
- **Python**: PEP 8, type hints encouraged (modern `X | None` syntax with `from __future__ import annotations`)
46+
- **Imports**: stdlib → third-party → local (enforced by `ruff`/isort)
3747
- **Docstrings**: Google style for public functions
3848
- **Tests**: Required for new checks (use `moto` for AWS mocking)
49+
- **Lint**: `ruff check cms tests` must pass before merge
50+
- **Coverage**: `pytest --cov=cms --cov-fail-under=80` is enforced by CI
3951

4052
## Adding a New AWS S3 Check
4153

42-
1. Add the check logic to `cms/providers/aws_s3.py`
43-
2. Add a rule entry to `cms/checks/aws_s3_rules.yaml`
44-
3. Test with `moto` mock or real AWS sandbox
54+
1. Add the rule entry to `cms/checks/aws_s3_rules.yaml` (id, title, severity, description, remediation)
55+
2. Add the check logic to `cms/providers/aws_s3.py` (call `self._add_finding(res, "RULE-ID", resource, evidence=...)`)
56+
3. Add a test in `tests/test_aws_s3_scanner.py` using `moto` to mock the bucket state
4557
4. Update README if user-facing
58+
5. Run `pytest` and `ruff check cms tests` before committing
4659

4760
## Adding a New Cloud Provider
4861

4962
1. Create `cms/providers/<provider>_<service>.py`
50-
2. Subclass `ProviderScanner` from `cms/providers/base.py`
51-
3. Implement `scan()` method with real API calls
52-
4. Add YAML rules in `cms/checks/<provider>_<service>_rules.yaml`
53-
5. Update `cms.py` CLI to support the new provider
54-
6. Add IAM permissions docs in `docs/iam/`
63+
2. Subclass `ProviderScanner` from `cms/providers/base.py` and set `provider` and `service` class attributes
64+
3. Implement the `scan()` method with real API calls
65+
4. Load rules via `RuleRegistry(load_registry(path))` so finding metadata comes from YAML
66+
5. Add YAML rules in `cms/checks/<provider>_<service>_rules.yaml`
67+
6. Register the provider in `cms/cli.py` (`_scan_all` function)
68+
7. Add IAM permissions docs in `docs/iam/`
69+
8. Add tests using `moto` (AWS) or the equivalent mock framework
5570

5671
## Security Considerations
5772

@@ -64,10 +79,10 @@ python cms.py --provider aws --simulated # verify it runs
6479

6580
- [ ] Azure Blob real API implementation (`azure-storage-blob`)
6681
- [ ] GCP Storage real API implementation (`google-cloud-storage`)
67-
- [ ] Test coverage with `pytest` + `moto`
68-
- [ ] GitHub Actions CI pipeline
82+
- [ ] AWS IAM scanning module
6983
- [ ] SARIF output format
7084
- [ ] Docker container support
85+
- [ ] Slack webhook notifications
7186

7287
## License
7388

README.md

Lines changed: 115 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,27 @@
11
# Cloud Misconfig Scanner
22

3+
[![CI](https://github.com/frangelbarrera/Cloud-Misconfig-Scanner/actions/workflows/ci.yml/badge.svg)](https://github.com/frangelbarrera/Cloud-Misconfig-Scanner/actions/workflows/ci.yml)
34
[![Python](https://img.shields.io/badge/Python-3.9+-blue.svg)](https://www.python.org/)
45
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
56
[![AWS](https://img.shields.io/badge/AWS-S3-orange.svg)](https://aws.amazon.com/s3/)
67
[![Last Commit](https://img.shields.io/github/last-commit/frangelbarrera/Cloud-Misconfig-Scanner)](https://github.com/frangelbarrera/Cloud-Misconfig-Scanner)
78

8-
> A Python CLI tool for detecting misconfigurations in cloud storage services. Currently supports **AWS S3 with real API scanning**. Azure Blob and GCP Storage are on the roadmap.
9+
> A Python CLI tool for detecting misconfigurations in cloud storage services. **AWS S3 is fully supported with real API scanning.** Azure Blob and GCP Storage currently run in simulated mode (roadmap).
910
1011
## Features
1112

1213
### AWS S3 (Active)
13-
- **Real API scanning** via boto3 (uses your AWS credentials)
14-
- **Account-level Public Access Block** verification
15-
- **Bucket-level Public Access Block** per bucket
14+
- **Real API scanning** via boto3 (auto-detects your AWS credentials)
15+
- **Account-level Block Public Access (BPA)** verification
16+
- **Bucket-level BPA** per bucket (all four flags checked)
1617
- **Server-side encryption** detection
1718
- **Versioning** status check
18-
- **Bucket ACL** analysis (public-read, public-read-write)
19-
- **Bucket Policy** analysis (public `Principal: *`)
20-
- **Access logging** verification
19+
- **Bucket ACL** analysis — flags `AllUsers` / `AllAuthenticatedUsers` grants as CRITICAL
20+
- **Bucket Policy** analysis — flags `Principal: "*"` Allow statements as CRITICAL
21+
- **Server access logging** verification
22+
- **MFA Delete** status check (when versioning is enabled)
23+
- **Simulated mode** as a graceful fallback when no AWS credentials are present
24+
- **YAML-driven rules** — all finding metadata lives in `cms/checks/aws_s3_rules.yaml`, consistent with Azure/GCP
2125

2226
### Azure Blob (Roadmap)
2327
- Simulated mode only (real SDK integration planned)
@@ -30,38 +34,63 @@
3034
### Prerequisites
3135
- Python 3.9+
3236
- AWS account with read-only S3 permissions (for real scanning)
33-
- AWS credentials configured (`~/.aws/credentials` or environment variables)
37+
- AWS credentials configured (`~/.aws/credentials` or `AWS_ACCESS_KEY_ID`/`AWS_SECRET_ACCESS_KEY` env vars)
3438

3539
### Installation
3640

3741
```bash
3842
git clone https://github.com/frangelbarrera/Cloud-Misconfig-Scanner.git
3943
cd Cloud-Misconfig-Scanner
44+
45+
# Runtime dependencies
4046
pip install -r requirements.txt
47+
48+
# OR: editable install (also registers the console script)
49+
pip install -e .
4150
```
4251

4352
### Usage
4453

45-
#### Real AWS S3 Scan
54+
#### Real AWS S3 Scan (default behaviour)
4655
```bash
47-
# Using default AWS profile
56+
# Auto-detects credentials and runs in real mode
4857
python cms.py --provider aws
4958

50-
# Using specific profile
59+
# Using a specific profile
5160
python cms.py --provider aws --profile my-profile
5261

5362
# JSON output
5463
python cms.py --provider aws --format json
5564

56-
# HTML report (saved to report.html)
57-
python cms.py --provider aws --format html
65+
# HTML report to a specific file
66+
python cms.py --provider aws --format html -o report.html
67+
68+
# Only show HIGH and CRITICAL findings
69+
python cms.py --provider aws --severity HIGH
70+
71+
# Scan only specific buckets
72+
python cms.py --provider aws --targets bucket-a,bucket-b
5873
```
5974

60-
#### Simulated Mode (no AWS credentials needed)
75+
#### Simulated Mode
6176

62-
The scanner automatically detects when AWS credentials are not available (`~/.aws/credentials` missing) and runs in simulated mode. No extra flag is needed — just run:
77+
Simulated mode emits one finding per rule in `cms/checks/aws_s3_rules.yaml` against a fake bucket, so you can exercise the CLI/reporters without cloud credentials.
6378

64-
python cms.py --provider aws
79+
```bash
80+
# Force simulated mode (useful for CI / sandbox testing)
81+
python cms.py --provider aws --simulated
82+
```
83+
84+
If no AWS credentials are detected at all, the scanner auto-falls-back to simulated mode.
85+
86+
### Exit Codes
87+
88+
| Code | Meaning |
89+
|------|------------------------------------|
90+
| 0 | No findings (posture looks clean) |
91+
| 1 | Findings detected |
92+
| 2 | CLI usage error |
93+
| 3 | Scanner runtime error |
6594

6695
### Required AWS IAM Permissions
6796

@@ -76,69 +105,105 @@ Key permissions:
76105
- `s3:GetBucketPolicy`
77106
- `s3:GetBucketLogging`
78107
- `s3control:GetPublicAccessBlock`
108+
- `sts:GetCallerIdentity`
79109

80110
## Architecture
81111

82112
```
83113
Cloud-Misconfig-Scanner/
84-
├── cms.py # CLI entry point
114+
├── cms.py # Legacy CLI entry point (delegates to cms.cli)
85115
├── cms/
116+
│ ├── __init__.py
117+
│ ├── __main__.py # Allows `python -m cms`
118+
│ ├── cli.py # CLI implementation (argparse + exit codes)
86119
│ ├── core/ # Core engine
87120
│ │ ├── models.py # Resource, Finding, ScanResult dataclasses
88-
│ │ ├── rules.py # YAML rule loader
89-
│ │ ├── reporter.py # Text/JSON output
90-
│ │ └── html_reporter.py # HTML report generator
91-
│ ├── providers/ # Cloud providers
92-
│ │ ├── base.py # ProviderScanner ABC
93-
│ │ ├── aws_s3.py # AWS S3 scanner (real API)
121+
│ │ ├── rules.py # YAML rule loader + RuleRegistry
122+
│ │ ├── reporter.py # Text/JSON output + severity filtering
123+
│ │ └── html_reporter.py # HTML report (XSS-safe, CRITICAL supported)
124+
│ ├── providers/ # Cloud providers (all subclass ProviderScanner)
125+
│ │ ├── base.py # ProviderScanner ABC + shared helpers
126+
│ │ ├── aws_s3.py # AWS S3 scanner (real API + simulated fallback)
94127
│ │ ├── azure_blob.py # Azure Blob (simulated, roadmap)
95128
│ │ └── gcp_storage.py # GCP Storage (simulated, roadmap)
96129
│ └── checks/ # YAML rule definitions
97130
│ ├── aws_s3_rules.yaml
98131
│ ├── azure_blob_rules.yaml
99132
│ └── gcp_storage_rules.yaml
100-
└── docs/
101-
└── iam/
102-
└── aws_least_privilege.json
133+
├── tests/ # pytest + moto test suite
134+
│ ├── conftest.py
135+
│ ├── test_rules.py
136+
│ ├── test_reporter.py
137+
│ ├── test_html_reporter.py
138+
│ ├── test_aws_s3_scanner.py
139+
│ └── test_cli.py
140+
├── docs/
141+
│ └── iam/
142+
│ ├── aws_least_privilege.json
143+
│ ├── azure_least_privilege.md
144+
│ └── gcp_least_privilege.md
145+
├── .github/workflows/ci.yml # GitHub Actions: pytest + ruff on push/PR
146+
├── pyproject.toml # pip install -e . support + tool config
147+
├── requirements.txt # Runtime deps
148+
└── requirements-dev.txt # Test/lint deps
103149
```
104150

105151
## Output Formats
106152

107153
### Text (default)
108154
```
109-
[CRITICAL] S3-BUCKET-PUBLIC-ACL
110-
Resource: my-bucket
111-
Description: Bucket ACL allows public read access
112-
Remediation: Remove AllUsers grant from bucket ACL
113-
114-
[HIGH] S3-NO-ENCRYPTION
115-
Resource: another-bucket
116-
Description: Server-side encryption is not enabled
117-
Remediation: Enable SSE-S3 or SSE-KMS on the bucket
155+
Findings:
156+
- [CRITICAL] AWS-S3-PUBLIC-ACL | Bucket ACL allows public access -> aws:my-bucket
157+
- [HIGH] AWS-S3-ENCRYPTION | No default server-side encryption -> aws:another-bucket
118158
```
119159

120160
### JSON
121161
```json
122-
{
123-
"findings": [
124-
{
125-
"rule_id": "S3-BUCKET-PUBLIC-ACL",
126-
"severity": "CRITICAL",
127-
"resource": "my-bucket",
128-
"description": "Bucket ACL allows public read access",
129-
"remediation": "Remove AllUsers grant from bucket ACL"
130-
}
131-
]
132-
}
162+
[
163+
{
164+
"rule_id": "AWS-S3-PUBLIC-ACL",
165+
"title": "Bucket ACL allows public access",
166+
"severity": "CRITICAL",
167+
"description": "...",
168+
"remediation": "...",
169+
"resource": {
170+
"provider": "aws", "service": "s3", "account": "123456789012",
171+
"region": "us-east-1", "name": "my-bucket", "meta": {}
172+
},
173+
"evidence": { "public_grants": [{"grantee": "http://acs.amazonaws.com/groups/global/AllUsers", "permission": "READ"}] }
174+
}
175+
]
133176
```
134177

135178
### HTML
136-
Interactive HTML report with severity color-coding (CRITICAL=red, HIGH=orange, MEDIUM=yellow, LOW=blue).
179+
Self-contained HTML report with severity color-coding (CRITICAL=red, HIGH=pink, MEDIUM=yellow, LOW=blue). All fields are HTML-escaped to prevent XSS.
180+
181+
## Development
182+
183+
```bash
184+
pip install -r requirements.txt
185+
pip install -r requirements-dev.txt
186+
pip install -e .
187+
188+
# Run tests with coverage (>=80% enforced)
189+
pytest -v
190+
191+
# Lint
192+
ruff check cms tests
193+
```
137194

138195
## Roadmap
139196

140197
- [x] AWS S3 real API scanning
141-
- [ ] AWS S3 Bucket Policy deep analysis
198+
- [x] AWS S3 Bucket Policy deep analysis (Principal: "*" detection)
199+
- [x] AWS S3 Bucket ACL analysis (AllUsers / AllAuthenticatedUsers)
200+
- [x] AWS S3 Access Logging verification
201+
- [x] AWS S3 MFA Delete verification
202+
- [x] YAML-driven AWS rules (consistency with Azure/GCP)
203+
- [x] Tests with moto + pytest (>=80% core coverage)
204+
- [x] GitHub Actions CI (pytest + ruff)
205+
- [x] CLI improvements: --output, --severity, exit codes
206+
- [x] HTML reporter: XSS fix, CRITICAL severity
142207
- [ ] AWS IAM scanning (overly permissive policies)
143208
- [ ] Azure Blob real API scanning
144209
- [ ] GCP Storage real API scanning
@@ -155,8 +220,8 @@ Contributions welcome! See [CONTRIBUTING.md](CONTRIBUTING.md).
155220
Areas needing help:
156221
- Azure Blob real API implementation
157222
- GCP Storage real API implementation
158-
- Test coverage (pytest + moto)
159-
- CI/CD pipeline
223+
- AWS IAM scanning module
224+
- SARIF output format
160225

161226
## Security
162227

0 commit comments

Comments
 (0)