---
name: audit-cicd-workflows
description: This skill should be used when the user asks to "audit my CI/CD workflows", "check GitHub Actions for hardcoded secrets", "verify action versions are pinned", "review workflow security", "check for secrets in my workflows", or wants to audit `.github/workflows/` files for security issues including SHA pinning, hardcoded credentials, environment protection rules, Dependabot config, GHCR usage, CODEOWNERS, and build provenance.
argument-hint: "[path/to/.github/workflows/]"
---

# Audit CI/CD Workflows

Scan GitHub Actions workflow files and apply the security checklist at
`${CLAUDE_SKILL_DIR}/references/checklist.md`.

**Argument**: `$ARGUMENTS`

---

## Step 1 — Locate the workflows directory

If `$ARGUMENTS` is provided, use it as the workflows directory path.

Otherwise, auto-detect: look for `.github/workflows/` starting from the current
working directory. If not found, report:

> No `.github/workflows/` directory found. Skipping CI/CD workflow audit.

Then stop.

---

## Step 2 — Read all workflow files

Using the Glob tool, find all `*.yml` and `*.yaml` files under the workflows
directory. Read each file with the Read tool.

If no files are found, report:

> `.github/workflows/` directory is empty — no workflow files to audit.

Then stop.

Also check for these adjacent files (look from the repo root, one level up from
`.github/`):
- `.github/dependabot.yml` or `.github/dependabot.yaml`
- `.github/CODEOWNERS`

Note which files were found and which were absent.

---

## Step 3 — Apply the checklist

Read `${CLAUDE_SKILL_DIR}/references/checklist.md` in full. Apply each check to
the collected workflow files. For each check, record:

- **File** and **line number** for each finding
- **Severity** (from the checklist Priority field)
- **What was found** (the concrete pattern)
- **Remediation** (from the checklist — condensed to the key fix)

### Check-specific instructions

**SHA pinning** — scan every `uses:` line across all workflow files. A line is
a FINDING if it references a tag (e.g. `@v4`, `@main`, `@master`, `@latest`) or
a branch name rather than a 40-character hex SHA digest. Record the action name
and line number. A line is PASSING if it uses `@<40-hex-chars>`.

Severity guidance:
- First-party GitHub actions (`actions/*`, `github/*`): flag as **Recommended** — these are maintained by GitHub and have additional trust, but pinning is still best practice.
- Third-party actions: flag as **Important** — mutable tags from external publishers are a supply-chain risk.

**Hardcoded secrets** — scan all `env:` blocks and inline `run:` steps. Flag
any literal value (not `${{ secrets.* }}` or `${{ vars.* }}`) that matches
these specific high-signal patterns:
- Known token prefixes: `sk_`, `pk_`, `AKIA`, `ghp_`, `ghs_`, `glpat-`, `xoxb-`, `xoxp-`
- Private key PEM headers: `-----BEGIN`
- Connection strings with credentials embedded: `postgresql://user:pass@`, `https://user:token@`
- IP addresses hardcoded as hostnames in `ssh` or `docker` deploy commands

Do NOT flag: Docker image SHA digests, commit SHAs, build IDs, UUIDs, or other
long alphanumeric strings that are not credentials. The goal is low noise, high
signal.

NEVER include the actual value in output. Report the finding as:
> `[file]:[line]` — `env.VAR_NAME` contains a hardcoded value matching pattern
> `[pattern type]`. Replace with `${{ secrets.VAR_NAME }}`.

**Environment protection** — look for jobs that contain deploy steps (ssh
commands, `docker compose`, push to registry). Check whether those jobs include
an `environment:` key. Flag any deploy job that lacks `environment: production`
(or equivalent).

**Dependabot** — check for `.github/dependabot.yml`. If absent: FINDING
(Recommended). If present, check whether it includes a `github-actions`
ecosystem entry. If missing that entry: FINDING (Recommended).

**GHCR** — scan for `docker/login-action` usage. If it logs in to `docker.io`
or uses `DOCKERHUB_` secrets without a `registry: ghcr.io` entry: FINDING
(Recommended).

**Build provenance** — check for `actions/attest-build-provenance` or SLSA
generator usage. If `docker/build-push-action` is present but no attestation
step follows it: FINDING (Nice-to-have).

**CODEOWNERS** — check for `.github/CODEOWNERS`. If absent: FINDING
(Recommended). If present, check that `.github/workflows/` is covered by an
owner rule. If not covered: FINDING (Recommended).

**Deploy user / ForceCommand** — this check cannot be verified from workflow
files alone. Note it as:
> Check "Deploy user has minimal permissions with ForceCommand" requires SSH
> server access. Use `audit-server-config` to verify this check.

---

## Step 4 — Output findings

Organise output as follows.

### Header

```
## CI/CD Workflow Security Audit
Workflows directory: <path>
Files scanned: <list of filenames>
Adjacent files: dependabot.yml [found/not found], CODEOWNERS [found/not found]
```

### Findings by severity

Group findings under these headings (omit a heading if there are no findings in
that tier):

```
### 🔴 Critical
### 🟠 Important
### 🟡 Recommended
### ⚪ Note
```

Each finding entry:

```
**[CHECK NAME]** — `<file>:<line>`
<One-sentence description of what was found and why it is a risk.>
Fix: <concrete remediation — the key change required, with a short code snippet if helpful.>
```

### Summary table

After all findings, output a summary table:

| Check | Status | Findings |
|---|---|---|
| SHA pinning | PASS / FAIL / SKIP | N actions not pinned |
| Environment protection | PASS / FAIL / SKIP | N deploy jobs without environment |
| Hardcoded secrets | PASS / FAIL / SKIP | N potential secrets found |
| Dependabot (GitHub Actions) | PASS / FAIL / SKIP | — |
| GHCR usage | PASS / FAIL / SKIP | — |
| Build provenance | PASS / FAIL / SKIP | — |
| CODEOWNERS | PASS / FAIL / SKIP | — |
| Deploy user / ForceCommand | SKIP | Requires SSH access |

### Passed checks

List all checks that passed (no findings) as a brief bullet list.

---

## Notes

- This skill is read-only. It does not modify any files.
- Never include actual secret values in output — refer to them by type and
  location only.
- If a workflow file cannot be read, note it as a skipped file and continue.
