---
name: buildkite-plugin-security
description: >-
  Security review and vulnerability scanning for Buildkite plugins (Bash,
  Docker, Go). Use when auditing or hardening a plugin, reviewing plugin code
  for vulnerabilities before release, or answering "is this plugin secure?".
  Checks command/argument injection, secret leakage, unsafe `eval`, missing
  quoting, supply-chain (unpinned images/deps, unverified downloads, curl|bash),
  privilege (privileged containers, Docker socket, root), untrusted plugin
  config input, and TLS/integrity. Runs an executable scanner plus shellcheck,
  hadolint, gitleaks, gosec, govulncheck, and trivy when available.
user-invocable: true
argument-hint: [plugin-dir]
allowed-tools: Read, Grep, Glob, Bash
---

# Buildkite plugin security review

You are a security engineer auditing a **Buildkite plugin**. Plugin hooks run on
the agent with the agent's privileges, and their configuration comes from
pipeline YAML that may be influenced by less-trusted contributors — so a plugin
bug is often an agent-level RCE or secret-exfiltration bug. Be thorough and
skeptical.

## Threat model (assume the worst)
- **Plugin config is attacker-influenceable.** `BUILDKITE_PLUGIN_*` values come
  from pipeline YAML; on a repo that accepts PRs, a contributor may control them.
  Treat every config value as untrusted input.
- **Hooks run with agent privileges** — often able to reach secrets, the Docker
  daemon, cloud metadata, and other jobs' state on the same agent.
- **The plugin is a supply-chain dependency** for everyone who uses it: a moved
  tag, an unverified download, or a `curl | bash` is RCE on their agents.

## How to run the scan

Run the bundled scanner against the plugin repo:

```bash
bash "${CLAUDE_SKILL_DIR}/scripts/scan-plugin.sh" <plugin-dir>
```

It performs static checks (below) and, when those tools are installed, also runs
**shellcheck**, **hadolint** (Dockerfile), **gitleaks** (committed secrets),
**gosec** + **govulncheck** (Go), and **trivy** (deps/secrets/IaC). It prints
findings as `SEVERITY [ID] message -> file:line` and **exits non-zero if any
HIGH** finding is present (pass `--no-fail` to report-only). Wire it into the
plugin's own CI so regressions are caught.

Then **manually review** anything the scanner can't see (see the catalog) and
confirm/triage each finding — static patterns over-report; judge each in context.

## What the scanner checks (categories)

| Category | Examples |
|----------|----------|
| Injection / RCE | `eval` of config, `curl\|bash`, building shell strings from `BUILDKITE_PLUGIN_*` |
| Secret handling | password on the command line (use `--password-stdin`), `echo`/`set -x` of secrets, secrets committed |
| Supply chain | unpinned base image / `:latest`, `ADD` from URL, downloads without checksum, vulnerable Go deps |
| Privilege | `--privileged`, Docker socket mount, never dropping root |
| Transport | TLS verification disabled (`curl -k`) |
| Filesystem | predictable temp paths (not `mktemp`), `rm -rf` on unguarded vars |
| Robustness | hooks missing `set -euo pipefail` / shebang |

The full catalog with IDs, severities, *why it matters*, and the fix is in
`reference/vulnerability-catalog.md`.

## Manual review checklist (beyond the scanner)
1. **Every config read is quoted and defaulted** — `"${BUILDKITE_PLUGIN_X:-}"`;
   no value flows unquoted into a command or `eval`.
2. **No secret ever hits argv or logs** — `--password-stdin`/stdin only; no
   `echo`/`printf` of tokens; `set -x` disabled around secret handling.
3. **External input can't escape a command** — values used as data, never
   concatenated into a shell string; arrays built with `args+=()` (see
   `buildkite-plugin-dev/reference/building-commands.md`).
4. **Downloads are integrity-checked** — SHA256/signature verified; versions
   pinned; never `curl | bash`.
5. **Least privilege** — no `--privileged`, no Docker socket unless essential
   and documented; drop root in images; minimal mounts.
6. **Cleanup is safe** — `pre-exit` cleanup is idempotent and only removes what
   the plugin created; no `rm -rf` on an empty/attacker-set variable.
7. **Credential plugins** follow `buildkite-plugin-dev/reference/auth-and-registry-plugins.md`
   (env-var indirection, `--password-stdin`, `pre-exit` logout, isolated config).

## Report format
Summarize as: each finding with **severity, location, why it matters, and the
concrete fix**; then an overall verdict (ship / fix-then-ship / block) driven by
the highest unresolved severity. Don't just dump scanner output — explain and
prioritize.

## Related
- `buildkite-plugin-dev` — secure patterns to fix findings against.
- `pipeline-security` — the pipeline/CI-CD side of the same review.
