◐partialThe 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.weeklyapi
developer-change-control-and-integrityGitHub repository rulesets · GitHub pull requests
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.