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

Which taint-tracking ruleset actually ran over this repository — the query suite, the languages selected, and the threat model that decides what counts as an untrusted source — together with whether the recurring scan is still scheduled, when it last ran per analysed language, and how many rules were in the run; and then the open findings in the injection families SI-10 is about, identified by the CWE tags the queries carry. The ruleset and the freshness are the load-bearing half: a finding names a sink that exists, but only the run record says the absence of findings means anything at all.

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

partial — needs judgementapicontinuousGitHub code scanningGitHub CodeQL

Fetch

$ gh api --paginate "/orgs/<ORG>/code-security/configurations"
$ gh api --paginate "/orgs/<ORG>/code-security/configurations/<CONFIGURATION_ID>/repositories?status=attached,enforced&per_page=100"
$ gh api "/repos/<ORG>/<REPO>/code-scanning/default-setup"
$ gh api --paginate "/repos/<ORG>/<REPO>/code-scanning/analyses?tool_name=CodeQL&ref=refs/heads/<DEFAULT_BRANCH>&per_page=100"
$ gh api --paginate "/orgs/<ORG>/code-scanning/alerts?state=open&tool_name=CodeQL&per_page=100"
$ gh api -H "Accept: application/sarif+json" "/repos/<ORG>/<REPO>/code-scanning/analyses/<ANALYSIS_ID>"

Expected output

Report names used below, and the command each comes from: `configurations` is the first, `configuration-repositories` the second, `default-setup` the third, `analyses` the fourth, `alerts-open` the fifth, and the sixth returns SARIF rather than a report this recipe asserts on. RUN COMMANDS 3, 4 AND 6 ONCE PER REPOSITORY. Neither default setup nor analyses has an organization-scoped endpoint; the alerts endpoint does, and it returns the owning `repository` on each alert, so the findings arrive in one list while the run record is assembled one repository at a time. AND READ THE TWO RUN-RECORD CLAUSES ONCE PER ANALYSED LANGUAGE, not once per repository. GitHub documents `category` as what distinguishes "multiple analyses for the same tool and commit, but performed on different languages or different parts of the code", and the endpoint has NO `category` filter — its filters are `tool_name`, `tool_guid`, `pr`, `ref` and `sarif_id`. So on a Java-plus-JavaScript repository the newest row is whichever language finished last, and a clause reading position zero of the unfiltered list can be satisfied by a run that says nothing about the language carrying the risk. The command pins `ref` to the default branch for the same class of reason — without it a `refs/pull/N/merge` analysis can be the newest row — and `<CATEGORY>` in the two clauses below is a placeholder the collector fills once per entry in `default-setup.languages`. The exact category string default setup emits per language is NOT documented and was not verified; read it off the `category` field of the response rather than constructing it. From `default-setup`, the fields this recipe exists to read — `query_suite` (`default | extended`), `threat_model` (`remote | remote_and_local`) and `languages` (from `actions, c-cpp, csharp, go, java-kotlin, javascript-typescript, python, ruby, swift`) — plus `state` (`configured | not-configured`), `updated_at` and `schedule` (`weekly | null`). From `analyses`, `created_at`, `rules_count`, `results_count`, `category`, `commit_sha`, `ref`, `error`, `warning` and the `tool` object; `sort` accepts only `created` and `direction` defaults to `desc`, so the first row of any category-filtered projection is that category's newest run. From `alerts-open`, alerts whose `rule` object carries `tags` — the field the CWE clause below reads. CodeQL query metadata puts CWE classification in that array in the form `external/cwe/cwe-089`, verified on three query help pages: `js/sql-injection` carries `security`, `external/cwe/cwe-089`, `external/cwe/cwe-090` and `external/cwe/cwe-943`; `js/command-line-injection` carries `correctness`, `security`, `external/cwe/cwe-078` and `external/cwe/cwe-088`; `js/reflected-xss` carries `security`, `external/cwe/cwe-079` and `external/cwe/cwe-116`. Two things about that array decide how the clause below is written. `tags` is documented as "array of string or null", and a null array is the case a `contains()` filter cannot state an opinion about — so a separate clause counts the rows that have no array at all, rather than letting them pass silently. And the alerts endpoint has NO server-side tag filter — its filters are `tool_name`, `tool_guid`, `state`, `severity`, `sort`, `direction` and `assignees` — so the CWE narrowing is a client-side filter over a FULL page walk, and a collector that fetches one page of thirty and filters it has produced a smaller answer rather than a filtered one. The command passes `tool_name=CodeQL` so the list holds only rows from the analyses the run-record clauses measure. The sixth command returns the run's SARIF, whose `runs[].tool.driver.rules[]` is the closest thing available to a list of the queries that actually executed. GitHub documents the response as "a subset of the analysis data that was uploaded", which is why nothing below asserts on it: it is collected so a human can read which rules were present, and a subset is not a population.

Assertions — what makes it a pass

Assertions for Which taint-tracking ruleset actually ran over this repository — the query suite, the languages selected, and the threat model that decides what counts as an untrusted source — together with whether the recurring scan is still scheduled, when it last ran per analysed language, and how many rules were in the run; and then the open findings in the injection families SI-10 is about, identified by the CWE tags the queries carry. The ruleset and the freshness are the load-bearing half: a finding names a sink that exists, but only the run record says the absence of findings means anything at all.: the field checked, the condition it must satisfy, the rows it applies to, and the controls a pass proves.
FieldMust beForProves
default-setup.stateeq "configured"This repository has code scanning configured, read from the repository rather than from an organization configuration that claims to govern it. First clause because it is the precondition for every other statement here: on `not-configured` the analyses list is empty, the alert clause is vacuously green, and the recipe has measured nothing.every rowSI-10
default-setup.languages[?@=='<LANGUAGE>'] | [0]existsThe language this part of the boundary's code is written in is among those default setup selected. Read once per language on the provider's own list of implementation languages, which is external to every response here — that list is the whole point, because `languages` is a SELECTION and a repository whose Go service was never selected is `configured`, scheduled, fresh, rule-carrying and completely silent about that service. This is the one limb of SI-10's coverage question that is writable at all, and it is writable only because the comparison value comes from outside; nothing in this output would reveal the omission.every rowSI-10
default-setup.scheduleeq "weekly"The recurring scan for this repository is still scheduled. GitHub documents the schedule switching itself off — "If no pushes and pull requests have occurred in a repository with default setup enabled for 6 months, the weekly schedule will be disabled to save your GitHub Actions minutes" — and `schedule` is documented `weekly | null`, so a quiet boundary repository reports `configured`, has a real analysis history, and is no longer being analysed. This clause is what entitles the freshness clause below to treat a gap as a failure rather than as a repository nobody pushed to. An organization that enabled monthly scans of inactive repositories fails it while behaving correctly, which is a finding wanting one sentence rather than a defect.every rowSI-10
analyses[?category=='<CATEGORY>'] | [0].rules_countgt 0The most recent analysis FOR THIS LANGUAGE ran with at least one rule in it. The category filter is load-bearing rather than tidy: without it this reads position zero of a list that mixes languages, and a green result can come from a run that never touched the code in question. It catches the pathological run — a configured repository whose analysis completed with an empty or failed query pack produces an analysis record, a satisfying `created_at`, `results_count` of zero, and no alerts. `rules_count` is what distinguishes that from a clean scan; `error` and `warning` sit on the same object and are worth reading beside it, and are unasserted because their empty forms are not documented. It counts rules and says nothing about WHICH rules — see the note on why the CWE-family coverage limb is not assertable.every rowSI-10
analyses[?category=='<CATEGORY>'] | [0].created_atmax_age_days 7The most recent CodeQL analysis for this language on the default branch is no more than seven days old. Seven comes from the `schedule: weekly` field asserted above, not from the once-a-week sentence in the workflow documentation — that sentence is scoped to the default analysis workflow which advanced setup installs and a default-setup repository has no copy of. With the schedule clause green, a wider gap means the scheduled run did not happen. Read the note on cadence before treating seven as sufficient: the indicator's own class-c MUST is shorter than the schedule this clause enforces.every rowSI-10
alerts-open[?rule.tags==`null`]count_eq 0Every open alert carries the tag array the CWE clause below reads. This is a guard, not a finding about the code: `tags` is documented "array of string or null", and a `contains()` filter over a null array is the one case that cannot be answered — depending on the evaluator it either raises or quietly drops the row, and in an offender-form `count_eq 0` clause dropping it makes the result GREENER. So the rows with no array are counted here instead of being exempted there. The command's `tool_name=CodeQL` narrowing already removes the likeliest source of untagged rows, third-party SARIF uploaded into the same store; this clause is what makes that a checked fact rather than an assumption.every rowSI-10
alerts-open[?contains(rule.tags,'external/cwe/cwe-089') || contains(rule.tags,'external/cwe/cwe-078') || contains(rule.tags,'external/cwe/cwe-079')]count_eq 0No open alert names a sink in the three injection families verified for this recipe — SQL and NoSQL injection (CWE-089), OS command injection (CWE-078) and reflected cross-site scripting (CWE-079). Offender form over a list built to hold offenders, so it reports the rows that fail rather than projecting over the rows that already pass, and it is readable only once the guard above has established that every row has a tag array to search. Two limits are its meaning rather than caveats beside it: the family list is a verified SUBSET of what SI-10 covers rather than a definition of it, and a green result is a statement about paths the ruleset models from sources it treats as untrusted, not about the provider's own list of information inputs.every rowSI-10

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:SI 10/35

Outside the boundaryGitHub Enterprise Cloud

The analysis engine, the ruleset it ran and the findings it produced are all held and operated by a third party, so reading them brings the platform into the assessment as an information resource under SA-09 and CA-03 and into the external-system inventory AC-20 covers. The dependency is sharper here than on most recipes because the EVIDENCE ITSELF is a property of someone else's ruleset: what counts as an unvalidated input is decided by queries the provider does not write, version, or necessarily see, and a query pack update can change what a clean result means without anything in the provider's system changing. SI-10 is a class c and class d control, so every reader is running a Moderate or High system, and the impact level a platform's Marketplace listing carries is not necessarily the one those systems need — that is the SA-09 question, it is answered by reading the cited listing, and no value for it is written here because it is a dated fact that changes. This recipe was authored against GitHub Enterprise Cloud; github.com and the data-residency offering are different deployments on different infrastructure.

Notes & assertions

SI-10 asks the provider to check the validity of organization-defined information inputs, and the defining is the half no scanner does. Taint-tracking analysis is genuine telemetry about the other half — a path from a source the ruleset models to a sink it knows is exactly what an input-validation failure looks like in code — but a clean result says "no sink the ruleset knows about, reachable from a source it models" and the control says "the inputs we defined are checked". Those are different sentences, and `scan_scope` names the second list because nothing in this output contains it. THE THREAT MODEL IS THE SHARPEST TRAP ON THIS CONTROL AND IT LOOKS LIKE A SOLVED PROBLEM. `threat_model` is a field in the response with a value of `remote` or `remote_and_local`, so it reads like a setting a provider can simply turn up. GitHub documents that "The default threat model includes remote sources of untrusted data" and that extending it to local sources — "command-line arguments, environment variables, file systems, and databases" — is "currently in public preview and subject to change" and "supported only by analysis for Java/Kotlin and C#". Two consequences follow, and both are findings rather than caveats. On a codebase in any other language, there is no local-source configuration to make: a CLI argument, an environment variable or a row read back out of the database is NOT in the model the scan used, and SI-10's information inputs frequently include exactly those. And where the language does support it, the capability is in public preview, which is a poor foundation for a control statement in a Moderate or High system. Nothing below asserts `remote_and_local`, deliberately — asserting it would fail every provider whose language cannot express it, while implying the ones that can have covered local inputs. WHY THE RULESET-COVERAGE LIMB IS COLLECTED AND NOT ASSERTED, AND WHY THAT IS WHAT KEEPS THIS RECIPE `partial`. The clause an assessment actually wants is "a query for this CWE family was in the run", because that is what makes an empty finding list meaningful. It is not writable from the documented API. `rule.tags` exists only on alerts that exist, so the CWE tags are visible precisely when something was found and invisible in the case where the claim matters. `rules_count` counts rules without naming them. The SARIF response is the closest available and GitHub documents it as "a subset of the analysis data that was uploaded" — a subset cannot establish the absence of a rule, and a recipe that asserted over it would be reading an unspecified sample as a population. What IS writable, and is now asserted, is the coarser half of the same question: that the language was selected at all. The residue — which QUERIES ran within that language — is what keeps this `partial`, and the residue is a property of the documented API rather than of this batch's effort. Two things could close it and neither is in scope here: a documented endpoint listing a run's rules, or the published per-language suite membership read against `query_suite`, which is an inference the cited pages do not license. WHAT THE QUERY SUITE CHANGES ABOUT THE WORD "CLEAN". GitHub documents the `default` suite as "highly precise" with "few false positive" results, and `security-extended` as "all the queries in the default query suite, plus additional queries with slightly lower precision and severity" that "may return a greater number of false positive code scanning results". So a clean result under `default` is a weaker statement than a clean result under `extended` — fewer queries asked fewer questions — and a provider that switched to `extended` and now has open alerts has not regressed. The value is collected and not asserted because SI-10 names no suite; treating `extended` as a requirement would be authoring a preference as a control. A CADENCE GAP WORTH STATING PLAINLY, BECAUSE IT IS THE PLATFORM FAILING THE INDICATOR RATHER THAN THE PROVIDER. The freshness clause enforces seven days because that is what `schedule: weekly` means. KSI-CNA-MAT's own class-c floor is the VDR-TFR-MVX MUST — verify and validate the status of machine-based information resources at least once every three days — which is tighter, and SI-10 is a class c and class d control, so this reaches every reader. On an actively developed repository the gap closes by itself, because default setup also scans on pushes and pull requests; on a boundary repository that is quiet, the weekly schedule is the only thing running and the schedule clause is the only reason its silence is visible at all. For class d the binding MUST is LOOSER rather than tighter — read it off `classClocks[].tightestMust`, because the tightest class-d clock is a SHOULD — so this is a class-c fact and not a monotonic one. A provider needing to close it moves to advanced setup and its own schedule, which is a different recipe against a different endpoint. The default-branch limit applies here as it does to the sibling recipe on this platform: alert status reflects the default branch, the organization-scoped alerts endpoint takes no `ref` filter, and the analyses command pins `ref` to the default branch for the same reason. This is a default-branch statement about a repository, not a repository-wide one. KSI-PIY-RSD also reaches this control and is deliberately NOT claimed. That indicator asks whether the effectiveness of building security into the SDLC is persistently reviewed, and a scan record is an input to such a review rather than the review itself — the same reason the secret-scanning recipe declined the rotation limb of its adjacent indicator. KSI-CNA-MAT is claimed because an injection sink reachable from untrusted input is attack surface in the indicator's own terms — the dataset's own glossary counts code among machine-based information resources — and a recurring analysis over a named repository set is the persistent review it asks for.

Scanned population

Two lists, both held OUTSIDE the platform, and this control needs both. First, the repositories inside the authorization boundary from the provider's CM-08 component inventory — a repository never attached to a configuration and with scanning off in its own settings is absent from every response here rather than false in any of them. The enumeration is narrowed to `status=attached,enforced` on purpose: the documented default is `all`, which returns `failed`, `detached` and `removed` rows as well, and reconciling those against the boundary list reads as coverage for repositories the scanner never reached — the anti-vacuity mechanism over-counting in the one direction that hides the trap. The dropped rows are not ignored; a `failed` attachment is what the sibling recipe `static-analysis-coverage-and-flaw-disposition` asserts on directly. Second, and specific to this control, SI-10's organization-defined information inputs: the interfaces the provider's own design documentation says accept untrusted data — request bodies and query parameters, file uploads, message-queue payloads, webhook receivers, CLI arguments, environment and configuration read at start-up, and rows read back from a database populated by users. That list is authored by a human and it is what the scan's coverage has to be reconciled against, because a taint-tracking query finds a path from a source it MODELS to a sink it knows; an input the model does not treat as a source produces no finding no matter how unvalidated it is. The `threat_model` and `languages` fields returned by the third command are the platform's half of that reconciliation and are the reason this recipe collects them — that command is not in `enumerated_by` because it enumerates no population; it describes one repository.

  • configurations
  • configuration-repositories

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 Cloud Native Architecture run (7)

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

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