skip to content

In ZAP's `activeScan-policy` job, what does the `alertTags` block select, and what can it never reach?

level: seniorimportance: nice to knowfreq 30%

answer

  1. select rules without writing ids
  2. the patterns are anchored at both ends
  3. one list only narrows the other
  4. an untagged rule is invisible to it

basics

~20 s

It adds rules whose declared alert-tag keys match its include regexes, matched as a full match. It never reaches a rule that declares no alert tags, and its exclude list only narrows the include pass rather than removing any rule.

solid answer

~40 s

`alertTags` sits inside an `activeScan-policy` job's `policyDefinition` and takes four keys: `include`, `exclude`, `strength` and `threshold`. The two lists are validated as regexes, compiled, and matched with `matches()` — a **full** match — against the **keys** of each rule's declared alert-tag map, so a substring pattern needs `.*` on both ends. A rule whose tag keys match an include pattern and no exclude pattern is added to the effective rule set carrying the block's own `strength` and `threshold`. Two limits matter: `exclude` removes nothing, it only narrows what `include` adds; and a rule that declares **no** alert tags is invisible to the block entirely, so an untagged noisy rule cannot be silenced by tag — you must name its id under `rules`.

go deeper

for a junior

Recall the purpose: it selects active scan rules by the tags they declare, so a plan does not have to list rule ids one by one.

for a middle

Explain the matching — regexes compiled and applied as a full match against tag keys — and that the block carries its own strength and threshold for whatever it adds.

for a senior

Show you know its limits: exclude only narrows the include pass, explicitly listed rules win, and a rule declaring no tags cannot be reached at all.

for a principal

Own the reviewability question: a closed default opened by tag reads honestly, while a permissive default with an exclude list looks restrictive and is not.

## Where the block sits and what it is for An automation plan's `activeScan-policy` job builds a named policy that later active-scan jobs use. Its `policyDefinition` has three ways to say which rules get which dials: 1. `defaultStrength` and `defaultThreshold` — the policy-wide pair applied to every installed rule. 2. `rules` — an explicit list, each entry an id with its own `strength` and `threshold`. 3. `alertTags` — a way to reach a *set* of rules without enumerating their ids, by matching the tags the rules themselves declare. The third exists because ids are brittle to write and to review. Rules declare tags describing what class of issue they cover, and a plan can select on those instead. ## The four keys | key | type | what it does | |---|---|---| | `include` | list of regexes | a rule is a candidate if **any** of its tag keys fully matches **any** of these | | `exclude` | list of regexes | a candidate is dropped if **any** of its tag keys fully matches **any** of these | | `strength` | one strength value | the attack strength given to every rule this block adds | | `threshold` | one threshold value | the alert threshold given to every rule this block adds | Both lists are validated as regular expressions when the plan is read — a bad pattern is reported against the plan rather than silently treated as a literal — then compiled and matched with `matches()`. That is a **full** match against the whole tag key, not a search, so a pattern intended as a prefix has to spell out the rest: a bare fragment matches nothing. Matching is against the tag **keys**, not the values. A rule's tags are a map from a short key to a reference; the block never looks at the value side. ## Three behaviours that are easy to get backwards **`exclude` does not remove rules.** Read literally, it looks like a way to take a rule out of the scan. It is not. The merge only ever *adds*: it starts from the explicit `rules` list and folds in tag-matched rules, and `exclude` narrows which rules the include pass folds in. A rule that was going to run anyway — because the policy default leaves it enabled, or because it is named under `rules` — is unaffected. If both lists are empty the whole computation is skipped and the explicit list is returned unchanged. **An explicitly listed rule always wins.** The fold is an "add if absent": a rule already present from the `rules` list keeps the strength and threshold you wrote there, and the tag block's values do not overwrite it. Naming a rule explicitly is therefore the way to make an exception to a tag selection. **A tag-matched rule is pinned, not inherited.** The block's `strength` and `threshold` are never absent — they hold Medium when the plan does not set them — and they are applied to every rule the block adds. So a plan that sets `defaultStrength: High` and then adds rules by tag gets those tag-added rules at **Medium**, not High. That is what the shipped template means by *overrides default settings*, and it is easy to miss because you did not write a value anywhere. ## What the block can never reach Rules that declare no alert tags. The selection stream drops any rule whose tag map is absent, and `AbstractPlugin` — the base class every active rule extends — returns nothing for its tags unless the rule overrides the method. Plenty of rules do declare tags; the ones that do not are simply invisible here, to `include` and `exclude` alike. The consequences are worth stating separately: - You cannot **add** an untagged rule by tag. It has to be named by id under `rules`. - You cannot **skip** an untagged rule with `exclude`, either — but that was never what `exclude` did anyway. - A tag-based selection is therefore not a complete description of the scan. It is an addition on top of whatever the policy defaults already left enabled. ## Using it safely in a pipeline The reviewable pattern is to make the policy closed first and then open it deliberately: set `defaultThreshold: 'Off'` so nothing runs, then use `alertTags.include` to bring back the classes you care about — the block's own threshold re-enables each rule it adds — and name individual exceptions under `rules`, where they override the tag block. Written the other way round, with a permissive default and an `exclude` list, the plan reads as though it restricts the scan and does not.

  • Your `include` pattern is a tag prefix and matches nothing. Why?
    The patterns are applied with `matches()`, a full match against the whole tag key, so a prefix only works if you spell out the rest — typically by appending `.*`. Nothing is anchored implicitly and nothing falls back to a substring search.
  • A rule appears under both `rules` and an `alertTags` include. Which dials does it get?
    The ones written under `rules`. The merge folds tag-matched rules in only where the id is absent, so an explicit entry is never overwritten — which makes `rules` the place to write exceptions to a tag selection.
  • How does `alertTags` differ from filtering findings by tag after a scan?
    It changes which rules run, so it changes what traffic the target receives. A post-scan filter changes what you are shown; the requests were already sent. They are different mechanisms owned by different parts of the tool and only one of them is a scan-shaping control.

saying these in an interview costs you the question

  • Thinks exclude removes a rule from the scan
  • Expects an include pattern to match a tag as a substring
  • Believes a tag block overrides an explicitly listed rule
  • Assumes tag-added rules inherit the policy default strength
  • Expects include to reach a rule that declares no tags