DevSecOps automation: security gates in the pipeline
Security that requires a human to remember it does not scale. Security that is a pipeline gate cannot be forgotten. Here is the gate layout I use.
The gates
| Stage | Gate | Tooling |
|---|---|---|
| Merge request | SAST on diff | Semgrep / vendor SAST |
| Build | Dependency scan | Trivy / grype |
| Build | Image scan (HIGH/CRITICAL fails) | Trivy |
| Plan | IaC security | Checkov / tfsec |
| Staging | DAST | ZAP baseline |
Each gate has a single behavior: fail the pipeline by default. Anything else — allowlists, exceptions, postponed fixes — requires a reviewed exception, not a silent pass.
What it looks like
# .gitlab-ci.yml — extract
stages: [sast, build, scan, deploy]
sast:
stage: sast
image: semgrep/semgrep
script:
- semgrep ci --config auto --sarif-output=semgrep.sarif
build:
stage: build
script:
- docker build -t "$IMAGE_TAG" .
scan_image:
stage: scan
image: aquasec/trivy
script:
- trivy image --exit-code 1 --severity HIGH,CRITICAL "$IMAGE_TAG"
scan_iac:
stage: scan
script:
- checkov -d infra --framework terraform --soft-fail-off
GitOps then promotes only what passed: ArgoCD deploys the tested tag, and the exception process — not the pipeline — is the special case.
The operational lessons
- Fail early, fail loudly. A scan that warns but never blocks becomes noise within a month. Gates must break the pipeline.
- Exceptions must expire. Every allowlist entry gets an expiry date and an owner. No permanent exceptions.
- Ownership stays with developers. The security team maintains the gates; teams fix what the gates find. Take fixing away from the author and it stops being fixed.
- Measure the boring numbers. Time-to-fix, false-positive rate, images shipped unscanned. If you can’t measure the gate, you’re not running it — you’re hoping.
The result
Vulnerabilities caught in minutes instead of months, and a deployment path where security is infrastructure: boring, repeatable, and hard to bypass.
Written by Rulx Philomé Alexis · ← All notes