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 rules that made every change to the mainline arrive through a reviewed pull request, as a signed commit, onto a history that cannot be rewritten — together with the record of when those rules themselves last changed and who changed them, and the platform's own per-push record of any change that got past them.

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

partial — needs judgementapiweeklyGitHub repository rulesetsGitHub pull requests

Fetch

$ gh api --paginate "/orgs/<ORG>/repos?per_page=100"
$ gh api "/repos/<ORG>/<REPO>/rules/branches/<DEFAULT_BRANCH>"
$ gh api --paginate "/repos/<ORG>/<REPO>/rulesets?includes_parents=true&per_page=100"
$ gh api --paginate "/repos/<ORG>/<REPO>/rulesets/<RULESET_ID>/history?per_page=100"
$ gh api "/repos/<ORG>/<REPO>/rulesets/<RULESET_ID>/history/<VERSION_ID>"
$ gh api --paginate "/repos/<ORG>/<REPO>/rulesets/rule-suites?ref=refs/heads/<DEFAULT_BRANCH>&time_period=month&evaluate_status=active&rule_suite_result=bypass&per_page=100"

Expected output

Report names used below, and the command each comes from: `repos` is the first command, `branch-rules` the second, `rulesets` the third, `ruleset-history` the fourth, `ruleset-version` the fifth, `rule-suites-bypassed` the sixth. RUN COMMANDS 2, 3 AND 6 ONCE PER REPOSITORY AND ASSESSED BRANCH; run command 4 ONCE PER RULESET returned by command 3, and command 5 once per version of a ruleset you actually want to read. Every clause below speaks only for the repository and branch walked. `branch-rules` and `rulesets` return the shapes described in the sibling recipe `change-verification-status-checks-and-run-records`, and the rule types this recipe filters on are `pull_request`, `required_signatures` and `non_fast_forward`. The `pull_request` rule's parameters are `required_approving_review_count`, `dismiss_stale_reviews_on_push`, `require_code_owner_review`, `require_last_push_approval`, `required_review_thread_resolution` and `allowed_merge_methods`. `non_fast_forward` is the rule GitHub describes in its interface as blocking force pushes, and `required_signatures` the one under which "contributors and bots can only push commits that have been signed and verified to the branch". From `ruleset-history`, one entry per version of the ruleset: a `version_id`, an `actor` with an `id` and a `type`, and an `updated_at`. It says WHEN the change-control rules changed and WHO changed them, and it does not say what they changed to. From `ruleset-version`, the same three fields plus a `state` object, which is the ruleset as it stood at that version — this is the command to run when a history entry needs to become an answer. From `rule-suites-bypassed`, the push-evaluation records filtered by GitHub's own `rule_suite_result` parameter, documented as accepting `pass`, `fail`, `bypass` and `all`. The command passes `bypass`, so the response is built to contain only the pushes that got past the rules, each with `actor_name`, `before_sha`, `after_sha`, `ref` and `pushed_at`. `time_period` tops out at `month` and no retention period is documented for these records. The command also passes `evaluate_status=active` — documented as returning "only rule suites resulting from rulesets in active (non-evaluate) mode" — so the bypasses counted are bypasses of rules that were actually enforcing.

Assertions — what makes it a pass

Assertions for The rules that made every change to the mainline arrive through a reviewed pull request, as a signed commit, onto a history that cannot be rewritten — together with the record of when those rules themselves last changed and who changed them, and the platform's own per-push record of any change that got past them.: the field checked, the condition it must satisfy, the rows it applies to, and the controls a pass proves.
FieldMust beForProves
branch-rules[?type=='pull_request'] | [0]existsA pull-request rule is in effect on the assessed branch, so no change reaches the mainline except as a proposal — GitHub's wording is that the rule requires "that all changes to the target branch be associated with a pull request". Read from the endpoint that returns rules in effect, so an organization-level ruleset satisfies it exactly as a repository-level one does. It says changes are routed through a review mechanism; the next clause says whether that mechanism requires anybody.every rowSA-10
branch-rules[?type=='pull_request' && (!parameters || parameters.required_approving_review_count==`0`)]count_eq 0No pull-request rule in effect requires zero approvals. This is the clause above's other half: zero is a legal, selectable value for `required_approving_review_count`, and a rule set to it routes every change through a pull request the author can merge unread. Both limbs are needed because JMESPath treats the number zero as truthy — `!parameters.required_approving_review_count` cannot catch a count of zero, and the equality test alone cannot catch a rule object with no parameters — so the disjunction catches the empty process and the malformed row alike.every rowSA-10
branch-rules[?type=='required_signatures'] | [0]existsA required-signatures rule is in effect, so that under it "contributors and bots can only push commits that have been signed and verified to the branch". This is the integrity of a change made checkable after the fact: each change carries a cryptographic claim about its author. It asserts that the RULE is in force, not that any given commit is signed — history predating the rule is untouched by it — and a signature attributes a change without authorizing it.every rowSA-10
branch-rules[?type=='non_fast_forward'] | [0]existsA rule blocking force pushes is in effect on the assessed branch. It is asserted rather than left as hygiene because every other artifact this recipe collects is a claim ABOUT a history — a merge record, a signature, an approval — and all of them are statements about commits a force push can silently replace. This clause is the precondition of the others being worth reading, rather than a fourth independent property.every rowSA-10
rulesets[?enforcement=='evaluate' && target=='branch']count_eq 0No BRANCH ruleset applying to this repository is in dry-run mode. This clause is NOT what makes the clauses above trustworthy, and the first draft of this batch claimed it was. The endpoint those clauses read settles it instead: GitHub documents `/rules/branches/{branch}` as returning "all active rules that apply to the specified branch" and states that "Rules in rulesets with \"evaluate\" or \"disabled\" enforcement statuses are not returned" — so a rule that appears there is enforcing, and an evaluating ruleset is invisible to every `exists` clause rather than passing one. What this clause is, then, is a separate finding: a repository operating under a branch ruleset that somebody believes is in force and that blocks nothing. `evaluate` is the status the rule-suites API distinguishes when it offers to return "only rule suites resulting from rulesets in active (non-evaluate) mode". Narrowed to `target=='branch'` because the same response carries `tag`, `push` and `repository` rulesets, and a tag ruleset left evaluating has no bearing on the change path this recipe assesses.every rowSA-10

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:SA 4/25

Outside the boundaryGitHub Enterprise Cloud

SA-10 is a control about the DEVELOPER's configuration management, and on this plane the developer's configuration management system is a third party's hosted service. Adopting it to collect this evidence makes the provider's own change-control mechanism an external information resource under SA-09 and CA-03, and one that belongs in the external-system inventory AC-20 already covers. The consequence is sharper here than on a scanning control, because what is outsourced is not the detection of a problem but the ENFORCEMENT of the process: the rules that decide which changes may reach the mainline are held, evaluated and logged by the vendor, and the ruleset history that says who changed those rules is held by the same vendor. SA-10 is a class c and class d control, so every reader runs a Moderate or High system, and the offering named above is not necessarily the one such a reader can use: the Marketplace listing cited below is the listing for GitHub Enterprise Cloud, and a class c or class d reader has to check it against the class they operate at and look to whichever offering answers it — GitHub Enterprise Cloud for Government is a different product on different infrastructure with its own listing. No status value is written down here, because a certification status is a dated fact that changes.

Notes & assertions

SA-10 requires the provider to make the developer perform configuration management during development, implementation and operation; to document, manage and control the integrity of changes; to implement ONLY ORGANIZATION-APPROVED changes; to document approved changes and their potential security impacts; and to track flaws. This output answers the integrity-and-control limb with mechanism rather than with assertion: a rule that every change arrives through a pull request, a rule that its commits are signed, a rule that the history behind it cannot be rewritten, and the platform's own record of anything that got past all three. The gap the disposition named is the word ORGANIZATION-APPROVED. The configuration management plan naming the approving authority and the classes of change it covers is a document, and a merge record cannot show that the person who approved held that authority. Nothing here closes that, and the rating is unchanged by having written the commands down. A PULL-REQUEST RULE THAT REQUIRES NO APPROVAL. `required_approving_review_count` is a documented, selectable parameter and zero is a legal value for it. A `pull_request` rule with zero required approvals routes every change through a pull request and lets its author merge it unread — it satisfies the first clause, appears in every listing, and is a process with nobody in it. The second clause counts it, and it carries `!parameters` as a disjunct because the zero test alone cannot see a rule object with no parameters at all: JMESPath treats the number zero as truthy, so `!parameters.required_approving_review_count` would NOT catch a count of zero and `parameters.required_approving_review_count == 0` would not catch an absent object. The two together catch both, and a rule that legitimately carries no parameters fails loudly rather than passing silently. THE DRY-RUN TRAP is closed by the endpoint the first four clauses read, not by the fifth, and the first draft of this batch had that backwards in both siblings. GitHub documents `/rules/branches/{branch}` as returning "all active rules that apply to the specified branch" and says that "Rules in rulesets with \"evaluate\" or \"disabled\" enforcement statuses are not returned" — so a `pull_request`, `required_signatures` or `non_fast_forward` rule found there is enforcing, and a ruleset in dry-run is invisible to those clauses rather than satisfying them. That matters more here than in the sibling recipe, because four of this recipe's five clauses read that endpoint. The fifth is kept as an independent finding — a branch ruleset believed to be in force that blocks nothing — and not as the guard the draft called it. SIGNED COMMITS PROVE ATTRIBUTION, NOT AUTHORITY. Under `required_signatures`, "contributors and bots can only push commits that have been signed and verified to the branch", which is the integrity limb of SA-10 in the most literal available sense: every change carries a cryptographic claim about who made it, checkable long after the fact. What a signature does not carry is that the signer was entitled to make that change, which is the organization-approved gap again one layer down. Note also what the clause asserts and does not: that the RULE is in effect, not that any particular commit on the branch is signed. A history that predates the rule is unaffected by it, and nothing in this recipe reads the commits themselves. BLOCKING FORCE PUSHES IS WHAT MAKES THE REST OF THE RECORD EVIDENCE. `non_fast_forward` is the rule GitHub describes as preventing force pushes, and it is asserted here rather than treated as hygiene because every other artifact this recipe collects is a claim about a history: a merge record, a signature, an approval. All of them are statements about commits that a force push can replace. The fourth clause is therefore not a fourth control property but the precondition of the other three being worth reading. THE HISTORY ENDPOINT IS THE ONE PIECE OF EVIDENCE ABOUT THE CONTROL ITSELF, AND IT CARRIES NO CLAUSE ON PURPOSE. `/rulesets/{ruleset_id}/history` returns a `version_id`, an `actor` and an `updated_at` per version — who changed the change-control rules, and when. That is as close as this plane comes to configuration management OF the configuration management, and no threshold over it would mean anything: one entry is a ruleset created once and never touched, which is a good state, and forty entries is a ruleset under active maintenance, which is also a good state. It is collected because an assessment reading a green ruleset today needs to know whether it was green last quarter, and the version list is the only thing here that answers. Turning an entry into an answer takes the per-version command, which returns the ruleset's `state` as it stood. THE BYPASS LIST IS FILTERED BY THE VENDOR, AND THAT IS WHY ITS EMPTINESS MEANS SOMETHING. The sixth command passes `rule_suite_result=bypass`, so the response is constructed to hold only the pushes that got past the rules; an empty response is the meaningful kind of empty rather than the ambiguous kind, in the way a `status=failure` list is and a full list is not. Its bounds are the same as the sibling recipe's — at most a month, and only as much of that month as the platform retains — which is why the cadence here is weekly rather than monthly despite everything else in the recipe being configuration that changes rarely. KSI-SCR-MIT asks the provider to persistently identify, review and mitigate potential supply chain risks, and this recipe is claimed on the MITIGATE limb alone. The developer is a supply chain participant, and constraining what any developer — or any credential belonging to one — can put into the mainline is a mitigation of that participant's risk. It is not an identification of any risk and not a review of one: nothing here surfaces a finding, ranks it, or records a decision about it. The identify and review limbs of this indicator are carried on this plane by `dependency-vulnerability-monitoring`, and an indicator page carrying this recipe should be read as carrying evidence for a part. The evidence platform is itself an external system; see `external_system`. Two sibling recipes in the same batch read overlapping commands for different controls, and are separate recipes for the reason given in `change-verification-status-checks-and-run-records`. ONE LIMB OF THE DISPOSITION IS NOT CARRIED FORWARD, and it is recorded here because deleting the disposition deletes the only other place it was written down. The rationale this recipe spends argued from "branch protection, required reviews, signed commits AND MERGE RECORDS", and there is no merge record in this recipe: no `/pulls`, no `/commits`, no review endpoint. What replaced that limb is the rule that makes a pull request compulsory and the bypass list that says nothing got around it, which is the mechanism rather than the instances. The instances are collected on this plane by `security-representative-change-approval`, under a different control, and an assessment wanting per-change evidence for SA-10 should read that recipe's fifth and sixth commands beside this one. The rating is unaffected — `partial` was right on either reading — but the covered ground is narrower than the register said it would be. The type-string-to-interface-name join is unsourced and is stated here rather than implied. `required_signatures` and `non_fast_forward` are the API `type` values, taken from the organization-rules reference; "Require signed commits" and "Block force pushes" are the names the ruleset documentation uses for the same two rules. No page cited here maps one spelling to the other. The mapping is not in doubt, and it is not cited either, which is the honest way to leave it.

Scanned population

The repositories inside the authorization boundary, taken from the component inventory the provider maintains for CM-08 and enumerates in its Certification Package Overview — a list held OUTSIDE the platform, because the first command returns one organization's view of itself and a boundary repository owned by another organization or hosted elsewhere is absent from it rather than false. The branch dimension applies here exactly as it does to the sibling recipe on CM-04 (02): nothing in any response enumerates branches, and the branch these rules are read against has to be the branch the provider's release procedure says it ships from. There is a third reconciliation this control needs and neither of the others does. SA-10 is about a DEVELOPER, and the population of developers is not the population of repositories: a boundary component built by a contractor in their own tenancy, or vendored in as a source drop, has a configuration management process no command here can reach, and its absence from these responses looks identical to a component that has no changes.

  • repos

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.