Scanverra
Back to Articles
Code Quality

Securing Your CI/CD Pipeline: A Practical Guide

·10 min read

A CI/CD pipeline is, by design, a system that runs code you didn't write (dependencies, third- party actions) with credentials that can push to production. That combination makes it one of the highest-leverage places to get security fundamentals right - and one of the most commonly ignored.

Why CI/CD Is a Real Target

  • Pipelines routinely hold deploy credentials, cloud provider keys, and package-registry publish tokens - compromise one workflow and you may have compromised production.
  • Every dependency your build installs, and every third-party action your workflow calls, runs with the pipeline's own permissions - not sandboxed.
  • Pipelines are automated and often under-monitored compared to production systems, so a compromise can persist unnoticed for longer.

Secrets Management

  • Never hardcode a credential in a workflow file or commit it "temporarily" - git history is forever, even after a later commit removes it.
  • Use your CI provider's secret store (GitHub Actions secrets, GitLab CI/CD variables) rather than .env files checked into the repo.
  • Scope secrets to the narrowest environment that needs them - a secret available to every branch and every PR is a secret available to anyone who can open a PR.
  • Rotate credentials on a schedule, and immediately if a workflow that had access to them is ever modified by an untrusted contributor.

Dependency and Supply Chain Risk

  • Pin third-party GitHub Actions to a full commit SHA, not a mutable tag like @v1 - a tag can be moved to point at different, potentially malicious code without your workflow file changing at all.
  • Treat your build's own dependencies the same way - a compromised transitive dependency runs with full build permissions during npm install, not just at runtime in production.
  • Be deliberate about which third-party actions you use - prefer actions maintained by the platform itself or a well-known vendor over a random low-star repository.

Least-Privilege Permissions

The single highest-leverage change most repos can make: default every workflow's token to read-only, and grant write access explicitly only to the specific job that needs it.

Least-privilege GITHUB_TOKEN scopetypescript
1# .github/workflows/ci.yml
2name: CI
3
4on: [pull_request]
5
6# Default every workflow to read-only. Grant write access only to the
7# specific job that actually needs it, not the whole workflow.
8permissions:
9  contents: read
10
11jobs:
12  test:
13    runs-on: ubuntu-latest
14    steps:
15      - uses: actions/checkout@v4
16      - run: npm ci
17      - run: npm test

The other classic mistake: workflows triggered by pull_request_target or workflow_runthat check out and execute code from a fork with write-level permissions. A malicious PR from an external contributor can then run arbitrary code with your repo's own credentials. If a workflow needs elevated permissions, make sure it's not also running untrusted, attacker-controlled code in the same job.

A Practical Hardening Checklist

  • Workflow permissions default to read-only, elevated only where explicitly needed
  • Third-party actions pinned to a commit SHA, not a mutable tag
  • Secrets scoped per-environment, not available to every branch and PR by default
  • No workflow runs untrusted fork code with write-level permissions
  • Dependencies scanned for known vulnerabilities on every PR, not just periodically
  • Credentials rotated on a schedule and immediately after any workflow-file change from an untrusted contributor

Automate this in your next PR

Run a free repo scan and see outdated dependencies, CVEs, and code quality issues before they ship.

Run a free repo scan