Your ZAP `httpsender` script stamped every request, then quietly stopped mid-run. Why?
answer
- the failure is handled against the script
- nothing in the verdict records it
- one throw is enough
- flagged with an error, then disabled
- the off switch persists between runs
basics
~20 sThe likeliest cause is that the script threw once. An exception is handled against the script, not the message: it is flagged with an error and disabled, traffic keeps flowing unstamped, and nothing in the run's verdict records it.
solid answer
~40 sWhen a script raises an exception, the program records the error against the script and then **disables** it. Nothing else changes: the message that triggered it still goes out, later messages go out unstamped, and the run's exit status is unaffected because a script error is not a finding. In a headless run the exception text goes to standard output, so the only evidence is in the log. The disable is written to the script's saved properties, so a reused home directory can start the next run with the script already off. Two neighbouring causes look identical from outside: a script whose functions do not match the interface never binds in the first place, and a script of this type is not enabled merely by being loaded.
go deeper
The fact to hold on to is that a script failure is the script's problem, not the traffic's. Requests keep going out; only the script stops.
Explain the three steps an exception triggers — error recorded, script disabled, message unaffected — and name the two causes that look identical: a script that never bound to the interface, and one that was loaded but never enabled.
Demonstrate the diagnosis: read the run log for the exception, check whether the saved state carried a disable in from a previous run, and distinguish a dead hook from a hook that is deliberately skipping traffic by initiator.
Own the reliability question. This seam fails open, fails silently, and can persist its failure across runs through shared state. Decide whether a pipeline may depend on it at all, and what evidence a run must produce before its result is trusted.
## The symptom, and why it is quiet A sender hook that stamps a header is a piece of code on the path of every request. When it stops, the traffic does not stop with it — the header simply disappears from the requests after some point in the run, and everything else looks normal. The scan finishes. The exit status is whatever the findings made it. Nothing in the report mentions the failure. That silence is the design: script failures are handled **against the script**, not against the message or the run. ## What happens when a script throws When a script raises an exception while being invoked, the handler does three things and then returns: 1. The failure is counted and the exception text is written to the writers attached to the script. With no desktop present, standard output is attached as a writer, so in a headless run the text lands in the run log and nowhere else. 2. The script is **flagged with an error**. 3. The script is **disabled**. The message in flight is unaffected and later messages are not stamped. There is no retry and no back-off, because there is nothing to retry: the script is out of the rotation until something re-enables it. The disable is not purely in memory. It is written into the script's saved properties, so a home directory reused between runs can begin the next run with the script already off — which turns a one-off failure into a permanent one that nobody notices. ## The disable only applies to types that can be disabled A script type declares whether its scripts are **enableable**, and the disable-on-error step is skipped for types that are not. This produces an asymmetry worth knowing: | script type | enableable | effect of an exception | |---|---|---| | `httpsender` | yes | error recorded, script disabled for the rest of the run | | `proxy` | yes | error recorded, script disabled; the message is **not** dropped | | `targeted` | no | error recorded for that invocation only | | `standalone` | no | error recorded for that invocation only | So the very types that run unattended on live traffic are the ones that can silently switch themselves off. ## Three causes that look the same from outside When the header is missing, work through these in order: - **The script threw.** Look in the run log for the exception text. This is the case above. - **The script never bound.** The program resolves the script against the whole interface before calling it. Rename or delete `responseReceived` and the binding fails with an interface error — and the script is disabled before it handles a single message, so the header never appears at all rather than disappearing partway. - **The script was never enabled.** Adding and enabling are separate, and the paths differ: scripts picked up from a tracked directory arrive **disabled**, while the control surface's load action enables what it loads. A hook that was added but never enabled produces exactly the same evidence as one that was disabled — nothing. ## And one that is not a failure at all If the header is missing only from *some* requests, the hook may be working exactly as written. Two ordinary reasons: - The script branches on the **initiator** and is deliberately skipping a class of traffic — the authentication exchange is the usual one. - The request was sent **by the script itself** through the helper. Listener notification is suppressed while a listener is running, so a message the hook sends does not pass through the hook and carries no header. ## How to stop it being silent The honest summary is that this seam has no health signal of its own, so build one. Have the script count what it stamps into a variable the control surface can read back, or emit a marker the run can check afterwards; assert the marker rather than assuming the hook ran. Treat the absence of the header as a failed run rather than a clean one, because on the tool's own terms a run whose hook died is indistinguishable from a run whose hook did nothing.
- Where would you look for evidence that a script failed during a headless run?Standard output. With no desktop present the program attaches standard output as the script's writer, so the exception text goes to the run log. It does not become an alert, it does not reach the report, and it does not change the exit status.
- Why can the same failure persist into the next pipeline run?Because the disable is written to the script's saved properties, not just held in memory. A run that reuses the same home directory picks up the saved state, so the hook starts off. A run that starts from a clean home directory would load it afresh.
- The header is missing from the authentication requests only. Is the script broken?Probably not. Scripts of this type normally branch on the initiator and skip the authentication exchange deliberately, because a header the application under test expects can break the login. Check the branch before treating it as a fault.
- How would you prove the hook ran at all in a completed run?Do not infer it from the findings — a dead hook and a working one produce the same report. Have the script record a counter or a marker the run can read back afterwards, and treat a missing marker as a failed run rather than a clean one.
saying these in an interview costs you the question
- Thinks a script error fails the scan or changes the exit status
- Expects the script to retry after an exception
- Says a failing hook stops the traffic it was watching
- Assumes every way of adding a script also enables it
- Looks for the error in the report rather than the run log
- Believes the disable is forgotten when the run ends