Safeguard Docs

CI/CD Security

Check GitHub Actions workflows and GitLab CI files for unpinned actions, risky triggers and permissions, script injection, and curl-to-shell installs

CI/CD Security

CI/CD Security reads a repository's pipeline definitions and reports settings an attacker can use to run code in your builds or reach their secrets. It is separate from CI/CD Integration, which runs a Safeguard scan inside your pipeline: this checks the pipeline files themselves.

CI/CD Security is turned on per workspace. If Scan pipeline configs is not offered, ask your Safeguard contact to enable it.

What it reads

FilePlatform
.github/workflows/*.yml, .github/workflows/*.yamlGitHub Actions
.gitlab-ci.yml, .gitlab-ci.yamlGitLab CI/CD

Bitbucket Pipelines, Jenkins and Azure Pipelines files are not checked yet.

Checks

RuleSeverityPlatformWhat it finds
SG-CICD-001HighGitHub ActionsA third-party action pinned to a tag or branch instead of a full 40-character commit SHA, so whoever controls that ref controls your build. GitHub's own actions/* and github/* are not flagged.
SG-CICD-002HighGitHub ActionsThe pull_request_target trigger, which runs fork code with a read/write token and the repository's secrets.
SG-CICD-003CriticalGitHub ActionsA run: step that puts untrusted event data (${{ github.event.* }}, github.head_ref) straight into a shell command: a pull request title or issue body can then run commands on the runner.
SG-CICD-004MediumGitHub Actionspermissions: write-all, which gives the job's token write access to every scope.
SG-CICD-005HighGitHub Actions, GitLab CI/CDA download piped straight into a shell (curl … | bash, wget … | sh), which runs the fetched script unverified.

Run a scan

  1. In Safeguard, open Integrations → CI/CD Pipelines & Security.
  2. Choose Scan pipeline configs.
  3. Enter the repository URL, for example https://github.com/org/repo.

Safeguard clones the repository, checks its pipeline files and lists what it finds with the file and line. Private repositories need a connected source control integration that covers them.

Fixing what it finds

  • Pin actions to a commit SHA, and let Dependabot or Renovate keep the pins current.
  • Prefer pull_request over pull_request_target. If you need the latter, do not check out or run the pull request's code in that job.
  • Pass event data through env: and quote it in the script, instead of interpolating ${{ … }} into run:.
  • Grant the least permissions: each job needs, starting from contents: read.
  • Download, verify, then run installers instead of piping them into a shell.

On this page