Skip to main content
Pipefort runs deterministic checks per scan across several surfaces:
  • 34 GitHub Actions workflow checks parse .github/workflows/*.yml (one, forbidden-uses, is driven by your .pipefort.yml policy).
  • 7 online supply-chain audits verify the integrity of pinned actions (known-vulnerable, impostor-commit, ref/version-mismatch, typosquat, archived-action, stale-action-ref, ref-confusion) — run automatically when a GitHub token is available, forced with --audit-pins, disabled with --offline.
  • 17 GitHub repository-configuration checks call the GitHub API for branch protection, default workflow permissions, secret scanning, and Dependabot.
  • 11 GitLab CI workflow checks parse .gitlab-ci.yml and .gitlab-ci/**/*.yml.
  • 5 GitLab project-configuration checks call the GitLab API for protected branches, merge policy, public-pipeline visibility, and approvals.
Workflow checks cover all ten OWASP CI/CD Top 10 categories (CICD-SEC-1 through CICD-SEC-10) on both platforms.

GitLab CI workflow checks

The GitLab rule IDs are parallel to the GitHub ones — same OWASP category, different IDs (e.g. cicd-sec-1-gl-mr-target for the merge-request-target analog of cicd-sec-1-ppe-checkout). Findings tagged -gl- only fire on .gitlab-ci.yml / .gitlab-ci/*.yml. The two structural rules best-prac-1-pipe-to-shell and cicd-sec-9-download-without-checksum share their ID across both platforms.
CategoryGitLab rule IDSeverityAuto-fix
CICD-SEC-1cicd-sec-1-gl-mr-targetHIGH
CICD-SEC-2cicd-sec-2-gl-pat-secretMEDIUM
CICD-SEC-3cicd-sec-3-gl-unpinned-includeMEDIUM
CICD-SEC-4cicd-sec-4-gl-shell-injectionHIGH
CICD-SEC-6cicd-sec-6-gl-hardcoded-secretsHIGH
CICD-SEC-7cicd-sec-7-gl-debug-traceHIGH
CICD-SEC-8cicd-sec-8-gl-trigger-unfilteredMEDIUM
CICD-SEC-10cicd-sec-10-gl-allow-failureLOW
BEST-PRAC-2best-prac-2-gl-missing-timeoutLOW
BEST-PRAC-3best-prac-3-gl-self-hosted-tagsLOW
BEST-PRAC-4best-prac-4-gl-missing-resource-groupLOW

GitLab project-settings checks

These read the GitLab project configuration over the API (CLI: --git at a GitLab host with --gitlab-token; the web app uses its connected GitLab token) and surface under the Repository-configuration group.
CategoryGitLab rule IDSeverityAuto-fix
CICD-SEC-1cicd-sec-1-gl-bp-missingHIGH
CICD-SEC-1cicd-sec-1-gl-bp-force-pushHIGH
CICD-SEC-1cicd-sec-1-gl-merge-without-pipelineMEDIUM
CICD-SEC-1cicd-sec-1-gl-no-approvalsMEDIUM
CICD-SEC-4cicd-sec-4-gl-public-pipelinesMEDIUM
The two auto-fixable settings (merge-without-pipeline, public-pipelines) are remediated by the CLI’s --fix-settings-gl flag.

v1 limitations

  • include: graph traversal is not performed — the scanner inspects only the literal .gitlab-ci.yml and .gitlab-ci/*.yml contents, not files referenced from another project or URL.
  • Approval-rule auditing (cicd-sec-1-gl-no-approvals) needs the GitLab Premium approvals API; on Free tier it is skipped rather than firing.
Rules carry framework tags so they can be filtered by the standard they serve: OWASP Top 10 CI/CD Security Risks and the SLSA v1.2 specification (Build and Source tracks). A single rule can belong to multiple frameworks.

Confidence and personas

Every finding carries a confidence (HIGH/MEDIUM/LOW) alongside its severity: severity says how bad the issue would be, confidence says how sure the check is that the finding is real. Deterministic checks (a missing permissions: block either exists or doesn’t) are HIGH; heuristic ones (typosquat edit distance, secret-name patterns) are MEDIUM. Filter with the CLI’s --min-confidence flag; the web app badges medium/low-confidence findings and offers a “High confidence only” toggle. Rules are also tiered into personas for noise control: regular (the default — high-signal security checks), pedantic (adds hygiene nits like missing timeouts and job-level continue-on-error), and auditor (adds everything, e.g. self-hosted-runner usage). Select with the CLI’s --persona flag. The web app runs all tiers and relies on rule settings for per-rule control.

Workflow file checks

CategoryTitleSeverityAuto-fix
CICD-SEC-1Dangerous checkout in pull_request_target / workflow_runHIGH
CICD-SEC-1workflow_run downloads artifacts from the triggering runHIGH
CICD-SEC-1Checkout persists credentials under a privileged triggerMEDIUM
CICD-SEC-1Security decision based on a spoofable actor checkMEDIUM
CICD-SEC-1Job/step condition is always trueHIGH
CICD-SEC-1Spoofable contains() membership checkMEDIUM
CICD-SEC-2Long-lived personal access tokenMEDIUM
CICD-SEC-2Package published with a long-lived token instead of OIDCMEDIUM
CICD-SEC-3Unpinned third-party actionMEDIUM
CICD-SEC-3Unpinned container imageMEDIUM
CICD-SEC-4Poisoned Pipeline Execution (shell injection, incl. laundered ${{ env.X }})HIGH
CICD-SEC-4Untrusted input written to GITHUB_ENV/GITHUB_PATHHIGH
CICD-SEC-4Reusable workflow called with secrets: inherit under a privileged triggerHIGH
CICD-SEC-4Obfuscated expression or run scriptMEDIUM
CICD-SEC-4Caching enabled in a publishing workflowMEDIUM
CICD-SEC-5Missing permissions specificationMEDIUM
CICD-SEC-5Action not permitted by forbidden-uses policy (config-driven)HIGH
CICD-SEC-6Hardcoded credentialsHIGH
CICD-SEC-6Secret printed to logs or written to step outputHIGH
CICD-SEC-6Secrets over-provisioned to the whole workflow (toJSON(secrets), workflow env)HIGH
CICD-SEC-7Actions debug logging enabled in workflowHIGH
CICD-SEC-8repository_dispatch trigger without types: allowlistMEDIUM
CICD-SEC-9Downloaded artifact has no integrity checkMEDIUM
CICD-SEC-10Job-level continue-on-error suppresses failure visibilityLOW
BEST-PRAC-1Command piped directly to shell (pipe, process-sub, or PowerShell iex)HIGH
BEST-PRAC-2Job timeout not configuredLOW
BEST-PRAC-3Self-hosted runner usageLOW
BEST-PRAC-4Deploy/release workflow has no concurrency guardLOW
SLSA-BUILD-L2Build provenance is not generatedHIGH
SLSA-BUILD-L2Provenance/signing step missing id-token: writeMEDIUM
SLSA-BUILD-L2Permissions block overly broadHIGHpartial
SLSA-BUILD-L2Workflow consumes artifacts without verifying provenanceINFO
SLSA-BUILD-L3Provenance generated in-job (not isolated)MEDIUM
SLSA-BUILD-L3Cache key in pull_request_target from PR-controlled inputHIGH

Online pinned-action audits

These audit the integrity of pinned actions and need network access. They run automatically whenever a GitHub token is supplied — --github-token, $GITHUB_TOKEN, or $GH_TOKEN on the CLI, and the web app’s scan always runs them with its installation token. Without a token the scan stays offline unless you force the pass with --audit-pins (subject to GitHub’s low anonymous rate limit); --offline disables it unconditionally. Typosquat matching is itself offline but ships in the same pass.
CategoryTitleSeverityAuto-fix
CICD-SEC-3Known-vulnerable action version (GHSA)HIGH
CICD-SEC-3Impostor commit pinHIGH
CICD-SEC-3Pinned SHA does not match its version commentMEDIUM
CICD-SEC-3Possible typosquatted actionMEDIUM
CICD-SEC-3Action’s upstream repository is archivedHIGH
CICD-SEC-3Pinned SHA is not any released tagMEDIUM
CICD-SEC-3Action ref exists as both a branch and a tagHIGH

Repository configuration checks

These read GitHub-side settings and surface under a “Repository configuration” group in the UI (CLI: <repository settings> file label). They need the expanded GitHub App permissions described in GitHub App permissions; the CLI needs --github-token (or $GITHUB_TOKEN, or gh auth token).

Branch protection (CICD-SEC-1)

RuleTitleSeverityAuto-fix
BP-MISSINGDefault branch has no branch protection ruleHIGH
BP-FORCE-PUSHDefault branch allows force pushesHIGH
BP-DELETIONDefault branch can be deletedHIGH
BP-NO-REVIEWDefault branch does not require pull request reviewsHIGH
BP-FEW-REVIEWERSDefault branch requires fewer than 2 approving reviewsMEDIUM
BP-STALE-REVIEWSDefault branch does not dismiss stale reviews on new commitsMEDIUM
BP-NO-STATUS-CHECKSDefault branch does not require status checks to passMEDIUM
BP-ADMIN-BYPASSAdmins can bypass branch protectionHIGH
BP-NO-CODEOWNERS-REVIEWCODEOWNERS exists but their review is not requiredLOW
BP-NO-SIGNED-COMMITSDefault branch does not require signed commitsLOW

Actions runtime (CICD-SEC-4, CICD-SEC-5)

RuleTitleSeverityAuto-fix
WPERM-WRITEDefault GITHUB_TOKEN permissions are read-writeHIGH
WPERM-PR-APPROVEGitHub Actions can approve pull requestsHIGH
ACTIONS-ALL-ALLOWEDAll GitHub Actions and reusable workflows are allowedMEDIUM

Dependency hygiene (CICD-SEC-3)

RuleTitleSeverityAuto-fix
DEPENDABOT-ALERTS-OFFDependabot alerts are disabledMEDIUM
DEPENDABOT-FIXES-OFFDependabot security updates are disabledLOW

Credential hygiene (CICD-SEC-6)

RuleTitleSeverityAuto-fix
SECRET-SCANNING-OFFSecret scanning is disabledMEDIUM
SECRET-PUSH-PROTECTION-OFFSecret-scanning push protection is disabledHIGH
Auto-fix on repo-settings rules is powered by the CLI’s --fix-settings flag and the web app’s per-finding Fix button — see Auto-fix.

Rulesets

The CLI’s --ruleset flag (and the web app’s ruleset selector) controls which checks contribute to the final list. Filtering is by framework membership, not by category prefix — see SLSA framework overview for a rule-by-rule mapping.
  • all (default) — every check listed above.
  • owasp — every rule tagged with the OWASP framework.
  • slsa — every rule tagged with any SLSA v1.2 framework (Build or Source).
  • slsa-build-l1 / slsa-build-l2 / slsa-build-l3 — rules for that specific Build level (and only that level — to find every rule a repo needs to satisfy L3, run with slsa and look at the heatmap).
  • slsa-source-l2 / slsa-source-l3 / slsa-source-l4 — rules for that specific Source level. (L1 is “Version Controlled” — trivially satisfied for any GitHub repo.)

Enabling and disabling individual rules

The web app lets you toggle any individual rule on or off without changing the ruleset — per user, with optional per-repository overrides. See Rule settings for the model and the UI. The CLI’s filtering is limited to the coarser --ruleset choice above; multi-tenant preferences depend on the database and are web-only.

How the checks run

Each workflow check is a function that takes the parsed workflow YAML AST and returns a list of findings. The CLI and the web app both invoke the same ScanBytes(name, content) entrypoint. Repository-configuration checks are a separate pass: the API client first fetches the relevant GitHub settings (FetchRepositorySettings) and then runs ScanRepositorySettings(context) to produce findings. These findings carry a synthetic file path (<repository settings>) and a zero line/column so consumers can render them apart from per-file findings. See Auto-fix for which workflow categories the CLI’s --fix flag rewrites. Repository-configuration findings have no auto-fix — they’re flagged for manual remediation via GitHub’s UI.