SAST, Secret Detection, Dependency Scanning, Container Scanning, DAST, and Coverage-Guided Security: Guided Hands-On Workflow and Core Operations
Run or safely simulate Free-compatible SAST and secret-detection evidence, inspect raw reports separately from hosted security presentation, compare scanner scopes, and verify analyzer and runner trust.
Learning objectives
- Create a disposable project/branch and inspect pipeline configuration before running scanners.
- Enable only Free-compatible SAST and pipeline secret detection in the required path.
- Inspect job logs and raw report artifacts without printing all environment variables or real secret material.
- Use a safe report fixture to compare source, dependency, container, and DAST evidence domains.
- Verify analyzer/template provenance, runner type, pipeline source, commit SHA, and report scope independently.
1. Disposable lab and no-paid learning path
Create a disposable project named
gitlab-ch25-security-lab or use a disposable branch in
a throwaway project. The required path enables
SAST and pipeline secret detection only, because
those two can run and produce downloadable reports on GitLab Free.
Container scanning is an optional extension if your project already
has a tiny disposable image. Dependency scanning and DAST remain
read-only/fixture exercises because they are Ultimate.
2. Preflight: prove the project, commit, pipeline model, and runner
Record state before changing CI:
git switch -c ch25/security-lab
git rev-parse HEAD | tee ch25-base-sha.txt
git status --short
# If a pipeline already exists, inspect only selected fields:
glab api "projects/$PROJECT_ID/pipelines?per_page=5" | jq 'map({id,sha,ref,source,status})'
In GitLab, inspect Build → Pipeline editor → Validate and the project runner list. Required scanner jobs need a Linux/amd64 Docker or Kubernetes executor; GitLab.com hosted runners normally satisfy that, subject to current compute entitlement. If no runner is available, use CI Lint plus the report fixtures later in this lesson.
3. Create a tiny intentionally vulnerable source fixture
This code is deliberately unsafe and must remain in the disposable project only. It demonstrates source-code data flow without running a vulnerable server:
from flask import Flask, request
import subprocess
app = Flask(__name__)
@app.get("/lookup")
def lookup():
host = request.args.get("host", "localhost")
# DELIBERATELY VULNERABLE LAB CODE: shell injection sink.
return subprocess.check_output("nslookup " + host, shell=True, text=True)
Save it as app.py. SAST analyzer selection is
language/rule dependent, so do not promise a particular finding
count. The goal is to inspect what the current analyzer actually
reports for this exact commit.
4. Add an obviously synthetic secret-shaped fixture
Add a text fixture whose value is intentionally nonfunctional:
LAB_ONLY_TOKEN = "glpat-FAKE_DO_NOT_USE_00000000000000000000"
If the current secret rules do not flag this exact synthetic pattern, that is a valid observation—not a reason to use a real token. Use the official-style fixture path below to continue the lesson safely.
5. Enable only the Free-compatible scanners
Use current GitLab-managed job templates. Keep the
test stage:
stages:
- test
include:
- template: Jobs/SAST.gitlab-ci.yml
- template: Jobs/Secret-Detection.gitlab-ci.yml
lab_scope:
stage: test
image: alpine:3.22
script:
- printf "project=%s\nsha=%s\nsource=%s\n" "$CI_PROJECT_PATH" "$CI_COMMIT_SHA" "$CI_PIPELINE_SOURCE"
Validate the configuration before committing. Do not add Dependency Scanning or DAST templates to the mandatory Free path.
6. Write predictions before execution
| Question | Prediction to record |
|---|---|
| SAST target | Current branch workspace at the pipeline commit SHA; source files only. |
| Secret target | Committed Git content according to pipeline secret-detection scan mode. |
| Dependency evidence | Not produced by the mandatory pipeline; Ultimate/fixture only. |
| Container evidence | Not produced unless the optional image extension is enabled. |
| DAST evidence | Not produced; there is no running test site and DAST is Ultimate. |
| Coverage-guided | No GitLab 19.3 job; removed product feature. |
7. Commit and inspect the scanner jobs
Push the disposable branch and inspect pipeline/job state without printing credentials:
git add app.py .gitlab-ci.yml
git commit -m "Add Chapter 25 disposable scanner fixture"
git push -u origin ch25/security-lab
# After the pipeline starts:
glab api "projects/$PROJECT_ID/pipelines/$PIPELINE_ID" | jq '{id,sha,ref,source,status}'
glab api "projects/$PROJECT_ID/pipelines/$PIPELINE_ID/jobs" | jq 'map({id,name,status,stage,runner:(.runner.description // null)})'
Expected jobs include one or more SAST analyzer jobs based on the
detected language and a secret_detection job. Analyzer
job names can evolve; trust the expanded configuration and current
job list rather than a stale screenshot.
8. Inspect raw reports separately from the security UI
On Free/Premium, the raw JSON report artifacts are the mandatory
evidence surface. Download them through the job artifact UI or
authenticated API without echoing credentials. Common conventional
filenames include gl-sast-report.json and
gl-secret-detection-report.json.
Record only safe summary fields in your evidence ledger: report type/schema version, scanner/analyzer name/version, vulnerability count, file paths, and commit SHA. In real projects, raw secret-detection details can be sensitive; do not paste matched values into tickets or chat.
9. Compare raw evidence with GitLab-hosted presentation
If the project is on Ultimate, inspect the pipeline Security tab, merge-request security report, vulnerability report, and dashboard as appropriate. Compare one finding to the raw JSON. If the project is Free, document that the report exists even when the richer hosted triage surface is unavailable. This distinction prevents “I cannot see the dashboard” from being misdiagnosed as “the scanner did not run.”
10. Safe triage exercise: evidence, not button-clicking
Choose one synthetic result or use this fixture:
{
"finding": "synthetic command-injection example",
"scanner": "SAST fixture",
"source_sha": "<pipeline SHA>",
"decision": "remediate",
"reason": "untrusted request data reaches shell=True",
"proof_after_fix": "new scan + code review"
}
For the fake token, a correct real-world response would be
revocation/rotation if it were genuine. Because it is synthetic,
record test fixture — no live credential exists. Never
practice incident response by creating a real credential and leaking
it.
11. Compare scanner scopes with safe fixtures
| Evidence item | What it proves | What it does not prove |
|---|---|---|
SAST report referencing app.py:line |
A source analyzer matched a rule at that commit/location. | That the deployed binary contains the code, or that exploitability is confirmed. |
| Secret report | A configured detector matched repository content. | That a detected value is active; if real, validate safely and revoke first. |
| Dependency fixture: package/version/CVE | A dependency/version intersects advisory data. | That the code path is reachable/exploitable. |
| Container fixture: image@sha256 + package/CVE | The scanned image digest contains an affected component. | That another tag/digest or runtime actually uses it. |
| DAST fixture: URL + request evidence | A running target responded in a way consistent with a vulnerability. | That source/dependency scanners cover the same runtime path. |
12. Optional Free extension: scan one disposable image by digest-aware identity
If Chapter 23 left a disposable image, enable the current container template and set the exact image coordinate. Record the digest separately from the tag:
include:
- template: Jobs/Container-Scanning.gitlab-ci.yml
container_scanning:
variables:
CS_IMAGE: "$CI_REGISTRY_IMAGE:ch25-lab"
Do not add registry credentials to logs. Verify the digest through the registry before treating the scan report as evidence for a release candidate.
13. No-runner fallback: validate configuration and use labeled report fixtures
If hosted compute is unavailable, complete the learning path by:
- Use CI Lint/merged configuration to prove the SAST and secret-detection jobs would be injected.
-
Use a saved official-style SAST report fixture and
secret-detection report fixture labeled
SIMULATION — NOT PRODUCED BY THIS PIPELINE. - Compare required scope fields and explain what evidence is missing without execution.
Do not fabricate a successful live scan. A simulation is useful only when its provenance is explicit.
14. Challenge: which surface is correct?
A developer says “Dependency Scanning is missing from our Free
pipeline, so I will rename a SAST JSON artifact to
gl-dependency-scanning-report.json.” Explain the error.
Expected reasoning: filename does not determine report type, and SAST evidence is not dependency evidence. Dependency Scanning is an Ultimate capability; use an external dependency scanner/SBOM fixture for learning or an appropriate supported integration rather than relabeling evidence.
Knowledge check
Why do you inspect raw SAST/secret reports separately from the GitLab security UI?
Because scanner execution/report production and hosted presentation are different layers, with different tier availability and failure modes.
If the synthetic token does not trigger current secret rules, should you paste a real token to test the scanner?
No. Keep the lab synthetic and use an official-style fixture if necessary.
What three values should identify a source-scan run?
At minimum project, pipeline/ref/source, and exact commit SHA, plus analyzer/report identity.
Can a container scan of :latest be bound safely to
a release without a digest?
No. Resolve and record the immutable OCI digest.
What does the no-runner fallback prove?
Configuration/evidence reasoning only. It must be labeled as simulation and must not claim a live scanner executed.
Summary
The guided workflow proves Free-compatible scanner execution and raw evidence without conflating it with Ultimate UI features. It also establishes a repeatable scope ledger for source, secret, dependency, container, and runtime evidence.
Official references
Primary sources used for the current GitLab 19.3 behavior taught in this lesson:
- GitLab Docs — Application security testing
- GitLab Docs — SAST
- GitLab Docs — SAST analyzers
- GitLab Docs — Pipeline secret detection
- GitLab Docs — Customize pipeline secret detection
- GitLab Docs — Dependency scanning
- GitLab Docs — Container scanning
- GitLab Docs — DAST
- GitLab Docs — Security scanning results
- GitLab Docs — Security report validation
- GitLab Docs — CI/CD artifacts report types
- GitLab Docs — Security scanner integration
- GitLab Docs — Coverage-guided fuzz testing (deprecated)
- GitLab Docs — Deprecations and removals
Keep the academy open
Support free, practical DevOps education.
Every lesson is designed to remain readable in a browser, downloadable from GitHub, and usable without a paid learning platform. Contributions help expand and maintain the curriculum.
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0Send only Ethereum/ERC-20 compatible assets to this
address.