Compliance as code:
SOC 2 and ISO 27001 evidence from CI.
What compliance as code means in practice, which SOC 2 criteria and ISO 27001:2022 controls a CI pipeline can evidence, what an auditor asks to see for each check, and the three ways teams build a security gate that produces no usable evidence at all.
Last updated Published by TryTrustableNot legal advice
What is compliance as code?
Compliance as code means expressing security and compliance requirements as machine-checked rules that run in the delivery pipeline, and keeping every result as dated evidence. A change that breaches a rule fails its pull request; a change that passes leaves a record of what was checked. The rule and the record are both versioned alongside the code.
The term gets used for two different things, and separating them explains most failed attempts.
- Policy as code is the enforcement half: rules that decide whether a change may proceed. A Semgrep rule forbidding string-built SQL, a Trivy threshold on critical CVEs, an OPA policy refusing a public storage bucket, a branch protection rule requiring one approval
- Evidence as code is the record half: every run of those rules, its inputs, its result, the commit it ran against and when, kept where an auditor can retrieve it months later
Most teams build the first and assume they have the second. They have a gate that works and a CI system that deletes its logs after ninety days. The gate is a control; without the record it is a control you cannot show was operating, which for a SOC 2 Type 2 report is roughly the same as not having it.
Which SOC 2 and ISO 27001 controls can a CI pipeline evidence?
A CI pipeline can evidence SOC 2 change management (CC8.1) and much of vulnerability detection (CC7.1), and supports CC6.1, CC6.8 and CC4.1. On ISO 27001:2022 it speaks to Annex A 8.25 to 8.29 on secure development, 8.8 technical vulnerabilities, 8.9 configuration, 8.4 source code access and 8.32 change management.
The SOC 2 references are to the AICPA's 2017 Trust Services Criteria with the 2022 revised points of focus. CC8.1 is the centre of gravity: it asks that changes to infrastructure, data and software are authorised, designed, developed, configured, documented, tested, approved and implemented. A pull request with a required reviewer and required checks is close to a literal implementation of that sentence.
The ISO references are to Annex A of ISO/IEC 27001:2022, where the secure development controls sit together in the technological theme: 8.25 secure development life cycle, 8.26 application security requirements, 8.27 secure system architecture and engineering principles, 8.28 secure coding, and 8.29 security testing in development and acceptance. The 2013 edition scattered these across A.12 and A.14; if your mapping still cites A.14.2.8, it predates the current standard.
For an Indian company the same pipeline also contributes to the reasonable security safeguards Section 8(5) of the DPDP Act requires, though the Act names outcomes rather than specific controls.
Pipeline checks, the control they evidence, and what the auditor asks
The third column is the one to design for. A check the auditor cannot sample is a check that did not happen, as far as the report is concerned.
| Pipeline check | SOC 2 (TSC 2017) | ISO 27001:2022 Annex A | What an auditor asks to see |
|---|---|---|---|
| SASTSemgrep, CodeQL | CC8.1, CC7.1 | 8.28 secure coding, 8.29 security testing | The ruleset in force, the result for each sampled change, and how findings over the threshold were resolved or excepted |
| SCA / dependency scanningOSV-Scanner, Trivy | CC7.1 | 8.8 technical vulnerabilities, 5.21 ICT supply chain | Your remediation timeframes by severity, and evidence that critical findings were fixed inside them |
| Secrets scanninggitleaks, push protection | CC6.1 | 5.17 authentication information, 8.4 access to source code | What happened after a hit: the rotation record, not only the commit that removed the string |
| DASTZAP, Nuclei | CC7.1, CC4.1 | 8.29 security testing, 8.8 | Which environment and targets were scanned, that scanning was authorised, and how findings were tracked to closure |
| IaC scanningCheckov, Trivy misconfig | CC7.1, CC8.1 | 8.9 configuration management, 8.27 | The baseline being enforced, and that a misconfiguration was stopped before apply rather than found after |
| Branch protection and required review | CC8.1, CC6.1 | 8.32 change management, 8.4, 5.3 segregation of duties | The full population of merged changes, and for each sample an approval by someone other than the author; admin bypasses listed |
| Signed commits and build provenance | CC8.1, CC6.8 | 8.32, 8.4 | That what reached production was built from the reviewed commit, and who could have altered it in between |
| SBOM per releaseCycloneDX, SPDX | CC7.1 | 5.21, 8.8 | A component list per release, and whether you used it to answer “were we affected by this CVE?” |
Mappings are the common reading of each criterion, not an AICPA or ISO table. Auditors differ; agree yours in advance. SOC 2 CC6.8 and ISO 8.7 (protection against malware) are usually evidenced from endpoints, not CI.
What does a merge-blocking security gate look like in GitHub Actions?
A merge-blocking security gate is a pull request workflow that runs SAST, secrets and dependency scans, fails when a finding crosses an agreed severity, and is listed as a required status check in branch protection. The workflow produces the failure; branch protection is what turns that failure into a blocked merge.
The example below uses three widely used open-source tools. It is illustrative, not a recommended configuration: tune the rulesets to your stack, and pin every third-party action to a commit SHA rather than a tag, because a tag can be moved and a supply-chain compromise of the scanner is a finding in itself.
# .github/workflows/security-gate.yml
# ILLUSTRATIVE. Pin every third-party action to a full commit SHA before use.
name: security-gate
on:
pull_request:
branches: [main]
permissions:
contents: read
jobs:
gate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0 # secret scanning needs history
- name: SAST (Semgrep)
run: |
pipx install semgrep
semgrep scan --config p/default --error \
--sarif --output semgrep.sarif
- name: Secrets (gitleaks)
if: always()
run: |
docker run --rm -v "$PWD:/repo" zricethezav/gitleaks:latest \
detect --source /repo --redact \
--report-format json --report-path /repo/gitleaks.json
- name: Dependencies and IaC (Trivy)
if: always()
uses: aquasecurity/trivy-action@<commit-sha>
with:
scan-type: fs
scanners: vuln,misconfig
severity: CRITICAL,HIGH
exit-code: '1' # fail the check, which blocks the merge
format: sarif
output: trivy.sarif
- name: SBOM (CycloneDX)
if: always()
uses: aquasecurity/trivy-action@<commit-sha>
with:
scan-type: fs
format: cyclonedx
output: sbom.cdx.json
- name: Keep the results
if: always()
uses: actions/upload-artifact@v4
with:
name: security-evidence-${{ github.sha }}
path: |
*.sarif
gitleaks.json
sbom.cdx.json
retention-days: 90 # shorter than a Type 2 period: copy it out
Three things the YAML does on purpose. Every scanner step runs if: always(), so one failure does not hide the others' results. The job fails if any scanner fails, which is what a required check needs. And the last step keeps the output, because a gate whose results vanish is the first failure mode below.
Two things it deliberately leaves out. DAST needs a running environment and an authorised target, so it belongs in a post-deploy job against staging rather than in the pull request. And branch protection is a repository setting, not a workflow: require this check, require one approving review from someone other than the author, and restrict who can bypass. Those settings are themselves evidence for 8.32 and CC8.1, and their change history is worth exporting.
Why do CI security gates fail audits?
CI security gates usually fail audits on record-keeping, not on scanning. The scans ran; the results were not kept for the observation period, exceptions were granted without an owner or expiry, or the evidence was assembled from screenshots in audit week. Each makes a working control impossible to demonstrate.
- Scan results nobody keeps. GitHub's default artefact retention is ninety days and log retention is similar. A SOC 2 Type 2 period is commonly six to twelve months, and the auditor samples from all of it. Copy results to storage whose retention you control, keyed by commit, as each run completes
- Exceptions without expiry. Every real gate needs a way to accept a finding: a vulnerable dependency with no fix yet, a false positive. A suppression file with no owner, reason or date is a permanent hole that looks identical to a fixed finding. Trivy's ignore file accepts an expiry date per entry; whatever tool you use, make exceptions expire and record who granted them
- Evidence as screenshots. A screenshot of a green pipeline proves one run on one day, the day somebody went looking. It cannot be sampled, cannot be tied to a specific change, and says nothing about the failures in between. Evidence should be the machine output, stored when it was produced
- Silent skips. A scanner that crashes, times out or is not installed often exits cleanly. If a skipped check reads as passed, your record is wrong in the most flattering direction possible
- Bypass nobody logs. Admins merging past a required check is sometimes necessary. Unlogged, it is an exception to change management that the auditor will find in the merge history before you do
Recording failures matters as much as recording passes. An unbroken run of green across a period when the team remembers an incident invites exactly the questions you did not want; a dated failure followed by a dated fix is the more defensible record. That is the principle behind our evidence ledger.
How TryTrustable fits
Everything above can be built from open-source parts, and if you have one repository and one framework it is reasonable to do so. The work grows with the second repository, the second framework, and the first auditor who asks which control each finding relates to.
TryTrustable's SDK and CI gate are built for that point. The gate runs on GitHub Actions and GitLab CI, runs your enabled control set against the diff, and fails the pull request with the control reference and the fix: CC6.6, say, rather than a bare rule ID. SAST, SCA, IaC, DAST and secret scanning run inside your own pipeline, so results describe the code you actually built rather than being inferred from cloud configuration. Failures block by default and can be set to warn during adoption; a skipped check reports as skipped and is never recorded as satisfied. The same findings appear in VS Code and JetBrains as the code is written.
Results land in the evidence ledger, which is append-only and hash-chained, and records failing controls as well as passing ones. On the security intelligence side, a failing check moves attack-path, residual-risk and framework-readiness figures at the same moment. Because controls are shared across frameworks, one pipeline result can answer SOC 2, ISO 27001 and DPDP together; coverage lists the regimes.
The things people ask us
What is compliance as code?
Compliance as code means expressing security and compliance requirements as machine-checked rules that run in the same pipeline as the software, and keeping their results as dated evidence. It has two halves: policy as code, which decides whether a change may merge, and evidence as code, which records that the decision was made and what it found.
Can GitHub Actions evidence SOC 2?
It can evidence part of it. A pull request workflow with required review and security scans produces evidence for change management (CC8.1) and vulnerability detection (CC7.1), provided the results are retained for the whole observation period and every exception is recorded. It does not touch HR, vendor management, physical security or risk assessment, which still need their own evidence.
Which SOC 2 criteria does a CI pipeline cover?
Mainly CC8.1, change management: changes are authorised, tested, approved and implemented in a controlled way. Scanning also supports CC7.1 (detecting vulnerabilities and risky configuration changes), CC6.8 (preventing unauthorised or malicious software) and CC6.1 (logical access, including secrets). Penetration and DAST results feed CC4.1 monitoring. Your auditor makes the final mapping.
Which ISO 27001:2022 controls relate to secure development?
Annex A 8.25 to 8.29: secure development life cycle, application security requirements, secure architecture and engineering principles, secure coding, and security testing in development and acceptance. Around them sit 8.8 management of technical vulnerabilities, 8.9 configuration management, 8.4 access to source code and 8.32 change management, all of which a pipeline can partly evidence.
Should a security gate block the merge or only warn?
Block, eventually, on a severity threshold you have agreed. A warning nobody acts on is a finding you have logged rather than fixed, and auditors read it that way. Most teams start in warn mode for a few weeks, tune out false positives, then switch to blocking on critical and high findings with a documented, expiring exception route.
Is a screenshot of a passing pipeline acceptable audit evidence?
Often accepted, rarely good. A screenshot proves one run looked green on one day. For a Type 2 report the auditor samples changes across months, so you need the population of merged changes and, for each sampled one, the review, the checks that ran and their results, retrievable after the fact without anyone reconstructing them.
How long should we keep CI scan results for an audit?
At least as long as the audit period plus the time it takes the auditor to test it. A SOC 2 Type 2 observation period is commonly six to twelve months, and an ISO 27001 surveillance audit looks back over the year. Default CI artefact retention is usually far shorter, so results need copying somewhere durable.
Does compliance as code replace an auditor or a policy?
No. Code can enforce and evidence a policy; it cannot decide what the policy should be, accept a risk or attest that controls are designed appropriately. SOC 2 still needs a CPA firm's examination and ISO 27001 an accredited certification body. What compliance as code changes is how cheaply and credibly you can answer their questions.
Run the gate on one of your pull requests.
Bring a repository. We add the gate on the call, run it on a real diff and show the finding, its control reference and the evidence record it leaves behind.