pipenv install --dev # once
make test # pytest (tests/)
pipenv run pytest tests/test_collect.py -k partial # single test
make lint # ruff, terraform fmt/validate, tflint, trivy, cfn-lint
make fmt # ruff format + terraform fmt
make build # pip install openpyxl + copy files/*.py into security-hub/build/
make diagram # regenerate docs/architecture.png (needs uv + graphviz)
make sample-report # regenerate sample-report.html + docs/sample-report.html
make docs # build this site with --strict
make docs-serve # live preview at http://127.0.0.1:8000
Tests import the Lambda modules directly (pythonpath = ["files"] in pyproject.toml). AWS
calls are stubbed with botocore.stub.Stubber for request-shape checks (Organizations, STS,
Security Hub) and small fakes in tests/conftest.py for S3 and SNS. Stubber validates response
shapes against the service model, so fake findings need every required field; use the
finding() helper.
CI (.github/workflows/python.yml) runs ruff check, ruff format check and pytest on every
push and pull request.
make lint runs ruff on files and tests, terraform fmt -check, terraform validate,
tflint, trivy config (CRITICAL, HIGH, MEDIUM) and cfn-lint on the CloudFormation template.
The .github/workflows/tfsec.yml workflow is legacy; trivy is the maintained scanner.
Findings that are deliberate and stay ignored: trivy AVD-AWS-0136 (SNS uses the AWS-managed key, ignored inline) and the Checkov set for VPC, X-Ray, DLQ, KMS on logs, code signing and concurrency limits. See design decisions.
docs/architecture.py draws architecture.png with the diagrams package; make diagram
runs it through uv so nothing is added to the Pipfile.docs/sample_report.py renders sample-report.html (root and docs/); it imports the
real aggregate code, so make sample-report sets PYTHONPATH=files.Both .py files are excluded from the built site by exclude_docs in mkdocs.yml.