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

> FedRAMP Consolidated Rules for 2026 v2026.07.14.01 · updated 2026-07-14
> Canonical page: /collect/mobile-code-admission-and-bundle-provenance

Recipe id: `mobile-code-admission-and-bundle-provenance` · cadence on-change · partial

> **Authored opinion.** pipeline overlay v0.8.0, written
> against dataset 2026.07.14.01. The upstream
> FedRAMP rules name none of these tools; this mapping is ours.

## What it proves

- KSI `KSI-PIY-RSD`
- KSI `KSI-SCR-MIT`
- control `sc-18`

## Collection

Kind: `api`

```sh
# repos
gh api --paginate "/orgs/<ORG>/repos?per_page=100"
# branch-rules
gh api "/repos/<ORG>/<REPO>/rules/branches/<DEFAULT_BRANCH>"
# dependency-review
gh api "/repos/<ORG>/<REPO>/dependency-graph/compare/<BASE_SHA>...<HEAD_SHA>"
# verify-results
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

- {"field":"branch-rules[?type=='required_status_checks'] | [0].parameters.required_status_checks | [0].context","op":"exists","controls":["sc-18"],"description":"At 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."}
- {"field":"branch-rules[?type=='required_status_checks' && parameters.strict_required_status_checks_policy==`false`]","op":"count_eq","value":0,"controls":["sc-18"],"description":"No 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."}
- {"field":"dependency-review[?change_type=='added' && scope=='runtime' && length(vulnerabilities) > `0`]","op":"count_eq","value":0,"controls":["sc-18"],"description":"No 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."}
- {"field":"dependency-review[?change_type=='added' && scope=='unknown' && length(vulnerabilities) > `0`]","op":"count_eq","value":0,"controls":["sc-18"],"description":"No 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."}
- {"field":"dependency-review[?change_type=='added' && license==`null`]","op":"count_eq","value":0,"controls":["sc-18"],"description":"No 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."}
- {"field":"verify-results | [0].verificationResult.statement.subject","op":"exists","controls":["sc-18"],"description":"The 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."}

## Outside the boundary

Platform: GitHub 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

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.

## References

- {"title":"GitHub REST: dependency review — GET /repos/{owner}/{repo}/dependency-graph/compare/{basehead} with `basehead` documented in the form `{base}...{head}`; each change carrying `change_type` added | removed, required `manifest`, `ecosystem`, `name` and `version`, `package_url`, `license` and `source_repository_url` each string or null, a required `vulnerabilities` array of `{severity, advisory_ghsa_id, advisory_summary, advisory_url}`, and `scope` unknown | runtime | development","url":"https://docs.github.com/en/rest/dependency-graph/dependency-review"}
- {"title":"GitHub: about dependency review — \"Dependency review lets you catch insecure dependencies before you introduce them to your environment, and provides information on license, dependents, and age of dependencies\", scoped to \"pull requests that contain changes to package manifests or lock files\", with the action's check failing by default \"if it discovers any vulnerable packages\"","url":"https://docs.github.com/en/code-security/supply-chain-security/understanding-your-software-supply-chain/about-dependency-review"}
- {"title":"GitHub: configuring the dependency review action — the enforcement options an acceptability list is expressed through: `fail-on-severity` accepting critical, high, moderate or low, `allow-licenses`, `deny-licenses`, `allow-ghsas`, `fail-on-scopes` accepting development, runtime or unknown, `config-file` and `external-repo-token`","url":"https://docs.github.com/en/code-security/supply-chain-security/understanding-your-software-supply-chain/configuring-the-dependency-review-action"}
- {"title":"GitHub REST: rules for a branch — GET /repos/{owner}/{repo}/rules/branches/{branch}, which \"Returns all active rules that apply to the specified branch\"; for `required_status_checks`, a required array of \"Status checks that are required\" whose entries carry a required `context` (\"The status check context name that must be present on the commit\") and an optional `integration_id`, plus the required boolean `strict_required_status_checks_policy` (\"Whether pull requests targeting a matching branch must be tested with the latest code\") and the optional `do_not_enforce_on_create`","url":"https://docs.github.com/en/rest/repos/rules"}
- {"title":"GitHub CLI manual: gh attestation verify — the synopsis `gh attestation verify [<file-path> | oci://<image-uri>] [--owner | --repo] [flags]`, so a built file and not only a registry image can be verified; `--signer-workflow` enforcing that the signing workflow matches `[host/]<owner>/<repo>/<path>/<to>/<workflow>`, `--predicate-type` defaulting to https://slsa.dev/provenance/v1, and `--format json` emitting one entry per verified attestation carrying `attestation` and a `verificationResult` with `signature.certificate`, `verifiedTimestamps` and `statement`","url":"https://cli.github.com/manual/gh_attestation_verify"}
- {"title":"GitHub: artifact attestations — \"Artifact attestations enable you to create unfalsifiable provenance and integrity guarantees for the software you build\", covering binaries, packages and manifests, and its own limit: \"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\"","url":"https://docs.github.com/en/actions/concepts/security/artifact-attestations"}
- {"title":"FedRAMP Marketplace: GitHub Enterprise Cloud — the listing to re-read rather than quote, and the place to check the impact level against the system this recipe is being run for; SC-18 reaches class c and class d only, so every reader of this recipe is Moderate or High and the gap between a product's listed level and their own is the SA-09 question this recipe's external-system note raises and does not answer","url":"https://www.fedramp.gov/marketplace/products/FR1812058188/"}
