🍪#7 A Developer-Friendly Approach to Security in CI/CD Pipelines
Illustrating secure delivery workflows with minimal friction for developers.
📚 Newsletter on Secure Coding and Web Security
The Secure Cookie is meant to help you write safer code, ship secure applications with less frustration, and expand your skills as a security-aware developer. Expect deep dives into OWASP guidelines, coding safeguards, secure architecture designs, web security tips, and best CI/CD security practices.
Hi Friends,
Welcome to the 7th drop of the Secure Cookie newsletter.
The concept of runtime environments (RTEs) was explored in the previous post, along with the distinct purposes, permissions, and boundaries each one introduces into the delivery process. As described, in DevOps, runtime environments represent all infrastructure and configuration elements required to run applications across their lifecycle. They generally include development, test, staging, and production, each fulfilling a distinct role in the software delivery pipeline:
But concepts are just theory, and being pragmatic in security is extremely important, because real constraints become only visible when trying to reach the desired security state, as technology limitations usually remain hidden during the design phase. These constraints eventually require adjustments to the original plan, and a practical approach is the only way to uncover these realities before altering the direction of an entire project.
From this standpoint, and to make these ideas tangible and understandable, a public repository has been created that implements a minimal application deployed through a fully automated CI/CD pipeline, defining different runtime environments and integrating real security controls such as SCA, SAST, IaC scanning, Secret Detection, and DAST. It also includes all instructions needed to recreate it within your own setup.
The project is intentionally simple, but the pipeline is intentionally complete. It demonstrates what a developer-friendly, security-aligned software delivery looks like, and more importantly, it illustrates how much easier security becomes when the SDLC is predictable, automated, and grounded in clear environment definitions.
Let’s break it down.
A Minimal Notes API… with Maximum Security Visibility
The application itself is deliberately uncomplicated: a small Express.js REST API with two endpoints:
POST
/notes— create a noteGET
/notes— list existing notes
There is no authentication, no validation, and intentionally no protections.
Why?
Because the objective is not the app, it’s the pipeline.
The code is built to reveal:
How insecure defaults become evident when placed within a CI/CD process.
How GitLab detects vulnerabilities at multiple layers.
How cloud-native deployments can be automated securely.
How clearly defined environments shape your security posture.
It may be viewed as a “Hello World” for DevSecOps foundations.
The Architecture: Small App, Real Deployment Model
The repository deploys the application to Google Cloud Run, fully orchestrated through Pulumi, using GitLab CI/CD as the automation engine.
It includes:
Three isolated environments:
dev,stage,prod— each on their own GCP project.Dedicated service accounts per environment for deployment.
A separate identity for DAST, so staging remains locked down.
Environment-specific configuration and seeding behaviour:
dev: purged + seeded on each deploy.stage: seeded but existing data preserved.prod: never seeded, to protect real production data.
These decisions mirror the environment definitions explained in the latest post:
different expectations → different permissions → different controls.
This is precisely what healthy RTEs look like.
How the Delivery Flow Is Intended to Operate
The repository suggests a simple, clean, and predictable workflow:
feature/* → dev → main → prodEach branch moves the application through the SDLC:
Commits to dev deploy to the development environment.
Merging into main deploys to staging environment and runs DAST.
Merging into prod deploys to production environment.
Static scans run on every branch and MR to provide early feedback. However, since main is the default branch, it is the only one whose scan results appear in the GitLab Vulnerability Report Dashboard.
The main branch serves as the central and stable source of truth for the project.
Security Scans Integrated End-to-End
The following GitLab security analyzers run automatically:
SCA (Dependency Scanning): identifies vulnerable third-party libraries.
Secret Detection: uncovers exposed tokens and sensitive credentials.
SAST (Semgrep): detects insecure coding patterns within the source code.
IaC Scanning (KICS): flags misconfigurations in Pulumi and other infrastructure definitions.
DAST (API Security): performs live security testing against the running service in staging.
This gives a multi-layered view of risk across:
The codebase.
The dependencies.
The infrastructure.
The running service.
All without requiring developer intervention and operating as non-blocking stages within the software delivery process.
Security becomes frictionless once pipelines do the heavy lifting.
Access Control: IAM as a Security Boundary
One of the most important aspects of this repo is how access to each environment is controlled:
dev: intentionally unreachable (reserved for internal developers access).
stage: only reachable by the GitLab DAST service account (also reserved for internal developers access).
prod: publicly accessible.
This is a practical example of how you can use IAM-based access restrictions to enforce clear separation of concerns in the SDLC.
Most security incidents don’t occur because encryption was missing or because a fancy system wasn’t configured correctly, they happen because access control boundaries were blurry.
⚠️ According to the OWASP 2025 release, Broken Access Control remains positioned as the highest-ranked vulnerability group.
This repo shows how to draw them sharply.
The Purpose Behind This Repo
This project is not just a template, it can serve as a practical reference.
It shows how defining runtime environments clearly, and aligning CI/CD logic with those definitions, naturally reduces the friction of applying security controls.
It also demonstrates how:
access boundaries
identity separation
predictable configurations
automation
and environment isolation
…all contribute to making security scalable rather than painful.
Because security isn’t just about adding scanners. It’s about creating the right conditions for security to flourish.
Explore the Repository
👉 GitHub Link: Notes-App-with-GitLab-SCA-SAST-and-DAST
Fork it, clone it, run it locally, inspect the pipeline, review the Pulumi code, and break it freely. It’s built for your own experimentation.
🍪 Thanks for Reading The Secure Cookie
If you are enjoying these contributions to real-world application security, consider subscribing or sharing it with someone who might benefit. And if you are into great security reads, I just picked up the book 📖 Alice and Bob Learn Secure Coding by Tanya Janca, and I can honestly recommend diving into it.
See you in the arena, gladiators!



