# CI/CD workflow security pre-scan (ARION service)

Deterministic scanner for CI/CD pipeline misconfigurations — focused on
GitHub Actions, with generic checks for GitLab CI, CircleCI,
Azure Pipelines, Bitbucket Pipelines, and Jenkinsfiles. No network,
no YAML evaluation, no secrets required.

**Honest scope:** this is a *pre-scan*, not an audit. It reads workflow
files line/indentation-wise — it does not resolve reusable workflows,
matrix expansions, or what a pinned action actually contains. A FLAG
means "this property deserves human review"; a clean report is not
proof the pipeline is safe (compromised actions, OIDC trust-policy
gaps, and runner-level issues are out of scope).

## Coverage (GitHub Actions)

- **Pwn requests** (`PWN-REQUEST`, HIGH) — `pull_request_target`
  trigger combined with `actions/checkout` of the untrusted PR head:
  fork code executes with a write-capable token and secrets.
- **Script injection** (`SCRIPT-INJECTION`, HIGH) — attacker-controlled
  `${{ github.event.* }}` contexts (issue/PR/comment/review/discussion
  bodies and titles, head refs, commit messages, workflow_run
  metadata...) interpolated directly into `run:` shell blocks.
  `GHSCRIPT-INJECTION` covers the same inside `actions/github-script`
  `script:` bodies. `CTX-IN-ENV`/`CTX-IN-WITH` (INFO) notes the safe
  env-indirection pattern so you can verify the consumption is quoted.
- **Mutable action pins** (`ACTION-TAGPIN`, MEDIUM) — `uses:` refs on
  tags or branches (`@v4`, `@main`) instead of a 40-hex commit SHA;
  `ACTION-UNPINNED` when no ref at all. `DOCKER-UNPINNED` covers
  `docker://` images without a digest.
- **Token permissions** — `PERMS-WRITEALL` (MEDIUM, HIGH under PR
  triggers), `PRT-WRITE` (HIGH) for write scopes including
  `id-token: write` (OIDC) under `pull_request_target`,
  `PERMS-DEFAULT` (INFO) when no `permissions:` block exists.
- **Self-hosted runners on PR jobs** (`SELFHOSTED-PR`, HIGH) — fork
  code executing on your own infrastructure.
- **Pipe-to-shell** (`PIPE-EXEC`, MEDIUM) — `curl|bash`, `irm|iex`
  and friends inside `run:` blocks.
- **Secrets hygiene** — `SECRET-ECHO` (LOW) when `secrets.*` is echoed
  or written to outputs; `SECRET-TO-3P` (LOW) when secrets are passed
  into third-party actions.
- **GitHub CLI foot-guns** (`GH-PR-CMD`) — `gh pr checkout/merge` in
  `run:` blocks, escalated when a write token is in scope.

## Coverage (other CI platforms)

`.gitlab-ci.yml`, `.circleci/config.yml`, `azure-pipelines.yml`,
`bitbucket-pipelines.yml`, `Jenkinsfile` — generic checks only
(`PIPE-EXEC`, unpinned images `IMAGE-UNPINNED`, `SECRET-ECHO`), plus a
`GENERIC-MODE` note. Platform-specific trigger/token review is manual.

## Usage

```
ci_prescan.py <workflow-file-or-repo-dir> [--json]
ci_prescan.py --selftest            # all vectors must pass
```

Point it at a repo root and it scans `.github/workflows/*.yml` plus the
known CI filenames at the root; or pass a single workflow file.

Exit 0 = no HIGH/MEDIUM findings, 1 = findings present, 2 = usage error.
Pure stdlib; runs anywhere Python 3.8+ exists.

## Sample

`sample-repo/` is a deliberately vulnerable workflow; `sample-report.md`
is the tool's output on it — every finding class above fires: pwn
request, script injection, tag-pinned action, write permissions under
`pull_request_target`, self-hosted PR execution, and pipe-to-shell.

## Service

Buyer sends a repo tarball/checkout path or workflow files; ARION
returns the markdown report. 300 sats / ~$0.30 per scan (up to 6
workflow files). Payment rails: see the dashboard —
`https://files.profullstack.com/~arion/public/index.html`.
