Chapter 25Lesson 04~330 minutes

SAST, Secret Detection, Dependency Scanning, Container Scanning, DAST, and Coverage-Guided Security: Diagnostics, Failure Modes, Security, and Performance

Diagnose malformed reports, wrong scan targets, leaked-secret response errors, analyzer drift, fork/MR trust mistakes, and false assurance by preserving evidence and proving what actually ran.

DiagnosticsMalformed reportWrong targetCredential responseAnalyzer driftFalse assurance

Learning objectives

  • Use preserve → scope → inspect → minimally repair → re-verify for scanner/report failures.
  • Diagnose a successful job whose Secure report is malformed and rejected.
  • Prove whether the scanner analyzed the intended commit, image digest, dependency set, or URL.
  • Respond to a leaked credential by revoking/rotating first, not by rewriting Git history first.
  • Recognize analyzer/template drift and “no findings” as evidence-quality problems rather than proof of safety.
Availability baseline — verified 2026-08-22 against current GitLab 19.3 documentation. Basic SAST, pipeline secret detection, and pipeline container scanning run on Free/Premium/Ultimate across GitLab.com, Self-Managed, and Dedicated, and Free/Premium users can download their JSON report artifacts. Rich merge-request security presentation, vulnerability-management workflows, security dashboards, policy enforcement, and several advanced analyzer features are Ultimate. GitLab Dependency Scanning is currently Ultimate. GitLab DAST is currently Ultimate. The curriculum phrase “Coverage-Guided Security” is retained for path/title stability, but GitLab coverage-guided fuzz testing was deprecated in 18.0 and removed in 19.0; it is therefore taught as historical/migration context, not as a runnable GitLab 19.3 feature. Required labs use only SAST/secret-detection raw evidence or safe fixtures and do not require a paid tier.

1. Diagnostic sequence: preserve → scope → inspect → minimally correct → verify

Do not start by rerunning scans repeatedly or deleting the evidence. Preserve the pipeline/job URL, commit SHA, pipeline source, runner identity, expanded CI configuration, analyzer image/version, report artifact, report checksum, and target identity. Then classify the problem: execution failure, target mismatch, report-generation failure, schema/ingestion failure, authorization/tier mismatch, or true security finding.

2. Failure A — scanner job passes, but the report is malformed

An intentionally broken job can exit zero and upload invalid SAST JSON:

broken_sast_fixture:
  stage: test
  image: alpine:3.22
  script:
    - printf '{"vulnerabilities": []}\n' > broken-sast.json
  artifacts:
    reports:
      sast: broken-sast.json

The shell job can succeed, but the report lacks required Secure report fields such as a supported version and scan metadata. GitLab validates security reports before ingestion and surfaces a validation error. Repair: do not suppress the error or rename the file. Fix the analyzer/integration so it emits a current supported schema, validate locally where practical, rerun, and compare the new report checksum/schema.

3. Failure B — report exists, but scanner execution failed

If the scanner job itself fails, do not assume the uploaded report is normal ingested security evidence. GitLab’s scanner integration contract says failed jobs are not ingested as ordinary security results, even if artifacts remain downloadable. Preserve stderr/exit code and report, then fix the scanner execution first.

Observation Meaning
Job success + valid report Normal evidence path; findings may still be present.
Job success + invalid report Ingestion/schema failure.
Job failed + report artifact exists Execution failed; artifact is diagnostic, not normal ingested result.
Job success + empty valid report No findings from that scanner/scope; not proof of security.

4. Failure C — source scanner analyzed the wrong ref or pipeline source

Compare the report/job to the intended release candidate:

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,ref,commit:(.commit.id // null)})'

A branch pipeline, merge-request pipeline, schedule, child pipeline, or downstream pipeline can have different inputs/variables/trust. If the report SHA is not the candidate SHA, do not “relabel” the finding. Run the correct pipeline or bind the release gate to the correct scan evidence.

5. Failure D — container scan analyzed the wrong image

A tag such as latest may have moved between build and scan. Preserve the report’s image identity and registry metadata; resolve the candidate digest independently. If digests differ, the scan is not evidence for the candidate image.

candidate=registry.example.invalid/team/app@sha256:<candidate-digest>
scanned=registry.example.invalid/team/app@sha256:<report-digest>
# Evidence is valid for promotion only when the immutable identities match.

Repair the pipeline so the build exports the immutable digest and the scan consumes that identity, then the release/deployment consumes the same digest.

6. Failure E — DAST points at the wrong URL/environment

DAST evidence is meaningful only for the running deployment it exercised. A test URL can be stale, point to another branch, or front a different image. Preserve environment/deployment record, URL, source SHA, image digest, DAST profile/config, and scan timestamp. Never compensate by scanning production “to be sure.” Deploy the intended disposable candidate to a test environment and rescan there.

7. Failure F — a secret finding is “fixed” only by history deletion

This is an incident-response error. If a real credential was exposed, anyone who saw it may already have copied it. Rewriting Git history cannot invalidate the credential.

Correct order: revoke/rotate/disable → contain access and inspect use → replace dependent configuration → then remove the secret from reachable history/logs/artifacts where required → rescan and verify.

History rewrite is destructive and can disrupt collaborators. It belongs after credential invalidation and with an explicit recovery/coordination plan.

8. Failure G — template/analyzer update changes behavior unexpectedly

GitLab security templates and analyzer images evolve. A new version can add rules, change language detection, upgrade a scanner, alter advisory content, or fix parsing. Preserve the old/new expanded configuration and analyzer image/version; compare report schema, scanner metadata, finding set, and release notes. Test scanner configuration changes in a merge request before default-branch rollout.

If a temporary pin is necessary, document why, exact version, owner, and removal date. Do not treat a pin as permanent security configuration.

9. Failure H — untrusted MR needs a secret or privileged runner to scan

Do not solve this by exposing the secret or loosening runner protections. Identify why the scanner needs the privileged resource. Options include a reduced untrusted-MR scan, public/mirrored test dependencies, a post-review trusted pipeline, or short-lived narrowly scoped credentials with no production reach. If no safe path exists, fail closed and document the missing evidence.

10. Failure I — “no findings” is treated as proof of security

An empty report means only that the configured scanner found nothing in its scope with its current rules/intelligence. It says nothing about unscanned languages, business logic, architecture, authorization design, runtime configuration, supply-chain compromise, or unknown vulnerabilities. Compare scanner coverage to the threat model and release asset inventory.

11. Performance and cost failures that weaken security

Security jobs can become the critical path. Diagnose before disabling controls:

Symptom Likely cause Safer response
Long SAST job Large scan scope/analyzer mix/cold image pulls. Measure analyzer durations; exclude only unsupported/generated paths with review; use appropriate runner/cache.
Repeated container downloads Large image + remote registry/network. Scan candidate once per immutable digest where policy allows; colocate runner/registry appropriately.
Duplicate scans Project config + policy both add same scanner. Inspect expanded configuration/policy source; remove unintended duplicate.
Frequent analyzer failures Runner/resource/network incompatibility. Fix runner/executor/resource prerequisites; do not turn scanner off indefinitely.
Teams bypass scan Chronic latency/noisy false positives. Fix feedback loop and triage ownership; preserve required gates.

12. Broken-report repair exercise

Starting from the malformed report fixture above, the correct diagnostic is:

  1. Confirm job exited zero.
  2. Open the pipeline Security/report validation message.
  3. Download the exact broken artifact and checksum it.
  4. Inspect declared report type and report version.
  5. Replace the hand-crafted malformed report with output from a current analyzer or a fixture validated against the current schema.
  6. Rerun and verify the validation error disappears.

Do not change artifacts:reports:sast to a plain artifact merely to hide validation.

13. Minimum diagnostic evidence packet

project: group/project
pipeline_id: 1234
pipeline_source: merge_request_event
source_sha: <40-hex SHA>
job_id/name/status: ...
runner: hosted-or-isolated-description
analyzer/template: exact configured source
report_type: sast|secret_detection|container_scanning|...
report_sha256: ...
target_identity: source SHA | image digest | test URL+deployment
validation_status: valid | invalid + error
triage_decision: ...

Knowledge check

A security job succeeds but GitLab says its report cannot be parsed. Where do you debug first?

Why is deleting a leaked secret from Git history not the first remediation step?

A container report references digest A, but release candidate is digest B. Can the report gate B?

What should you compare after an analyzer template update causes many new findings?

Does a valid empty SAST report prove secure design?

Summary

Scanner troubleshooting is evidence forensics. Preserve the failing state, prove scope and target identity, distinguish job execution from report validation/ingestion, repair the smallest broken layer, and rerun without hiding the original cause.

Official references

Primary sources used for the current GitLab 19.3 behavior taught in this lesson:

Next lesson

Checkpoint Lab

Combine two evidence types, a deliberately malformed report, scope predictions, triage, repair, and cleanup into one production-style security evidence exercise.

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.

Ethereum / ERC-20
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0Send only Ethereum/ERC-20 compatible assets to this address.