Portcullis: CI/CD Pipeline Security Scanner

Statically analyzes GitHub Actions, GitLab CI, and Jenkins pipelines through one normalized model — and scores every finding by whether attacker-controlled input can actually reach it.

CI/CD pipelines hold repository secrets, carry write-capable tokens, and routinely execute code from untrusted pull requests. One misconfiguration — a pull_request_target job that checks out PR code, a PR title interpolated into a shell line, a third-party action on a mutable tag — turns that combination into full repository compromise. Portcullis detects those misconfigurations statically and, unlike a linter, scores them by context: the same flaw is one severity level more dangerous wherever attacker-controlled input can reach it. Packaged as a PEP 561-typed, src-layout PyPI distribution with a MkDocs docs site and CodeQL CI — its own SHA-pinned, least-privilege release/CI workflows pass Portcullis's own dogfood scan at 100/100.

Portcullis normalizes GitHub Actions, GitLab CI, and Jenkins into a single Pipeline model, then runs one set of rules (R1–R6) across all three platforms. Its distinguishing idea is trigger-context escalation: a `curl | bash` step on `push` is P2 hygiene, but the identical step reachable from `pull_request_target` escalates toward P0 because an attacker can now control what runs. Confidence is a first-class property — LOW-confidence findings hard-cap at P3 (surface, don't block), hygiene findings can't corroborate into P0, and Jenkins/Groovy findings are honestly capped at MEDIUM. Output is deterministic JSON, self-contained HTML, SARIF 2.1.0, and PR-comment Markdown, each with a documented 0–100 score.

Key metrics

  • Platforms (one normalized model): GitHub · GitLab · Jenkins
  • Real pipeline files evaluated: 66 across 10 OSS projects
  • Scan time (66 files): ~1.4s, zero crashes
  • Secret-value leaks across 4 output formats: 0
  • Detection rule families: R1–R6
  • Portcullis' own pipeline self-scan: 100/100

Engineering rigor

  • 175 tests
  • 100/100 own self-scan
  • 3 platforms
  • 1 runtime dep
  • deterministic byte-identical
  • 95% coverage

Tech stack

Python 3.11+, ruamel.yaml (line-preserving parser), Taint analysis, SARIF 2.1.0, Self-contained HTML, PR-comment Markdown, GitHub Actions / GitLab CI / Jenkins

Roadmap

  • Research manuscript in preparation: “Portcullis: Trigger-Context Reachability for Multi-Platform CI/CD Pipeline Security Analysis” — targeting an arXiv preprint / software-security workshop.
  • Deeper reusable-workflow following across repositories.
  • More platforms behind the same normalized model.
  • Expanded taint sources for emerging trigger types.

View on GitHub

← All projects