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

For a service that ships code to a browser, the two things a pipeline can say about the mobile code it delivers: what was allowed INTO it, and whether what shipped is what this pipeline built. The first is the dependency diff for the change — every component added, its ecosystem, its version, its licence and any advisory against it, separated by whether it reaches the runtime or stops at the build — and the gate that makes the check mandatory rather than advisory. The second is a provenance attestation over the built bundle, verified against the repository and workflow that are supposed to have produced it. Neither is a statement about which mobile code technologies the organization decided to permit, and that is the control's first limb.

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

partial — needs judgementapion-changeGitHub dependency reviewGitHub artifact attestationsGitHub repository rulesets

Fetch

$ gh api --paginate "/orgs/<ORG>/repos?per_page=100"
$ gh api "/repos/<ORG>/<REPO>/rules/branches/<DEFAULT_BRANCH>"
$ gh api "/repos/<ORG>/<REPO>/dependency-graph/compare/<BASE_SHA>...<HEAD_SHA>"
$ gh attestation verify <BUNDLE_PATH> --repo <ORG>/<REPO> --signer-workflow <ORG>/<REPO>/.github/workflows/<BUILD_WORKFLOW> --format json

Expected output

Report names used below, and the command each comes from: `repos` is the first, `branch-rules` the second, `dependency-review` the third and `verify-results` the fourth. From `branch-rules`, GitHub documents the endpoint as returning "all active rules that apply to the specified branch" — the effective set, which is what this recipe needs, because a rule can arrive from a repository ruleset or from an organization one and a reader asking whether the branch is gated does not care which. For the `required_status_checks` rule type the documented parameters are a required `required_status_checks` array of "Status checks that are required", each entry carrying a required `context` — "The status check context name that must be present on the commit" — and an optional `integration_id`, "The optional integration ID that this status check must originate from"; a required boolean `strict_required_status_checks_policy`, "Whether pull requests targeting a matching branch must be tested with the latest code" — whose documentation continues "This setting will not take effect unless at least one status check is enabled", which is why the clause asserting it is not the first clause in this recipe and why the existence clause that precedes it is not redundant with it; and an optional `do_not_enforce_on_create`. From `dependency-review`, the endpoint is `GET /repos/{owner}/{repo}/dependency-graph/compare/{basehead}` and `basehead` is documented as expecting the form `{base}...{head}`. Each returned change carries `change_type`, documented `added | removed`; required `manifest`, `ecosystem`, `name` and `version`; `package_url`, `license` and `source_repository_url`, each documented string or null; a required `vulnerabilities` array whose entries carry `severity`, `advisory_ghsa_id`, `advisory_summary` and `advisory_url`; and `scope`, documented `unknown | runtime | development`. From `verify-results`, the CLI documents `gh attestation verify [<file-path> | oci://<image-uri>] [--owner | --repo]`, so the same command verifies a built file and not only a registry image — which is the form this recipe needs, because a browser bundle is a file. With `--format json` it emits an array with one entry per verified attestation, each carrying `attestation` and a `verificationResult` holding the parsed bundle: `signature.certificate`, `verifiedTimestamps` and `statement`. `--signer-workflow` is documented as enforcing that the signing workflow matches `[host/]<owner>/<repo>/<path>/<to>/<workflow>`, and it is the flag that carries this clause's weight; `--predicate-type` defaults to `https://slsa.dev/provenance/v1`.

Assertions — what makes it a pass

Assertions for For a service that ships code to a browser, the two things a pipeline can say about the mobile code it delivers: what was allowed INTO it, and whether what shipped is what this pipeline built. The first is the dependency diff for the change — every component added, its ecosystem, its version, its licence and any advisory against it, separated by whether it reaches the runtime or stops at the build — and the gate that makes the check mandatory rather than advisory. The second is a provenance attestation over the built bundle, verified against the repository and workflow that are supposed to have produced it. Neither is a statement about which mobile code technologies the organization decided to permit, and that is the control's first limb.: the field checked, the condition it must satisfy, the rows it applies to, and the controls a pass proves.
FieldMust beForProves
branch-rules[?type=='required_status_checks'] | [0].parameters.required_status_checks | [0].contextexistsAt least one status check is REQUIRED to pass on the branch the delivered bundle is built from. This is the positive clause the two dependency clauses below depend on: both read a diff for a change that, on a branch with no such rule, could have been merged with the review job skipped, failing or never triggered — a check that runs and does not gate is not enforcement, and a report of its findings is not evidence of one. What the endpoint cannot say is WHICH context is the dependency review job: `context` is documented as "The status check context name that must be present on the commit" and that name is chosen by the provider's own workflow, so reconciling the listed contexts against the workflow that runs dependency review is the human step `scan_scope` names. The clause asserts a gate exists; the reader confirms it is this gate.every rowSC-18
branch-rules[?type=='required_status_checks' && parameters.strict_required_status_checks_policy==`false`]count_eq 0No required-status-checks rule on this branch permits merging code that was not tested with the latest base. The parameter is documented and required — "Whether pull requests targeting a matching branch must be tested with the latest code", with the documentation adding that "This setting will not take effect unless at least one status check is enabled" — and with it false the check that passed may have run against a tree that is not the one that shipped. That second sentence is the reason the existence clause above is not redundant with this one: on a branch with no enabled check this parameter is inert whatever it says, so a `true` here is worth exactly as much as the clause above establishes. Mobile code is where that gap is least forgiving: the artifact the user's browser executes is built from the merged result, so a dependency admitted by a stale check is delivered without ever having been reviewed against what it was merged into. The literal comparison is to `false` rather than to `null`, and that is a choice the parameter's documented shape licenses rather than an oversight: it is required, so an absent key is not a state the endpoint returns. Were it to become optional, this clause would read the absent key as passing, which is the direction to watch.every rowSC-18
dependency-review[?change_type=='added' && scope=='runtime' && length(vulnerabilities) > `0`]count_eq 0No component added into the runtime scope of this change carries a known advisory. `change_type` is documented `added | removed` and `scope` `unknown | runtime | development`, and the narrowing to added-and-runtime is what makes this a mobile code clause rather than a general dependency one: a development-scoped package is a build-time tool that is not delivered to a browser, and a removal cannot introduce anything. Offender form over a list built to hold offenders — `vulnerabilities` is a required array, so a clean component is present with an empty one rather than dropped, and the count is over rows that exist. It does NOT cover the whole delivered population on its own: `unknown` is the third documented value and a component that lands there is absent from this clause's rows rather than false in them, which is why the clause below exists. Read `notes` for what the runtime scope does not delimit even when it is populated.every rowSC-18
dependency-review[?change_type=='added' && scope=='unknown' && length(vulnerabilities) > `0`]count_eq 0No component added with an UNDETERMINED scope carries a known advisory either. This clause is the audit correction of the batch and it is the same defect the sibling recipe on SA-08 guards against one file over, arriving through a different field: `scope` is documented `unknown | runtime | development`, an ecosystem that does not classify production against development resolves every component to `unknown`, and a clause narrowing to `runtime` then reads a population that excludes the entire change. A critical advisory on a delivered dependency passed the runtime clause silently, on a repository that had done nothing wrong — absent from the rows rather than false in them, which is the vacuity shape this plane's card opens with. Both clauses are needed rather than one merged clause over `scope!='development'`, because the two say different things to a reader: the one above is a finding about delivered code, and this one is equally a finding about an ecosystem whose scope metadata cannot be used to tell delivered from build-time at all.every rowSC-18
dependency-review[?change_type=='added' && license==`null`]count_eq 0No component was admitted whose licence the platform could not determine. `license` is documented string or null, and the null is not a formality here: SC-18's first limb is a decision about which mobile code is acceptable, and an acceptability decision cannot be made about code whose terms are unknown. It is also the row an allow-list check must skip — the action's `allow-licenses` and `deny-licenses` operate on this same field, so an undeterminable licence passes a deny-list by being absent from it rather than by being permitted. Asserting it here rather than leaving it to the action is deliberate: the action's outcome is a check run's conclusion, and this report is the thing a reader can re-read a month later.every rowSC-18
verify-results | [0].verificationResult.statement.subjectexistsThe verification returned at least one attestation whose parsed statement names the subject it covers. The subject is the digest of the delivered bundle, so this is the clause that ties the mobile code shipped to a build the platform signed for — the enforcement limb's "signed by a trusted source" in the only form this pipeline emits it. The clause is deliberately thin and the command carries the weight: `--repo` and `--signer-workflow` are what make a successful verification mean THIS repository and THIS workflow rather than an attestation existing somewhere for these bytes, and a reader who drops them has a green clause and a much weaker claim. It establishes provenance and integrity for the artifact and nothing about the behaviour of the code inside it.every rowSC-18

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:SC 13/35

Outside the boundaryGitHub Enterprise Cloud

The dependency metadata, the advisory data behind it, the signing infrastructure that issues the attestation and the trust root it is verified against are all held and operated by a third party, 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. SC-18 makes the signing half pointed in a way the other pipeline recipes are not: the control's own discussion contemplates mobile code being digitally signed by a TRUSTED SOURCE, and the evidence that it was signed is produced, held and validated by the same third party whose trust root decides what trusted means. SC-18 sits in the class c and class d baselines and NOT in class b, so every reader of this recipe is a Moderate or High system, which sharpens rather than softens the SA-09 question: the impact level a platform's Marketplace listing carries is not necessarily the one a given system needs, and because this control starts at Moderate a reader here never matches a lower-listed offering by default. Read the cited listing and treat any gap as the SA-09 finding rather than as a footnote; no value for it is written here because it is a dated fact that changes, and a gap that is real today may be closed or widened by the time this recipe is run. The admission half is also behind a separate entitlement, which is a purchasing fact rather than a caveat: GitHub documents the dependency review action as "available for all public repositories, as well as private repositories that have GitHub Code Security or GitHub Advanced Security enabled", and every repository inside an authorization boundary is private. A reader without it does not get a weaker version of these two clauses; they get no diff to read. The attestation half carries no such gate on the pages cited here. Authored against GitHub Enterprise Cloud; github.com and the data-residency offering are different deployments on different infrastructure.

Notes & assertions

WHY PARTIAL, AGAINST THE CONTROL'S TWO LIMBS. SC-18 has a definition limb — establishing which mobile code technologies are acceptable — and an enforcement limb: authorizing, monitoring and controlling their use, with the discussion contemplating mobile code digitally signed by a trusted source. This plane reaches the second limb and cannot reach the first. A gate enforces an acceptability list and does not establish one, and no output collected here names a single decision about a technology. That is the same shape the sibling recipes on CM-03 (04) and SA-22 carry, and it is why this is `partial` rather than `full` even though two of its clauses are as mechanical as any in this overlay. DEPENDENCY REVIEW ONLY SEES PULL REQUESTS THAT TOUCH A MANIFEST. GitHub documents the feature for "pull requests that contain changes to package manifests or lock files". A change that alters the delivered mobile code without touching one — a vendored script edited in place, an inline handler added to a template, a CDN URL swapped in a page, a build configuration change that pulls a different chunk — produces a diff with no entries, and both dependency clauses below are then vacuously green over a change that did exactly what this control is about. The clauses are written in offender form so an empty result is empty rather than false, and this note is the honest reading of what an empty result means. WHAT `scope=='runtime'` DOES AND DOES NOT DELIMIT. The scope value is the ecosystem's own classification of a dependency as production or development, and for a browser bundle it is the closest available proxy for "ends up in the code the user's browser executes". It is a proxy: the mapping from a runtime-scoped package to bytes actually emitted belongs to the bundler, not the platform, and tree-shaking, lazy chunks and server-only imports all break it. Where the scope IS populated it breaks in the safe direction — more components are claimed as delivered than are delivered. Where it is not, it broke the other way and this recipe shipped the clause wrong until the audit: a component the ecosystem cannot classify resolves to `unknown`, falls outside a clause narrowed to `runtime`, and takes its advisories out of the count with it. That is now asserted separately rather than described, because a residue that hides findings is not a residue, and the sentence you are reading replaced one that claimed the failure direction was safe in both cases. WHY THE ATTESTATION CLAUSE IS THIN AND THE COMMAND IS NOT. The clause below is an existence check over a parsed statement, and nearly all of this evidence's weight is carried by the flags on the command that produced it: `--repo` scopes the attestation lookup and `--signer-workflow` is documented as enforcing that the signing workflow matches a named path. A reader who drops those flags gets a verification that proves an attestation exists somewhere for the bytes, which is a much weaker claim than the one this recipe describes. The sibling recipe `build-provenance-attestation-verification` reads the same machinery at depth on SI-07 (07), including the version-specific reason a clause has to read the parsed statement rather than trust the exit code, and this recipe deliberately does not repeat it. WHAT AN ATTESTATION CANNOT SAY, IN THE PLATFORM'S OWN WORDS. GitHub's page states that "artifact attestations are not a guarantee that an artifact is secure. Instead, artifact attestations link you to the source code and the build instructions that produced them", and that defining and evaluating the policy is the consumer's job. For SC-18 that is the right size of claim and is worth stating as a limit rather than as a caveat: the enforcement limb asks that mobile code be signed by a trusted source, and this establishes that the delivered bundle came from a named repository and workflow. It says nothing whatsoever about what the code in it does. TWO INDICATORS REACH THIS CONTROL AND THEY ARE CLAIMED FOR DIFFERENT HALVES. KSI-SCR-MIT — persistently identify, review and mitigate supply chain risks — is claimed for the admission half, where a component with a known advisory or an undeterminable licence is identified before it enters the delivered artifact. KSI-PIY-RSD, whose statement is about the effectiveness of building security into the SDLC being persistently reviewed, is claimed for the gate itself: a required check and a signed build are the SDLC's shape, and their persistent review is the human act neither clause performs.

Scanned population

Three lists, all held OUTSIDE the platform, and the second is the one that decides whether this recipe is about the mobile code a user executes or only about the part of it this pipeline happens to build. First, the repositories that produce delivered front-end artifacts, from the component inventory the provider maintains for CM-08. A bundle built in a second organization, vendored in as a committed file, or assembled by a pipeline that is not this one is absent from `repos` rather than reported unattested. Second, THE DELIVERED PAGES AND THE ORIGINS THEY LOAD AT RUNTIME. This is the blind spot that matters most for this control and it is a whole category rather than an edge case. A tag manager, an analytics snippet, a chat widget, an A/B testing script or an ad tag fetched from a third-party origin when the page loads is mobile code delivered to and executed by the user's browser, and it appears in no manifest, no dependency diff and no attestation — it was never in the build. Nothing in this recipe's output sees it. What sees it is the delivered page itself and the Content Security Policy and Subresource Integrity that constrain it, and neither is a pipeline artifact. The list of permitted runtime origins, reconciled against what the production page actually loads, is a step somebody performs outside everything collected here, and an evidence set read as "the mobile code in the boundary" is wrong by exactly that set. Third, THE ACCEPTABILITY LIST ITSELF: which mobile code technologies the organization has decided are permitted, and the restrictions on their use. Every clause below enforces a list; none of them establishes one. The dependency review action's own `allow-licenses`, `deny-licenses` and `allow-ghsas` options are where such a list gets expressed as configuration, and the configuration is the written decision's shadow rather than the decision.

  • repos
  • dependency-review
  • verify-results

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 Policy and Inventory run (4)

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

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