KSI-PIY-RSDReviewing Security in the SDLC
The effectiveness of building security and privacy considerations into the Software Development Lifecycle and aligning with CISA Secure By Design principles is persistently reviewed.
Mapped NIST 800-53 controls (12)
- recipe5
- KSI only7
The mark says whether an authored AWS recipe fetches evidence for the control; the tier strip shows which Rev5 baselines also require it — where automated KSI evidence doubles as Rev5 control evidence. = in the class B, C and D baselines, left to right
- recipe— an authored recipe collects evidence for this control
- KSI only— a Key Security Indicator reaches it, but no recipe is authored yet
- orphan— no Key Security Indicator reaches it — a person writes it up instead
- KSI onlyAC-05ACIn baseline C, D
- KSI onlyAU-03 (03)AUIn no class baseline
- recipe · 1 recipeCM-03 (04)CMIn baseline C, D
- KSI onlyPL-08PLIn baseline B, C, D
- KSI onlyPM-07PMIn no class baseline
- KSI onlySA-03SAIn baseline B, C, D
- recipe · 1 recipeSA-08SAIn baseline B, C, D
- KSI onlySC-04SCIn baseline C, D
- recipe · 1 recipeSC-18SCIn baseline C, D
- recipe · 1 recipeSI-10SIIn baseline C, D
- recipe · 1 recipeSI-11SIIn baseline C, D
- KSI onlySI-16SIIn baseline C, D
Collect evidence (3)
Authored pipeline recipes whose output is evidence for KSI-PIY-RSD. This mapping is this project’s opinion (overlay v0.8.0), versioned separately from the dataset — the upstream FedRAMP rules name none of these tools.
- partialThe 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.continuousapisecurity-representative-change-approvalGitHub code owners · GitHub repository rulesets · GitHub pull requests
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`.
- partialWhich policy decisions were actually enforced against the infrastructure definitions the boundary deploys from: that a policy scan ran, on which branch, how many rules it applied and when it last ran, together with the failures still open and the record of which were dismissed and with what justification. The load-bearing half is the scan record rather than the findings. SA-08 asks whether security engineering principles were applied, and a clean findings list is the same output whether every principle held or the scan applied no rules, ran last quarter, or parsed nothing — so the count of rules run and the date it ran are the part of this evidence that makes the rest of it mean anything.continuouscliinfrastructure-policy-scan-coverage-and-deviationsCheckov · GitHub code scanning
WHY PARTIAL, STATED AGAINST THE CONTROL'S OWN LIMBS. SA-08 requires the principles to be applied in specification, design, development, implementation and modification. Implementation is the limb a pipeline can see — a rule that fails a world-readable bucket or an over-broad role is telemetry about layered protection or least privilege actually holding in the artifact being deployed — and it is one limb of five. Which principles were selected, and how they were applied in specification and design, is an SDLC document; a passing rule evidences A principle without naming the one that was chosen. The dataset in this repo carries no FedRAMP-specific parameter or guidance for SA-08 at all, so there is no organization-defined value here to read a selection out of, which is the same absence SA-22 has and it lands in the same place: the selection is the provider's to write down. THE INLINE SUPPRESSION BLIND SPOT, AND IT IS THE LARGEST ONE HERE. Checkov's documented suppression syntax is a comment of the form `checkov:skip=<check_id>:<suppression_comment>`, and the documentation is explicit that the comment is OPTIONAL. A check suppressed that way is not a finding: it produces no SARIF result, so it never becomes an alert, so no clause in this recipe can see it, and the suppression can carry no stated reason at all. The dismissal clause below reads dismissals made in the PLATFORM, which is a different act by a different person leaving a different record. A boundary can therefore hold a green alert list and a repository full of skips, and the two facts are collected by different commands here on purpose. WHY `scan-json` AND `version` ARE COLLECTED AND NOT ASSERTED. The third command is how a reader enumerates the skipped checks the paragraph above describes, along with parse errors and the resource count — the scan that parsed nothing is the vacuity case that reaches the alert list looking identical to the clean one. No clause reads it, because Checkov's JSON schema is not documented on any page cited here, and this repo does not assert over field names it has not verified. That is a limit of the citation and it is written down rather than papered over with a plausible path; a batch that fetches a schema for it can add the clauses and should. `--soft-fail` IS THE OTHER WAY THIS GOES GREEN. Checkov documents `--soft-fail` as "Runs checks but always returns a 0 exit code". A workflow can run the scan, upload the SARIF, populate every report this recipe reads, and never block a merge. Nothing in the analyses or alerts output says whether the scan gates anything — that question is a ruleset question and it belongs to the sibling recipes on CM-04 (02) and SA-10, which read required status checks and rule suites directly. Read this recipe as evidence that the policy scan RUNS and what it FINDS, never as evidence that a failing policy stops a deployment. WHAT THE DISMISSAL CLAUSE ESTABLISHES. `dismissed_reason` is documented required, string or null, with five values: `false positive`, `won't fix`, `used in tests`, `mitigated` and null. An earlier reading of that page recorded three of them and built this paragraph on `won't fix` alone, which was the wrong shape twice over — the enum was short, and `mitigated` is a direct competitor for the role the paragraph was giving `won't fix`. Both are records that a policy failure was closed by a decision rather than by a fix, and they say different things: `won't fix` is a principle deliberately not satisfied in a named place, `mitigated` is a claim that something else covers it, and THAT claim is a compensating-control argument no field here carries the text of. Between them they are the closest thing this plane holds to a recorded deviation, and the clause below asserts of both only that a justification was typed. The clause asserts that a justification was TYPED, not that it was a good one, and a dismissal with a written reason is unambiguously better evidence than one without — which is all it claims. KSI-PIY-RSD is the only indicator that reaches this control. Its statement is about the effectiveness of building security into the SDLC being PERSISTENTLY REVIEWED, and the review is a human act on a schedule; what this recipe supplies is the material that review reads and the evidence that the material is current.
- partialFor 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.on-changeapimobile-code-admission-and-bundle-provenanceGitHub dependency review · GitHub artifact attestations · GitHub repository rulesets
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.
Default evidence owed by every indicator (5)
From info.default_artifacts.KSI — no indicator carries its own artifacts; requirement-specific evidence lives on /evidence.
- Explanation of measures (and their objectives) that demonstrate the Key Security Indicator, or an explanation of the reason and resulting risk to customers for not having measures available for that Key Security Indicator.
- Explanation of the cycle for any measures that are implemented persistently (if applicable).
- Verification that the measures demonstrate the Key Security Indicator, or that the reason for not having them is accepted.
- Verification that the automation in place is accurate and sufficient to demonstrate appropriate measures for the Key Security Indicator, or that automation is not necessary for each measure.
- Validation that the measures are accurately produced and are in place and working as intended, or that the reason for not having them is valid.
Defined terms used (1)
- Persistently
- Occurring in a firm, steady way that is repeated over a long period of time in spite of obstacles or difficulties. Persistent activities may vary between actors, may occur irregularly, and may include interruptions or waiting periods between cycles. These attributes of persistent activities should be intentional, understood, and documented; the status of persistent activities will always be known.
The Policy and Inventory run (5)
2/5 have an authored AWS recipe- 2 authored recipesKSI-PIY-GIVGenerating Inventories
- no authored recipeKSI-PIY-RESReviewing Executive Support
- no authored recipeKSI-PIY-RISReviewing Investments in Security
- 3 authored recipesKSI-PIY-RSDReviewing Security in the SDLCyou are here
- no authored recipeKSI-PIY-RVDReviewing Vulnerability Disclosures