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.
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