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 every change that reached the assessed branch, the automated verification that ran against it — which workflows ran, on which commit, and what each concluded — together with the two things that decide whether those runs were a condition of the change or merely adjacent to it: the rule that made the checks required, and the platform's own per-push record of whether that rule held, failed, or was bypassed. The runs alone are activity; the rule and the per-push record are what make them a gate.

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

partial — needs judgementapiweeklyGitHub ActionsGitHub repository rulesets

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/rule-suites?ref=refs/heads/<DEFAULT_BRANCH>&time_period=month&evaluate_status=active&per_page=100"
$ gh api --paginate "/repos/<ORG>/<REPO>/actions/runs?branch=<DEFAULT_BRANCH>&status=completed&per_page=100"
$ gh api --paginate "/repos/<ORG>/<REPO>/actions/runs?branch=<DEFAULT_BRANCH>&status=failure&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, `rule-suites` the fourth, `runs-completed` the fifth, `runs-failure` the sixth. RUN COMMANDS 2 THROUGH 6 ONCE PER REPOSITORY AND ONCE PER ASSESSED BRANCH, not once for the organization. The first command returns the repository list and the rest take a single `<REPO>` and a single `<DEFAULT_BRANCH>`; every clause below speaks only for the repository and branch actually walked, and collecting one repository's rules and reading them as the organization's is the arithmetic error this recipe is most likely to be assessed with. From `branch-rules`, an array of the repository rule objects IN EFFECT on that branch, each carrying a `type` drawn from `creation`, `update`, `deletion`, `required_linear_history`, `required_deployments`, `required_signatures`, `pull_request`, `required_status_checks`, `non_fast_forward`, `commit_message_pattern`, `commit_author_email_pattern`, `committer_email_pattern`, `branch_name_pattern`, `tag_name_pattern`, `file_path_restriction`, `max_file_path_length`, `file_extension_restriction`, `max_file_size`, `workflows`, `code_scanning` and `copilot_code_review`, and a `parameters` object whose shape depends on the type. For `required_status_checks` the parameters are a `required_status_checks` array of `{context, integration_id}` entries, `strict_required_status_checks_policy` and `do_not_enforce_on_create`. From `rulesets`, the rulesets that apply to the repository — `includes_parents` defaults to TRUE, so organization and enterprise rulesets are included and `source_type` (`Repository`, `Organization`, `Enterprise`) says which is which. Each carries `id`, `name`, a `target` of `branch`, `tag`, `push` or `repository`, an `enforcement` of `disabled`, `active` or `evaluate`, `bypass_actors` entries with an `actor_type` of `Integration`, `OrganizationAdmin`, `RepositoryRole`, `Team`, `DeployKey` or `User` and a `bypass_mode` of `always`, `pull_request` or `exempt`, plus `current_user_can_bypass`, `created_at` and `updated_at`. From `rule-suites`, one record per push evaluated against the rules: `id`, `actor_id`, `actor_name`, `before_sha`, `after_sha`, `ref`, `repository_id`, `repository_name`, `pushed_at`, a `result` of `pass`, `fail` or `bypass`, and an `evaluation_result`. The per-suite endpoint adds `rule_evaluations`, each with a `rule_source`, a `rule_type`, a `result` and an `enforcement` of `active`, `evaluate` or `deleted ruleset` — worth fetching for any suite this recipe's clauses flag, because it is the only place that says WHICH rule decided. `time_period` accepts `hour`, `day`, `week` and `month` and defaults to `day`; the command above passes `month`, which is the documented ceiling, and `evaluate_status=active`, documented as returning "only rule suites resulting from rulesets in active (non-evaluate) mode" — without it the response mixes pushes measured against enforcing rulesets with pushes measured against dry-run ones, and nothing on the page describes what `result` holds for the second kind. From `runs-completed` and `runs-failure`, workflow runs with `id`, `name`, `head_branch`, `head_sha`, `path`, `run_number`, `run_attempt`, `event`, `status`, a `conclusion` — documented on the workflow-runs page as a string or null and NOT as an enum, so treat `success`, `failure`, `neutral`, `cancelled`, `skipped`, `timed_out` and `action_required` as the values to expect rather than as a closed set this citation establishes — `workflow_id`, `created_at`, `updated_at`, `run_started_at`, `triggering_actor`, `head_commit` and `referenced_workflows`. `failure` is a value of the `status` FILTER as well as of `conclusion`, so the sixth command returns a list built to hold only failing runs.

Assertions — what makes it a pass

Assertions for For every change that reached the assessed branch, the automated verification that ran against it — which workflows ran, on which commit, and what each concluded — together with the two things that decide whether those runs were a condition of the change or merely adjacent to it: the rule that made the checks required, and the platform's own per-push record of whether that rule held, failed, or was bypassed. The runs alone are activity; the rule and the per-push record are what make them a gate.: 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]existsA required-status-checks rule is in effect on the assessed branch — GitHub's own words for it are that such checks "ensure that all required CI tests are passing before collaborators can make changes to a branch". Read from `/rules/branches/{branch}`, which returns the rules in effect rather than the rulesets that might contain them, so an organization-level ruleset counts here exactly as a repository-level one does. It says a gate is declared; it does not say the gate names any check, which is the next clause, and it does not say the ruleset behind it enforces anything, which is the one after.every rowCM-04 (02)
branch-rules[?type=='required_status_checks' && !(parameters.required_status_checks)]count_eq 0No required-status-checks rule in effect declares an empty set of checks. This is the clause the one above cannot be trusted without: the rule is present in every listing, reads as a gate in every screenshot, and requires nothing. Written as an offender count over rows present in the same response. THE PARENTHESES ARE LOAD-BEARING and were absent in the first draft of this batch: `!parameters.X` parses as `(!parameters).X`, which evaluates to null, which is falsy, so the unparenthesised clause selects no row on any input and is green by construction. `!(parameters.required_status_checks)` is the form that works, and it catches the empty array and the absent key alike, because both are falsy — where `length()` would raise on the row that has no `parameters` at all.every rowCM-04 (02)
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 rowCM-04 (02)
rule-suites[?result=='bypass']count_eq 0No change reached the assessed branch in the retrieved window by bypassing the rules that verify it. The list is the platform's own per-push evaluation record, so a bypassed push is present-and-labelled rather than absent — this is the rare clause on this plane whose population is built by the vendor to contain the failure. The command passes `evaluate_status=active`, documented as returning "only rule suites resulting from rulesets in active (non-evaluate) mode", so a push measured against a dry-run ruleset is not counted here. Its window is at most a month, because `time_period` tops out there and no retention period is documented for these records at all, and it is green on a repository with no ruleset at all — which is why it is read with the first clause and not alone. Unlike a push-protection bypass, this record carries no reason field, so it cannot be read as a per-event authorized exception; what it can be read against is `bypass_actors` in the `rulesets` response, which is the STANDING authorization, and a provider with a designated break-glass integration will fail this clause while operating as configured.every rowCM-04 (02)

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:CM 14/34

Outside the boundaryGitHub Enterprise Cloud

There is no independent observer anywhere in this recipe, and that is a sharper external-system question than the usual one. The rule that makes verification a condition of a change, the evaluation of that rule, and the log of that evaluation are three views held by one party, and the workflow runs the rule points at are executed by the same party on the same infrastructure. A provider's assurance that its changes were verified therefore rests entirely on an outside vendor's account of its own enforcement, which makes the platform an information resource whose potential impact has to be addressed under SA-09 and CA-03 and accounted for in the external-system inventory AC-20 already covers. CM-04 (02) is a class c and class d control, so every reader of this recipe 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. That is the SA-09 question here. It is answered by reading the listing rather than by this sentence, and no status value is written down, because a certification status is a dated fact that changes.

Notes & assertions

CM-04 (02) requires the provider to verify that the IMPACTED CONTROLS are implemented correctly, operating as intended, and producing the desired outcome. This output shows that automated checks ran against each change, that passing them was a condition of the change, and what each concluded. Which controls a change impacts, and whether a passing test verifies one of them, is a mapping from test to control that a human writes; a pipeline that publishes such a mapping is publishing an authored artifact rather than a measurement. The disposition this recipe spends said exactly that, and writing the commands down has not changed the rating. THE DRY-RUN TRAP, AND THE ENDPOINT CHOICE THAT ALREADY CLOSED IT. A ruleset in `evaluate` mode is a complete, well-formed set of rules that enforces nothing, and it is this plane's empty list wearing a different coat: it appears in `/rulesets` intact and generates rule suite records. The first draft of this batch hedged about whether `/rules/branches/{branch}` returns such rules, called it a live risk, and told the reader to make one call against a live repository to settle it. That was a manufactured uncertainty, and the page the recipe already cites settles it in one sentence: the endpoint "Returns all active rules that apply to the specified branch", and "Rules in rulesets with \"evaluate\" or \"disabled\" enforcement statuses are not returned." So reading the rules IN EFFECT rather than the rulesets that contain them is what closes the trap here, and it was closed before any clause was written. The consequence runs both ways and both are worth stating: the first two clauses are stronger than the draft claimed, because a rule they find is enforcing by construction; and the evaluate clause is weaker, because it guards nothing above it. It stays as a finding in its own right — a branch ruleset somebody believes is in force and that blocks nothing — narrowed to `target=='branch'`, since the same response carries tag, push and repository rulesets whose dry-run state says nothing about this change path. A REQUIRED-CHECKS RULE THAT REQUIRES NOTHING. The `required_status_checks` rule takes a `required_status_checks` array of contexts, and an empty one is a rule that is present in every listing, satisfies the first clause, and gates on no check whatsoever. The second clause is spelled `!parameters.required_status_checks` rather than with `length()` deliberately: an empty array and an absent key are both falsy under that spelling and both are genuine offenders, while `length()` raises on the row where `parameters` is missing entirely — which is the row most worth seeing. A rule object that legitimately carries no `parameters` would fail this clause; that is a false red that makes somebody look, which is the direction this repo has chosen every time it has had the choice. WHY THE BYPASS CLAUSE IS ASSERTED HERE AND THE PUSH-PROTECTION ONE WAS NOT. `secret-exposure-detection-and-push-protection` deliberately does not count push-protection bypasses to zero, because a bypass there is a documented decision with a named actor and one of three stated reasons, and a provider with a legitimate test fixture would fail such a clause while behaving correctly. A rule suite bypass has no reason field in anything fetched: the record carries `actor_name`, `result` and the rule evaluations, and nothing that says why. It therefore cannot be read as an authorized exception — all it says is that a change reached the assessed branch without the verification the rule required, which for CM-04 (02) is the finding rather than a footnote. What it CAN be read against, and the first draft of this batch missed this, is `bypass_actors` — returned in the same `rulesets` response this recipe already collects, with a `bypass_mode` of `always`, `pull_request` or `exempt`. That is a standing authorization to bypass, configured and visible, so a provider with a designated break-glass integration fails this clause while operating exactly as configured. The per-event reason is absent; the standing permission is not, and a failure of this clause is read against that list before it is read as a finding. Note one collection caveat that would make any future clause over `bypass_actors` vacuous: GitHub documents that "To prevent leaking sensitive information, the bypass_actors property is only returned if the user making the API request has write access to the ruleset", so a read-only assessment token sees an empty bypass list rather than an error. THE WINDOW, AND WHY THE CADENCE IS WEEKLY. `time_period` accepts `hour`, `day`, `week` and `month`, defaults to `day`, and `month` is the ceiling — and no retention period is documented for rule suite records at all. The bypass clause is therefore a statement about at most a month, and about however much of that month the platform still holds. Collecting monthly against a monthly ceiling leaves no margin: a collection that slips by a day loses records silently rather than erroring, and the records it loses are exactly the ones nobody saw. Weekly is four chances at the same evidence. WHY NO CLAUSE COUNTS THE FAILED RUNS. The sixth command returns a list built to contain offenders, and it carries no assertion. A workflow run that failed on the protected branch is the verification apparatus working — the desired state is that failures happen and are addressed, not that none occur — and a scheduled nightly that failed once says nothing about whether any change was verified. Both run lists are corroboration for a human: that runs exist at all, that they run on the branch the rules protect, and that their names correspond to the contexts the required-checks rule names. Nothing here checks that last correspondence, and it is the join an assessment most wants — a required context is a string, a workflow run has a name, and no response in this recipe says the string was produced by the run. NO CLAUSE READS A POSITION, deliberately. Nothing fetched documents the sort order of `/actions/runs`, so nothing here reads `[0]` as "the most recent". The immediately preceding batch on this plane put a freshness clause on `analyses[0]` and had to be corrected, because position zero was whichever language finished last. The same shape is available here and is not taken; a freshness claim over these runs would need a `created` range in the query rather than an index into the response. KSI-CMT-VTD is the direct claim: persistent testing and validation of changes throughout deployment, automated. KSI-CMT-LMC — modifications to the offering are logged and monitored — is claimed on the LOGGED limb only. The rule suite record is a per-push log of every change measured against the rules, and the workflow runs are a per-change record of what ran against it, which is logging in the most literal sense. Monitored is weaker and is not claimed: nothing in this recipe routes any of it to a person or to an alert, and no threshold in it fires. The evidence platform is itself an external system; see `external_system`. Two sibling recipes in the same batch — `developer-change-control-and-integrity` and `security-representative-change-approval` — read overlapping commands for different controls. They are separate recipes rather than one, because a single recipe carrying all three would put one `automatable` rating and one assertion set over three separate arguments, and the argument is the thing being assessed.

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. The first command returns the platform's own view of one organization, which cannot see a boundary repository owned by a second organization, hosted on a different platform, or mirrored from elsewhere, and a repository absent from that response is absent rather than false. There is a SECOND dimension here that the other recipes on this plane do not have, and it is the one more likely to be skipped: every command after the first is scoped to ONE branch of ONE repository, and nothing in any response enumerates branches. The branch that actually deploys is named by the provider's own deployment procedure, so the assessed population is repositories times branches, reconciled against that procedure as well as against the inventory. A recipe run against `main` on a repository that releases from `release/*` has verified a branch nobody ships.

  • 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 Change Management run (7)

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

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