HackBlue

Tools · Web testing tools · 13 / 23

Reading a ZAP alert as a claim

An alert names a rule, a URL, a risk, a confidence, and an evidence snippet. On Harbor Ledger you read those fields as a claim about one response, then you open that response and see whether the claim is true.

16 min read · practice only with authorization

The fields on one alert

Open the Alerts tab after the baseline and select a single row. The fields you want on paper are the rule name, the URL, the method, the risk, the confidence, the parameter if one is named, and the evidence. Description and solution text below that are the rule author’s generic advice. They are often right about the class of problem and vague about your app. Your note has to add the Harbor Ledger specifics they cannot know.

A useful first row on this lab is a missing header on a page you understand, such as the invoice list. Header alerts are good teachers because the claim is checkable with your eyes: either the response contains the header or it does not. You are not asked to interpret a stack of guesses.

The history entry is the evidence

From the alert, jump to the message in History, or find it by method and URL. Read the status line first. A claim about a document that came back as 404 is a claim about your error page, which may still be a real header bug, and it is a different page from the invoice list. Then read the response headers from top to bottom, not only the one in the snippet.

On Harbor Ledger’s HTML responses you should be able to see Content-Type and the Set-Cookie line for the lab session, with HttpOnly and SameSite. The absence of Content-Security-Policy is the alert. The presence of HttpOnly is a neighboring fact the alert did not congratulate you for. Write both. A reading of an alert that only copies the red field misses the headers you already got right.

Snippet
Rule: Content Security Policy (CSP) Header Not Set
URL: http://127.0.0.1:3000/invoices
Method: GET
Risk (tool default): Medium
Confidence: High
Evidence: no Content-Security-Policy response header

History agrees: yes
Also observed: Set-Cookie includes HttpOnly and SameSite=Lax
Change in the lab app: send a Content-Security-Policy on HTML responses

Confirm the change with one new request

If you add the header in the lab app, reload /invoices through the proxy and read the new response. The header should now be present, with a policy you can explain. Re-running the entire baseline is optional and noisy. One request is the confirmation. The alert row from the old report will still say the header was missing, because the report is a snapshot. Your note has the before and the after.

Leave the generic solution text in the tool. In your note, the change is one sentence a future you can implement: which responses, which header, which value. That sentence is the work. The risk color was only the sorting key that put this row on your screen.

Worked case: two alerts, two notes

From the baseline report, choose the CSP alert on /invoices and one other alert on a lab URL, preferably something informational such as a missing X-Content-Type-Options on /health. Fill the field list for each. Open both history entries. For each, write “History agrees: yes” or “History agrees: no,” plus one sentence of what you saw. If History does not agree, the later lesson on skepticism is the one you are already doing, and you should mark the row as not confirmed.

Stop after two. A baseline can produce many rows that are the same missing header on many URLs. Collapsing those into “HTML responses from this origin omit this header” is a better note than twenty identical screenshots. You can say how many URLs you sampled and that the rest are the same claim.

Diagram

From an alert row to the response it cites

  1. Name

    The rule name says what ZAP thinks it saw, such as a missing content security policy.

  2. Where

    The URL and the method say which response to open. Risk and confidence say how sure the rule is, by default.

  3. Evidence

    The snippet is the header or the byte the rule pointed at. It can be incomplete.

  4. History

    You open that URL in History and read the status line and the headers yourself.

A header alert, checked against the response

Alert rowHistory response

alerts · CSP header not set

Rule: Content Security Policy (CSP) Header Not Set
URL: http://127.0.0.1:3000/invoices
Risk: Medium (the rule’s default)
Confidence: High
Evidence: response has no Content-Security-Policy header
This row is a claim.
It does not show the rest of the headers.
It does not say who is signed in.
  • The rule looked for one header and did not find it.
  • Medium is the rule’s default rating, not your impact write-up.
  • The next screen is the history entry for this URL.

history · GET /invoices

GET /invoices HTTP/1.1
Host: 127.0.0.1:3000
Cookie: session=lab-session-avery
HTTP/1.1 200 OK
Content-Type: text/html
Cache-Control: no-store
Set-Cookie: session=lab-session-avery; HttpOnly; SameSite=Lax

(no Content-Security-Policy header)
  • The claim matches: the CSP header is absent.
  • The cookie flags are a separate fact, visible here and not in the alert title.
  • The fix, in an app you own, is to send the header. The alert already did its job.
Alert fields to copy into the note
Rule:
URL:
Method:
Risk (tool default):
Confidence:
Evidence snippet:
What History actually shows:
What you would change in the lab app:

Check yourself

1. An alert says “CSP Header Not Set” on http://127.0.0.1:3000/invoices with high confidence. What does high confidence mean here?

2. The evidence snippet is one line. Where do you read the rest of the headers?

3. You confirm the CSP header is missing, and you own the lab app. What is the end of this exercise?