List a site's scripts
const url = 'https://api.us.headerhawk.com/v1/sites/2489E9AD-2EE2-8E00-8EC9-32D5F69181C0/scripts?pageScope=any&status=unreviewed&identification=identified&advisories=affected&limit=50';const options = {method: 'GET', headers: {Authorization: 'Bearer <token>'}};
try { const response = await fetch(url, options); const data = await response.json(); console.log(data);} catch (error) { console.error(error);}curl --request GET \ --url 'https://api.us.headerhawk.com/v1/sites/2489E9AD-2EE2-8E00-8EC9-32D5F69181C0/scripts?pageScope=any&status=unreviewed&identification=identified&advisories=affected&limit=50' \ --header 'Authorization: Bearer <token>'The site’s script inventory: every script browsers or the daily scans of its payment pages have seen, with its authorization status, what its bytes were identified as, the advisories against that version, and whether browsers are integrity-checking it. Reads reported data, so it spends the tenant’s budget too.
Needs the Team plan or above (scripts.inventory), or any decision on
record: a tenant that downgraded keeps reading its own evidence.
pageScope=payment needs the Professional plan (pages.payment) on
the same terms, and advisories needs it (scripts.authorization).
Otherwise 403 plan_upgrade_required.
Window: the last 31 days by default; at most 31, longer is 400.
A script outside the window is still listed, with activity null;
with pageScope=payment, only scripts seen on a payment page in the
window are. Measured worst case: 11–36 ms reading 16,395 script
reports (4 MB) of one site in 90 days, about 0.7 µs a report.
Authorizations
Section titled “Authorizations”Parameters
Section titled “Parameters”Path Parameters
Section titled “Path Parameters”The site’s id.
Query Parameters
Section titled “Query Parameters”payment: only scripts seen on the site’s payment pages in the window. Defaults to any.
A script’s current decision. unreviewed until someone
authorizes or rejects it.
Only scripts with this decision.
Only scripts in this identification state. counts is taken before it narrows the list.
Only scripts in this advisory state. counts is taken before it narrows the list.
Only reports received at or after this instant. Defaults to the start
of your retention window, or where the operation bounds its window, to
that many days before to. Earlier than your retention reaches is
400.
Only reports received before this instant. Defaults to now.
How many scripts a page holds. Up to 1,000, the whole inventory: every page reads the inventory and its activity whole, so one page of everything costs the same as one page of 50, and spends one request of the Tinybird budget rather than twenty.
The previous page’s nextCursor. Omit it for the first page.
Responses
Section titled “Responses”A page of scripts
object
A script in a site’s inventory: its URL with the query and fragment dropped, its current decision and the hashes it has been seen with.
object
A script’s current decision. unreviewed until someone
authorizes or rejects it.
any_content or pinned_hashes while authorized, otherwise null.
A nullable string rather than the enum, which cannot hold a null
that CloudFormation will import.
Who made a decision, as recorded at the time.
object
When it was last seen with a hash it had not had before
A version of the script. hash is empty for a script loaded
cross-origin without CORS, which browsers report unhashed. The daily
scan of a payment page is not subject to CORS, so the same
script can have an empty browser hash beside a scan hash, and the scan
hash can be pinned.
object
What saw these bytes first: a browser’s 'report-sha256' report,
or HeaderHawk’s daily scan of a payment page, which fetches the
page’s scripts and hashes them itself.
Every source that has seen these bytes, source first. Both
hash the same bytes the same way, so one version seen both ways
lists both.
Whether the current authorization pins it
What published package these exact bytes came from, or null for a hash the fingerprint index does not hold - which is most first-party bundles, and always will be.
Built from jsDelivr’s data API, which publishes a base64 SHA-256
for every file of every npm package version it serves,
byte-identical to what a browser reports under 'report-sha256'.
So the name rests on the hash matching a published file exactly,
not on the URL, the file name or anything the site said.
Written once, on the hash’s first sighting or by the loader’s backlog sweep, and never changed: an identity is a property of the bytes.
object
The npm package name, e.g. jquery
The package version. Where the same bytes appear in more than
one release - a dist/ file unchanged between two patch
versions - this is the earliest release the index holds
containing them, which is a lower bound on the age of the bytes
rather than a claim about which release the script came from.
The file’s path within the package, e.g. dist/jquery.min.js
Where the identification came from; jsdelivr today. A string
rather than an enum, so another catalog does not break a
client.
HeaderHawk’s attempt to keep a verified copy of these bytes, or null when none was made: the hash was first seen before captures existed, on a plan without script authorization or a site without payment pages, or is empty (a script loaded without CORS has no hash to verify against).
When a hash is new to a script on a site that captures, HeaderHawk
fetches the URL once, hashes what it receives and keeps the file
only if the SHA-256 equals this hash. Never retried. A capture
succeeds only while the origin still serves the same bytes, so
mismatched is common for exactly the file that mattered, and is
itself a finding: receivedHash is what the origin served instead.
object
captured: the file is held, and owners and admins can download it in the dashboard.
mismatched: the origin served other bytes; nothing is held.
refused: a guard said no or the fetch produced no file;
reason says which. pending: under way. interrupted: an
attempt that never reported back, which is not retried.
Set on refused, null otherwise. One of daily_cap (the
site’s 100 fetches for the UTC day were spent),
unknown_host (a browser report named a host the site had not
loaded a script from before), not_allowed (an IP address, an
internal name, a port, or a name that resolves to a private
address), unsupported_hash (not a SHA-256), or the fetch’s
own: too_large (over 5 MB), html_response, http_error,
timeout, tls_failure, too_many_redirects,
connection_failed.
Which sighting asked for the capture
The URL fetched, query included
The URL that answered, after redirects
On mismatched, the sha256- hash of what the origin served
Bytes received: the file on captured, the other file on
mismatched
The fetch matched bytes HeaderHawk already held, for this site or another, so the stored copy was reused rather than added. Every capture fetches its own URL either way
Whether a browser is integrity-checking this script, from
the integrity-violation reports
Integrity-Policy-Report-Only: blocked-destinations=(script) asks for.
The inference runs one way. A report proves the script loaded with no
integrity metadata, so unprotected is evidence. An absent report
proves nothing on its own, so protected is only ever claimed for a
site that produced at least one integrity report in the window - see
reporting on SriCoverageSummary - and unknown is the default.
object
What decided the state: reports, the Integrity Policy stream, or
scan, the daily page scan reading the integrity attribute out
of the HTML. Null while the state is unknown. A nullable string
rather than an enum, which cannot hold a null that CloudFormation
will import.
When a browser last reported it loading without integrity
metadata. Null unless the state is unprotected.
report or enforce; enforce means the site’s Integrity Policy
is enforcing, so the browser blocked the script rather than only
reporting it. Null unless the state is unprotected. A nullable
string rather than an enum, for the same reason as source.
What can be said about a script’s contents. Four states, and three of them are not findings:
identified: the latest hash matched a published package file. The library, version and file say which.name_mismatch: it matched, and the script’s own URL claims a different package or version. An observation worded as a question, never an alert: a path containingjquery-3.7.1.min.jsserved from a site’s own CDN may be a legitimately patched build. Only raised where both sides state a version cleanly, and the package name is only compared where the URL names npm itself - a cdnjs library name and an npm package name are different namespaces.unrecognised: no hash matched. A prompt to look, not a verdict; a site’s own build output is in no public registry and will always land here.unhashed: every sighting was unhashed, so there are no bytes to identify. A script loaded cross-origin without CORS. This is the opposite ofunrecognisedand is deliberately not folded into it.
object
Null unless the state is identified or name_mismatch
Where the identification came from, such as jsdelivr; null when there is none.
What the script’s URL claims to be serving. Set only on
name_mismatch, where the question needs both halves.
object
The published advisories against the version a script runs now. Five states, and only one is a finding:
affected: identified as a version at least one advisory names.none_known: identified, and nothing is published against the version as ofcheckedAt. Not a statement that it is safe.pending: identified, and the version has not been checked yet.not_assessed: nobody could identify the script, so there is no version to look up. Most first-party code is here for ever, and it is never shown as clean.not_included: the plan does not include advisories (scripts.authorization).
object
The worst severity among items (critical, high, medium,
low or unknown), or null when there are none. A nullable string
rather than the enum, which cannot hold a null that CloudFormation
will import.
When the version was last checked; null unless checked
Worst first. Empty in every state but affected.
One published advisory against one library version, from OSV (https://osv.dev), which aggregates the GitHub Advisory Database among others. About the version: it does not say the vulnerable code is reachable on the page.
object
The source’s id, e.g. GHSA-gxr4-xjj5-5px2
Other names for it, CVE ids first
The source’s own rating where it has one (GitHub’s, which is what
npm audit shows), otherwise from the CVSS v3 base score.
unknown is a record carrying neither, not low.
The lowest release that fixes it for this version; null when none is published
Where to read it
Where it came from; osv today
When the source last changed the record
The script’s reports in the requested range; null when it was not reported in it.
object
Reports received. Repeat sightings are sampled.
Distinct pages it was reported on
The site’s payment pages among them
object
Every hash reported for it in the range, at most 50, as the browser wrote it (sha256-<base64>). An empty string is a cross-origin load without CORS, which the browser could not hash. Scripts › Integrity offers an integrity= attribute only when this holds exactly one hash.
What HeaderHawk’s daily scan of the site’s payment pages found of it in the requested range: the scan fetched and hashed it on these pages, or found its tag on a page whose latest scan no longer does. Null when no scan in the range did, which is every script on a site whose payment pages are not scanned, and every script a page’s scripts add while it runs.
object
The latest scan in the range that found it
The site’s payment pages whose scans found it
object
The payment pages whose latest scan in the range no longer finds it, with the last scan that did. A script that has left every page it was on is still listed, with these dates, rather than dropped from the inventory. Read from the scan’s list of the page’s scripts, never from browsers going quiet, and only from scans that loaded the page itself (a complete scan answered with a 2xx).
object
The last scan in the range that found it on the page
The page’s latest scan in the range, which did not
How many of the listed scripts are in each identification and advisory state, before identification and advisories narrow the list.
object
object
object
The site’s Subresource Integrity coverage, counted over the scripts the filters list, and the unprotected scripts browsers reported that the inventory lacks.
object
The site produced an integrity report in the window, which is what makes protected claimable at all. False means every count but unknownCount is zero.
Scripts a browser reported loading without integrity metadata that the inventory has no entry for.
A script a browser reported loading with no integrity metadata.
object
The identity URL, query and fragment dropped
The window the scripts were read over.
object
The site has more scripts than one read returns (1,000), so a script past it may be missing.
Example
{ "data": [ { "status": "unreviewed", "hashes": [ { "source": "browser", "sources": [ "browser" ], "capture": { "state": "pending", "source": "browser" } } ], "coverage": { "state": "protected" }, "identification": { "state": "identified" }, "advisories": { "state": "affected", "items": [ { "severity": "critical" } ] } } ], "coverage": { "uninventoried": [ { "disposition": "report" } ] }}Headers
Section titled “Headers”The budget nearest to running out, per minute.
What is left of that budget this minute.
Seconds until that budget starts again.
A parameter was refused, or one the operation does not take was sent. The message names it.
object
object
What went wrong. An open enum: treat a code you do not know by its HTTP status.
For people. Branch on code.
Quote it when you write to us about this request.
Example
{ "error": { "code": "bad_request" }}invalid_api_key: no key, or one that is malformed, unknown, revoked
or expired. One answer for all of them.
object
object
What went wrong. An open enum: treat a code you do not know by its HTTP status.
For people. Branch on code.
Quote it when you write to us about this request.
Example
{ "error": { "code": "bad_request" }}plan_upgrade_required: the tenant’s plan does not include API access
(the key works again when it does), or does not include what this
operation reads.
object
object
What went wrong. An open enum: treat a code you do not know by its HTTP status.
For people. Branch on code.
Quote it when you write to us about this request.
Example
{ "error": { "code": "bad_request" }}No such resource in your tenant.
object
object
What went wrong. An open enum: treat a code you do not know by its HTTP status.
For people. Branch on code.
Quote it when you write to us about this request.
Example
{ "error": { "code": "bad_request" }}rate_limited: over a budget. Wait Retry-After seconds.
object
object
What went wrong. An open enum: treat a code you do not know by its HTTP status.
For people. Branch on code.
Quote it when you write to us about this request.
Example
{ "error": { "code": "bad_request" }}Headers
Section titled “Headers”Seconds to wait before trying again.
rate_limit_unavailable or upstream_unavailable: HeaderHawk could
not serve this now. Nothing is wrong with the request; try again after
Retry-After seconds.
object
object
What went wrong. An open enum: treat a code you do not know by its HTTP status.
For people. Branch on code.
Quote it when you write to us about this request.
Example
{ "error": { "code": "bad_request" }}Headers
Section titled “Headers”Seconds to wait before trying again.