In ZAP, how does a `proxy` script differ from an `httpsender` script in reach and power?
answer
- one returns a verdict, one returns nothing
- reach and power trade against each other
- false closes the client connection
- setting a response is the other stop
- the sender hook also sees scan traffic
basics
~20 sA proxy script returns a boolean and can stop a message: false drops it and closes the client connection. A sender script returns nothing and cannot veto, but it sees every message the program sends, not only proxied traffic.
solid answer
~40 sA `proxy` script implements `proxyRequest(msg)` and `proxyResponse(msg)`, each returning a boolean. Returning `false` stops the message — the pipeline closes the client connection and no later script or listener runs. Returning `true` after setting a response on the message is the other stop: the forwarder skips sending because a response is already present, so your reply is served and the server is never contacted. But a proxy script only fires for traffic crossing the local proxy listener, and it is skipped for a message already flagged as excluded. An `httpsender` script has no veto at all, yet it sees the crawlers, the active-scan engine and the authentication exchange as well as the proxy's own forwarding leg — so proxied traffic passes both hooks.
code
javascript · 10 linesfunction proxyRequest(msg) {
if (msg.getRequestHeader().getMethod() == "TRACE") {
return false;
}
return true;
}
function proxyResponse(msg) {
return true;
}go deeper
Remember the shape difference first: the proxy pair returns a boolean, the sender pair returns nothing. That single fact settles which of the two can stop a message.
Explain both stop mechanisms on the request side — returning false, and setting a response then returning true — and say which traffic each hook actually observes.
Show you know the reach limits that make a proxy script the wrong place for a control: it misses everything the tool generates itself, is skipped for excluded messages, and fails open when it throws.
The judgment is about where a limit belongs. A per-message veto written in a script is invisible, unversioned and fails open; deciding what a run may reach belongs in the run's configuration rather than in a hook nobody reviews.
## Two hooks, two different jobs Both of these script types are called for HTTP messages, which is why they get confused. They differ on the two axes that actually matter operationally: **what they can see** and **whether they can say no**. | | `proxy` script | `httpsender` script | |---|---|---| | entry points | `proxyRequest(msg)`, `proxyResponse(msg)` | `sendingRequest(msg, initiator, helper)`, `responseReceived(...)` | | return | a boolean verdict | nothing | | can stop a message | **yes** | no | | which traffic | only what crosses the local proxy listener | all traffic on the shared sending path | | sees scanner and crawler traffic | no | yes | | told who caused the message | no | yes, through the initiator | | runs for an excluded message | no | yes | ## What a proxy script can do that the other cannot Returning `false` from `proxyRequest` is a veto. In the live path this does two things: the handler that owns the client connection **closes it**, and the loop stops, so no further proxy script or listener is consulted for that message. The client gets a dropped connection, not an error page. There is a second, quieter way to stop a request, and it is the one shipped community scripts actually use: **set a response on the message and return `true`**. The handler that performs the send checks whether a response is already present and returns early if it is, so the message is never forwarded and your crafted response goes back to the client. One mechanism drops the conversation; the other answers it. `proxyResponse` has the same shape on the way back: `false` means the response is not forwarded to the client. ## What a proxy script cannot see The cost of that power is reach. A proxy script is driven from the proxy-listener chain, which fires only for messages crossing the local listener. Requests the program generates itself — the traditional spider, the browser-driven crawl, core's active-scan engine, the authentication exchange, a definition import — never cross it, so a proxy script does not see them at all. Two further limits are worth knowing: - The chain is skipped outright for a message already flagged as **excluded**, so a proxy script is not a place to enforce scope. - Proxied traffic is forwarded onward through the same sender every other component uses, and that leg carries the proxy initiator. So a browser request passes the proxy hook **and then** the sender hook; a scan request passes only the sender hook. ## Which one to reach for 1. **Must the hook see everything the tool sends?** Only the sender hook does. 2. **Must the hook be able to prevent a request?** Only the proxy hook can, and only for traffic that crosses the listener. 3. **Must it do both?** It cannot. That is a real limit, not an oversight, and the usual answer is to constrain what the run reaches rather than to try to veto per message from a script. ## Ownership, because it changes what is installed Both types, and both of the listeners that drive them, are registered by the **`scripts` add-on**. Core declares the interfaces and marks them deprecated for removal, and wires neither. The proxy hook additionally depends on a compatibility handler that the **`network` add-on** keeps for legacy proxy listeners — the local proxy itself moved out of core, and the old listener chain is preserved there rather than in core. The practical consequence for anyone assembling a build is that a hook you consider basic is two add-ons deep, plus a third for whichever language you write it in. ## Where each one sits in the life of one proxied request Following a single browser request makes the overlap concrete: - The client's request arrives at the local listener and the proxy hook is consulted, unless the message is already flagged excluded. - If it survives that, the request is forwarded onward through the shared sender, and the sending hook is consulted with the proxy initiator. - The response comes back through the sending hook first, then through the proxy hook on its way to the client. A request the scanners generate skips the first and third of those entirely. That is the whole difference in reach, in one sentence. ## Failure behaviour If a proxy script throws, the exception is caught, the script is flagged with an error and disabled, and the loop **continues** to the next script. The message is not dropped by the failure. A hook that exists to stop something therefore fails open, which is the opposite of what most people assume when they put a veto in a script.
- A proxy script sets a response on the message and returns true. What reaches the server?Nothing. The handler that performs the send returns early when a response header is already present, so the request is never forwarded and the response the script built is written back to the client. It is a stop that answers rather than a stop that hangs up.
- Why can a proxy script not be used to keep a scan inside its authorised scope?Two reasons. It never sees the requests the scanners and crawlers generate, because those do not cross the local proxy listener. And the proxy listener chain is skipped entirely for a message already flagged as excluded, so it is not consulted at the moment scope matters.
- If a proxy script throws while handling a request, is the request dropped?No. The exception is caught, the script is flagged with an error and disabled, and the loop moves on to the next script with the verdict unchanged. A veto implemented in a script fails open.
saying these in an interview costs you the question
- Says both script types can drop a request
- Thinks a proxy script sees active-scan and spider traffic
- Assumes returning false makes the client get an error page
- Believes a proxy script runs for excluded messages too
- Treats a throwing proxy script as blocking the message