The ZAP Decision That Ships With Every Closed Row
A passive ZAP baseline is cheap to re-run and proves nothing per issue, so each closed row must carry one explicit decision token.
TL;DR
KotaPortal's post-RBAC review found closed cards assumed to need a ZAP scan each, wasting pipeline time since passive baseline scans prove nothing new. The team now requires one decision token per row: ZAP:rescan with a valid path, or ZAP:none with a mandatory reason. Enforced by a small lint, this turns silent security bypasses into deliberate, auditable choices.
A Kanban board displaying closed rows after a post-RBAC review for KotaPortal reveals a consistent pattern: none of the completed cards carry scan notes. The initial assumption in many development workflows is that every closed security issue requires its own dedicated ZAP scan to validate the fix. This assumption leads to redundant pipeline executions, increased build times, and a false sense of confidence.
The Baseline Reality
What turned out to be true is that the passive baseline scan operates under strict, predictable constraints. The passive baseline time-boxes its spider at one minute by default [1]. It performs no active attacks, meaning it will not attempt to exploit vulnerabilities [1]. Furthermore, alerts default to WARN, which is entirely configurable to FAIL or IGNORE depending on the specific pipeline stage [1]. These characteristics make the baseline scan highly suitable for continuous integration and continuous deployment workflows, even when executed against production environments [1]. Because of these fixed parameters, running identical re-scans for every single closed card proves nothing new. It merely consumes compute resources without expanding the security surface tested.
The Decision Token Convention
To address this inefficiency, a strict convention was established for the repository. Each done row must carry exactly one decision token. The first valid option is ZAP:rescan <path>, which triggers an automated validator to confirm that the specified folder exists and contains the relevant test artifacts. The second valid option is ZAP:none - <reason> — in this case, providing a clear, actionable justification after the dash is mandatory. This binary choice eliminates workflow ambiguity. A developer cannot simply mark a task as done; they must explicitly declare whether a scan is warranted or why it is intentionally omitted.
Security and Audit Alignment
This approach mirrors a fundamental principle in application security architecture. An application must never remain neutral when no access rule matches; it must explicitly deny or permit the request [2]. Ambiguity in authorization logic is a direct vulnerability. This is particularly relevant given that Broken Access Control consistently tops the OWASP Top 10 2021 list [2]. Just as an authorization module must default to a secure, explicit state, the development workflow must default to an explicit security decision.
The logging tie-in further reinforces this operational discipline. Maintaining an audit trail is a legitimate and necessary security use case for compliance and forensics. However, systems must be carefully tuned to avoid logging too much, which creates noise and storage bloat, or logging too little, which leaves critical blind spots during incident response [3]. The ZAP:none - <reason> token serves as a precise, low-noise audit artifact. It records the rationale for skipping a scan without flooding the repository with redundant, empty scan reports.
The enforcement itself is a small addition to the board validator: a regex captures the token, a second check requires the rescan path to exist as a directory, and a missing reason after the dash rejects the row. Multiple tokens on one row are rejected just as firmly as none, because two decisions on one line mean no decision at all. The lint runs in the same validation pass that already checks review-status tokens, so there is no new pipeline stage to maintain.
Rolling the convention out also changed how the baseline results are read. A rescan session is judged by rule-level deltas against the previous session, not by the raw alert count, since benign instances of a rule can rise simply because new public surface was crawled. The session folder linked from the evidence column carries both the raw report and that delta comparison, which is what makes the token auditable months later instead of just present.
Decisions forced by a linter turn hidden security debt into a trackable item. When the workflow requires an explicit token, the team can no longer silently bypass security validation. The requirement to choose betweenZAP:rescan and ZAP:none ensures that every closed row represents a deliberate, documented security posture rather than an accidental omission.Sources
[1] ZAP Baseline Scan documentation