~/tutorials/051-zap-driven-from-a-command-line.md
051: ZAP, driven from a command line
The last lesson installed the proxy and held one conversation. This one drives the whole engine without opening a window, because ZAP is two products in one box: the desktop program people screenshot, and the daemon underneath, which speaks a plain HTTP API and does everything the windows do. Learning the second one changes what the tool is. A window you click is a tool you use. An engine that speaks text is a tool you can program, and programming your tools is where testing becomes engineering.
What this lesson is for
Three reasons the command line matters, and then the receipts. Repetition: the windows are built for doing a thing twice, and testing is doing things a thousand times, so the buttons lose. Recording: an API call is a line in a script, which means your session is reproducible, reviewable, and in lesson 044's shape, reportable. Reach: the daemon runs where no screen exists, a server, a container, the CI lane that tests your own app's build, and the window lane cannot follow you there.
What the reader meets
The lab from lesson 050, standing on the apparatus page the web lab, one change: the engine is driven entirely by calls like this one, which is the receipt that the door is open. The 172.31.11.x addresses in this lesson's receipts are that page's docker addresses, and your own build prints its own:
$ curl -s http://127.0.0.1:8090/JSON/core/view/version/
{"version":"2.17.0"}
That is the whole interface. Every capability of the desktop program is a URL under /JSON/, grouped by the part of the engine you are driving: core for history and messages, spider for crawling, ascan for active scanning, pscan for the passive rules. The verbs in the path tell you what kind of call it is. view reads state and changes nothing. action does the thing. Learn those two words and you can read the entire API off any ZAP instance, because it also documents itself: /JSON/core/view/ with no further path lists what core can tell you.
The scope seam from last lesson, met again and solved: the engine refuses API calls from addresses it does not trust, and in the docker lab your calls arrive from an address the default trust does not include. One config line at startup, api.addrs.addr.name=.* with regex=true, opens the API to your lab network. That line is scope configuration, the same act as lesson 048's worksheet: you are deciding who may drive the engine, and the engine is enforcing your decision at every call. Bind it open only inside a lab net, never on a shared host, and the tool's own gate keeps your decision honest.
The instrument
Four receipts, one engine, no windows. All fired against the lab box while writing this lesson.
Read the history. After the proxied browsing of last lesson, the count is one call:
$ curl -s http://127.0.0.1:8090/JSON/core/view/numberOfMessages/
{"numberOfMessages":"13"}
Thirteen conversations, held, numbered, queryable. The full bodies are one call away, core/view/messages/, and that pair, count and contents, is the recording instrument from last lesson in its exact shape: evidence, not memory.
Drive the spider. The crawler that walks a site's links, one action call:
$ curl -s "http://127.0.0.1:8090/JSON/spider/action/scan/?url=http://172.31.11.2/"
{"scan":"0"}
It answers immediately with a scan number, because crawls take time and the API does not wait. Poll the status, spider/view/status/, and when it reads 100 the walk is done. The receipt of what the walk found is the history count again: thirteen before, forty two after. Twenty nine pages discovered by the engine reading the site like a very fast visitor, each one now held in the history with its conversation, every one of them now passive-scanned as they arrived.
Send a request. This is the API's quiet superpower, the manual lane under all the automation:
$ curl -s "http://127.0.0.1:8090/JSON/core/action/sendRequest/" \
--data-urlencode "request=GET http://172.31.11.2/login.php HTTP/1.1
Host: 172.31.11.2"
You hand the engine a raw request, exactly as lesson 047 taught you to read them, and it sends it, records it, and hands you the response. This one call is the bridge between the two halves of the tier so far: the conversation literacy of 047, now executable through the instrument that holds everything. Change one character in that request and you have the editing power the desktop calls Intercept, without the window.
Fire the active scanner. The biggest gun, named here so it is not a mystery later, with its full weight taught in the scanner lessons that follow:
$ curl -s "http://127.0.0.1:8090/JSON/ascan/action/scan/?url=http://172.31.11.2/login.php&recurse=false"
{"scan":"1"}
Active means it sends attack traffic, which is why this call is the one the fence of lesson 048 governs most. Aimed at the lab box it filled the alert list in seconds, six cookie findings, four session management notes, header gaps, version leaks, the passive findings from last lesson now joined by everything the active rules could confirm. Aimed at anything without permission it is the same six letters as a lawsuit. The API does not ask twice. Neither does the next lesson's court.
Try it
- Drive the lab end to end from the command line: version, history count, one spider run, one sendRequest you composed yourself. Write the four calls in a file, run them in order, and note that you have just written your first testing script, four lines, and that this is what the window users cannot do.
- Break one call on purpose. Ask for
spider/view/status/before any scan exists, or send a malformed request through sendRequest. Read the error the engine gives you, because the errors are part of the interface and the window hides them. An engine you can read errors from is an engine you can trust. - Compose the two-verb audit of your own session: every
viewcall you know, tried against the engine after your browsing. Count messages, list alerts, dump one message body. Then delete nothing, and note that the session you just queried is reproducible from your script, which is the 044 discipline arriving in this tier.
Reveal the answer
The driver's card, the whole interface on one page. Every call fired in this lesson, in the order you will use them:
# the door
curl -s http://127.0.0.1:8090/JSON/core/view/version/
# read state (changes nothing)
curl -s http://127.0.0.1:8090/JSON/core/view/numberOfMessages/
curl -s "http://127.0.0.1:8090/JSON/core/view/messages/?start=0&count=20"
curl -s "http://127.0.0.1:8090/JSON/core/view/alerts/?start=0&count=50"
curl -s http://127.0.0.1:8090/JSON/spider/view/status/
# do the thing
curl -s "http://127.0.0.1:8090/JSON/spider/action/scan/?url=http://TARGET/"
curl -s "http://127.0.0.1:8090/JSON/core/action/sendRequest/" \
--data-urlencode "request=GET http://TARGET/path HTTP/1.1
Host: TARGET"
curl -s "http://127.0.0.1:8090/JSON/ascan/action/scan/?url=http://TARGET/path"
# the self-documenting surface
curl -s http://127.0.0.1:8090/JSON/core/view/
The last line is the one to keep. Point it at any component, core, spider, ascan, pscan, and the engine lists its own calls, which means the card above can never go stale in your hands: the interface documents itself, on the spot, from the version you are actually running. A tool that does that is a tool you never need a manual for, and the habit of asking a tool to describe itself is the same habit as lesson 049's census: read what is shipped, not what is remembered.
One warning the card cannot carry. Every action call here is one line away from a script, and a script is one character away from a loop. The day you wrap ascan in a for-loop across a host list, re-read lesson 048's worksheet, because the loop does not know what a fence is. You drew it. The loop drives. Make sure the list it walks was on the worksheet, and the rate line you wrote while calm is the rate your automation actually runs at.
What this lesson does not claim
The API is the engine's steering wheel, not its judgment. Every call does exactly what you said, to exactly what you pointed it at, with no second guessing: the spider will crawl a logout link and log you out, sendRequest will send your typo, and ascan does not check your permission, ever. Automation multiplies intent, and it multiplies error at the same rate. The receipts in this lesson all landed inside a lab built for them, and the same calls against somebody's production are the same six letters, minus the lab. The fence has not moved. The tool is just faster now, which was always the point and always the risk.