preflight-cli 0.1.0__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.
Files changed (41) hide show
  1. preflight_cli-0.1.0/.github/ISSUE_TEMPLATE/bug_report.md +30 -0
  2. preflight_cli-0.1.0/.github/ISSUE_TEMPLATE/config.yml +5 -0
  3. preflight_cli-0.1.0/.github/ISSUE_TEMPLATE/feature_request.md +19 -0
  4. preflight_cli-0.1.0/.github/PULL_REQUEST_TEMPLATE.md +23 -0
  5. preflight_cli-0.1.0/.github/workflows/ci.yml +78 -0
  6. preflight_cli-0.1.0/.github/workflows/release.yml +172 -0
  7. preflight_cli-0.1.0/.gitignore +68 -0
  8. preflight_cli-0.1.0/CHANGELOG.md +26 -0
  9. preflight_cli-0.1.0/CODE_OF_CONDUCT.md +43 -0
  10. preflight_cli-0.1.0/CONTRIBUTING.md +68 -0
  11. preflight_cli-0.1.0/LICENSE +201 -0
  12. preflight_cli-0.1.0/NOTICE +18 -0
  13. preflight_cli-0.1.0/PKG-INFO +107 -0
  14. preflight_cli-0.1.0/README.md +75 -0
  15. preflight_cli-0.1.0/SECURITY.md +56 -0
  16. preflight_cli-0.1.0/core/.gitkeep +0 -0
  17. preflight_cli-0.1.0/docs/.gitkeep +0 -0
  18. preflight_cli-0.1.0/docs/release.md +80 -0
  19. preflight_cli-0.1.0/iam/policy.json +65 -0
  20. preflight_cli-0.1.0/iam/role.cfn.yaml +117 -0
  21. preflight_cli-0.1.0/install.sh +103 -0
  22. preflight_cli-0.1.0/modules/.gitkeep +0 -0
  23. preflight_cli-0.1.0/pyproject.toml +61 -0
  24. preflight_cli-0.1.0/src/preflight/__init__.py +1 -0
  25. preflight_cli-0.1.0/src/preflight/cli.py +444 -0
  26. preflight_cli-0.1.0/src/preflight/core/__init__.py +0 -0
  27. preflight_cli-0.1.0/src/preflight/core/cfn.py +97 -0
  28. preflight_cli-0.1.0/src/preflight/core/iam.py +108 -0
  29. preflight_cli-0.1.0/src/preflight/keys.py +98 -0
  30. preflight_cli-0.1.0/src/preflight/modules/__init__.py +95 -0
  31. preflight_cli-0.1.0/src/preflight/modules/base.py +69 -0
  32. preflight_cli-0.1.0/src/preflight/modules/cost.py +36 -0
  33. preflight_cli-0.1.0/src/preflight/modules/planned.py +106 -0
  34. preflight_cli-0.1.0/src/preflight/templates/role.cfn.yaml.j2 +87 -0
  35. preflight_cli-0.1.0/src/preflight/ui.py +301 -0
  36. preflight_cli-0.1.0/tests/test_cfn.py +175 -0
  37. preflight_cli-0.1.0/tests/test_cli_scan.py +465 -0
  38. preflight_cli-0.1.0/tests/test_iam.py +112 -0
  39. preflight_cli-0.1.0/tests/test_keys.py +52 -0
  40. preflight_cli-0.1.0/tests/test_modules.py +105 -0
  41. preflight_cli-0.1.0/tests/test_policy.py +43 -0
@@ -0,0 +1,30 @@
1
+ ---
2
+ name: Bug report
3
+ about: Something isn't working as expected
4
+ title: "[BUG] "
5
+ labels: bug
6
+ assignees: ""
7
+ ---
8
+
9
+ **Describe the bug**
10
+ A clear, concise description of what went wrong.
11
+
12
+ **To reproduce**
13
+ Steps to reproduce, e.g.:
14
+ 1. Run `preflight scan --profile ...`
15
+ 2. ...
16
+
17
+ **Expected behavior**
18
+ What you expected to happen.
19
+
20
+ **Environment**
21
+ - Preflight version: `preflight --version`
22
+ - OS/platform:
23
+ - Python version (if run from source):
24
+ - Install method (curl / pipx / brew / docker):
25
+
26
+ **Logs / output**
27
+ Paste relevant output. **Please redact account IDs, resource names/ARNs, and any credentials before pasting.**
28
+
29
+ **Additional context**
30
+ Anything else that's relevant.
@@ -0,0 +1,5 @@
1
+ blank_issues_enabled: false
2
+ contact_links:
3
+ - name: Security vulnerability
4
+ url: https://github.com/jetonecloud/preflight/security/policy
5
+ about: Please do not report security vulnerabilities as public issues — see SECURITY.md instead.
@@ -0,0 +1,19 @@
1
+ ---
2
+ name: Feature request
3
+ about: Suggest a new check, module, or capability
4
+ title: "[FEATURE] "
5
+ labels: enhancement
6
+ assignees: ""
7
+ ---
8
+
9
+ **What problem does this solve?**
10
+ Describe the gap. If this is a new check, which module does it belong to (Cost, Security/IAM, Reliability, Delivery & IaC, Observability, Bus factor)?
11
+
12
+ **Proposed approach**
13
+ How would this be detected? Which read-only AWS API(s) would it need (`Get*`/`List*`/`Describe*` only)?
14
+
15
+ **False positive risk**
16
+ How could this check produce a false positive, and how would you guard against it?
17
+
18
+ **Alternatives considered**
19
+ Any other ways to solve this, including existing tools that might already cover it.
@@ -0,0 +1,23 @@
1
+ ## Summary
2
+
3
+ <!-- What does this change do, and why? -->
4
+
5
+ ## What does this read from AWS?
6
+
7
+ <!-- List any new API calls. Confirm each is Get*/List*/Describe* only and has been added to iam/policy.json and the module's declared permissions. -->
8
+
9
+ - [ ] No new AWS API calls, OR
10
+ - [ ] New calls added, all read-only, and added to `iam/policy.json`
11
+ - [ ] `SECURITY.md` updated if this changes what Preflight reads
12
+
13
+ ## Test plan
14
+
15
+ - [ ] Added/updated tests, including a no-finding case and a false-positive guard case
16
+ - [ ] `uv run pytest` passes
17
+ - [ ] `uv run ruff check . && uv run ruff format --check .` passes
18
+
19
+ ## Checklist
20
+
21
+ - [ ] No credentials, secrets, or raw API responses are logged or transmitted
22
+ - [ ] No write/mutating AWS calls introduced
23
+ - [ ] License-compatible if logic was adapted from another project (attribution added to `NOTICE`)
@@ -0,0 +1,78 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+ branches: [main]
8
+ # Release reuses these jobs, so a tag can't ship something that doesn't pass.
9
+ workflow_call:
10
+
11
+ permissions:
12
+ contents: read
13
+
14
+ jobs:
15
+ lint:
16
+ runs-on: ubuntu-latest
17
+ steps:
18
+ - uses: actions/checkout@v4
19
+ - uses: astral-sh/setup-uv@v3
20
+ - run: uv sync --all-extras --dev
21
+ - run: uv run ruff check .
22
+ - run: uv run ruff format --check .
23
+
24
+ install-script:
25
+ # The installer is what most people will actually run, so hold it to the
26
+ # same bar as the Python: POSIX sh only, no shellcheck warnings.
27
+ runs-on: ubuntu-latest
28
+ steps:
29
+ - uses: actions/checkout@v4
30
+ - run: shellcheck --shell=sh install.sh
31
+ - run: sh -n install.sh
32
+
33
+ test:
34
+ runs-on: ubuntu-latest
35
+ strategy:
36
+ matrix:
37
+ python-version: ["3.11", "3.12"]
38
+ steps:
39
+ - uses: actions/checkout@v4
40
+ - uses: astral-sh/setup-uv@v3
41
+ with:
42
+ python-version: ${{ matrix.python-version }}
43
+ - run: uv sync --all-extras --dev
44
+ - run: uv run pytest --cov --cov-report=term-missing
45
+
46
+ policy-check:
47
+ runs-on: ubuntu-latest
48
+ steps:
49
+ - uses: actions/checkout@v4
50
+ - name: Verify IAM policy contains no write/wildcard actions
51
+ run: |
52
+ python3 - <<'EOF'
53
+ import json, sys
54
+ with open("iam/policy.json") as f:
55
+ policy = json.load(f)
56
+ allowed_prefixes = ("Get", "List", "Describe")
57
+ # Read-only actions that don't follow Get/List/Describe naming but
58
+ # perform no mutation. Keep in sync with tests/test_policy.py.
59
+ exceptions = {"cloudtrail:LookupEvents"}
60
+ bad = []
61
+ for stmt in policy.get("Statement", []):
62
+ actions = stmt.get("Action", [])
63
+ if isinstance(actions, str):
64
+ actions = [actions]
65
+ for action in actions:
66
+ if action == "*" or ":" not in action:
67
+ bad.append(action)
68
+ continue
69
+ if action in exceptions:
70
+ continue
71
+ verb = action.split(":", 1)[1]
72
+ if not verb.startswith(allowed_prefixes) or verb == "*":
73
+ bad.append(action)
74
+ if bad:
75
+ print("Non-read-only actions found in iam/policy.json:", bad)
76
+ sys.exit(1)
77
+ print("iam/policy.json is read-only. OK.")
78
+ EOF
@@ -0,0 +1,172 @@
1
+ name: Release
2
+
3
+ # Tag a commit on main to cut a release:
4
+ #
5
+ # git tag v0.1.0 && git push origin v0.1.0
6
+ #
7
+ # Nothing ships unless CI passes and the tag matches preflight.__version__.
8
+ # Assets are named without a version, so the installer can always fetch
9
+ # .../releases/latest/download/preflight-<platform> without calling the API.
10
+
11
+ on:
12
+ push:
13
+ tags: ["v*"]
14
+ # Dry run: builds and smoke-tests the binaries, publishes nothing.
15
+ workflow_dispatch:
16
+
17
+ permissions:
18
+ contents: read
19
+
20
+ env:
21
+ PYTHON_VERSION: "3.12"
22
+
23
+ jobs:
24
+ ci:
25
+ uses: ./.github/workflows/ci.yml
26
+
27
+ version-check:
28
+ runs-on: ubuntu-latest
29
+ if: startsWith(github.ref, 'refs/tags/v')
30
+ steps:
31
+ - uses: actions/checkout@v4
32
+ - uses: astral-sh/setup-uv@v3
33
+ with:
34
+ python-version: ${{ env.PYTHON_VERSION }}
35
+ - name: Tag must match the packaged version
36
+ # Both places: __version__ is what the binary reports, the project
37
+ # metadata is what PyPI publishes. A release where they disagree
38
+ # ships a binary that misreports itself.
39
+ run: |
40
+ set -eu
41
+ uv sync --all-extras --dev
42
+ tag="${GITHUB_REF_NAME#v}"
43
+ attr=$(uv run python -c 'import preflight; print(preflight.__version__)')
44
+ meta=$(uv run python -c 'from importlib.metadata import version; print(version("preflight-cli"))')
45
+ echo "tag=$tag __version__=$attr metadata=$meta"
46
+ if [ "$tag" != "$attr" ] || [ "$tag" != "$meta" ]; then
47
+ echo "::error::tag v$tag doesn't match the package ($attr / $meta)."
48
+ echo "Bump src/preflight/__init__.py and pyproject.toml together, or retag."
49
+ exit 1
50
+ fi
51
+ echo "v$tag matches the package. OK."
52
+
53
+ build:
54
+ needs: [ci]
55
+ strategy:
56
+ fail-fast: false
57
+ matrix:
58
+ # Oldest available image per platform: a binary built against an older
59
+ # glibc/macOS SDK runs on newer systems, but not the other way round.
60
+ # macos-15-intel is the Intel runner now that macos-13 is retired.
61
+ include:
62
+ - { os: ubuntu-22.04, target: linux-x86_64 }
63
+ - { os: ubuntu-22.04-arm, target: linux-aarch64 }
64
+ - { os: macos-15-intel, target: macos-x86_64 }
65
+ - { os: macos-14, target: macos-arm64 }
66
+ runs-on: ${{ matrix.os }}
67
+ steps:
68
+ - uses: actions/checkout@v4
69
+ - uses: astral-sh/setup-uv@v3
70
+ with:
71
+ python-version: ${{ env.PYTHON_VERSION }}
72
+
73
+ - name: Install the project and PyInstaller
74
+ run: |
75
+ uv sync --all-extras --dev
76
+ uv pip install pyinstaller
77
+
78
+ - name: Build a single-file binary
79
+ # --add-data is load-bearing: core/cfn.py reads the CloudFormation
80
+ # template through importlib.resources, which finds nothing in a
81
+ # frozen bundle unless the file is copied in alongside the package.
82
+ #
83
+ # boto3 needs no flags here. Nothing imports it yet, so it isn't
84
+ # bundled at all; once the checks do, PyInstaller's bundled
85
+ # boto3/botocore hooks collect the service data on their own. The
86
+ # smoke test below is what proves that, build by build.
87
+ run: |
88
+ uv run pyinstaller \
89
+ --onefile \
90
+ --name preflight \
91
+ --paths src \
92
+ --add-data "src/preflight/templates/role.cfn.yaml.j2:preflight/templates" \
93
+ src/preflight/cli.py
94
+
95
+ - name: Smoke-test the binary
96
+ # Catches a missing template, a missing boto3 data file, and a binary
97
+ # that can't start at all — none of which the test suite can see.
98
+ run: |
99
+ set -eu
100
+ ./dist/preflight --help > /dev/null
101
+ ./dist/preflight scan --list-modules
102
+ ./dist/preflight scan -m cost --print | grep -q "AWSTemplateFormatVersion"
103
+ ./dist/preflight scan -m cost --services ec2 --print | grep -q "ec2:DescribeVolumes"
104
+ ./dist/preflight scan -m cost --out "$RUNNER_TEMP/role.yaml"
105
+ grep -q "PreflightReadOnlyRole" "$RUNNER_TEMP/role.yaml"
106
+ echo "binary works on ${{ matrix.target }}"
107
+
108
+ - name: Name the asset after its platform
109
+ run: mv dist/preflight "preflight-${{ matrix.target }}"
110
+
111
+ - uses: actions/upload-artifact@v4
112
+ with:
113
+ name: preflight-${{ matrix.target }}
114
+ path: preflight-${{ matrix.target }}
115
+ if-no-files-found: error
116
+
117
+ publish:
118
+ needs: [build, version-check]
119
+ if: startsWith(github.ref, 'refs/tags/v')
120
+ runs-on: ubuntu-latest
121
+ permissions:
122
+ contents: write # create the release and upload assets
123
+ id-token: write # OIDC, for build provenance
124
+ attestations: write
125
+ steps:
126
+ - uses: actions/checkout@v4
127
+
128
+ - uses: actions/download-artifact@v4
129
+ with:
130
+ path: dist
131
+ pattern: preflight-*
132
+ merge-multiple: true
133
+
134
+ - name: Checksums
135
+ run: |
136
+ cd dist
137
+ chmod +x preflight-*
138
+ sha256sum preflight-* > checksums.txt
139
+ cat checksums.txt
140
+
141
+ - name: Attest what built these binaries
142
+ uses: actions/attest-build-provenance@v2
143
+ with:
144
+ subject-path: dist/preflight-*
145
+
146
+ - name: Publish the release
147
+ uses: softprops/action-gh-release@v2
148
+ with:
149
+ files: |
150
+ dist/preflight-*
151
+ dist/checksums.txt
152
+ install.sh
153
+ generate_release_notes: true
154
+ fail_on_unmatched_files: true
155
+
156
+ publish-pypi:
157
+ # Makes `pipx install preflight-cli` and `uv tool install preflight-cli`
158
+ # real. Needs a PyPI trusted publisher for this repo + workflow; delete
159
+ # this job if you'd rather not publish to PyPI yet.
160
+ needs: [build, version-check]
161
+ if: startsWith(github.ref, 'refs/tags/v')
162
+ runs-on: ubuntu-latest
163
+ environment: pypi
164
+ permissions:
165
+ id-token: write # OIDC, so there's no API token to store
166
+ steps:
167
+ - uses: actions/checkout@v4
168
+ - uses: astral-sh/setup-uv@v3
169
+ with:
170
+ python-version: ${{ env.PYTHON_VERSION }}
171
+ - run: uv build
172
+ - uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,68 @@
1
+ # Python
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+ *.so
6
+ .Python
7
+ build/
8
+ dist/
9
+ *.egg-info/
10
+ .eggs/
11
+ .installed.cfg
12
+ *.egg
13
+ pip-wheel-metadata/
14
+
15
+ # Virtual environments
16
+ .venv/
17
+ venv/
18
+ env/
19
+ ENV/
20
+
21
+ # uv
22
+ .uv/
23
+ uv.lock.bak
24
+
25
+ # Testing / coverage
26
+ .pytest_cache/
27
+ .coverage
28
+ .coverage.*
29
+ htmlcov/
30
+ coverage.xml
31
+ .tox/
32
+ .nox/
33
+
34
+ # Type checking / lint caches
35
+ .mypy_cache/
36
+ .ruff_cache/
37
+ .dmypy.json
38
+ dmypy.json
39
+
40
+ # Preflight output (never commit scan output — may contain account-identifying data)
41
+ reports/
42
+ *.preflight.json
43
+ *.preflight.html
44
+ preflight-report-*
45
+
46
+ # AWS / secrets — defense in depth, these must never exist in this repo
47
+ .aws/
48
+ *.pem
49
+ *.key
50
+ credentials
51
+ credentials.json
52
+ .env
53
+ .env.*
54
+ !.env.example
55
+
56
+ # Local drafts and notes
57
+ .local/
58
+
59
+ # Editors / OS
60
+ .vscode/
61
+ .idea/
62
+ *.swp
63
+ *.swo
64
+ .DS_Store
65
+ Thumbs.db
66
+
67
+ # Logs
68
+ *.log
@@ -0,0 +1,26 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project are documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/).
4
+
5
+ ## [Unreleased]
6
+
7
+ ### Added
8
+ - `preflight scan` first slice: pick modules, then generate the read-only CloudFormation role those modules need. Runs entirely offline — no AWS calls, no credentials.
9
+ - Interactive picker listing every module, with Cost Check selectable and the rest marked as coming soon.
10
+ - A second picker for the permissions themselves: every AWS service the selected modules need starts checked, and you can switch off the ones you'd rather not grant. Up/Down to move, Enter to check or uncheck, `a` for all and `n` for none, then Enter on the Continue row. Where keypresses can't be read one at a time — a pipe, CI — the same picker takes typed answers instead. `sts:GetCallerIdentity` is always included. Non-interactively that's `--services ec2,rds`.
11
+ - One step to a screen: each step clears the last and keeps the wordmark, and step 3 just asks where to save — press Enter for the directory you ran the command from.
12
+ - `--modules cost --save` for non-interactive use, plus `--services`, `--print`, `--out`, `--force` and `--list-modules`.
13
+ - Generated templates are deterministic, carry the command that reproduces them (including any service narrowing), and name only the actions the selected modules declare.
14
+ - Module registry (`preflight.modules`) where each module declares the IAM actions its checks call. The union of all modules matches `iam/policy.json`, which a test enforces.
15
+ - Generation-time read-only guard (`preflight.core.iam`): a module that declares a write or wildcard action fails the build rather than ending up in a deployed role.
16
+ - `preflight --version`, on stdout, so scripts (including the installer) can read it.
17
+ - Release pipeline: tagging `v*` reruns CI, checks the tag against `preflight.__version__`, builds a single-file binary per platform with PyInstaller, smoke-tests each one by generating a role template with it, then publishes the binaries, `checksums.txt`, a signed build provenance attestation, and `install.sh` to GitHub Releases — plus the sdist and wheel to PyPI via trusted publishing. See [`docs/release.md`](docs/release.md).
18
+ - `install.sh`: detects platform, downloads from the latest release, verifies the SHA-256 against `checksums.txt` and refuses to install on a mismatch. Honours `PREFLIGHT_VERSION` and `PREFLIGHT_BIN`. This is what `curl -fsSL https://jetonecloud.com/preflight | sh` serves.
19
+ - Project scaffolding: license, contribution guidelines, security policy, issue/PR templates, CI workflow.
20
+
21
+ ### Fixed
22
+ - Repository links pointed at a `jet1-cloud/preflight` that doesn't exist; they're `jetonecloud/preflight` now.
23
+ - `SECURITY.md` described a `.sig`/`.sha256` verification flow and a `docs/verifying-releases.md` that didn't exist. It now documents the checksum and `gh attestation verify` steps the release pipeline actually produces.
24
+
25
+ ### Notes
26
+ - The checks themselves aren't wired up yet, so `--profile`, `--region` and `--role-arn` are accepted but inert. This release gets the permissions in place first.
@@ -0,0 +1,43 @@
1
+ # Contributor Covenant Code of Conduct
2
+
3
+ ## Our Pledge
4
+
5
+ We as members, contributors, and leaders pledge to make participation in our community a harassment-free experience for everyone, regardless of age, body size, visible or invisible disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, religion, or sexual identity and orientation.
6
+
7
+ We pledge to act and interact in ways that contribute to an open, welcoming, diverse, inclusive, and healthy community.
8
+
9
+ ## Our Standards
10
+
11
+ Examples of behavior that contributes to a positive environment:
12
+
13
+ - Demonstrating empathy and kindness toward other people
14
+ - Being respectful of differing opinions, viewpoints, and experiences
15
+ - Giving and gracefully accepting constructive feedback
16
+ - Accepting responsibility and apologizing to those affected by our mistakes, and learning from the experience
17
+ - Focusing on what is best not just for us as individuals, but for the overall community
18
+
19
+ Examples of unacceptable behavior:
20
+
21
+ - The use of sexualized language or imagery, and sexual attention or advances of any kind
22
+ - Trolling, insulting or derogatory comments, and personal or political attacks
23
+ - Public or private harassment
24
+ - Publishing others' private information, such as a physical or email address, without their explicit permission
25
+ - Other conduct which could reasonably be considered inappropriate in a professional setting
26
+
27
+ ## Enforcement Responsibilities
28
+
29
+ Project maintainers are responsible for clarifying and enforcing our standards of acceptable behavior and will take appropriate and fair corrective action in response to any behavior they deem inappropriate, threatening, offensive, or harmful.
30
+
31
+ ## Scope
32
+
33
+ This Code of Conduct applies within all community spaces (issues, pull requests, discussions) and also applies when an individual is officially representing the community in public spaces.
34
+
35
+ ## Enforcement
36
+
37
+ Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to the maintainers at **conduct@jetonecloud.com**. All complaints will be reviewed and investigated promptly and fairly.
38
+
39
+ All maintainers are obligated to respect the privacy and security of the reporter of any incident.
40
+
41
+ ## Attribution
42
+
43
+ This Code of Conduct is adapted from the [Contributor Covenant](https://www.contributor-covenant.org), version 2.1, available at https://www.contributor-covenant.org/version/2/1/code_of_conduct.html.
@@ -0,0 +1,68 @@
1
+ # Contributing to Preflight
2
+
3
+ Thanks for considering a contribution. Preflight is Apache-2.0 licensed and built in the open — see [`CLAUDE.md`](CLAUDE.md) for the full project context, principles, and roadmap before diving in.
4
+
5
+ ## Ground rules
6
+
7
+ 1. **Read-only, no exceptions.** Any code that calls a write/mutating AWS API will be rejected. Every new API call must be added to the relevant module's declared permissions and to [`iam/policy.json`](iam/policy.json), with only the minimal `Get*`/`List*`/`Describe*` action needed.
8
+ 2. **Never log or transmit credentials, secrets, or raw API responses.** Findings must be derived/minimized before they leave a module.
9
+ 3. **License hygiene.** Do not copy GPL/AGPL-licensed code into this repo. If you're porting check logic from another open-source project, confirm its license is compatible (Apache-2.0/MIT with attribution) and add the attribution to [`NOTICE`](NOTICE).
10
+ 4. **Avoid false positives over false negatives.** If a check can't confidently tell whether something is a real problem (e.g., "idle" without checking tags/recent activity), don't flag it, or lower its confidence and say why.
11
+ 5. **Plain language.** Findings and recommendations should read like something you'd say to a startup CTO on a call, not a raw API dump.
12
+
13
+ ## Development setup
14
+
15
+ ```sh
16
+ git clone https://github.com/jetonecloud/preflight.git
17
+ cd preflight
18
+ uv sync
19
+ uv run pytest
20
+ ```
21
+
22
+ - Python 3.11+, managed via `uv` and `pyproject.toml`.
23
+ - Lint/format with **Ruff**: `uv run ruff check . && uv run ruff format .`
24
+ - Tests with **pytest** + **moto** for AWS mocking: `uv run pytest`
25
+
26
+ ## Adding or changing a module
27
+
28
+ - Each module is a self-contained plugin under `modules/`; shared logic belongs in `core/`.
29
+ - Declare the exact IAM actions the module needs. Update `iam/policy.json` and `SECURITY.md` if the set of data read changes.
30
+ - Every finding must conform to the shared finding schema (`id`, `module`, `title`, `severity`, `resource`, `evidence`, `estimated_monthly_savings` (optional), `recommendation`, `confidence`).
31
+ - Add tests for every new check, including at least:
32
+ - A case with no finding (the check correctly stays quiet)
33
+ - A false-positive guard case (e.g., a resource that looks idle but is tagged/recently active)
34
+ - If the check estimates dollar savings, keep the estimate conservative and show the derivation (region, pricing API lookup, assumptions) in the finding's evidence.
35
+
36
+ ## Commit messages
37
+
38
+ This repo uses [Conventional Commits](https://www.conventionalcommits.org/): `<type>: <summary>`, optionally followed by a body explaining why.
39
+
40
+ Common types: `feat`, `fix`, `docs`, `chore`, `ci`, `test`, `refactor`.
41
+
42
+ ```
43
+ chore: scaffold enterprise repo structure
44
+
45
+ Add README, contribution/security/conduct docs, GitHub issue/PR
46
+ templates, CI workflow with a read-only IAM policy check, pyproject
47
+ packaging, a minimal CLI skeleton, and the baseline read-only IAM
48
+ policy and role template.
49
+ ```
50
+
51
+ Prefer one commit per logical change where practical, but a single well-described commit is fine for a batch of related scaffolding/setup work.
52
+
53
+ ## Pull requests
54
+
55
+ 1. Open an issue first for anything beyond a small fix, so we can discuss approach before you invest time.
56
+ 2. Keep PRs focused — one module/feature/fix per PR.
57
+ 3. Include tests. CI (lint + tests) must pass.
58
+ 4. Describe what the change reads from AWS and why, so reviewers can verify it stays read-only.
59
+ 5. By submitting a PR, you agree your contribution is licensed under Apache-2.0.
60
+
61
+ ## Code of conduct
62
+
63
+ This project follows the [Code of Conduct](CODE_OF_CONDUCT.md). Please read it before participating in issues, PRs, or discussions.
64
+
65
+ ## Reporting bugs vs. security issues
66
+
67
+ - Functional bugs: open a GitHub issue using the bug report template.
68
+ - Security vulnerabilities (especially anything that writes to AWS, leaks credentials, or transmits data without consent): follow [`SECURITY.md`](SECURITY.md) instead — do not open a public issue.