skip to content

In OWASP ZAP, what decides whether a URL is in scope, and what has no say in it?

level: middleimportance: should knowfreq 54%

answer

  1. it is computed, not stored
  2. two walks over the same collection
  3. a per-context flag gates participation
  4. exclusion is not confined to its own context
  5. included and not excluded

basics

~20 s

Contexts decide it, and nothing else does. A URL is in scope when some context with its in-scope flag set includes it and no in-scope context excludes it. Exclusion crosses context boundaries, and scope has no storage of its own.

solid answer

~40 s

`Session.isInScope(url)` computes the answer rather than reading it. It truncates the URL at the first `?`, then walks the contexts twice: a URL is **included** if any context whose `inScope` flag is set matches it with an include pattern, and **excluded** if any such context matches it with an exclude pattern. In scope means included and not excluded. Two details bite. The `inScope` flag governs participation and is enabled on a new context, so a context you forgot about is still contributing. And the exclusion loop does not stop at the context that included the URL — an exclude in one in-scope context removes a URL that another context included. The session's URL table declares scope-named constants, but nothing reads or writes them: there is no session-level scope list.

code

yaml · 10 lines
yaml
env:
  contexts:
    - name: app
      urls:
        - https://example.com/
    - name: docs
      urls:
        - https://example.com/docs
      excludePaths:
        - https://example\.com/docs/internal.*

go deeper

for a junior

Recall that scope comes from contexts and that a URL has to be matched by an include rule before anything else is considered.

for a middle

Explain the two walks and the flag: included by some in-scope context, excluded by none, with the flag deciding which contexts take part at all.

for a senior

Be ready to diagnose a run that reached less than expected by enumerating every context the session carries, not just the one the job names, and checking which of them still has the flag set.

for a principal

Decide how many contexts a standard plan may declare and who reviews an exclude entry, given that any one of them silently subtracts from every other context in the same session.

## Scope is a derived value, not a stored one There is no list in OWASP ZAP called "scope" that you edit. `Session.isInScope(String url)` computes the answer every time it is asked, from the contexts the session holds. It does three things, in order: 1. Truncate the URL at the first `?`, exactly as the context layer does. 2. Ask `isIncludedInScope`: walk every context, and return true if any context **whose `inScope` flag is set** has an include pattern matching the URL. 3. Ask `isExcludedFromScope`: walk every context again, and return true if any context whose `inScope` flag is set has an exclude pattern matching the URL. A URL is in scope when step 2 says yes and step 3 says no. That is the whole definition, and everything that claims to honour scope — the passive engine's in-scope-only option, the active scanner's restriction to scope, the mode that refuses to start a scan outside it — is reading this one derived boolean. ## The flag that takes a whole context out of the calculation Each `Context` carries a boolean `inScope`, and it **defaults to enabled** when a context is created. Both loops above skip any context whose flag is off. So a context can exist, be fully configured, have authentication and users attached and be named by a job, and still contribute nothing to scope, because the flag governs participation rather than the context's own membership test. Note the asymmetry this creates: - `Context.isInContext(url)` — "is this URL in **this** context" — ignores the flag entirely. - `Session.isInScope(url)` — "is this URL in scope" — skips every context whose flag is clear. A URL can therefore be in a context and out of scope at the same time. Jobs that take a context name operate on the first notion; anything asking "is this in scope" operates on the second. ## Exclusion crosses context boundaries This is the behaviour that surprises people who assume a context is a self-contained unit. The exclusion loop does not stop at the context that included the URL. It walks **every** in-scope context and returns true on the first exclude pattern that matches. The consequence: if context A includes `https://example.com/.*` and context B — a different context, also in scope, perhaps built for a different job — excludes `https://example\.com/api/.*`, then `/api/` is **out of scope for the whole session**, including for work driven through context A. Inclusion is per-context and additive; exclusion is session-wide and subtractive. The only way to stop context B's excludes from reaching context A's URLs is to take B out of scope, or to remove it. ## The session lists that look like a scope layer and are not Core's `RecordSessionUrl` declares a set of integer constants that key rows in the session's URL table. Four of them are live and used: the exclude-from-proxy list, the exclude-from-scan list, the exclude-from-spider list, and the exclude-from-websocket list, each read by a different component. Two of them are named exactly as though they were the session's own scope layer — `TYPE_EXCLUDE_FROM_SCOPE` and `TYPE_INCLUDE_IN_SCOPE`. Measured across the project's repositories, **their own declaration is the only place either of them appears**. Nothing reads them; nothing writes them. There is no session-level scope list, and no way to add a URL to scope without a context. The constants are a leftover, and the reason they matter is that their names invite a reader to assume a layer that does not exist. ## What that leaves you with | question | where the answer comes from | |---|---| | Is this URL in **this** context? | `Context.isInContext` — the context's own include and exclude lists, ignoring the flag | | Is this URL in **scope**? | `Session.isInScope` — every in-scope context's includes, minus every in-scope context's excludes | | Can I put a URL in scope without a context? | No. Scope has no storage of its own | | Does a session-level exclude list change scope? | No. Those lists are read by individual components; scope never consults them | ## In a pipeline Three practical rules follow for anyone running this unattended: - **Count your contexts.** A plan that declares more than one, or a saved session that already carried some, means every one of them with the flag set is contributing excludes to the whole run. - **A context you do not want participating should have its flag cleared**, not have its rules emptied — emptying the include list stops it contributing includes but its excludes still bite as long as the flag is on. - **"In scope" narrows what is acted on, not what is reachable.** A scan restricted to scope reports only what it visited, so a scope that is narrower than you think produces a quiet run rather than an error, and a clean result you should not trust.

  • Can a URL be inside a context and still be out of scope?
    Yes, and that is the normal case for a context whose `inScope` flag is off. `Context.isInContext` answers membership from the context's own lists and ignores the flag entirely, while `Session.isInScope` skips any context whose flag is clear. A job addressed to that context still works on it; anything that asks about scope will not see it.
  • What is the practical way to stop one context's excludes affecting another?
    Clear the `inScope` flag on the context you do not want participating, or delete it. Emptying its include list is not enough: the exclusion walk visits every in-scope context regardless of whether that context included anything, so its exclude patterns keep removing URLs other contexts included.

saying these in an interview costs you the question

  • Says scope is an editable list of hosts held by the session
  • Thinks an exclude rule only affects the context it was written in
  • Assumes a newly created context starts out of scope until enabled
  • Believes the session's exclude-from-scope constant removes a URL from scope
  • Concludes a URL is in scope because one context includes it, ignoring the others