Security is no longer a painful final stage, but a natural part of every pipeline. This episode enables GitLab's built-in security scanners — SAST, Secret Detection, Dependency Scanning, and Container Scanning — with just a few lines of templates, then reads the results from the Security Center.

In the previous episode 11 we covered pipeline modularization with include — including include: template which imports official GitLab templates. I mentioned we'd dissect the security templates more deeply, and now is that time. In this episode we enter PHASE 5 and cover the thing beginner pipelines most often neglect: security.
The old approach treated security as a final stage run once a year by a dedicated team, after the application was fully built — usually producing hundreds of vulnerabilities to be handled in panic. It's exactly like checking water right before you drink: useful, but too late. The modern approach is called DevSecOps or shift-left security: security is shifted left — to the earliest stages of development — and runs automatically on every commit, not every year.
The good news is that GitLab is one of the platforms most committed to building security into the pipeline itself. Because GitLab handles the repository, CI/CD, container registry, and security dashboard in one application, security scanners don't need to be connected manually — just add a few lines of include: template and the results are integrated directly into merge requests. Let's see how.
Most vendors separate their tools: repository in one place, CI somewhere else, security scanner in a third product you have to calibrate yourself. GitLab takes a different approach — everything in one platform. Because GitLab pipelines run on every commit and merge request, security scanning naturally follows the development flow instead of becoming a separate process.
A fitting analogy: traditional security is like putting a vault at the end of a building — anyone can carry anything in, and it's only checked once it reaches the vault. GitLab-style DevSecOps is like an airport security checkpoint — everyone passes through a scanner before boarding the plane. That way, problems are found while they're still cheap to fix: on a developer's laptop, not after release to production.
The four native scanners we'll discuss work on different layers:
SAST reads source code without running it and looks for patterns vulnerable to attack — SQL injection, path traversal, use of unsafe functions, and the like. GitLab uses engines matched per language: Semgrep for Python, GoSec for Go, Bandit for Python, and others. Enabling it is very simple:
include:
- template: Jobs/SAST.gitlab-ci.ymlJust add this YAML file to your pipeline's include list. The template defines all the SAST jobs needed, complete with their default configuration. When the pipeline runs, SAST findings appear as comments in the merge request — right on the offending code lines.
Nothing is more embarrassing than an API key or AWS_SECRET_ACCESS_KEY committed to the repository and then read in logs. Secret Detection scans the entire repository history — including old commits — to find leaked credentials.
include:
- template: Jobs/Secret-Detection.gitlab-ci.ymlWarning
Secret Detection finds credentials, but doesn't remove them. If a secret was ever committed, consider it leaked — revoke and replace it immediately, then rewrite git history with git filter-repo. Deleting the file from the current branch doesn't remove the secret from commit history.
Vulnerabilities rarely come from your own code — most come from the dependencies you use. Dependency Scanning compares your library list and versions against vulnerability databases like CVE and GitLab's own database.
include:
- template: Jobs/Dependency-Scanning.gitlab-ci.ymlThis template reads manifest files like package-lock.json, Gemfile.lock, requirements.txt, or pom.xml, then scans their dependencies. GitLab doesn't just display a CVSS score — it also shows the version that fixes the vulnerability, so teams immediately know what to upgrade.
After images are built in the registry (remember episode 6), the last layer is scanning the image itself. Container Scanning uses Trivy by default — an open-source tool that scans the base image's operating system (e.g. alpine or ubuntu) along with any language libraries contained in the image.
include:
- template: Security/Container-Scanning.gitlab-ci.ymlContainer Scanning needs access to images in the GitLab Container Registry. GitLab provides predefined variables like CI_REGISTRY_IMAGE and CI_COMMIT_SHA so the template knows which image to scan — usually the one tagged with the same commit SHA.
include:
- template: Jobs/SAST.gitlab-ci.yml
- template: Jobs/Secret-Detection.gitlab-ci.yml
- template: Jobs/Dependency-Scanning.gitlab-ci.yml
- template: Security/Container-Scanning.gitlab-ci.yml
stages: [build, test, deploy]
container_scanning:
stage: test
image: docker:latest
services: [docker:dind]Notice how all security is enabled with just four lines of include: template — that's the power of native DevSecOps. One thing to note is that the container scanning job needs an image already built in the previous stage, so arrange its dependencies correctly.
The results of all these scanners aren't just printed to logs — GitLab aggregates them into a centralized vulnerability report. In the UI, you can see:
The management flow can also be automated: when high severity is found, the pipeline can fail and block the merge request. GitLab compares findings against previous pipelines, so teams can tell whether the latest change adds or reduces vulnerabilities.
Tip
Start with only Critical and High vulnerabilities so the team isn't overwhelmed. Enable scanners one at a time, understand the findings, then tighten pipeline rules. Being too strict from day one just makes the whole team disable the scanners out of frustration.
| Mistake | Symptom | Solution |
|---|---|---|
Storing secrets in variables | Secrets leak in pipeline logs | Use Masked + Protected variables, or Vault |
| Scanning only on the main branch | Vulnerabilities found after merge | Scan on every MR with workflow: rules |
| Ignoring findings | Vulnerabilities pile up uncontrolled | Schedule regular finding reviews |
| Not scanning deployed images | Old images leak into production | Scan the image with the same tag being deployed |
| Templates run but don't block MRs | Green pipeline despite findings | Configure high severity to stop the pipeline |
| Deleting a secret but not revoking it | Secret still valid in the cloud | Revoke credentials and regenerate immediately |
In this episode we covered why GitLab is a leader in integrated DevSecOps — security fused into the development flow, not a separate process; enabling four native scanners — SAST for source code, Secret Detection for leaked credentials, Dependency Scanning for third-party libraries, and Container Scanning with Trivy for Docker images — each with just an include: template; and reading and managing findings via the MR widget, security dashboard, and Security Center.
The core of this episode: security is not an add-on feature, but a built-in pipeline layer. With DevSecOps, vulnerabilities are found while they're still cheap to fix — on the developer's laptop, not after release to production.
In the next episode 13 we'll tackle the credential side more deeply: Passwordless Cloud Authentication using OIDC — how to authenticate pipelines to AWS, GCP, and Azure without storing a single permanent secret key. See you there!