Skip to content

Keyboard shortcuts

Go

  • Scope — the control cataloggm
  • Plan — your last certification classgp
  • Collect — the recipe indexgc
  • The control you are workinggw
  • Startgh

Move

  • Next rowj
  • Previous rowk
  • Previous in this run[
  • Next in this run]
  • Filter this page's list/
  • Search everythingK

Act

  • Copy this page's permalinky
  • Toggle dark moded
  • This sheet?

Rows are whatever the current page lists — controls on Scope, recipes on Plan and Collect.

Control index

The organization's webhook configuration — which endpoints are subscribed to the supply-chain alert event, whether each is switched on, and where it points — together with the platform's own record of what it actually delivered to them and with what response code. Configuration says a path exists; the delivery log says the path carried something.

The API supplies the facts, but a human judgement against a documented baseline turns them into evidence.

partial — needs judgementapicontinuousGitHub webhooksGitHub Dependabot

Fetch

$ gh api --paginate "/orgs/<ORG>/hooks?per_page=100"
$ gh api "/orgs/<ORG>/hooks/<HOOK_ID>"
$ gh api --paginate "/orgs/<ORG>/hooks/<HOOK_ID>/deliveries?status=failure&per_page=100"
$ gh api --paginate "/orgs/<ORG>/hooks/<HOOK_ID>/deliveries?per_page=100"

Expected output

Report names used below, and the command each comes from: `hooks` is the first command, `deliveries-failed` the third, `deliveries` the fourth. RUN COMMANDS 2, 3 AND 4 ONCE PER HOOK returned by the first, not once for the organization. The first command returns every organization webhook and the rest take a single `<HOOK_ID>`; the delivery clause below speaks only for the hooks actually walked, and collecting one hook's deliveries and reading them as the organization's is the arithmetic error this recipe is most likely to be assessed with. From the hooks calls, webhook objects with `id`, `name`, `active`, an `events` array, a `config` object carrying `url`, `content_type` and `insecure_ssl`, plus `created_at`, `updated_at`, `url`, `ping_url`, `deliveries_url` and `type`. Read `events` knowing that GitHub documents the wildcard on the create body: the parameter "determines what events the hook is triggered for. Set to [\"*\"] to receive all possible events", and it defaults to `push`. Whether a GET returns the literal `"*"` or an expanded list is NOT documented and was not verified, which is why both clauses below carry the wildcard as a disjunct rather than testing for it — the disjunction is correct under either behaviour. From the deliveries calls, delivery records with `id`, `guid`, `delivered_at`, `redelivery`, `duration`, `status`, `status_code`, `event`, `action`, `installation_id`, `repository_id` and `throttled_at`. The third command uses GitHub's own `status` filter, documented on the organization webhooks reference as `success` for a response code of 200–399 and `failure` for 400–599, so it returns a list built to hold only offenders. The relevant `event` value is `dependabot_alert`, whose actions are `assignees_changed`, `auto_dismissed`, `auto_reopened`, `created`, `dismissed`, `fixed`, `reintroduced` and `reopened`, and which GitHub makes available on repository, organization and app webhooks.

Assertions — what makes it a pass

Assertions for The organization's webhook configuration — which endpoints are subscribed to the supply-chain alert event, whether each is switched on, and where it points — together with the platform's own record of what it actually delivered to them and with what response code. Configuration says a path exists; the delivery log says the path carried something.: the field checked, the condition it must satisfy, the rows it applies to, and the controls a pass proves.
FieldMust beForProves
hooks[?active==`true` && (contains(events,'dependabot_alert') || contains(events,'*'))] | [0].idexistsAt least one active organization webhook is subscribed to the supply-chain alert event. The wildcard is part of the clause rather than a caveat beside it: GitHub documents `events` as accepting `["*"]` "to receive all possible events", so the best-subscribed hook on the platform may carry no event name, and a clause matching only the literal string reports it as missing. Says nothing about WHERE the hook points — `config.url` is opaque and the reconciliation in `scan_scope` is what reads it.every rowSR-08
hooks[?(contains(events,'dependabot_alert') || contains(events,'*')) && active==`false`]count_eq 0No webhook subscribed to the supply-chain alert event is switched off. This is the failure that looks exactly like working configuration: the hook is present, correctly subscribed, visible in every listing, and dispatches nothing. It carries the same wildcard disjunct as the clause above, and it has to — the hook most likely to be subscribed and disabled is a wildcard one, and a literal-only filter would silently exempt exactly the case this clause exists to catch. Asserted as an offender count rather than as `active == true` over subscribed rows, because the projection form is evaluated over the rows that already carry the field and is green on a list where every subscribed hook is disabled.every rowSR-08
deliveries-failed[?event=='dependabot_alert']count_eq 0No supply-chain alert delivery in the retrieved window came back a failure. The list is GitHub's own `status=failure` filter — documented as a response code of 400–599 — so this clause reads a response built to contain only offending rows, which is what keeps it meaningful on a healthy organization instead of vacuously true. Its window is of unknown length: no retention period is documented for delivery records, so this is a statement about what is retained, not about all time. It speaks only for the hooks actually walked; see the iteration note in `expected_output`.every rowSR-08

Authored opinion, like the commands. Units live in the operator name — max-age-days is days, and nothing here is converted for you.

Map — what it proves

  • recipe1
Key Security Indicators
NIST 800-53 controls
  • recipean authored recipe collects evidence for this control
  • KSI onlya Key Security Indicator reaches it, but no recipe is authored yet
  • orphanno Key Security Indicator reaches it — a person writes it up instead

What else these families can fetch:SR 3/14

Outside the boundaryGitHub Enterprise Cloud

The routing configuration and its delivery log are held by the platform, not by the provider, so reading them brings the platform into the assessment as an information resource under SA-09 and CA-03 and into the external-system inventory AC-20 covers. The consequence is sharper on this control than on most: SR-08 is about notification reaching the provider, and the evidence that it does is itself held by an outside party whose availability is a dependency of the notification path. SR-08 sits in the class b, c and d baselines, so this recipe is read by providers at every level, and the impact level a platform's Marketplace listing carries is not necessarily the one a Moderate or High system needs — that is the SA-09 question, it is answered by reading the cited listing, and no value for it is written here because it is a dated fact that changes. Which offering is in use decides the rest — GitHub Enterprise Cloud, GitHub Enterprise Cloud with data residency and github.com are different deployments — and this recipe was authored against GitHub Enterprise Cloud.

Notes & assertions

SR-08 establishes agreements and procedures with entities in the supply chain for notification of compromise or of a vulnerability. It has two limbs and this recipe answers one of them. The agreements limb is a contract with a supplier and nothing here touches it. What this recipe deliberately does NOT read is the alert list, and that is the whole design. A public advisory feed is not an entity in your supply chain, so an alert stream — however rich — cannot evidence SR-08 on its own; the feed itself is SI-05's artifact and is already collected there. What is evidence is the procedure: that an upstream notification, once it arrives, is delivered to a named destination rather than into nothing. That is configuration plus a delivery record, which is what these commands collect. A future batch tempted to strengthen this recipe by adding `dependabot/alerts` to it should read this paragraph first. The emptiness trap here is worse than usual and it is worth being blunt about. GitHub's deliveries reference documents `per_page`, `cursor` and a `status` filter, and states NO retention period for delivery records. So the window these commands read is of unknown length, and an empty deliveries list means one of: nothing was ever delivered, nothing has been delivered recently, or nothing is retained that far back. None of the three is distinguishable from the others in the output, and none of them is a pass. The failure clause below is therefore written over the `status=failure` list — a list whose emptiness is meaningful in a way the full list's is not, because it is built to contain offenders — and the fourth command is collected as the corroborating record a human reads for volume and recency. No clause asserts the full list is non-empty, because on a quiet organization it legitimately is. The wildcard is in BOTH hook clauses rather than in a note, and it has to be in both. A hook created with `events: [\"*\"]` receives every event including this one and may contain the string `dependabot_alert` nowhere, so a clause matching the literal alone reports the most completely subscribed configuration on the platform as unsubscribed. An earlier draft carried the disjunct on the first clause and not the second, which was worse than omitting it from both: the second clause exists to catch a subscribed-but-disabled hook, and without the disjunct the hook it could not see was exactly the wildcard one that had been switched off. Note also that GitHub documents the wildcard on the create request body; nothing states what a GET returns for such a hook, so the disjunction is written to be correct whether the literal survives the round trip or is expanded. The deprecated event, which no clause tests and every assessment should look for. GitHub carries a closing-down notice on `repository_vulnerability_alert` — "this event is closing down, use the `dependabot_alert` event instead" — with actions `create`, `dismiss`, `reopen` and `resolve`. An organization whose only subscription is that event is being delivered to today and is on a path that ends. It fails the first clause below, and that failure is correct rather than a false positive: the finding is real, its severity is scheduled rather than immediate, and reading the failure as a configuration error would be reading it right for the wrong reason. Organization hooks are not the only hooks. `dependabot_alert` is available on repository, organization and app webhooks, and `/orgs/{org}/hooks` returns only the middle one. A provider routing alerts per repository, or through a GitHub App, has a correct configuration that is entirely absent from these commands. That is a scope limit rather than a finding, and an assessment that reads an empty org hooks list as a failure has misread it — walk the repositories in the boundary, or the app installation, and collect the same two calls there. What a delivery proves and where it stops. A `status_code` in the 200–399 band says the platform reached an endpoint and the endpoint accepted the payload. It does not say the payload was parsed, was routed onward, reached a person, or was acted on within any clock. Every one of those is the thing SR-08's procedures limb ultimately cares about, and every one of them lives in the receiving system rather than in this one — which is the honest reason this recipe is `partial` and would still be `partial` with a perfect delivery log. KSI-SCR-MIT was deliberately not claimed. The dataset reaches SR-08 from KSI-SCR-MON alone, and a routing path evidences monitoring reaching someone rather than a risk being identified, reviewed and mitigated.

Scanned population

The destinations the provider's incident response plan and communications procedures name as receiving supply-chain notifications — the on-call rota, the ticket queue, the SIEM ingestion endpoint — held OUTSIDE the platform. `config.url` is an opaque string: no response in this recipe says whether it belongs to the system a procedure names, to a service decommissioned last quarter, or to a personal endpoint someone added during an incident. Only the comparison against that named list decides it, and it is also the only step that sees a destination the procedure names and no webhook serves.

  • hooks

References

This pipeline mapping is authored opinion (overlay v0.8.0), versioned separately from the dataset and written against ruleset 2026.07.14.01. The upstream FedRAMP dataset names none of these tools.

The Supply Chain Risk run (8)

  • automatable
  • partial — needs judgement
  • narrative — no API proves this

Every authored recipe filed under SCR, in the order the plan works them. The mark says how much of the evidence the command produces on its own.