Skip to content

Commit 09a4bbb

Browse files
committed
Add actions-audit.py script for auditing Apache repo security tooling
Adds a new utility script that audits apache/ GitHub repositories for baseline Actions security configurations (dependabot, CodeQL, zizmor, allowlist-check) and can create PRs to add missing ones. Key features: - Uses GraphQL to batch-fetch workflow file contents per repo - Dry-run mode shows detailed preview of what PRs would contain - Prints zizmor findings so users can see issues before creating PRs - Skips secrets-outside-env zizmor rule (too noisy for initial rollout) - Includes zizmor error output in PR body when workflows are commented out
1 parent 4883581 commit 09a4bbb

2 files changed

Lines changed: 1373 additions & 0 deletions

File tree

README.md

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ This repository hosts GitHub Actions developed by the ASF community and approved
3232
- [Manual Version Addition](#manual-addition-of-specific-versions)
3333
- [Automatic Expiration of Old Versions](#automatic-expiration-of-old-versions)
3434
- [Removing a Version](#removing-a-version-manually)
35+
- [Auditing Repositories for Actions Security Tooling](#auditing-repositories-for-actions-security-tooling)
3536

3637
## Submitting an Action
3738

@@ -359,3 +360,107 @@ Routine removal is already automated: set `expires_at` on the entry and the dail
359360
The infrastructure team will prioritize these removal requests and may take additional steps to notify affected projects if necessary.
360361

361362
For 'regular' removals (not security responses), you can use `./utils/action-usage.sh someorg/theaction` to see if/how an action is still used anywhere in the ASF, and create a 'regular' PR removing it from `actions.yml` (or adding an expiration date) when it is no longer used.
363+
364+
## Auditing Repositories for Actions Security Tooling
365+
366+
Recent security breaches have shown that GitHub Actions can fail silently, leaving repositories vulnerable without any visible indication. The `actions-audit.py` script helps ensure that all Apache repositories using GitHub Actions have a baseline set of security tooling in place.
367+
368+
### Why This Matters
369+
370+
GitHub Actions workflows can introduce security risks in several ways:
371+
- **Unpinned or unreviewed action versions** may contain malicious code or vulnerabilities
372+
- **Missing static analysis** means workflow misconfigurations (secret exposure, injection vulnerabilities) go undetected
373+
- **No dependabot** means action versions never get updated, accumulating known vulnerabilities over time
374+
375+
The audit script checks each repository for four security configurations and can automatically open PRs to add any that are missing:
376+
377+
| Check | What it does |
378+
|-------|-------------|
379+
| **Dependabot** | Keeps GitHub Actions dependencies up to date with a 4-day cooldown to avoid overwhelming reviewers |
380+
| **CodeQL** | Runs static analysis on workflow files to detect security issues in Actions syntax |
381+
| **Zizmor** | Specialized scanner for GitHub Actions anti-patterns: credential leaks, injection vulnerabilities, excessive permissions |
382+
| **ASF Allowlist Check** | Ensures every action used is on the ASF Infrastructure approved allowlist |
383+
384+
### Prerequisites
385+
386+
- **Python 3.11+** and [**uv**](https://docs.astral.sh/uv/) **>= 0.9.17** (dependencies are managed inline via PEP 723). Make sure your uv is up to date — depending on how you installed it, run `uv self update`, `pip install --upgrade uv`, `pipx upgrade uv`, or `brew upgrade uv`
387+
- **`gh`** (GitHub CLI, authenticated via `gh auth login`) — or provide a `--github-token` with `repo` scope and use `--no-gh`
388+
- **`zizmor`** ([install instructions](https://docs.zizmor.dev/installation/)) — required for PR creation mode; not needed for `--dry-run`. If missing, zizmor pre-checks are skipped with a warning
389+
390+
### Usage
391+
392+
Always start with `--dry-run` to see what the script would do without making any changes:
393+
394+
```bash
395+
# Audit all repos for a specific PMC (prefix before first '-' in repo name)
396+
uv run utils/actions-audit.py --dry-run --pmc spark --max-num 10
397+
398+
# Audit multiple PMCs
399+
uv run utils/actions-audit.py --dry-run --pmc kafka --pmc flink
400+
401+
# Audit the first 50 repos (no PMC filter)
402+
uv run utils/actions-audit.py --dry-run --max-num 50
403+
404+
# Increase GraphQL page size for fewer API round-trips
405+
uv run utils/actions-audit.py --dry-run --max-num 200 --batch-size 100
406+
```
407+
408+
When satisfied with the dry-run output, remove `--dry-run` to create PRs:
409+
410+
```bash
411+
# Create PRs for spark repos missing security tooling
412+
uv run utils/actions-audit.py --pmc spark --max-num 10
413+
```
414+
415+
#### Options
416+
417+
| Flag | Description |
418+
|------|-------------|
419+
| `--pmc PMC` | Filter by PMC prefix (repeatable). The prefix is the text before the first `-` in the repo name, e.g. `spark` matches `spark`, `spark-connect-go`, `spark-docker`. |
420+
| `--dry-run` | Report findings without creating PRs or branches. |
421+
| `--max-num N` | Maximum number of repositories to check (0 = unlimited, default). |
422+
| `--batch-size N` | Number of repos to fetch per GraphQL request (default: 50, max: 100). |
423+
| `--github-token TOKEN` | GitHub token. Defaults to `GH_TOKEN` or `GITHUB_TOKEN` environment variable. |
424+
| `--no-gh` | Use Python `requests` instead of the `gh` CLI for all API calls. Requires `--github-token` or a token env var. |
425+
426+
#### How PMC Filtering Works
427+
428+
The `--pmc` flag matches repos by prefix: the text before the first hyphen in the repository name. For example, `--pmc spark` matches `apache/spark`, `apache/spark-connect-go`, and `apache/spark-docker`. If the repo name has no hyphen, the full name is used as the prefix.
429+
430+
The script downloads the list of known PMCs from `whimsy.apache.org` on first run and caches it locally (`~/.cache/asf-actions-audit/pmc-list.json`) for 24 hours. If a `--pmc` value doesn't match any known PMC, a warning is printed but it is still used as a prefix filter.
431+
432+
#### What the PRs Contain
433+
434+
For each repository that is missing one or more checks, the script creates a single PR on a branch named `asf-actions-security-audit` containing only the missing files:
435+
436+
- `.github/dependabot.yml` — created or updated to include the `github-actions` ecosystem with a 4-day cooldown
437+
- `.github/workflows/codeql-analysis.yml` — CodeQL scanning for the `actions` language
438+
- `.github/workflows/zizmor.yml` — Zizmor scanning with SARIF upload
439+
- `.github/workflows/allowlist-check.yml` — ASF allowlist verification on workflow changes
440+
441+
#### Zizmor Pre-Check
442+
443+
Before creating a PR, the script runs `zizmor` against the repository's existing workflow files. If zizmor finds errors, the **CodeQL and Zizmor workflow files are added but commented out**, with instructions explaining:
444+
- That zizmor found existing issues in the workflows
445+
- How to auto-fix common issues (`zizmor --fix .github/workflows/`)
446+
- That the PMC should uncomment the workflows and fix remaining issues in a follow-up PR
447+
448+
This avoids creating PRs that would immediately fail CI due to pre-existing problems.
449+
450+
#### Interactive Confirmation
451+
452+
When not in `--dry-run` mode, the script prompts for confirmation before creating each PR:
453+
454+
```
455+
Create PR for apache/spark?
456+
Will add: dependabot, codeql, zizmor, allowlist-check
457+
Proceed? [yes/no/quit] (yes):
458+
```
459+
460+
- **yes** (default) — create the PR
461+
- **no** — skip this repository and continue to the next
462+
- **quit** — stop processing entirely and print the summary
463+
464+
#### Idempotency
465+
466+
The script is safe to re-run. Before creating a PR for a repository, it checks whether a PR with the branch name `asf-actions-security-audit` already exists — open, closed, or merged — and skips the repo if so.

0 commit comments

Comments
 (0)