When does `zap-baseline.py` generate an automation plan instead of driving ZAP over its API?
answer
- only one of the three has it
- the flag is already the default
- the branch sits under a container check
- some options force the legacy path
basics
~20 sOnly when it runs inside a container and no option you passed is one a plan cannot express. Otherwise it starts a ZAP daemon and drives the run over the API. The other two packaged scripts have no plan path.
solid answer
~40 s`zap-baseline.py` is the only packaged script with a plan path — `--auto`, `--autooff` and `--plan-only` do not exist in the full-scan or API-scan wrappers. Inside a container, and unless you used an option the generated plan cannot carry, it writes a YAML plan to the home directory, copies it to the mounted work directory if one exists, and runs ZAP once with `-cmd -autorun` against that file, then reads the summary that run wrote. Options such as `-n`, `-p`, `-U`, `-i`, `-l`, `-g` and `--hook` each mark the run unsupported and send it down the legacy path: a `-daemon` process driven over the API. So does running the script from outside a container, because the whole plan branch sits under that check.
go deeper
Know that the baseline script has two ways of running the same scan, and that only one of them writes a plan file you can read afterwards.
Be able to list what sends a run down the legacy path — an unsupported option, or simply not being inside a container — and where the generated plan lands.
Explain why a job can silently change execution path after an unrelated edit, and how you would detect which path a given nightly run took.
Decide whether your pipeline depends on one path at all, and standardise the invocation so that the choice is deliberate rather than a side effect of where the step runs.
## Two ways one wrapper can run the same scan `zap-baseline.py` has two execution paths and they produce comparable output by different means: - **the plan path** — the wrapper writes a YAML automation plan, starts ZAP once with `-cmd -autorun <plan>`, lets the plan do the work, and then reads a summary file the run wrote to decide what to report; - **the legacy path** — the wrapper starts a `-daemon` process and issues the crawl, the wait and the alert queries itself over the control API. Neither the full-scan nor the API-scan wrapper has the first one. `--auto`, `--autooff` and `--plan-only` appear in the baseline script only; a claim about "the wrappers' plan path" is a claim about one wrapper. | | the plan path | the legacy path | |---|---|---| | how ZAP is started | once, with `-cmd -autorun <plan>` | as a `-daemon` the wrapper then talks to | | who orders the work | the plan's jobs | the wrapper, call by call | | what it leaves behind | a `zap.yaml` you can read, plus a summary file | the wrapper's own bookkeeping | | how the wrapper learns the outcome | by reading that summary file | from the alert queries it made | | start options | identical | identical | ## What actually decides which path you get 1. **Is this the baseline script?** If not, there is no plan path to choose. 2. **Is it running inside a container?** The whole plan branch sits under the check for that — `/.dockerenv`, `/run/.containerenv`, or `IS_CONTAINERIZED=true`, which the published images set. Run the script from a CI runner's host instead, and it starts a ZAP container itself and drives it over the API, whatever you passed. 3. **Did you use an option the plan cannot carry?** Each of `-g`, `-n`, `-p`, `-i`, `-l`, `-U`, `--hook` and `--autooff` marks the run unsupported and records a reason. Any one of them is enough. ## `--auto` is the default, and the flag itself is inert The script's usage text says `--auto` "is now the default", and it is: the variable that selects the plan path starts out true. What the flag additionally sets is a second variable that **is assigned and never read anywhere in the script**, and the script's own notes list the parameters it was meant to force the plan path for as *currently none*. So passing `--auto` changes nothing observable. `--autooff` is the switch that still does something — it turns the selector off and records itself as the reason. That asymmetry is worth saying out loud in an interview, because "we pass `--auto` to get the automation framework" is a very common sentence and it describes a no-op. ## What the plan run leaves behind, and how to tell the paths apart - the plan path prints that it is using the automation framework before it starts; - each path tags the process with a different statistics key, so the two are distinguishable after the fact; - the generated plan is written to the home directory and, when `/zap/wrk` is mounted, copied there as `zap.yaml` — which is how you inspect what the wrapper decided to run; - the plan's scope is built from the target, and its jobs are assembled by the wrapper rather than by you, so editing the copy does not change the next run. ## `--plan-only`, the one place an unsupported option is fatal `--plan-only` writes the plan and stops without scanning. It is the exception to two rules at once: it runs **before** the container check, so you can produce the file from outside a container, and an option the plan cannot express makes it exit with a warning instead of silently falling back. That makes it the right way to find out what a given invocation would produce before you schedule it. ## Why a pipeline notices The two paths differ in failure modes, not just mechanics. The plan path is one process that exits when the plan finishes; the legacy path is a daemon plus a client that has to stay in step with it. A job that quietly moved between them — because someone added `-n` for a context file, or moved the step from `docker run` to a pip-installed script on the runner — changes which of those you are debugging, with no change to the command's visible intent. It also changes what you can inspect afterwards. On the plan path there is an artefact: a YAML file naming every job the wrapper decided to run, in order, which you can read, diff between runs and attach to a ticket. On the legacy path the same decisions exist only as a sequence of calls that happened and were not written down. When a nightly scan starts behaving differently and nothing in the job definition changed, that artefact is the difference between an investigation and a guess — which is a reason to prefer the plan path deliberately rather than to let the environment pick it for you.
- What does passing `--auto` change, given the plan path is already the default?Nothing observable. The selector it sets is already true, and the second variable it assigns is never read anywhere in the script. The script's own notes list the parameters `--auto` was meant to force the plan path for as currently none. `--autooff` is the switch that still changes behaviour.
- How do you tell which path a run took without reading the script?The run says so: the plan path prints that it is using the automation framework, and the two paths tag the process with different statistics keys. The plan path also leaves the generated `zap.yaml` in the mounted work directory when one exists.
- What does `--plan-only` do differently?It writes the plan and stops without scanning, it runs before the container check so it works from outside a container, and it is the one place where an option the plan cannot express is fatal rather than a silent fallback.
saying these in an interview costs you the question
- Passing --auto is what switches the plan path on
- All three packaged scripts can generate an automation plan
- The plan path works the same whether or not you are in a container
- --auto and --autooff are symmetric opposites
- An option the plan cannot express makes the scan fail