In ZAP, what does the passive engine's scanOnlyInScope setting gate, and where is that check made?
answer
- gated before queueing, not inside a rule
- the controller decides; no rule is consulted
- a skipped record is skipped for good
- ask which default you mean, engine or job
basics
~20 sZAP's PassiveScanController checks scanOnlyInScope per history record before queueing. With it on, a record the session calls out of scope is never queued, so no passive rule sees it and the cursor moves past it permanently.
solid answer
~30 sThe check lives in the controller, not in the rules. For each history record it reaches, `PassiveScanController` submits work only when `!isScanOnlyInScope() || session.isInScope(href)` holds — so an out-of-scope record is never queued, no rule is consulted, and the one-way cursor moves past it permanently. Its default is genuinely ambiguous, and both answers are shipped: the engine's own persisted option, `PassiveScannerOptions`, defaults it **off** (everything recorded gets scanned), while the `passiveScan-config` automation job's parameter defaults it **on** and is applied whether or not you write the key. So adding that job to a plan silently narrows passive scanning to in-scope traffic.
go deeper
Remember what the switch does: with it on, only traffic the session considers in scope is passively scanned at all. Everything else is dropped before any rule sees it.
Place the check precisely. It is in the controller, evaluated per history record at queue time, so an out-of-scope record produces no task and no rule callback, and the cursor never returns to it.
Say which default you mean. The engine ships it off and the passiveScan-config job ships it on and applies its value whether or not the key is written, which is how a plan narrows passive coverage without anyone choosing to.
Set the house rule that scope-limiting switches are written explicitly in a plan rather than inherited. A setting whose effective default depends on which job happens to be present is a setting nobody owns.
## Where the decision is made `scanOnlyInScope` is a **queueing** decision, not a rule-level one. Inside `PassiveScanController`'s walk over the History table, the submit is guarded: > submit a `PassiveScanTask` for this record when the record exists **and** either `scanOnlyInScope` is off, **or** the session reports the record in scope. Everything that follows comes from that placement: - **No rule is consulted.** An out-of-scope record produces no task at all, so no passive rule's callbacks fire for it. This is not a per-rule filter and not something a rule can override. - **The skip is permanent.** The controller's cursor is a one-way walk over ascending history ids. A record it declined to queue is behind the cursor immediately, and no later change brings it back — turning the setting off afterwards rescans nothing. - **It is evaluated per record, at queue time.** If the session's scope changes mid-run, records reached before the change were judged by the old answer and records after it by the new one. The result is a report whose coverage depends on when in the run each page was recorded. ZAP answers the in-scope question through the session — the controller asks `Session.isInScope` for the record and does not decide anything about URLs itself. The rules that make a URL in scope are a separate subject; what matters here is that the passive engine delegates the question and then uses the answer as a gate. ## The defaults that disagree, and why both are real This is the part that costs people an afternoon. The same setting carries different shipped defaults in different places, and both are genuinely shipped: | where the default is declared | what it is | when it applies | |---|---|---| | `PassiveScannerOptions`, the engine's persisted option | **off** — every recorded message is scanned | an install with no plan, or a plan with no `passiveScan-config` job | | `PassiveScanConfigJob`'s parameters, in the `pscan` add-on | **on** | any plan containing a `passiveScan-config` job | The engine's own source is unambiguous about its half: the field's documentation reads *"Default is `false`, all messages are scanned"*, and the option is read with `false` as the fallback. The job's half is equally real but arrives differently. Its parameters object initialises `scanOnlyInScope` to true, and the job applies its parameters to the engine's options by copying every non-null value across. Because the field is never null, **it is copied whether or not you wrote the key in YAML.** Merely including a `passiveScan-config` job therefore turns the setting on. The shipped `passiveScan-config` template comment says *"Only scan URLs in scope (recommended), default: true"*. That describes the job accurately and the engine inaccurately, which is exactly the sort of stale-comment trap worth checking against source rather than believing. ## What it does to a pipeline run The combination that produces an empty passive report is specific and common: 1. A plan includes a `passiveScan-config` job — nearly all do, because it is where alert caps and per-rule thresholds go. 2. The setting therefore turns on, even though nobody wrote it. 3. The plan's environment does not actually put the crawled host in a context, or puts a different form of the URL there. 4. The session answers *not in scope* for every record, so nothing is ever queued. 5. The backlog counter sits at zero throughout, so a `passiveScan-wait` job completes immediately and the plan is green with nothing to report. Every step is individually reasonable, and no step logs an error. The signal to look for is a passive result set that is **empty rather than all-clear** — no records scanned, as opposed to records scanned and nothing found. ## How to be sure which way it is set - Read the plan: does it contain a `passiveScan-config` job, and does that job write `scanOnlyInScope` explicitly? If it does not write it, the job's default is what takes effect. - Ask the running instance: the `pscan` API exposes a `scanOnlyInScope` view and a matching action to set it, so a daemon run can report and change the live value. - Cross-check against the backlog: if the setting is on and scope is empty, the records-to-scan count never rises even while the crawl is clearly recording traffic. The rule to carry away is that **"the default" is not a single fact here.** Say which default you mean — the engine's, or the job's — and the confusion disappears.
- If you turn scanOnlyInScope off part-way through a ZAP run, are the skipped records scanned?No. The controller's cursor over history ids only moves forward, and a record it declined to queue is behind the cursor the moment it moves on. Changing the setting affects records the controller has not yet reached. Recovering the skipped traffic means recording it again, not toggling the option.
- Why can including a passiveScan-config job change scanOnlyInScope even when the key is absent?The job holds its parameters in an object whose `scanOnlyInScope` field is initialised to true, and applying the job copies every non-null parameter onto the engine's options. A field with a non-null initialiser is never absent at apply time, so the job's default is written through regardless of what the YAML contained.
saying these in an interview costs you the question
- Says each passive rule checks scope itself before deciding to run.
- Claims turning the setting off later rescans the records already skipped.
- States one default for scanOnlyInScope without saying engine or job.
- Assumes leaving the key out of a passiveScan-config job leaves it unchanged.
- Reads an empty passive result set as a clean target rather than as nothing scanned.