⎇ CI/CD PIPELINE DOCUMENTATION

DevSecOps Pipeline

Enterprise-Grade Security Pipeline — 4 Services × 20 Stages × 23 Jobs

Yassine Hakkache  |  yhakkache.tech

Python · Flask Go · net/http Node.js · Express Nginx · Frontend 20 Stages 23 Jobs 20+ Security Tools
Pipeline Flowchart

Visual representation of the full DevSecOps flow — from feature branch to production.

DEV
feature branch — code
Merge Request feature → dev · Code Review + Approve
Merged to dev branch
DEV Pipeline — dev branch
1
secret_scan
GitLeaks · TruffleHog
blocks
2
sast
Semgrep · Bandit · njsscan · gosec
3
sca
pip-audit · npm audit · govulncheck
4
test
Unit Tests + Coverage
blocks
5
build
Docker Build + Push to Harbor
blocks
6
container_scan
Trivy · Grype
7
sbom
Syft — CycloneDX + SPDX
blocks
8
sign
Cosign — Image Signing
blocks
9
promote
Auto-push to staging + save image SHA
blocks
FAIL
Fix code
re-push to dev
DEV Pipeline
result?
PASS
Auto-promote
push to staging
Auto-promoted to staging branch
STG Pipeline — staging branch (same image from DEV)
1
deploy_stg
Deploy SAME image → ArgoCD → K3s
blocks
2
smoke_stg
/health + /api checks
blocks
3
dast_stg
OWASP ZAP — Baseline Scan
4
quality_gate_stg
SonarQube — Coverage >= 70%
blocks
5
defectdojo_stg
Download dev artifacts via API + Upload all reports
6
promote_to_production
Manual — Lead/SRE approves → auto-merge staging → main
manual
FAIL
Fix → push to dev
re-flow to staging
STG Pipeline
result?
PASS
promote_to_production
Manual — Lead clicks Play
Auto-merges staging → main
Auto-merged to main branch
PROD Pipeline — main branch (same image from DEV)
1
deploy_prod
Deploy SAME image → ArgoCD → Production
manual
2
smoke_prod
/health + /api — triggers rollback if fails
blocks
3
dast_prod
ZAP DAST — Baseline only
4
defectdojo_prod
Upload PROD reports
5
rollback
Revert to previous image tag via ArgoCD
emergency
LIVE
https://{service}.apps.yhakkache.tech
Service Overview

user-service

LanguagePython 3.11
FrameworkFlask
Port:5000
Unit Tests26 tests (99% coverage)
SAST ToolsSemgrep, Bandit
SCA Toolspip-audit
DefectDojoEngagement #1
Image Size~150 MB

wallet-service

LanguageGo 1.24
Frameworknet/http (stdlib)
Port:8081
Unit Tests17 tests (80% coverage)
SAST ToolsSemgrep, gosec, go vet
SCA Toolsgovulncheck
DefectDojoEngagement #3
Image Size~15 MB (multi-stage)

transaction-service

LanguageNode.js 18
FrameworkExpress.js
Port:3000
Unit Tests22 tests (100% coverage)
SAST ToolsSemgrep, njsscan, ESLint
SCA Toolsnpm audit
DefectDojoEngagement #2
Image Size~120 MB

frontend

TypeStatic Dashboard
ServerNginx 1.25 (Alpine)
Port:80
Pipeline3 stages (build → deploy → verify)
DeployGitOps + ArgoCD (STG + PROD)
Health/health endpoint
Image Size~25 MB
Pipeline Architecture

Each microservice follows the Build Once, Deploy Everywhere model: DEV → STG → PROD. The DEV pipeline builds the image once and runs all security scans. Staging and Production deploy the same image — no rebuild. This ensures reproducibility and supply chain integrity.

DEV — Scan & Build
1. Secret Scan 2. SAST 3. SCA 4. Unit Tests 5. Docker Build 6. Container Scan 7. SBOM 8. Image Signing 9. Auto-Promote
Staging — Same Image
1. Deploy STG 2. Smoke Test 3. DAST (ZAP) 4. Quality Gate 5. DefectDojo 6. Promote Prod 🔒
Production — Same Image
1. Deploy PROD 🔒 2. Smoke Test 3. DAST Baseline 4. DefectDojo 5. Rollback 🔒

Blocks on failure    Allowed to fail    Manual trigger

Stage Details
1
Secret Scanning Detect hardcoded credentials before code is built

First line of defense. If secrets are found, the pipeline stops immediately — no image is ever built from compromised code.

Job 1: GitLeaks Shared

Scans the current source tree with 1000+ regex patterns for API keys, tokens, and passwords.

Image: zricethezav/gitleaks:v8.18.0
Output: gitleaks-report.json
Failure: Blocks pipeline
Job 2: TruffleHog Shared

Scans git history and uses entropy analysis to detect random strings that are likely tokens — only flags verified secrets (confirmed still active). Uses --only-verified and --exclude-paths=.git to reduce false positives.

Image: trufflesecurity/trufflehog:3.63.0
Output: trufflehog-report.json
Failure: Allowed (false positives)
2
SAST — Static Application Security Testing Analyze source code without executing it

Examines code patterns to find vulnerabilities. Each language gets its own specialized scanner in addition to shared Semgrep analysis.

Job 3: Semgrep Shared

Multi-language SAST engine with --config=auto that applies the right ruleset (p/python, p/golang, p/javascript, p/owasp-top-10).

Image: semgrep/semgrep:latest
Output: semgrep-report.json
Failure: Allowed (false positives)
Job 4: Language-Specific SAST Per-Language
Detail
Python — Bandit
Go — gosec
Node.js — njsscan
Image
python:3.11-slim
golang:1.24-alpine
python:3.11-slim
Detects
eval(), subprocess, pickle, weak crypto
Hardcoded creds, SQL concat, weak crypto
eval(), child_process, prototype pollution
Output
bandit-report.json
gosec-report.json
njsscan-report.json
Job 5: Linter / Extra SAST Go & Node.js only

Go: go vet — printf format mismatches, unreachable code, mutex issues.
Node.js: eslint + eslint-plugin-security — object injection, non-literal regexp (ReDoS), eval patterns.

3
SCA — Software Composition Analysis Scan third-party dependencies for known vulnerabilities

~80% of modern applications consist of third-party code. SCA checks those dependencies against vulnerability databases.

Detail
Python
Go
Node.js
Primary Tool
pip-audit (PyPI/OSV DB)
govulncheck (vuln.go.dev)
npm audit (npm Advisory)
Secondary Tool
Why This Tool
PyPI + OSV DB coverage
Official Go tool; only flags called functions
Built into npm; covers deep dep trees
4
Unit Tests + Coverage Verify application logic before building
Detail
Python
Go
Node.js
Framework
pytest + pytest-cov
go test (built-in)
Jest + Supertest
Tests
26 tests, 7 groups
17 tests, 8 groups
22 tests, 7 groups
Coverage
99%
80%
100%
Coverage Format
Cobertura XML
coverage.out → Cobertura
LCOV
Report
junit-report.xml
junit-report.xml
junit.xml
Race Detection
✓ -race flag
Failure
Blocks pipeline
Blocks pipeline
Blocks pipeline
5
Docker Build + Push to Harbor Build container image and push to private registry
Job: docker_build Shared
Image: docker:27-cli
Registry: devsecops-demo-harbor.yhakkache.tech
Tags: ${IMAGE_NAME}:${CI_COMMIT_SHORT_SHA} + :latest
Method: Host Docker socket mount (/var/run/docker.sock) — no DinD service
Dockerfile
Python
Go
Node.js
Base Image
python:3.11-slim
Multi-stage: golang:1.24alpine:3.18
node:18-alpine
Result Size
~150 MB
~15 MB
~120 MB
Install
pip install
go build → binary only
npm ci --production
6
Container Scanning Scan the built image for OS-level and library vulnerabilities
Job 9: Trivy (Aqua Security) Shared
Image: aquasec/trivy:latest
Severity: HIGH, CRITICAL
Output: trivy-report.json
Job 10: Grype (Anchore) Shared
Image: alpine:3.18 + curl install grype (anchore image is distroless — no shell)
Output: grype-report.json
Failure: Allowed
7
SBOM — Software Bill of Materials Full inventory of every component in the image

Produces a complete list of all packages, libraries, and their versions — essential for supply chain security and compliance. CycloneDX (OWASP) + SPDX (ISO). Retained 30 days.

Job 11: Syft (Anchore) Shared
Image: alpine:3.18 + curl install syft (anchore image is distroless — no shell)
Outputs: sbom-cyclonedx.json, sbom-spdx.json
Retention: 30 days
8
Image Signing — Cosign Cryptographically sign the image to prevent tampering

Signs the Docker image with a private key via Sigstore Cosign, then verifies the signature. Kubernetes can enforce that only signed images are deployed.

Job 12: Cosign (Sigstore) Shared
Image: bitnami/cosign:latest
Sign: cosign sign --key env://COSIGN_KEY
Verify: cosign verify --key env://COSIGN_PUB
9
Promote — Auto-push to Staging Push code to staging branch + save image tag for reuse

Final DEV stage. Uses git push to auto-promote code from dev to staging branch, which triggers the STG pipeline. Also saves $IMAGE_TAG to .promote-image-tag artifact so staging/prod deploy the exact same image — no rebuild.

Job 13: promote_to_staging
Mechanism: git push origin HEAD:staging
Artifact: .promote-image-tag (contains $CI_COMMIT_SHORT_SHA)
Failure: Blocks pipeline
10
Deploy to Staging (GitOps) Update image tag in GitOps repo → ArgoCD auto-syncs

Uses the GitOps pattern: the pipeline clones the infra-gitops repo using oauth2:${GITLAB_PAT} (cross-project access), reads the image tag from .promote-image-tag, updates the newTag in the Kustomize overlay, commits, and pushes. ArgoCD watches the repo and automatically deploys to K3s.

Job 14: deploy_staging
Image: alpine:3.18
Mechanism: sed → update kustomization.yamlgit push
Wait: 30s for ArgoCD sync
Failure: Blocks pipeline
11
Smoke Test (Staging) Verify the deployed application is alive and responding

Uses wget --no-check-certificate on alpine:3.18 instead of curl (Alpine musl libc has TLS read issues with HAProxy). Runner uses network_mode: host for direct access to the cluster network.

Job 15: smoke_test_stg Shared
Image: alpine:3.18
Tool: wget --spider --no-check-certificate
Target: https://stg-${APP_NAME}.apps.yhakkache.tech
Failure: Blocks pipeline
Endpoint
Python
Go
Node.js
Health
/health
/health
/health
API
/api/users
/api/wallets
/api/logs
12
DAST — Dynamic Application Security Testing Attack the running application to find runtime vulnerabilities

OWASP ZAP performs a baseline scan against the live staging application, testing for SQL injection, XSS, CSRF, missing security headers, and information disclosure.

Job 16: OWASP ZAP (Staging) Shared

ZAP Docker image requires /zap/wrk to exist for file-based output. The job creates this directory with mkdir -p /zap/wrk, then copies reports to the build directory for artifact collection.

Image: ghcr.io/zaproxy/zaproxy:stable
Mode: Baseline scan (5–10 min)
Workdir: mkdir -p /zap/wrkcp /zap/wrk/zap-*.* .
Outputs: zap-stg-report.json, zap-stg-report.html
13
SonarQube Quality Gate Code quality decision point — pass or no production

SonarQube v10.7.0 (self-hosted) analyzes code quality, coverage, bugs, and security hotspots. Configuration is read from sonar-project.properties at the repo root. With sonar.qualitygate.wait=true, the pipeline blocks until the result is returned.

Job 17: sonarqube_check Shared
Image: sonarsource/sonar-scanner-cli:latest
Instance: https://devsecops-demo-sonar.yhakkache.tech
Config: sonar-project.properties (projectKey, sources, exclusions, test paths, JUnit report)
Quality Gate: Sonar way (built-in, compliant)
Failure: Blocks pipeline — no production without passing
MetricThreshold
Coverage≥ 70%
Critical Bugs0
Security HotspotsAll reviewed
Code SmellsAcceptable level
Duplicated Code< 3%
14
DefectDojo — Vulnerability Management Centralize all scan reports for tracking and triage

Runs on the staging branch. Since scan artifacts are produced on the dev branch (different pipeline), the job downloads them via the GitLab API: curl --header "PRIVATE-TOKEN: ${GITLAB_PAT}" "${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/jobs/artifacts/dev/download?job={JOB_NAME}"unzip. The ZAP STG report is available from the current pipeline's dast_stg stage. Each service maps to a dedicated DefectDojo engagement.

Cross-Pipeline Artifact Download Architecture Note

GitLab limits needs: to 5 entries, and needs:project cannot use $CI_PROJECT_PATH variables. The solution: download artifacts from the latest successful dev pipeline via the GitLab REST API using GITLAB_PAT, then unzip them into the build directory before uploading to DefectDojo.

ReportScan TypeSource Stage
gitleaks-report.jsonGitleaks ScanSecret Scan
trufflehog-report.jsonTrufflehog ScanSecret Scan
semgrep-report.jsonSemgrep JSON ReportSAST
bandit / gosec / njsscanPer-language scan typeSAST
pip-audit / govulncheck / npm-auditPer-language scan typeSCA
trivy-report.jsonTrivy ScanContainer Scan
grype-report.jsonAnchore GrypeContainer Scan
sbom-cyclonedx.jsonCycloneDX ScanSBOM
zap-stg-report.jsonZAP ScanDAST
15
Promote to Production 🔒 Manual lead approval — merge staging → main

The gateway to production. Runs on the staging branch with when: manual. A Tech Lead or SRE must click "Play" in the GitLab UI. The job then auto-merges staging into main via git push origin staging:main, triggering the PROD pipeline.

Job: promote_to_production Shared
Branch: staging
Trigger: Manual — Lead/SRE approval
Mechanism: git push origin staging:main
Auth: oauth2:${GITLAB_PAT}
Result: Triggers PROD pipeline on main branch
16
Deploy to Production 🔒 Manual approval required — human-in-the-loop

Runs on main branch. Same GitOps mechanism as staging — clones infra-gitops via oauth2:${GITLAB_PAT}, reads the staging image tag from the staging kustomization overlay, and updates the production overlay. Requires when: manual — a team member must explicitly click "Deploy". ArgoCD auto-syncs the production namespace.

17
Smoke Test (Production) Verify production is alive — failure triggers rollback consideration

Same health + API checks as staging, using wget on alpine:3.18. Targets https://${APP_NAME}.apps.yhakkache.tech (no prod- prefix — matches SSL certificate SANs). If this fails, the rollback job becomes immediately relevant.

18
DAST (Production Baseline) Lightweight security scan on production

Baseline-only ZAP scan on production using mkdir -p /zap/wrk. A full scan would generate too much traffic and risk performance impact on live users.

19
DefectDojo (Production Reports) Upload production ZAP scan to DefectDojo

Uploads the production ZAP report to DefectDojo for tracking alongside staging findings. Uses python:3.11-slim with apt-get install curl (Alpine curl has musl TLS issues with HAProxy).

20
Rollback 🔒 Emergency — revert to previous version

Manual trigger, emergency-only. Clones infra-gitops via oauth2:${GITLAB_PAT} and uses CI_COMMIT_BEFORE_SHA to roll back to the previous image tag. ArgoCD detects the change and redeploys the old version.

Tool Comparison Across Pipelines
#StagePythonGoNode.jsShared?
1-2Secret ScanGitLeaks + TruffleHogGitLeaks + TruffleHogGitLeaks + TruffleHog
3SAST (generic)SemgrepSemgrepSemgrep
4SAST (lang)Banditgosecnjsscan
5Lintergo vetESLint + security
6SCA (primary)pip-auditgovulnchecknpm audit
7SCA (secondary)
8Testspytest (26)go test (17)Jest (22)
9BuildDocker CLI (socket)Docker CLI (socket)Docker CLI (socket)
10-11Container ScanTrivy + GrypeTrivy + GrypeTrivy + Grype
12SBOMSyftSyftSyft
13SignCosignCosignCosign
14DeployGitOps + ArgoCDGitOps + ArgoCDGitOps + ArgoCD
15Promote Prodgit push (manual)git push (manual)git push (manual)
16Smokewget (Alpine)wget (Alpine)wget (Alpine)
17DASTOWASP ZAPOWASP ZAPOWASP ZAP
18Quality GateSonarQubeSonarQubeSonarQube
19Vuln MgmtDefectDojoDefectDojoDefectDojo
Pipeline Artifacts

Every pipeline run produces the following artifacts, stored for 7 days (SBOM for 30 days).

gitleaks-report.json
Secret Scan · GitLeaks · 7d
trufflehog-report.json
Secret Scan · TruffleHog · 7d
semgrep-report.json
SAST · Semgrep · 7d
bandit / gosec / njsscan
SAST · Per-language · 7d
pip-audit / govulncheck / npm-audit
SCA · Per-language · 7d
coverage.xml / .out / lcov
Tests · Coverage · 7d
junit-report.xml
Tests · JUnit · 7d
trivy-report.json
Container Scan · Trivy · 7d
grype-report.json
Container Scan · Grype · 7d
sbom-cyclonedx.json
SBOM · CycloneDX (OWASP) · 30d
sbom-spdx.json
SBOM · SPDX (ISO) · 30d
zap-*-report.json/html
DAST · OWASP ZAP · 7d
CI/CD Variables

Configured in GitLab → Settings → CI/CD → Variables.

VariableDescriptionMaskedUsed By
HARBOR_USERHarbor registry usernameBuild, Container Scan, SBOM, Sign
HARBOR_PASSWORDHarbor registry passwordBuild, Container Scan, SBOM, Sign
SONAR_HOST_URLSonarQube instance URLQuality Gate
SONAR_TOKENSonarQube project analysis tokenQuality Gate
DEFECTDOJO_TOKENDefectDojo API tokenDefectDojo uploads
COSIGN_KEYCosign private key (signing)Image signing
COSIGN_PASSWORDCosign key passphraseImage signing
COSIGN_PUBCosign public key (verification)Image verification
GITLAB_PATPersonal Access Token for cross-project accessPromote, Deploy STG, Deploy PROD, Rollback, DefectDojo artifact download
Infrastructure

Self-hosted on DigitalOcean + GCP, interconnected via Tailscale mesh VPN.

GitLab
GitLab.com
Source control & CI/CD engine (SaaS)
SaaS + Self-Hosted Runner
Harbor
Harbor
Private container registry
Self-Hosted
SonarQube
SonarQube
Code quality & coverage
Self-Hosted
DefectDojo
DefectDojo
Vulnerability management
Self-Hosted
K3s
K3s Cluster
Kubernetes (master + worker)
Self-Hosted
ArgoCD
ArgoCD
GitOps CD — auto-syncs from infra-gitops repo
Self-Hosted
Pipeline Stop Points

These jobs block the pipeline on failure — the security and quality gates.

#StageJobRationale
1Secret ScanGitLeaksSecrets in code must never be built into an image
2Secret ScanTruffleHogallow_failure — only verified secrets; false positives common
3TestUnit TestsBroken logic must not be deployed
4BuildDocker BuildNo image = nothing to deploy
5SBOMSyftBlocks pipeline — supply chain transparency is mandatory
6SignCosignBlocks pipeline — unsigned images must not be deployed
7Deploy STGGitOps DeployMust deploy to staging before validation
8Smoke STGHealth CheckApplication must be alive before further testing
9Quality GateSonarQubeCode quality threshold — no production without passing
10Promote PRODpromote_to_productionManual — Lead/SRE approves merge to main
11Deploy PRODGitOps DeployManual — requires human approval
12Smoke PRODHealth CheckProduction failure = immediate rollback consideration
13RollbackRollbackManual — emergency revert