If you have audited change management before, you already understand most of this. The control objectives have not changed. Changes still need to be authorized, reviewed, and tested before they reach production. What changed is where that happens. Engineering teams stopped deploying by hand and moved the whole process into a CI/CD pipeline. Auditing CI/CD pipelines is just learning to test those same objectives in their new home.
GRC, Governance, Risk, and Compliance, has always been about evidence. The good news for auditors is that a pipeline produces better evidence than the manual process it replaced. It is timestamped, attributed to a person, and generated automatically every time someone ships code. You do not have to take anyone's word for it. You read the configuration and you pull the run history.
What is a CI/CD pipeline?
CI/CD stands for continuous integration and continuous delivery. It is the automated assembly line that takes a code change from a developer's laptop to production. When an engineer writes code, they save it to a shared repository. That save triggers the pipeline, which runs a series of steps automatically: it builds the software, runs tests, checks for security issues, waits for approvals, and then deploys.
Three tools dominate the market: GitHub Actions, GitLab CI/CD, and Jenkins. They differ in detail, but the shape is the same. Each defines its pipeline in a configuration file that lives in the code repository. That file is your starting point. It is the documented procedure, written in a format the system actually enforces, which is far stronger than a policy document that describes what people are supposed to do.
For the audit, think of the pipeline in two parts. The configuration says what is supposed to happen, the design of the control. The run history says what actually happened, the operating effectiveness. You test both, the same way you always have.
How to audit a CI/CD pipeline
Four controls carry most of the weight. Walk through them in order and you have covered the pipeline.
1. Segregation of duties between commit, approve, and deploy
The core question is simple: can one person write a change and push it to production alone? If yes, you have a segregation of duties gap. A healthy pipeline forces at least two people to touch a change. The author commits, a different person reviews and approves, and the deploy only proceeds once that approval exists.
You test this by checking that the platform blocks an author from approving their own change. In GitHub, that is a branch protection setting. In GitLab, it is a merge request approval rule. Then you confirm it held in practice by sampling merged changes and checking that the approver was not the author.
2. Approval gates and required reviews
An approval gate is a checkpoint the pipeline will not pass until a required condition is met, usually a human sign-off or a passing review. The control you are testing is that these gates are required, not optional. A gate that a developer can skip or disable is not a control.
Look for the setting that requires a minimum number of approving reviews before a change can merge, and the setting that requires those reviews to come from a specific team for sensitive areas. Then confirm administrators cannot bypass the gate quietly, or that bypasses are logged when they happen.
3. Automated security testing in the pipeline
Mature pipelines run security checks on every change automatically. Common ones include static analysis that scans the code for vulnerabilities, dependency scanning that flags known-vulnerable libraries, and secret scanning that catches passwords or keys accidentally committed. The control is that these run before deploy and that a failure stops the pipeline rather than just printing a warning everyone ignores.
You verify the scan steps exist in the configuration, then check the run history to confirm they actually executed and that the team acts on failures instead of overriding them. A scan that runs but never blocks anything is a control on paper only.
4. Build logs and run history as evidence
Every pipeline run produces a log and an entry in the run history. This is your evidence base. The history shows who triggered each run, when, what change it carried, whether the reviews and scans passed, and whether the deploy succeeded. You will sample from this history to test the first three controls, so understanding how to read it is the foundation of the whole audit.
Mapping pipeline checks to controls
The reason this matters to an audit is that pipeline controls line up directly with the control objectives you already test. One pipeline review can support several of them.
| Pipeline check | Control objective | Maps to |
|---|---|---|
| Required review before merge | Changes are reviewed and approved before deployment | SOC 2 CC8.1, change management |
| Author cannot approve own change | No single person controls a change end to end | Separation of duties |
| Approval gate before deploy | Production changes are authorized | SOC 2 CC8.1, change management |
| Security scans block on failure | Changes are tested for security issues before release | SOC 2 CC8.1, secure development |
| Run history retained and attributed | Change activity is logged and reviewable | SOC 2 CC8.1, audit logging |
SOC 2 CC8.1 is the change management criterion in the Trust Services Criteria. It asks, in plain terms, whether the organization authorizes, designs, develops, tests, approves, and implements changes in a controlled way. A well-configured pipeline is one of the cleanest ways to demonstrate it, because the controlled process is the system, not a memory of what someone did.
What good and bad look like
You do not need to write these files. You need to read them well enough to spot whether the control is present. Here is a GitHub Actions deploy job with an approval gate and a security scan that blocks. The environment line ties the deploy to a protected environment that requires a reviewer, and the deploy job will not run unless the security scan job before it passed.
jobs:
security-scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Run dependency scan
run: npm audit --audit-level=high
deploy:
needs: security-scan # deploy waits for the scan to pass
environment: production # production requires a reviewer approval
runs-on: ubuntu-latest
steps:
- name: Deploy
run: ./deploy.shThe same pipeline with the controls missing looks like this. There is no scan, no needs dependency, and no protected environment. Any change that reaches this file deploys straight to production with no gate. That is the finding.
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Deploy
run: ./deploy.sh # no review, no scan, no approval gateGitLab and Jenkins express the same ideas differently. In GitLab CI/CD, look for rules and when: manual on deploy jobs, plus merge request approval rules in the project settings. In Jenkins, look for an input step in the pipeline that pauses for a human approval before deploy, and check who has permission to provide it.
The pattern to internalize is this: a control you can read in the configuration is a control you can test. If the only evidence is someone telling you the team always reviews changes, you do not have a control yet, you have a hope. Auditing infrastructure this way is a broader skill, and auditing infrastructure as code covers the same read-the-config approach for cloud resources.
How to sample pipeline runs as evidence
Reading the configuration tests the design. Sampling run history tests whether the control operated across the period. Your sampling approach does not change because the evidence is in a pipeline. You define the population, pull a sample, and test each item against the control.
- Define the population: all deployments to production during the audit period. The run history is your complete list, so you are sampling from the full population, not a manual log that might be incomplete.
- Pull a sample of runs across the period, including some near the start and end so you catch any change in how the pipeline was configured.
- For each sampled run, confirm the change was reviewed by someone other than the author, the approval gate was satisfied, and the security scan passed before deploy.
- Note any run that deployed without a review or with a failed scan that was overridden. Those are your exceptions, and the run history tells you exactly who and when.
A pipeline run record reads about like this once you export it. Each row is one deployment, attributed and timestamped, which is exactly what you need to test the controls in your sample.
run_id change author approved_by scan deployed_at
8842 fix-login jdoe msmith passed 2026-03-04 14:12
8843 add-export msmith jdoe passed 2026-03-05 09:41
8844 hotfix-cron jdoe jdoe skipped 2026-03-06 22:03 <-- exceptionRun 8844 is what you are hunting for. The author approved their own change and the scan was skipped. That single row is a segregation of duties exception and a security testing exception, documented by the system itself. You did not have to reconstruct it from interviews. You sampled it.
Where to build this skill
Reading pipeline configurations and sampling run history is a learnable skill, and it is exactly what the CGE-AUD Auditor Specialty is built to teach. The specialty is designed for auditors who want to test modern engineering controls directly instead of relying on a client to summarize them for you.
The hands-on portion uses the open-source claude-grc-engineering toolkit to pull pipeline evidence and review it in a structured way, so you practice on real run history rather than a slide. If you are comparing certifications, the CGE-AUD vs CISA breakdown explains where each one fits.
Frequently Asked Questions
How do you audit a CI/CD pipeline?
You audit a CI/CD pipeline by tracing a code change from commit to production and checking the controls along the way: that the person who wrote the change is not the same person who approved and deployed it, that required reviews and approval gates are enforced in configuration, that automated security testing runs before deploy, and that every pipeline run leaves a log you can sample as evidence. Start with the pipeline configuration file, then pull run history to confirm the controls actually fired.
What evidence does a CI/CD pipeline provide?
A CI/CD pipeline provides pipeline run history, build logs, approval records, reviewer sign-offs, and security scan results. Each run is timestamped and attributed to a specific actor, which lets you test segregation of duties, change approval, and security testing controls from a single source. Because the pipeline runs on every change, this evidence is generated continuously rather than assembled by hand before an audit.
What controls apply to CI/CD pipelines?
The most common controls are change management (SOC 2 CC8.1), separation of duties, required code review and approval before merge, and security testing in the build. These map to control objectives around authorized changes, reviewed changes, and tested changes. The pipeline is where those controls are enforced and where the evidence lives.
Can build logs be audit evidence?
Yes. Build logs are strong audit evidence because they are timestamped, attributed to an actor, and generated automatically by the system rather than produced manually for the auditor. A build log shows what ran, when it ran, who triggered it, and whether tests and security scans passed. Pair the log with the pipeline configuration that required those steps and you have evidence that a control operated.