Skip to main content

handle-cve

Use when the dependency audit goes red — `.github/scripts/check/dependency_audit.sh` or the bundler-audit CI job — or when a newly published CVE/GHSA on a dependency gem blocks a PR.

Datos de origen

Repositorio
DataDog/dd-trace-rb
Última actividad en el origen
7 de octubre de 2026 a las 10:52
Idioma detectado de SKILL.md
inglés
Estrellas
417
Forks
419

Opciones de instalación

De forma predeterminada está seleccionado el prompt que primero revisa el origen. Puedes cambiar a un comando directo o descargar una copia local.

Revisa los archivos de origen

Lee SKILL.md y los archivos complementarios que muestra SkillsMP antes de decidir si quieres instalarlo.

Explorador de archivos
2 archivos

Mostrando SKILL.md

SKILL.md
Instrucciones de origen · Vista previa de solo lectura
name
handle-cve
description
Use when the dependency audit goes red — `.github/scripts/check/dependency_audit.sh` or the bundler-audit CI job — or when a newly published CVE/GHSA on a dependency gem blocks a PR.
# Handling a CVE that blocks CI (bundler-audit) Goal: turn the red audit task green, locally. Run the audit on your machine, research the CVE, triage the impact, decide the fix, and validate before you push. CI runs the same audit later and only confirms what you already saw. ## 1. Role and priority The owner is whoever hits the red audit, locally or on a PR. There is no security on-call. This is routine maintenance. The whole purpose of the audit is supply-chain health: the gems under `gemfiles/` get installed and run, locally and in CI. We protect the machines that run the audit and the test suite, not the shipped gem. The `datadog` gem declares its runtime dependencies as version ranges. Each customer picks the versions that install, so we control only what we install ourselves. Priority: 1. The advisory is supply-chain (a compromised or malicious package), or the vulnerable code runs during our tests. Tell the maintainers, and get the gem off the lockfiles. 2. The audit fails on `master` or a shared branch. Run it locally to confirm. Unblock the same day, with the loop below. 3. The audit fails only in your branch. Fix it before you open the PR. Same loop, but branch off your branch, and merge into your PR. The unblock loop: ```text red audit on master, and on every open PR → branch off the latest master → run sections 2 to 5 → fast draft PR, merge → other PRs merge the updated master, and turn green ``` ## 2. Identify the CVE and its blast radius Run the audit. It scans every covered lockfile, so one container is enough. `docs/DependencyAudit.md` is authoritative on what the job covers and which advisories fail it. The audit runs standalone, outside any bundle: the script installs its own pinned gems and loads only the audit task. The findings JSON is written only on failure, and the command below prints it because the file stays in the container's `tmp/` volume. Each entry gives the advisory id, the gem and version, the criticality, and the lockfile. ```bash docker compose run --rm tracer-4.0 sh -c \ '.github/scripts/check/dependency_audit.sh || cat tmp/dependency_audit_findings.json' ``` Every command in this skill runs inside the containers from `docker-compose.yml`. Each `tracer-X.Y` service pins one Ruby and its own default Gemfile. To map the blast radius, walk each finding from the lockfile to the tests that run it, through the `Matrixfile`. The blast radius is the full matrix a gemset covers, which runs wider than audit coverage: an unaudited Ruby still runs the gem, so an upgrade can break it, but it never turns the audit red on its own. 1. The lockfile name carries the gemset: `ruby_3.3_mongo_latest.gemfile.lock` is gemset `mongo-latest`. Underscores in the file name become hyphens in the `Matrixfile`. 2. Open the `Matrixfile` and find the gemset. The entry names the rake task and the Rubies it covers. 3. A gem can have more than one gemset. `mongo` also has `mongo-min`, which pins the oldest tested driver. Both gemsets map to `test:mongodb`, so one advisory can hit two lockfiles per Ruby. ```ruby "mongodb" => { "mongo-latest" => "✅ 2.5 / ✅ 2.6 / ✅ 2.7 / ✅ 3.0 / ✅ 3.1 / ✅ 3.2 / ✅ 3.3 / ✅ 3.4 / ✅ 4.0", "mongo-min" => "✅ 2.5 / ✅ 2.6 / ✅ 2.7 / ✅ 3.0 / ✅ 3.1 / ✅ 3.2 / ✅ 3.3 / ✅ 3.4 / ✅ 4.0", }, ``` After this section you must be able to answer: - Which advisory hits which gem, at which pinned versions? - Which lockfiles pin those versions? - Which rake tasks install and run the affected gem, and on which Rubies? Section 4 picks the lockfiles to update from these answers. Section 5 runs the tasks from these answers, on the earliest and the latest Ruby. ## 3. Research the CVE Goal: turn the advisory id from section 2 into the facts that section 4 needs to pick a resolution path. Research stops when the questions below have answers. More depth does not change the decision. Process: 1. Read the advisory record: the GHSA entry at github.com/advisories, or the CVE record. It carries the description, the affected range, and the patched versions. The audit's ruby-advisory-db clone lives inside the container, and a new run recreates it. Read the record from the web. 2. The gem's own repository adds the changelog entry, the upstream issue, and the fix commit. 3. Check the patch against our constraints: the patched version's `required_ruby_version` against every Ruby in the blast radius (`gem specification -r -v VERSION GEM required_ruby_version`). A floor above an audited Ruby splits the lockfiles: that one cannot take the patch and needs an exception entry, while the rest upgrade. A floor above an unaudited Ruby only limits how far the fix reaches, since that lockfile had no finding to clear. Example advisory record, the mongo case from section 2: ```yaml --- gem: mongo cve: 2026-88030 ghsa: 4ww7-gqv6-mffc title: Improper neutralization of special elements in data query logic in the GridFS component cvss_v3: 8.3 patched_versions: - ">= 2.26.0" related: url: - https://github.com/advisories/GHSA-4ww7-gqv6-mffc ``` After this section you must be able to answer: - What is vulnerable: the component, the bug class, and what an attacker must control. - Is it exploited in the wild? - How dangerous is it for us: the CVSS vector, plus whether the vulnerable component runs in the blast radius tasks. - What fixes it: the patched versions, and is it a breaking major? - Can we install the fix on the Rubies in the blast radius? Close with one sentence: what is vulnerable, what fixes it, and what the fix requires of this repo. ## 4. Decide how to unblock Separate two jobs. The **unblock** turns the audit green and is due now. The **fix** removes the vulnerable code and can be due later. Two outcomes exist, per lockfile: 1. Upgrade: the lockfile moves to a patched version, so the unblock and the fix are the same change. Small breakage in the integration rides along in it. 2. Exception: the lockfile keeps the vulnerable pin, and a documented `.bundler-audit.yml` entry suppresses the finding. The entry is the unblock; a follow-up issue carries the fix. The entry takes one of two forms: the entire advisory id, or one exact gem and version. Ship the unblock by the fastest safe path, and let a large fix follow on its own schedule. The one case with no exception is section 1 case 1 — a supply-chain advisory, or vulnerable code that runs in our tests — which needs the gem gone from the lockfiles. Decision tree. Walk every affected lockfile: ```text Can this lockfile take a patched version? │ The patch must resolve on the lockfile's Ruby, and inside the │ appraisal constraints. ├─ yes → lock the update, then ask: │ Does the integration survive the update? │ │ Run the task from the blast radius. │ ├─ passes → upgrade │ ├─ fails, fix is small → upgrade, with the fix in the same change │ └─ fails, fix is large → revert the lockfile, keep the │ vulnerable pin └─ no → keep the vulnerable pin Every kept pin is the exception. Collect the versions left: ├─ one version left → one `ignore_gem_versions` entry └─ many versions left, and no upgrade can shrink them → one advisory-id `ignore` entry ``` To lock the update, use the container whose Ruby matches the lockfile: ```bash docker compose run --rm -e BUNDLE_GEMFILE=gemfiles/<affected>.gemfile \ tracer-<RUBY> bundle lock --update GEM_NAME ``` This command changes only the lockfile. It does not install the gem. `bundle update GEM_NAME` writes the same lockfile, but it also installs the gem. To run the blast-radius task: ```bash docker compose run --rm tracer-<RUBY> bundle exec rake test:<TASK> ``` To record the exception, write the entries in `.bundler-audit.yml` and land them. The tree above picks the form; the existing entries in that file show the shape. Every entry documents the advisory id, the gem and exact version, why it cannot upgrade, the removal conditions, and the follow-up issue if the fix is deferred. For sizing an upgrade when the lockfile diff does not settle it, read `CASE-STUDIES.md`: two real advisories where the blocker was a deliberately pinned gemset, the test infrastructure, or a patch that shipped only as a prerelease. Section 4 is done when every lockfile from section 2 has a verdict — upgraded, or kept with its vulnerable version recorded — and the kept versions are collected into the narrowest entry form that covers them. ## 5. Validate and ship Run every command in a `tracer-X.Y` container, per section 2. Validate what you changed: - Lockfile changed: `bundle exec rake dependency:checksum_coverage`. A lockfile rewrite can drop its `CHECKSUMS` section, which pins the content digest of every gem. This task catches the loss. - `.bundler-audit.yml` changed: the audit re-run at the end of this section validates it. The ignore lists are consumed only by the audit, so a green re-run is the check. - Gem version changed: run the blast-radius task in the containers of the earliest and the latest Ruby among the lockfiles that actually moved. A partial upgrade leaves the others on the old version, so the matrix's earliest Ruby may not run the new gem at all. Then re-run the audit. It must print `No high or critical advisories found.` Ship: - No changelog fragment. Internal tooling. - Never commit on `master`. Draft PR, `AI Generated` label, title `chore(security): ...`. - The PR states the advisory id, the affected lockfiles, the decision, and the follow-up issue for deferred work. Ignore entries also state their removal conditions. ## Hard guardrails Two failures are unrecoverable by a later commit, so they get a stop rather than a rule: - Every file under `gemfiles/` is Bundler's output. Regenerate it with `bundle lock --update`; a hand edit produces a lockfile that resolves differently from the one Bundler would write. - A supply-chain advisory, or one reachable in our tests, goes to the maintainers before anything else. An ignore entry here hides a live compromise on the machines running the suite.
Ver en GitHub