๐งช Test Phase โ Fast Gates, Deep Tests, and What Still Belongs Out of Band
Intro: The test phase is where many teams either slow delivery to a crawl or fool themselves with green dashboards. Good DevSecOps testing separates fast gates that must happen often from deeper testing that should happen deliberately, and it makes that split explicit.
What this page covers
- how to structure security testing without breaking delivery speed;
- what still belongs inside CI and what should run out of band;
- practical examples for baseline DAST, IaC checks, container scanning, and higher-confidence IAST-style testing;
- older versus current testing patterns.
The operating model
A useful test strategy has three lanes:
Lane 1 โ developer-fast checks
Run on pull request or every merge:
- secret scanning;
- OpenAPI linting;
- IaC scanning;
- fast dependency or image scanning;
- baseline web or API checks where startup is cheap.
Lane 2 โ acceptance and release checks
Run before promotion to production:
- smoke tests for auth, routing, and deployment correctness;
- targeted DAST on critical routes;
- permission and config assertions;
- artifact and provenance checks;
- release evidence collection.
Lane 3 โ out-of-band depth
Run on cadence or for high-risk systems:
- fuzzing;
- manual penetration testing;
- deeper DAST coverage;
- red teaming or game-day scenarios;
- specialist review for crypto, auth, multi-tenant boundaries, or business logic.
This split is one of the most valuable ideas reinforced by the books: not every useful security test belongs in a PR pipeline.
Older pattern versus current pattern
| Older pattern | Why it happened | Better modern framing |
|---|---|---|
| One big โsecurity testโ stage | Easier to explain | Multiple small stages with fail-fast ordering |
| Weekly or monthly DAST only | Scanners were slow and security owned them | Baseline DAST in CI plus deeper scheduled or pre-release scans |
| Manual pen test before every release | Traditional release governance | Targeted manual testing for high-risk changes, product milestones, or regulated surfaces |
| Treating IAST as exotic | Narrow vendor adoption | Use IAST selectively where apps are testable and teams need lower-noise signal |
| Testing only app code | App team owned the release | Test contracts, templates, images, permissions, and deployment behavior too |
Practical pipeline principle
Put cheap, high-signal checks first. Push slow and expensive checks later, or off the critical path, unless the risk requires otherwise.
Practical snippet โ fast security checks in GitLab CI
stages:
- lint
- scan
- test
openapi_lint:
stage: lint
image: node:22-alpine
script:
- npm install -g @redocly/cli
- redocly lint openapi.yaml
checkov_iac:
stage: scan
image: bridgecrew/checkov:latest
script:
- checkov -d infra --quiet
trivy_image:
stage: scan
image: aquasec/trivy:latest
script:
- trivy image --exit-code 1 --severity HIGH,CRITICAL "$CI_REGISTRY_IMAGE:$CI_COMMIT_SHA"
zap_baseline:
stage: test
image: ghcr.io/zaproxy/zaproxy:stable
script:
- zap-baseline.py -t https://review.example.internal -r zap-baseline.html
artifacts:
when: always
paths:
- zap-baseline.html
Practical snippet โ legacy Jenkins-style ZAP example
This style is still understandable and still works in some organizations, especially where Jenkins remains the control plane.
docker run --rm \
--network host \
ghcr.io/zaproxy/zaproxy:stable \
zap-baseline.py -t http://127.0.0.1:8080 -r zap.html
Why it is โlegacy but validโ
- good for internal pipelines and lab environments;
- easy to reason about;
- still useful for quick baseline checks.
Why many teams now prefer a different packaging model
- GitLab and GitHub reusable workflows are easier to templatize across teams;
- containerized jobs integrate better with artifact and report handling;
- modern pipelines often already have built-in environments and policy stages.
Practical snippet โ targeted integration tests for security features
security_functional_tests:
stage: test
image: python:3.12-slim
script:
- pip install pytest requests
- pytest tests/security/test_authz.py -q
- pytest tests/security/test_tenant_isolation.py -q
- pytest tests/security/test_export_limits.py -q
Use this for business-critical checks that scanners miss.
IAST guidance in plain English
IAST can be useful when:
- the team has realistic test traffic;
- scanner noise is too high;
- the application stack is supported well enough;
- the team wants runtime context without doing production blocking.
IAST is a good confirmation layer, not a magic replacement for code review or design review.
What still belongs out of band
These should usually not block every merge:
- full-coverage DAST on a large application;
- manual pentest;
- fuzzing campaigns;
- red-team exercises;
- broad external audits.
Run them on cadence, before major launches, or when risk materially changes.
Practical checklist for the test phase
- Are fast checks ordered so that cheap failures happen first?
- Are auth, authz, tenant isolation, and admin workflows covered by focused tests?
- Does the pipeline test deployed behavior, not only build output?
- Are baseline DAST results triaged and tuned instead of ignored?
- Are high-risk flows covered by at least one human-reviewed test path?
Common mistakes
- running slow scans on every commit without tuning;
- trusting a clean DAST run more than focused authorization tests;
- never testing deployment correctness and secret/permission injection;
- blocking developers on scanner noise that no one owns;
- treating โsecurity testingโ as a separate track from release readiness.
Related pages
- Security Quality Gates and Release Blocking
- ๐ท๏ธ OWASP ZAP in the Real World: Tuning, Reports, and Quality Gates
- ๐งพ API Design and Contract Security
- ๐งญ DevSecOps Toolchain Practical Map โ Legacy to Current
---Author attribution: Ivan Piskunov, 2026 - Educational and defensive-engineering use.