skip to content

In ZAP, what can quietly reduce what a completed passive sweep reports, without raising any error?

level: seniorimportance: nice to knowfreq 30%

answer

  1. finished is not the same as complete
  2. limits that log but never fail
  3. a cap that disables, not truncates
  4. maxAlertsPerRule calls setEnabled false

basics

~20 s

Limits inside ZAP's passive engine cut findings without failing anything: a per-rule alert cap that disables the rule outright once exceeded, a maximum body size that skips oversized bodies, and a rule exception that is caught and logged.

solid answer

~40 s

The per-rule cap is the sharpest. When `maxAlertsPerRule` is set, the engine counts alerts per rule and, once a rule passes the cap, calls `setEnabled(false)` on it — the rule is **switched off for the rest of the run**, so it also stops examining every later page, and the only trace is an info-level log line. `maxBodySizeInBytesToScan` skips whichever half of a message exceeds it, incrementing a `stats.pscan` counter rather than reporting anything. And an exception thrown by a rule is caught per record, logged against the record id and URL, and the sweep continues. The engine's own defaults for both limits mean *no limit*; a generated baseline plan writes a cap in.

go deeper

for a junior

Take away one habit: a finished passive scan is not the same as a complete one. Several limits inside the engine can reduce what was examined without anything looking wrong.

for a middle

Name the mechanisms and what each costs: the per-rule alert cap, the maximum body size, and a rule throwing. Be able to say which one disables a rule outright rather than just dropping findings.

for a senior

Explain why this bites in CI specifically: the engine's own defaults impose no cap, while a generated baseline plan writes one in, so the behaviour is on by default in the unattended path and absent on a workstation.

for a principal

Own the reporting contract. If the pipeline's artefact is meant to be evidence, then the engine's statistics and the disable line belong beside it, and a silence that could have been produced by a limit must not be published as an all-clear.

## The premise: a passive report is a sample A completed passive sweep does not mean *every enabled rule looked at every recorded message*. Several mechanisms inside the engine reduce coverage without failing a job, turning a run red, or writing anything a pipeline reads. Knowing them is the difference between reading a report as a census and reading it as a sample. ## 1. The per-rule alert cap, which disables the rule The passive engine's `maxAlertsPerRule` option is usually described as a cap on how many findings a rule contributes. What it actually does is stronger. Each time a rule raises an alert, the engine increments a count keyed by that rule's plugin id, and once the count passes the cap it looks the rule up and calls **`setEnabled(false)`** on it. The effects compound, and the second is the one people miss: 1. The finding list from that rule is truncated at the cap. 2. **The rule stops running at all.** Every record queued afterwards is scanned by every rule *except* that one. So if the cap is hit early — which a chatty rule on a large, uniform site will do — that rule contributes nothing for the rest of the crawl, including on the pages you actually cared about. The only trace is a log line at info level naming the rule and the cap. Nothing is written to the plan's progress and no job fails. The defaults diverge, which is why this bites in CI and not on a desktop: | where | cap | |---|---| | the engine's own persisted option | none — a rule may raise as many findings as it finds | | the plan a packaged baseline wrapper generates | a cap is written into its `passiveScan-config` job | So the behaviour is normal on a workstation and on by default in the unattended path, which is precisely the inversion that makes it hard to reproduce locally. ## 2. The maximum body size, which skips half a message `maxBodySizeInBytesToScan` is checked inside the per-record task, separately for the request and the response. When a body reaches the configured size: - that callback is not made for that record, - a counter is incremented — `stats.pscan.reqBodyTooBig` or `stats.pscan.respBodyTooBig`, - a debug-level line is logged. The other half of the same message is still scanned, so the record is neither reported as skipped nor obviously missing. The engine's default means no limit, but an operator who set one to protect memory on a large site has traded away exactly the large pages most likely to carry something interesting. The stat counters are the only way to see it, which is a good argument for exporting them alongside the report. ## 3. A rule that throws The per-record task wraps each rule in its own `try`/`catch`. An exception is logged at error level with the rule name, the record id, the request method and the URL — and the loop moves on to the next rule. The record is still marked complete and the backlog still drains. A rule that throws on every page of a particular shape therefore produces a clean-looking run with a silent hole in it. The engine also warns when a rule takes an unusually long time on one record, naming the rule, the URL and the response content type, which is the same logging surface and worth watching for the same reason. ## More ways the set shrinks For completeness, because they belong to the same "nothing failed, less was scanned" family: - **Records the engine never queued.** If the engine is set to scan only in-scope traffic, out-of-scope records are dropped before any rule sees them, and the one-way cursor never returns to them. - **A queue that was emptied rather than drained.** The `pscan` component's `clearQueue` action jumps the cursor to the latest record and abandons what was pending, so those messages are discarded unscanned. ## What to do about it 1. **Decide the cap deliberately.** If you inherit a generated plan, write `maxAlertsPerRule` into its `passiveScan-config` job explicitly, at a value you chose, rather than accepting whichever default arrived with the wrapper. Removing the cap entirely is legitimate when report size is not the constraint. 2. **Export the engine's statistics with the report.** The `stats.pscan` counters — per-rule timings, oversized bodies, per-rule alert counts — are the only record that a limit fired. 3. **Read the logs for the disable line and the rule failures.** Both are the engine telling you the truth at a level nobody reads in CI. 4. **Distinguish empty from all-clear.** A result set showing that rules ran and found nothing is a different artefact from one showing that nothing was scanned, and only the first is evidence about the target. The underlying judgement is the same one every time: a passive sweep's silence is a claim about the run, not about the application, and the engine will not tell you which of its own limits produced that silence unless you go and look.

  • Why is ZAP's maxAlertsPerRule worse than a simple truncation of a rule's finding list?
    Because the engine enforces it by calling `setEnabled(false)` on the rule rather than by dropping surplus alerts. Once the cap is passed, that rule stops being run against every subsequent record, so the loss is not only the alerts beyond the cap but everything it would have found on the rest of the crawl — and the only trace is an info-level log line.
  • How would you tell after the fact that a ZAP passive rule was disabled by the alert cap?
    The engine logs an info-level line naming the rule and the cap when it disables it, and the per-rule alert counters under the `stats.pscan` prefix show the rule stopping at the cap while other rules keep accumulating. Neither reaches the report, so both need exporting deliberately if the pipeline is to notice.

saying these in an interview costs you the question

  • Thinks the per-rule alert cap only truncates the list and leaves the rule running.
  • Assumes an oversized body makes the whole record fail or be reported as skipped.
  • Believes a rule throwing an exception aborts or fails the passive sweep.
  • Treats a completed sweep as proof every enabled rule examined every record.
  • Expects these limits to surface in the report rather than only in logs and statistics.