DevSecOps · Project case study
Secure CI/CD on Azure
A GitHub Actions workflow that checks a containerized web app for secrets, unsafe code, and vulnerable dependencies before it can be built and deployed.

The problem
A successful build says little about whether a release is safe. Credentials can enter source control, dependencies can contain known vulnerabilities, and insecure patterns can pass code review. Without a component inventory, investigating a later supply-chain issue becomes harder.
The solution
I put security ahead of the build. Four checks run concurrently; a blocking finding stops the downstream jobs. Successful runs produce an SBOM, publish a Docker image, and deploy it to Azure.
- 01
Security gate
Gitleaks, TruffleHog, Semgrep, and Trivy run in parallel on pushes and pull requests.
- 02
SBOM
Once checks pass, Syft creates a CycloneDX inventory and saves it as an artifact and release asset.
- 03
Build & push
On the main branch, Docker builds the Next.js app and pushes the image to Azure Container Registry.
- 04
Deploy
Azure App Service receives the image only after the preceding jobs succeed.
Pipeline architecture
From pull request to security evidence, container registry, and Azure App Service.

Live test results
The workflow was tested with real credential states, a vulnerable package, and a clean release. “Fail” means a check raised a blocking finding; “Pass” means it did not.
| Scenario | Gitleaks | TruffleHog | Semgrep | Trivy | What happened |
|---|---|---|---|---|---|
| #20Example AWS key | Pass | Pass | Pass | Pass | Fake, low-entropy key ignored. |
| #22Real IPinfo token in URL | Pass | Pass | Pass | Pass | No distinctive token pattern matched; the scans passed. |
| #23Revoked GitHub token | Fail | Pass | Fail | Pass | Pattern flagged; live verification confirmed revocation. |
| #24Active GitHub token | Fail | Fail | Fail | Pass | Verified live credential blocked the pipeline. |
| #25Unchanged token + vulnerable lodash | Pass | Pass | Fail | Fail | Full SAST scan and dependency scan caught issues. |
| #27Clean production run | Pass | Pass | Pass | Pass | All gates passed; the app deployed to Azure. |
Why the IPinfo test passed: the token was real, but unlike a GitHub personal access token, its string had no distinctive prefix or fixed format that matched the configured detectors. This exposed a coverage gap in pattern-based scanning: the gate can only block secrets its checks recognize.
A traceable software inventory
After the security jobs pass, Syft generates a CycloneDX JSON SBOM. The workflow stores it as a run artifact and attaches it to a GitHub release, so the dependency inventory stays associated with the shipped version.
The scans also leave SARIF reports or logs for investigation. This creates useful evidence of what ran and what it found, without claiming the checks eliminate every risk.
Why the SBOM matters in Europe
The EU Cyber Resilience Act requires manufacturers of covered products with digital elements to document components, including a machine-readable SBOM covering at least top-level dependencies. Its main requirements apply from 11 December 2027. This pipeline demonstrates automated inventory generation; an SBOM alone does not establish CRA compliance. Read the regulation ↗

Built with
Next.js · GitHub Actions · Gitleaks · TruffleHog · Semgrep · Trivy · Syft · CycloneDX · Docker · Azure Container Registry · Azure App Service