Skip to content

Commit 68ba2f3

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 bdffe5b commit 68ba2f3

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

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

366367
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.
368+
369+
## Auditing Repositories for Actions Security Tooling
370+
371+
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.
372+
373+
### Why This Matters
374+
375+
GitHub Actions workflows can introduce security risks in several ways:
376+
- **Unpinned or unreviewed action versions** may contain malicious code or vulnerabilities
377+
- **Missing static analysis** means workflow misconfigurations (secret exposure, injection vulnerabilities) go undetected
378+
- **No dependabot** means action versions never get updated, accumulating known vulnerabilities over time
379+
380+
The audit script checks each repository for four security configurations and can automatically open PRs to add any that are missing:
381+
382+
| Check | What it does |
383+
|-------|-------------|
384+
| **Dependabot** | Keeps GitHub Actions dependencies up to date with a 4-day cooldown to avoid overwhelming reviewers |
385+
| **CodeQL** | Runs static analysis on workflow files to detect security issues in Actions syntax |
386+
| **Zizmor** | Specialized scanner for GitHub Actions anti-patterns: credential leaks, injection vulnerabilities, excessive permissions |
387+
| **ASF Allowlist Check** | Ensures every action used is on the ASF Infrastructure approved allowlist |
388+
389+
### Prerequisites
390+
391+
- **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`
392+
- **`gh`** (GitHub CLI, authenticated via `gh auth login`) — or provide a `--github-token` with `repo` scope and use `--no-gh`
393+
- **`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
394+
395+
### Usage
396+
397+
Always start with `--dry-run` to see what the script would do without making any changes:
398+
399+
```bash
400+
# Audit all repos for a specific PMC (prefix before first '-' in repo name)
401+
uv run utils/actions-audit.py --dry-run --pmc spark --max-num 10
402+
403+
# Audit multiple PMCs
404+
uv run utils/actions-audit.py --dry-run --pmc kafka --pmc flink
405+
406+
# Audit the first 50 repos (no PMC filter)
407+
uv run utils/actions-audit.py --dry-run --max-num 50
408+
409+
# Increase GraphQL page size for fewer API round-trips
410+
uv run utils/actions-audit.py --dry-run --max-num 200 --batch-size 100
411+
```
412+
413+
When satisfied with the dry-run output, remove `--dry-run` to create PRs:
414+
415+
```bash
416+
# Create PRs for spark repos missing security tooling
417+
uv run utils/actions-audit.py --pmc spark --max-num 10
418+
```
419+
420+
#### Options
421+
422+
| Flag | Description |
423+
|------|-------------|
424+
| `--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`. |
425+
| `--dry-run` | Report findings without creating PRs or branches. |
426+
| `--max-num N` | Maximum number of repositories to check (0 = unlimited, default). |
427+
| `--batch-size N` | Number of repos to fetch per GraphQL request (default: 50, max: 100). |
428+
| `--github-token TOKEN` | GitHub token. Defaults to `GH_TOKEN` or `GITHUB_TOKEN` environment variable. |
429+
| `--no-gh` | Use Python `requests` instead of the `gh` CLI for all API calls. Requires `--github-token` or a token env var. |
430+
431+
#### How PMC Filtering Works
432+
433+
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.
434+
435+
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.
436+
437+
#### What the PRs Contain
438+
439+
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:
440+
441+
- `.github/dependabot.yml` — created or updated to include the `github-actions` ecosystem with a 4-day cooldown
442+
- `.github/workflows/codeql-analysis.yml` — CodeQL scanning for the `actions` language
443+
- `.github/workflows/zizmor.yml` — Zizmor scanning with SARIF upload
444+
- `.github/workflows/allowlist-check.yml` — ASF allowlist verification on workflow changes
445+
446+
#### Zizmor Pre-Check
447+
448+
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:
449+
- That zizmor found existing issues in the workflows
450+
- How to auto-fix common issues (`zizmor --fix .github/workflows/`)
451+
- That the PMC should uncomment the workflows and fix remaining issues in a follow-up PR
452+
453+
This avoids creating PRs that would immediately fail CI due to pre-existing problems.
454+
455+
#### Interactive Confirmation
456+
457+
When not in `--dry-run` mode, the script prompts for confirmation before creating each PR:
458+
459+
```
460+
Create PR for apache/spark?
461+
Will add: dependabot, codeql, zizmor, allowlist-check
462+
Proceed? [yes/no/quit] (yes):
463+
```
464+
465+
- **yes** (default) — create the PR
466+
- **no** — skip this repository and continue to the next
467+
- **quit** — stop processing entirely and print the summary
468+
469+
#### Idempotency
470+
471+
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)