Security Checks That Belong in CI

Put secret scanning, dependency review, and a ZAP baseline in CI. Fail the build only on findings you already treat as release blockers.

This post is part of the Security Testing Basics series. If you missed the previous post, read Part 4 — Sessions, Cookies, and CSRF first.


Introduction — The Checklist Will Not Run Itself

Parts 1 to 4 are passes a person can do. They will be skipped on a Friday release unless the ones that are mechanical live in the pipeline. The mistake is to put all of them in the pipeline and fail the build on every line the tools emit.

Three Checks, Three Jobs

Secrets. A committed key, token, or private certificate should fail the pull request. The finding is almost never a false positive you want to ship. Gitleaks, or the platform’s own secret scanner, is enough.

Dependencies. A known vulnerability in a library you ship is a review item. Failing every pull request on a low-severity issue in a dev tool will train people to ignore the job. Fail on the severities you would actually stop a release for. Review the rest on a schedule.

A ZAP baseline against an ephemeral or staging URL. Part 2 was the triage method. The pipeline only stays honest if it uses that method. Fail on alerts you have not recorded as accepted. Do not fail on the entire default rule set.

- name: Secret scan
  run: gitleaks detect --source . --redact --exit-code 1

- name: ZAP baseline
  if: github.ref == 'refs/heads/main'
  run: docker run --rm -v "$PWD:/zap/wrk:rw" -t ghcr.io/zaproxy/zaproxy:stable
    zap-baseline.py -t ${{ vars.STAGING_URL }} -r zap.html -c zap-rules.conf

The rules file is the triage, checked in. An alert id in that file is an accepted risk with a reason in the commit message. A new alert fails until someone adds it on purpose.

What Stays Manual

The access-control pass from part 3 needs two users and an object. You can automate a single swapped-id request, and you should, as an API test. You cannot replace the pass with a scanner plugin and delete the test.

The hostile-page CSRF check from part 4 is awkward in CI and easy by hand on a schedule. Do not block merges on a flaky reproduction of a browser policy.

Logout invalidating a session is a normal API test. That one belongs in the suite you already run.

Do Not Page QA for Every CVE

Dependency review is a queue. Name an owner. QA’s part is to ask, on a release that touches auth or payments, whether the queue has an open item of the severity you agreed to block. QA is not the person who patches the framework on a Saturday unless that is actually their job.

Conclusion

Automate the checks that have a clear fail: a secret in the diff, a vulnerability above the line you set, a new ZAP alert you have not accepted. Leave the two-user and cross-site passes as tests and as scheduled work. That is the series: a mindset, a scanner you can triage, the cases the scanner misses, the session, and the part that fits in CI.

Action for this week: Add a secret scan to the pull-request workflow. Then take one accepted ZAP alert from part 2 and put its id in a rules file so the baseline can fail on anything else.


This concludes the Security Testing Basics series.