Metadata-Version: 2.4
Name: gha-audit
Version: 0.1.0
Summary: Scan .github/workflows for known GitHub Actions supply-chain and CI security misconfigurations (unpinned actions, pwn requests, script injection, over-broad permissions).
Author: HiroCheck
License: MIT
Project-URL: Homepage, https://github.com/Hiro-012/gha-audit
Project-URL: Repository, https://github.com/Hiro-012/gha-audit
Project-URL: Issues, https://github.com/Hiro-012/gha-audit/issues
Keywords: github-actions,github-actions-security,ci-security,ci-cd,supply-chain,supply-chain-security,workflow-security,static-analysis,security,audit,devsecops,sast
Classifier: Development Status :: 3 - Alpha
Classifier: Environment :: Console
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Security
Classifier: Topic :: Software Development :: Quality Assurance
Requires-Python: >=3.11
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: PyYAML>=6.0
Dynamic: license-file

# gha-audit

Scan `.github/workflows/*.yml` for known GitHub Actions supply-chain and
CI security misconfigurations, before they become an incident.

```bash
pip install gha-audit
gha-audit .
```

## What it catches

| Check | Severity | Real-world pattern |
| --- | --- | --- |
| `unpinned-action` | warn | `uses: owner/repo@main` (or any tag) instead of a full commit SHA. A mutable ref can be repointed to run different code without a corresponding change in your repo to review — this is exactly how the [tj-actions/changed-files compromise](https://github.com/tj-actions/changed-files) played out in 2025. |
| `pull-request-target-checkout` | error | A workflow triggers on `pull_request_target` (runs with your repo's secrets and write token) *and* checks out the pull request's own head. A fork's PR can then run its own code with your secrets — the canonical ["pwn request"](https://securitylab.github.com/resources/github-actions-preventing-pwn-requests/) pattern. |
| `script-injection` | error | A `run:` step interpolates `${{ github.event.issue.title }}` (or PR title/body, comment body, `github.head_ref`, ...) directly into the shell command. These values are attacker-controlled and are substituted as raw text before the shell runs — arbitrary shell input is possible. |
| `broad-permissions` | warn | Top-level `permissions: write-all`. GitHub's own hardening guide recommends the opposite: grant only the specific scopes each job needs. |

All four are static, deterministic checks with no network access beyond
reading files already in your checkout — `gha-audit` never executes or
fetches the actions it inspects.

## Usage

```bash
gha-audit .                # scan the current repo, human-readable output
gha-audit path/to/repo     # scan a different path
gha-audit --json .         # machine-readable output
gha-audit --strict .       # also fail (non-zero exit) on 'warn' findings, not just 'error'
```

Exit code is `0` when nothing is found (or only `warn`-level findings
without `--strict`), `1` when an `error`-level finding exists.

## Accuracy

Measured against a labelled fixture corpus (`tests/test_accuracy.py`),
each fixture isolated to exercise exactly one check:

- Recall: **100.00%** (8/8 known-vulnerable fixtures detected)
- False-positive rate: **0.00%** (0/7 known-safe fixtures flagged)

Numbers are transcribed from the test harness's own output, not narrated.

## What it doesn't do (yet)

- Trigger-aware permissions analysis (e.g. flagging `contents: write`
  specifically when combined with `pull_request_target`) — only the
  unambiguous `write-all` case is checked today.
- Traversal into reusable/composite workflows (`workflow_call`).
- Pinning checks for `docker://` actions.

## License

MIT. Author: HiroCheck.
