Skip to content

Get one script, with its decisions

GET
/v1/sites/{siteId}/scripts/{scriptId}
curl --request GET \
--url https://api.us.headerhawk.com/v1/sites/2489E9AD-2EE2-8E00-8EC9-32D5F69181C0/scripts/2489E9AD-2EE2-8E00-8EC9-32D5F69181C0 \
--header 'Authorization: Bearer <token>'

One script from the inventory with every decision made about it, oldest first: the record a PCI DSS 6.4.3 assessor asks for. The same plan rule as the list. Reads reported data, so it spends the tenant’s budget too.

Window: the last 31 days by default; at most 31, longer is 400.

siteId
required
string format: uuid

The site’s id.

scriptId
required
string format: uuid

The script’s id.

from
string format: date-time

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.

to
string format: date-time

Only reports received before this instant. Defaults to now.

The script

Media typeapplication/json
object
data
required

A script with every decision made about it, oldest first.

object
id
required
string
url
required
string
status
required

A script’s current decision. unreviewed until someone authorizes or rejects it.

string
Allowed values: unreviewed authorized rejected
integrityMode
required

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.

string
nullable
justification
required
string
nullable
decidedAt
required
string format: date-time
nullable
decidedBy
required

Who made a decision, as recorded at the time.

object
name
required
string
nullable
email
required
string
firstSeenAt
required
string format: date-time
lastHashSeenAt
required

When it was last seen with a hash it had not had before

string format: date-time
hashes
required
Array<object>

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
hash
required
string
firstSeenAt
required
string format: date-time
source
required

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.

string
Allowed values: browser scan
sources
required

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.

Array<string>
Allowed values: browser scan
pinned
required

Whether the current authorization pins it

boolean
identification
required

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
library
required

The npm package name, e.g. jquery

string
version
required

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.

string
file
required

The file’s path within the package, e.g. dist/jquery.min.js

string
source
required

Where the identification came from; jsdelivr today. A string rather than an enum, so another catalog does not break a client.

string
capture
required

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
state
required

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.

string
Allowed values: pending captured mismatched refused interrupted
reason
required

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.

string
nullable
source
required

Which sighting asked for the capture

string
Allowed values: browser scan
fetchUrl
required

The URL fetched, query included

string
finalUrl
required

The URL that answered, after redirects

string
nullable
statusCode
required
integer
nullable
receivedHash
required

On mismatched, the sha256- hash of what the origin served

string
nullable
sizeBytes
required

Bytes received: the file on captured, the other file on mismatched

integer
nullable
reused
required

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

boolean
attemptedAt
required
string format: date-time
completedAt
required
string format: date-time
nullable
coverage
required

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
state
required
string
Allowed values: protected unprotected unknown
source
required

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.

string
nullable
lastReportedAt
required

When a browser last reported it loading without integrity metadata. Null unless the state is unprotected.

string format: date-time
nullable
disposition
required

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.

string
nullable
identification
required

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 containing jquery-3.7.1.min.js served 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 of unrecognised and is deliberately not folded into it.
object
state
required
string
Allowed values: identified name_mismatch unrecognised unhashed
library
required

Null unless the state is identified or name_mismatch

string
nullable
version
required
string
nullable
file
required
string
nullable
source
required

Where the identification came from, such as jsdelivr; null when there is none.

string
nullable
urlClaim
required

What the script’s URL claims to be serving. Set only on name_mismatch, where the question needs both halves.

object
name
required
string
version
required
string
advisories
required

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 of checkedAt. 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
state
required
string
Allowed values: affected none_known pending not_assessed not_included
highestSeverity
required

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.

string
nullable
checkedAt
required

When the version was last checked; null unless checked

string format: date-time
nullable
items
required

Worst first. Empty in every state but affected.

Array<object>

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
id
required

The source’s id, e.g. GHSA-gxr4-xjj5-5px2

string
aliases
required

Other names for it, CVE ids first

Array<string>
summary
required
string
severity
required

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.

string
Allowed values: critical high medium low unknown
fixedVersion
required

The lowest release that fixes it for this version; null when none is published

string
nullable
url
required

Where to read it

string
source
required

Where it came from; osv today

string
publishedAt
required
string format: date-time
nullable
modifiedAt
required

When the source last changed the record

string format: date-time
activity
required

The script’s reports in the requested range; null when it was not reported in it.

object
reportCount
required

Reports received. Repeat sightings are sampled.

integer
firstSeen
required
string format: date-time
lastSeen
required
string format: date-time
pageCount
required

Distinct pages it was reported on

integer
paymentPages
required

The site’s payment pages among them

Array<object>
object
pageId
required
string
label
required
string
hashes
required

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.

Array<string>
scan
required

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
lastScannedAt
required

The latest scan in the range that found it

string format: date-time
paymentPages
required

The site’s payment pages whose scans found it

Array<object>
object
pageId
required
string
label
required
string
missingFrom
required

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).

Array<object>
object
pageId
required
string
label
required
string
lastFoundAt
required

The last scan in the range that found it on the page

string format: date-time
latestScanAt
required

The page’s latest scan in the range, which did not

string format: date-time
decisions
required

Every decision, oldest first: the append-only record a PCI DSS 6.4.3 assessor asks for.

Array<object>

One recorded decision. Decisions are never changed or removed.

object
id
required
string
status
required
string
Allowed values: authorized rejected
integrityMode
required

any_content or pinned_hashes for an authorization, null for a rejection

string
nullable
justification
required
string
pinnedHashes
required
Array<string>
decidedBy
required

Who made a decision, as recorded at the time.

object
name
required
string
nullable
email
required
string
decidedAt
required
string format: date-time
window
required

The window the scripts were read over.

object
from
required
string format: date-time
to
required
string format: date-time
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"
}
]
},
"decisions": [
{
"status": "authorized"
}
]
}
}
RateLimit-Limit
integer

The budget nearest to running out, per minute.

RateLimit-Remaining
integer

What is left of that budget this minute.

RateLimit-Reset
integer

Seconds until that budget starts again.

A parameter was refused, or one the operation does not take was sent. The message names it.

Media typeapplication/json
object
error
required
object
code
required

What went wrong. An open enum: treat a code you do not know by its HTTP status.

string
Allowed values: bad_request validation_error invalid_api_key plan_upgrade_required forbidden not_found rate_limited rate_limit_unavailable upstream_unavailable internal_error
message
required

For people. Branch on code.

string
requestId
required

Quote it when you write to us about this request.

string
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.

Media typeapplication/json
object
error
required
object
code
required

What went wrong. An open enum: treat a code you do not know by its HTTP status.

string
Allowed values: bad_request validation_error invalid_api_key plan_upgrade_required forbidden not_found rate_limited rate_limit_unavailable upstream_unavailable internal_error
message
required

For people. Branch on code.

string
requestId
required

Quote it when you write to us about this request.

string
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.

Media typeapplication/json
object
error
required
object
code
required

What went wrong. An open enum: treat a code you do not know by its HTTP status.

string
Allowed values: bad_request validation_error invalid_api_key plan_upgrade_required forbidden not_found rate_limited rate_limit_unavailable upstream_unavailable internal_error
message
required

For people. Branch on code.

string
requestId
required

Quote it when you write to us about this request.

string
Example
{
"error": {
"code": "bad_request"
}
}

No such resource in your tenant.

Media typeapplication/json
object
error
required
object
code
required

What went wrong. An open enum: treat a code you do not know by its HTTP status.

string
Allowed values: bad_request validation_error invalid_api_key plan_upgrade_required forbidden not_found rate_limited rate_limit_unavailable upstream_unavailable internal_error
message
required

For people. Branch on code.

string
requestId
required

Quote it when you write to us about this request.

string
Example
{
"error": {
"code": "bad_request"
}
}

rate_limited: over a budget. Wait Retry-After seconds.

Media typeapplication/json
object
error
required
object
code
required

What went wrong. An open enum: treat a code you do not know by its HTTP status.

string
Allowed values: bad_request validation_error invalid_api_key plan_upgrade_required forbidden not_found rate_limited rate_limit_unavailable upstream_unavailable internal_error
message
required

For people. Branch on code.

string
requestId
required

Quote it when you write to us about this request.

string
Example
{
"error": {
"code": "bad_request"
}
}
Retry-After
integer

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.

Media typeapplication/json
object
error
required
object
code
required

What went wrong. An open enum: treat a code you do not know by its HTTP status.

string
Allowed values: bad_request validation_error invalid_api_key plan_upgrade_required forbidden not_found rate_limited rate_limit_unavailable upstream_unavailable internal_error
message
required

For people. Branch on code.

string
requestId
required

Quote it when you write to us about this request.

string
Example
{
"error": {
"code": "bad_request"
}
}
Retry-After
integer

Seconds to wait before trying again.