A critical CVE lands in a common library. How do you use GitHub to find every affected repository fast?
answer
- The answer was decided before the CVE
- Query across repos, not repo by repo
- Declared is not the same as shipped
- Report what you could not determine
basics
~20 sQuery the dependency graph across the organisation rather than repository by repository — organisation dependency insights and scripted SBOM exports answer which repos declare the package and at what version. Answering in minutes depends on preparation: the graph enabled everywhere, lockfiles committed, and build-time dependencies submitted.
solid answer
~50 sThe answer is mostly about what you did *before* the CVE. **Preconditions.** The dependency graph enabled on every repository including private ones, lockfiles committed, and a dependency submission workflow wherever the build resolves things the manifests do not express. Without those, no query can be right. **The sweep.** Organisation-level dependency insights answer "who declares this package, at what version" across repositories. For anything scripted or auditable, loop the SBOM endpoint with `gh api` and filter — that gives you a file you can attach to an incident record. The security overview shows where alerts already exist. **Then the harder question.** Declaring it is not shipping it. The default branch is not production. Answer "which running services actually contain it" from deployed artifact digests and their build provenance or build-time SBOMs, not from the repository graph. Report exposure with its blind spots named: vendored code, container base images, archived repos, and repos outside the organisation.
code
bash · 7 linesgh repo list my-org --limit 500 --no-archived \
--json nameWithOwner --jq '.[].nameWithOwner' |
while read -r repo; do
gh api "/repos/$repo/dependency-graph/sbom" \
--jq ".sbom.packages[] | select(.name | test(\"log4j-core\")) | \"$repo \(.name) \(.versionInfo)\"" \
2>/dev/null
donego deeper
Know that GitHub can tell you which repositories declare a given package and version, so the first move is a dependency query rather than cloning repositories.
Explain the sweep concretely — organisation dependency insights, scripted SBOM exports through the API — and why lockfiles and enabled graphs determine whether the result is complete.
Show the gap between what a repository declares and what a running service ships, and how deployed digests plus build-time records close it.
Own preparedness and honest reporting: coverage as a standing programme, three numbers including the undetermined population, and post-incident work that shrinks the unknown rather than speeding up the search.
## Reframe the question The clock starts when the CVE is published, but the outcome was decided months earlier. A principal-level answer says so immediately: this is a **preparedness** question wearing an incident costume. Anyone can describe searching; the differentiator is knowing which searches are trustworthy and why. ## The preconditions that make an answer possible - **Dependency graph on everywhere.** On by default for public repositories, a setting on private ones — and an organisation can enable it broadly, including for new repositories. A repository with the graph off looks identical to a clean one. - **Lockfiles committed.** Ecosystems with lockfiles give exact transitive resolution. Without one, you see declared intent and miss the transitive package that is usually where the CVE actually lives. - **Dependency submission where the build resolves.** A workflow that runs the real build and posts a snapshot closes the build-time gap. - **Artifact-level records.** Build-time SBOMs and provenance attestations attached to shipped artifacts, keyed by digest. - **An inventory linking deployment to repository.** Which digest is running where. Without it you can find the source but not the exposure. ## The sweep, in order of speed 1. **Organisation dependency insights.** The dependency graph aggregated across repositories, searchable by package. Seconds, no scripting, and it covers exactly what the graph knows. 2. **Existing alerts.** The organisation security overview shows where the platform has already matched the advisory to a repository. Useful as corroboration, but it is a lagging view — advisory publication and graph refresh both take time, so absence of alerts early is not evidence of absence. 3. **Scripted SBOM sweep.** Loop repositories through the dependency-graph SBOM endpoint with `gh api` and filter on the package. Slower, but it produces a reproducible artifact you can attach to the incident record and re-run after remediation to prove the number went to zero. 4. **Code search over manifests.** The fallback when the graph is untrustworthy for an ecosystem, and the only way to catch a version pinned in something the graph does not parse. ## The gap between "declares" and "ships" This is where most answers stop and where the real risk lives. - **The default branch is not production.** The graph reflects the default branch now. The service running in production was built from some commit, possibly weeks ago, possibly from a release branch the graph never saw. - **Vendored and build-injected code is invisible.** Source copied into the tree declares nothing. - **Container base images are outside the repository.** A vulnerable library in the base layer will never appear in a repository's dependency graph. - **Archived and forked repositories.** Archived repositories may still be deployed somewhere; forks outside the organisation are outside your query. The way to close the gap is artifact-first: take the digests actually running, resolve each to the build that produced it via its provenance attestation, and read the SBOM produced by that build. That chain answers "what is in the thing that is running", which is the question the incident commander actually asked. The repository sweep answers "where do we need to change code", which is the remediation question. Both are needed; conflating them produces a confident, wrong all-clear. ## Reporting Report three numbers, not one: repositories that declare the package, services confirmed to ship it, and the population you could not determine. That third number is the honest part, and it is the one that drives investment — every incident should shrink it, by enabling the graph somewhere it was off, adding a submission workflow, or attaching an SBOM to a build that had none. ## What good looks like afterwards The post-incident action is never "search faster next time". It is: enable the graph where it was missing, mandate lockfiles or submission per ecosystem, attach build-time SBOMs and provenance to release artifacts, and keep a digest-to-repository inventory current. Do that and the next CVE is a query rather than a week of archaeology — and the answer comes with a coverage figure attached, so nobody has to trust it blindly.
- Why report an undetermined population alongside the affected count?Because a single number implies coverage you do not have. Repositories with the graph disabled, ecosystems without lockfiles, vendored code and base images are all unknown rather than clean. Naming the unknown population is what turns each incident into concrete investment — and it should shrink every time.
- What is the highest-value preparation after an incident like this?Coverage, not speed. Enable the dependency graph everywhere including private repositories, require lockfiles or a dependency submission workflow per ecosystem, attach build-time SBOMs and provenance to release artifacts, and maintain a digest-to-repository inventory. Then the next CVE is a query with a known confidence level.
- Is the absence of platform alerts early in an incident reassuring?No. Advisory publication, affected-range accuracy and graph refresh all take time, and a repository with the graph disabled generates no alerts by construction. Treat early quiet as missing data, and drive the sweep from your own queries plus deployed-artifact records instead.
saying these in an interview costs you the question
- Cloning every repository and grepping lockfiles
- Treating the default branch as what is in production
- Reading no alerts as proof of no exposure
- Ignoring container base images and vendored code
- Reporting a single number with no coverage caveat