Whether Dependabot alerting is configured in this organization and which repositories it actually reaches, together with the alerts themselves — each carrying the advisory that raised it, the package, ecosystem and manifest path it was found in, the reason a human gave for closing it, and, for a remediated one, the date it was fixed. The first half is the population; the second half is what was found in it, and the second half means nothing without the first.
The API supplies the facts, but a human judgement against a documented baseline turns them into evidence.
Fetch
$ gh api --paginate "/orgs/<ORG>/code-security/configurations"$ gh api --paginate "/orgs/<ORG>/code-security/configurations/<CONFIGURATION_ID>/repositories?per_page=100"$ gh api --paginate "/orgs/<ORG>/dependabot/alerts?state=open&per_page=100"$ gh api --paginate "/orgs/<ORG>/dependabot/alerts?state=dismissed&per_page=100"$ gh api --paginate "/orgs/<ORG>/dependabot/alerts?state=auto_dismissed&per_page=100"$ gh api --paginate "/orgs/<ORG>/dependabot/alerts?state=fixed&per_page=100"Expected output
Report names used below, and the command each comes from: `configurations` is the first command, `configuration-repositories` the second, `alerts-open` the third, `alerts-dismissed` the fourth, `alerts-auto-dismissed` the fifth, `alerts-fixed` the sixth. RUN THE SECOND COMMAND ONCE PER CONFIGURATION returned by the first, not once for the organization. The first command returns every configuration and the second takes a single `<CONFIGURATION_ID>`; collecting one response and reading it as the organization's is the arithmetic error this recipe is most likely to be assessed with, and the clause on `configuration-repositories` speaks only for the configurations actually walked. From the configurations call, an array of code security configurations, each with an id, a name, a `target_type` of `global`, `organization` or `enterprise`, and the feature fields as THREE-STATE strings rather than booleans — `dependabot_alerts`, `dependabot_security_updates`, `dependency_graph`, `secret_scanning`, `secret_scanning_push_protection`, `code_scanning_default_setup` and `private_vulnerability_reporting` each taking `enabled`, `disabled` or `not_set` — plus an `enforcement` of `enforced` or `unenforced`. `not_set` is the value to read carefully: it is neither on nor off, it is the absence of a decision, and a clause comparing against `disabled` alone does not see it. From the configuration-repositories call, rows carrying a `status` of `attached`, `attaching`, `detached`, `removed`, `enforced`, `failed`, `updating` or `removed_by_enterprise` and the repository object each refers to; the call passes no status filter, so a failing row is present-and-false rather than absent. From the alerts calls, alerts with `number`, a `state` of `open`, `dismissed`, `fixed` or `auto_dismissed`, a `severity` of `low`, `medium`, `high` or `critical`, a `dependency` object naming `package.ecosystem`, `package.name`, `manifest_path`, a `scope` of `development` or `runtime` and a `relationship` of `direct`, `transitive`, `inconclusive` or `unknown`, a `security_advisory` object carrying `ghsa_id`, a nullable `cve_id`, `cvss_severities` with `cvss_v3` and `cvss_v4` scores, `cwes`, a `classification` of `general` or `malware` and `epss` percentage and percentile, a `security_vulnerability` with the affected version range, `created_at`, `updated_at`, `dismissed_at`, `fixed_at` and `auto_dismissed_at` timestamps, a `dismissed_reason` from the enum `fix_started`, `inaccurate`, `no_bandwidth`, `not_used` and `tolerable_risk`, and the `repository` the alert belongs to. Note the calls that look like one: `dismissed`, `auto_dismissed` and `fixed` are SEPARATE values of `state`, so a query for any one returns none of the others. The organization endpoint requires an organization owner or a security manager, and a token carrying the `security_events` scope.
Assertions — what makes it a pass
| Field | Must be | For | Proves |
|---|---|---|---|
| configurations[?target_type=='organization' && dependabot_alerts=='enabled'] | [0].id | existsAt least one code security configuration the organization itself owns sets `dependabot_alerts` to `enabled`. The field is three-state and the middle value is the trap: `not_set` is neither on nor off, and a clause written against `disabled` passes an organization that never decided. `target_type` is narrowed to `organization` because the enum admits a `global` scope no administrator creates. This says a configuration exists and decided; it does not say which repositories it reaches, and it does NOT say nothing is scanned when it fails — repository-level enablement bypasses configurations entirely. Read it with the clause below and with the reconciliation `scan_scope` names. | every row | SR-06 |
| configuration-repositories[?status=='failed'] | count_eq 0No repository the configuration was applied to failed to receive it. `failed` is the status that produces silence rather than an error: the repository is attached in intent, collects nothing, and contributes no row to any alert query — so an alert list over the organization is partly clean BECAUSE of it. The call passes no status filter, so the failing row is present-and-false rather than absent, which is why the offender form works here. It speaks only for the configurations actually walked; see the iteration note in `expected_output`. | every row | SR-06 |
| configurations[?target_type=='organization' && dependabot_alerts!='enabled'] | count_eq 0No configuration the organization owns leaves Dependabot alerting unenabled. `!=` rather than `== 'disabled'` on purpose: the field is three-state, and `not_set` — a configuration that decided nothing — is the value a comparison against `disabled` walks straight past. The failing row is present-and-false in the same response the clause above reads, which is what makes this the offender form and not a projection over the rows that already pass. A deliberate exception, such as a configuration governing archived or public repositories, fails this clause correctly: it is a finding that wants a written justification, not a defect in the check. | every row | SR-06 |
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
- 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
What else these families can fetch:SR 3/14 →
Outside the boundaryGitHub Enterprise Cloud
Collecting this evidence puts the code-hosting and scanning platform inside the assessment rather than beside it. The repositories, the dependency graph and the alert store are held and operated by a third party, so the platform is itself 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 — the plane that answers a supply-chain control raises supply-chain controls. Ask the cited Marketplace listing one question in particular before relying on this recipe: SR-06 is a class c and class d control, so every reader of it is running a Moderate or High system, and the impact level a platform's listing carries is not necessarily the one those systems need. That is the SA-09 question here, it is answered by reading the listing rather than by this sentence, and no value for it is written down because it is a dated fact that changes. Which offering is in use decides the rest: GitHub Enterprise Cloud, GitHub Enterprise Cloud with data residency and github.com are different deployments on different infrastructure, and this recipe was authored against the first.
Notes & assertions
SR-06 asks the provider to ASSESS AND REVIEW the supply chain risk associated with suppliers and contractors. This output assesses one population — the third-party software components resolved into the build — continuously, and it produces a reviewable record for each finding. That is a real answer to one limb and it is not the control. Provenance, contract terms, geographic and financial exposure, and the frequency at which a supplier is re-reviewed are a human record, and a manifest entry names a package, not a supplier's risk posture. The disposition this recipe spends said exactly that, and the rating is unchanged by having written the commands down. What the population is, and what it is not. GitHub documents that when Dependabot alerts are enabled it "immediately generates the dependency graph and creates alerts for any vulnerable dependencies it identifies", so the graph is not a separate switch a clause below needs to police — which is why the first assertion reads `dependabot_alerts` and not both fields. What the same page says under its own heading of limitations, and what bounds this recipe more than anything else in it, is that "only advisories reviewed by GitHub trigger alerts". The detection surface is the GitHub Advisory Database's reviewed set. A vulnerability that is real, published, and not in that set produces no alert, and an empty list is silent about it. A second, sharper limit sits inside the first: alerting on MALICIOUS packages, as opposed to vulnerable ones, is a separate opt-in that has to be enabled after Dependabot alerts are, and no command in this recipe can see whether it is on — the code security configuration response carries no property for it. So an alert list with no malware finding in it is equally an organization with no malicious dependency and an organization that never switched the detection on, and a clause asserting the absence of malware would be vacuous for exactly that reason. Both limits are ceilings on the evidence rather than defects in the collection, and they belong in the assessment rather than in a footnote. A code security configuration is not the only way Dependabot gets switched on, and the first clause below is wrong in one direction because of it. Alerts can be enabled on a repository directly, in that repository's own settings, with no configuration involved. An organization that never adopted configurations therefore returns an empty list from the first command and FAILS the clause while scanning every repository it owns. Read a failure there as "no organization-level configuration governs this", which is a real and useful finding, and NOT as "nothing is scanned" — the second reading is unsupported and the reconciliation named in `scan_scope` is what distinguishes them. The multi-configuration case, named because the first assertion is weaker than it reads in the other direction too. The clause is satisfied by ANY qualifying configuration, including one applied to no repository at all. The `target_type` narrowing on both configuration clauses is a deliberate trade and it costs something. The enum admits `global` and `enterprise` alongside `organization`, and GitHub ships a recommended configuration that an administrator does not create but DOES apply from the organization's own configurations table — applying it is a decision, and a decision this clause will not count. So the narrowing buys a false FAIL on GitHub's recommended path and on an enterprise-applied one, in exchange for not passing on a row the organization never chose. That is the right way round only because of how a failure is to be read, below: not as "nothing is scanned" but as "no configuration this organization owns governs this", which is literally true in the global and enterprise cases too. What documentation does NOT settle is whether the org endpoint returns those rows at all — its description says configurations "available in" an organization rather than owned by one — and the live risk is not that the narrowing is unnecessary but that the rows it excludes are the organization's real answer. One call against a live organization settles it, and an assessment leaning on either configuration clause should make that call first. Why there is no clause on `dismissed_reason`. An earlier draft asserted that no dismissed alert had a null reason, and it was decorative: GitHub requires a `dismissed_reason` when an alert's state is set to `dismissed`, so on a `state=dismissed` list there is no documented path that yields null and the clause could not fail. It is recorded here so it is not re-added. The review limb of the claim rests on the fourth command's output being collected and read, not on a check that cannot go red. `state=dismissed`, `state=auto_dismissed` and `state=fixed` are three separate queries, and the last two carry no assertion deliberately. The auto-dismissed list exists because an organization whose alerts close mostly by rule has a short `dismissed` list, and any review clause would be near-vacuously true while very little was reviewed by anyone; an auto-dismissal is a rule firing, not a person deciding, and a clause about its count would assert that a policy is correct rather than that a review happened. The fixed list exists because it is the mitigation record, and it is the reason KSI-SCR-MIT can be claimed at all — but a count of remediated alerts is a measure of how much was broken, not of how well it was handled, and no threshold over it would mean anything without the response clock the plan surfaces separately. It carries the emptiness problem the open list carries, and it carries it while bearing the whole weight of the mitigation claim: an empty fixed list is equally an organization that has never had a vulnerable dependency, one that has never remediated one, and one whose scanning was switched on last week. Nothing in this recipe distinguishes those, which is why the claim on that indicator is a collected record for a human to read and not a check that can go red. MAS-CSO-TPR was deliberately not claimed, and the reasoning is recorded so the next batch does not re-derive it. Its artifact is a machine-readable output of the third-party information resources of the offering, and a dependency IS a third-party information resource on the dataset's own definition — an information resource not entirely inside the Minimum Assessment Scope, where information resources expressly include software and code. But the requirement's statement names the data that output must carry: general usage and configuration, an explanation or justification for use, mitigation measures, and compensating controls. An alert stream carries none of the four, and the enumeration it implies is a by-product rather than the artifact. A recipe that exported the dependency graph SBOM would have a genuine partial claim on that requirement; this one does not, and adjacency is not a join. KSI-SCR-MIT is claimed on the software-component limb only. Identify is the alert, review is `dismissed_reason`, mitigate is the transition to `fixed` — those three are the indicator's three verbs, over one supplier population, and all three are now collected rather than two of them being collected and the third described. Hardware, managed services, and every supplier relationship that never resolves into a manifest are outside it, 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`. It is not a footnote on this control in particular — SR-06 is a supply chain control, and answering it by adopting a supplier is a move that has to be visible in the assessment rather than only in the recipe.
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. Every command in this recipe reads the platform's own view of itself, and the failing case is structurally invisible to every command in it: a repository that is in the boundary and was never attached to a code security configuration, and whose own repository-level settings leave Dependabot off, produces no configuration row, no repository row, and no alert. It is absent, not false. Reconciling the repository ids returned by the second command against that external inventory is the only step in this recipe that can see it, and without that step an empty alert list is equally the output of a well-run organization and of one scanning a third of its code.
- configurations
- configuration-repositories
References
- GitHub REST: Dependabot alerts — GET /orgs/{org}/dependabot/alerts; state open | dismissed | fixed | auto_dismissed, severity, dependency.package.ecosystem/name, manifest_path, scope, relationship, security_advisory ghsa_id/cve_id/cvss_severities/cwes/classification general | malware/epss, dismissed_reason enum and the PATCH rule that a reason must be provided when dismissing; organization owner or security manager, security_events scope https://docs.github.com/en/rest/dependabot/alerts
- GitHub REST: code security configurations — GET /orgs/{org}/code-security/configurations and .../{configuration_id}/repositories; target_type global | organization | enterprise, dependabot_alerts, dependency_graph, secret_scanning and code_scanning_default_setup each enabled | disabled | not_set, enforcement enforced | unenforced, repository association status attached | attaching | detached | removed | enforced | failed | updating | removed_by_enterprise https://docs.github.com/en/rest/code-security/configurations
- GitHub: about Dependabot alerts — "When enabled, GitHub immediately generates the dependency graph and creates alerts for any vulnerable dependencies it identifies", and under its own limitations heading "Only advisories reviewed by GitHub trigger alerts", sourced from the GitHub Advisory Database https://docs.github.com/en/code-security/dependabot/dependabot-alerts/about-dependabot-alerts
- FedRAMP Marketplace: GitHub Enterprise Cloud — the listing to re-read rather than quote, and the place to check the impact level against the class c and class d systems SR-06 applies to; a certification status is a dated fact about a product, not about your deployment https://www.fedramp.gov/marketplace/products/FR1812058188/
- GitHub and Government: FedRAMP FAQ — what GitHub states about which offering is covered and under which baseline; re-read rather than quote https://government.github.com/fedramp-faq
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.
- partialThe machine-generated inventory of every resource an external entity can reach — IAM Access Analyzer's active ExternalAccess findings — read against the declared zone of trust, so the terms-and-conditions review has a list to work from rather than a memoryquarterlyexternal-access-inventory-and-trust-boundaryAWS IAM Access Analyzer · AWS Organizations · AWS Config
- partialThe advisories AWS itself has issued against this account — operational issues, scheduled changes and account notifications, each dated and scoped — together with the subscribers on the topic those alerts are published to and the confirmation state of each subscription, which is the difference between an address that was entered and an address that receivescontinuoussecurity-advisories-receipt-and-disseminationAWS Health · Amazon SNS
- partialWhether the tooling that examines acquired software is switched on and covering the estate, and what it found: Inspector's per-account enablement state for each scanned resource type, the registry-wide ECR scanning configuration (scan type and frequency, and the repository filters that decide which repositories it applies to), Inspector's own coverage statistics, and a CycloneDX 1.4 or SPDX 2.3 SBOM exported per monitored resource — the component-level inventory of what was actually acquired.continuousacquisition-scanning-and-sbom-inventoryAmazon Inspector · Amazon ECR
- partialWhether Dependabot alerting is configured in this organization and which repositories it actually reaches, together with the alerts themselves — each carrying the advisory that raised it, the package, ecosystem and manifest path it was found in, the reason a human gave for closing it, and, for a remediated one, the date it was fixed. The first half is the population; the second half is what was found in it, and the second half means nothing without the first.you are herecontinuousdependency-vulnerability-monitoringGitHub Dependabot · GitHub code security configurations
- partialThe organization's webhook configuration — which endpoints are subscribed to the supply-chain alert event, whether each is switched on, and where it points — together with the platform's own record of what it actually delivered to them and with what response code. Configuration says a path exists; the delivery log says the path carried something.continuoussupply-chain-alert-notification-routingGitHub webhooks · GitHub Dependabot
- partialWhether static analysis is configured in this organization and which repositories it actually reaches; for each of those repositories, which query suite ran over which languages, whether the recurring schedule is still alive, when the analysis last ran and with how many rules in the run; and what the analysis found, split into what is still open and what a person closed by hand — each closure carrying who closed it, which of four fixed reasons they chose, and whatever they wrote down. The first two halves are the population and the proof that testing happened; the third is the half an assessment asks for, and read without the other two it cannot be told apart from the output of a scanner that never ran.continuousstatic-analysis-coverage-and-flaw-dispositionGitHub code scanning · GitHub CodeQL · GitHub code security configurations
- 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.weeklydeveloper-change-control-and-integrityGitHub repository rulesets · GitHub pull requests
- partialThe exact component-and-version inventory the boundary's repositories build from — every package the dependency graph resolved, carrying the version string that is the only thing SA-22's question can be asked about — together with whether that graph is switched on across the boundary at all, and then the open advisories for which the ecosystem offers no patched version, which is the closest thing a pipeline emits to a component nobody maintains any more. The inventory is the load-bearing half and it is why this recipe exists separately from the vulnerability one: "is this component past end of support" is a question about a name and a version, and a check that cannot produce the version has not asked it.monthlyunsupported-component-inventory-and-lifecycle-reviewGitHub dependency graph · GitHub Dependabot · GitHub code security configurations