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 rule requiring the designated owners of the changed code to approve before it merges, the file that names who those owners are, the platform's own report of whether that file actually parses — and, per change, who approved, on which commit, and when.

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

partial — needs judgementapicontinuousGitHub code ownersGitHub 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 "/repos/<ORG>/<REPO>/codeowners/errors?ref=<DEFAULT_BRANCH>"
$ gh api -H "Accept: application/vnd.github.raw" "/repos/<ORG>/<REPO>/contents/.github/CODEOWNERS?ref=<DEFAULT_BRANCH>"
$ gh api --paginate "/repos/<ORG>/<REPO>/pulls?state=closed&base=<DEFAULT_BRANCH>&per_page=100"
$ gh api --paginate "/repos/<ORG>/<REPO>/pulls/<PULL_NUMBER>/reviews?per_page=100"

Expected output

Report names used below, and the command each comes from: `repos` is the first command, `branch-rules` the second, `codeowners-errors` the third, `codeowners-file` the fourth, `pulls-closed` the fifth, `reviews` the sixth. RUN COMMANDS 2 THROUGH 5 ONCE PER REPOSITORY AND ASSESSED BRANCH, and command 6 once per pull request selected from the fifth. The fourth and fifth commands are a SAMPLE AND WALK for a human reader, not a population any clause below reads — the closed pull request list is unbounded and no assertion here is written over it. From `branch-rules`, the rule objects in effect on the branch, of which this recipe reads the `pull_request` type and its parameters `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`. From `codeowners-errors`, an object with an `errors` array, each error carrying `line`, `column`, `kind`, `source`, `suggestion`, `message` and `path`. The `ref` parameter is "A branch, tag or commit name used to determine which version of the CODEOWNERS file to use" and DEFAULTS TO THE REPOSITORY'S DEFAULT BRANCH — the command passes it explicitly, because a recipe assessing any other branch would otherwise be checking a different file from the one in force there. From `codeowners-file`, the file itself. GitHub documents three legal locations — "the .github/, root, or docs/ directory of the repository" — so a 404 from the path above is not an absence, it is the wrong one of the three; try `CODEOWNERS` and `docs/CODEOWNERS` before concluding anything. From `pulls-closed`, the closed pull requests targeting the branch, and from `reviews`, the review records: `id`, `user`, `body`, a `state` — the page documents it as a required string and enumerates nothing, so `APPROVED`, `COMMENTED`, `DISMISSED` and `PENDING` are the values to expect rather than a closed set, and no clause here depends on the enum being complete, `html_url`, `submitted_at`, `commit_id` and an `author_association` of `COLLABORATOR`, `CONTRIBUTOR`, `FIRST_TIMER`, `FIRST_TIME_CONTRIBUTOR`, `MANNEQUIN`, `MEMBER`, `NONE` or `OWNER`. `commit_id` is the field that makes a review an answer rather than a timestamp: it says WHICH revision was approved.

Assertions — what makes it a pass

Assertions for The rule requiring the designated owners of the changed code to approve before it merges, the file that names who those owners are, the platform's own report of whether that file actually parses — and, per change, who approved, on which commit, and when.: 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' && parameters.require_code_owner_review==`true`] | [0]existsA pull-request rule in effect on the assessed branch requires review from the code owners of the changed files — the platform's mechanism for putting a designated reviewer on every change that touches what they own. This is the membership claim in its enforceable form. It says a designated owner must approve; it does not say any owner is actually designated, which is the next clause, and it does not say the owner is the security representative CM-03 (04) names, which is the gap no clause closes.every rowCM-03 (04)
codeowners-errors.errorscount_eq 0The CODEOWNERS file in force on the assessed branch parses without a single syntax error. This is the clause the one above is worthless without, and the reason is the vendor's own sentence: "If any line in your CODEOWNERS file contains invalid syntax, that line will be skipped." A skipped line does not fail anything — it silently removes the owners of the paths it covered, so the rule above stays green while requiring approval from nobody for exactly the directory whose line was broken. The endpoint that reports these errors is the only thing in this recipe that can see it. Its silence has a residue: nothing documents what it returns when there is no CODEOWNERS file at all, so read a zero here together with the file the third command fetches.every rowCM-03 (04)
branch-rules[?type=='pull_request' && !(parameters.dismiss_stale_reviews_on_push)]count_eq 0No pull-request rule in effect lets an approval survive a later push. Without this parameter a code owner approves, the author pushes again, and the approval carries over to code no owner has seen — the record afterwards shows a designated reviewer approving the pull request, which is true, and does not show that they approved what merged. Offender form over rows present in the same response, and the parentheses are load-bearing: `!parameters.X` parses as `(!parameters).X`, which is null, which is falsy, so the unparenthesised form selects nothing at all. `!(parameters.dismiss_stale_reviews_on_push)` catches the parameter set to false and the parameter absent alike, both of which leave approvals standing.every rowCM-03 (04)

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

What this recipe reads is a membership claim, and a membership claim is an identity claim: the platform's account of which of its users approved which change. Bringing it into the assessment makes the platform an information resource under SA-09 and CA-03 and an entry in the external-system inventory AC-20 already covers, and it does so for a control about WHO IS ON a body rather than about what a scanner found — so the dependency runs through the provider's identity federation as well as through the platform itself. A code owner is a platform identity, and the chain from that identity to a named person holding a named role runs through the identity provider, the group memberships synchronised into the platform, and the platform's own account of who clicked approve. CM-03 (04) 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

CM-03 (04) requires an information security representative to be a MEMBER of the configuration change control element. This is the closest this plane comes to a membership claim it can check: a rule requiring the designated owners of the changed code to approve, a file naming who those owners are, and per-change approval records carrying identities, revisions and timestamps. What none of it establishes is that those identities ARE the security and privacy representatives the control names. That mapping lives in the change control charter, and a repository team called `security` is an assertion about a name. The gap is an identity mapping rather than a missing signal, which is exactly what the disposition said, and no amount of further collection on this platform closes it. THE SKIPPED LINE — this recipe's vacuity trap, and unusually it is documented by the vendor rather than inferred from an API shape. GitHub states plainly that "If any line in your CODEOWNERS file contains invalid syntax, that line will be skipped." So a `pull_request` rule with `require_code_owner_review` set to true, sitting over a CODEOWNERS file whose one line covering the infrastructure directory has a typo, requires review from the code owners of that directory — of which there are now none. The rule is green, the platform requests no reviewer, and the change merges on whatever approvals its author could find. Nothing in the rules API can see this, and nothing in a screenshot of the branch protection settings can either. `GET /repos/{owner}/{repo}/codeowners/errors` is the only endpoint in this recipe that sees it, and it is the entire reason the second clause exists. A MISSING FILE AND A CLEAN FILE ARE NOT DISTINGUISHED BY THE CLAUSE, AND THE THIRD COMMAND IS WHY. Nothing fetched documents what the errors endpoint returns when there is no CODEOWNERS file at all, so an empty `errors` array is the passing shape for a valid file and the plausible shape for an absent one — the plane's standing emptiness problem, arriving through a door that looks like a validator. The third command reads the file itself for exactly this reason: it is collected so a human can confirm there IS a file and see who it names. No clause is written over its contents, because who ought to own which path is the identity mapping this recipe cannot make, and a clause counting lines would be a clause asserting that a file is long. WHY THE STALE-APPROVAL CLAUSE IS HERE AND A LAST-PUSH ONE IS NOT. `dismiss_stale_reviews_on_push` is what stops an approval from outliving the change it approved: without it, a code owner approves, the author pushes again, and the approval carries over to code no owner has seen — a membership failure wearing the appearance of a membership success, and the one failure mode in this recipe that leaves a complete and convincing paper trail. `require_last_push_approval`, GitHub's option to "require an approval from someone other than the last person to push to a branch", is deliberately NOT asserted: it is a separation-of-duty property rather than a membership one, a small team can be operating correctly without it, and CM-03 (04) asks who sits on the element rather than whether two distinct people acted. WHAT A REVIEW RECORD PROVES AND WHERE IT STOPS. A review carries `state`, `user`, `submitted_at`, `commit_id` and `author_association`, so the record says a named platform identity approved a named revision at a named time — and `commit_id` is what makes it an approval of something rather than a timestamp. It does not say that the identity belongs to a person, that the person holds the role, or that the approval was informed. `author_association` is the closest thing to a role anywhere in the response and it is a repository-relationship enum — `MEMBER`, `COLLABORATOR`, `OWNER` — describing a relationship to the repository rather than a position in the organization, so no clause is written over it. The fourth and fifth commands are collected as a sample for a human to read against the charter, and no assertion reads them; a clause over an unbounded list of closed pull requests would be a clause about how many were sampled. NO `enforcement` CLAUSE APPEARS IN THIS RECIPE, and after the audit of this batch that is the correct answer for a better reason than the one first written. Every clause here except the second reads `/rules/branches/{branch}`, which GitHub documents as returning "all active rules that apply to the specified branch" and from which "Rules in rulesets with \"evaluate\" or \"disabled\" enforcement statuses are not returned" — so a dry-run ruleset cannot satisfy the first or third clause, and no guard against it is needed. The second clause reads a file parser that no ruleset governs at all: a CODEOWNERS file with a syntax error is broken whether the ruleset requiring code-owner review is active, evaluating or absent. An assessment wanting the dry-run finding itself gets it from `developer-change-control-and-integrity` on the same repository, where it belongs, being a property of the repository rather than of this control. KSI-CMT-RVP — the effectiveness of documented change management procedures is persistently reviewed — is claimed on the MECHANISM limb. A required approval per change, with the approver and the revision recorded, is the material a review of procedure effectiveness reads, and the rule is the documented procedure in executable form. Whether the procedure is EFFECTIVE is a judgement, and no API in this recipe makes it. KSI-PIY-RSD — the effectiveness of building security and privacy considerations into the Software Development Lifecycle and aligning with CISA Secure By Design principles is persistently reviewed — is claimed narrowly and should be read narrowly: requiring a security owner's approval at merge is one security consideration built into one point of the lifecycle. The indicator is about the whole lifecycle and about alignment with an external body of principles that nothing here reads. The evidence platform is itself an external system, and on this control the dependency runs through the identity provider as well; see `external_system`.

Scanned population

The membership of the configuration change control element, as the provider's own change management plan and CCB charter define it — a list of PEOPLE AND ROLES held entirely outside the platform, and the only thing that can decide whether the identities in these responses are the security and privacy representatives CM-03 (04) requires. This is a different shape of reconciliation from the other recipes on this plane, and a harder one: elsewhere the external inventory is a list of repositories and the question is coverage, whereas here it is a list of roles and the question is identity. The repository dimension applies as well: the first command returns the platform's own view of one organization, which is the enumeration to reconcile against the CM-08 component inventory the provider maintains, and a boundary repository owned by a second organization or hosted elsewhere is absent from that response rather than false — and so does a third gap that no command touches: the change control element governs changes to the offering, of which changes to a git repository are one kind. A configuration change made in a console, a support ticket or a vendor portal is a change the element is responsible for and this evidence has never seen.

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