Skip to content

fix(cooldown): extract deps from TOML lockfiles + fail-loud guard#53

Merged
j7an merged 7 commits intomainfrom
fix/cooldown-lockfile-extraction
Apr 21, 2026
Merged

fix(cooldown): extract deps from TOML lockfiles + fail-loud guard#53
j7an merged 7 commits intomainfrom
fix/cooldown-lockfile-extraction

Conversation

@j7an
Copy link
Copy Markdown
Owner

@j7an j7an commented Apr 21, 2026

Summary

  • Adds section-aware TOML lockfile parser for uv.lock / poetry.lock to extract-deps.sh
  • Adds PR-body fallback primitive (pr-body-to-deps.sh) for defense-in-depth when diff parsing fails
  • Adds fail-loud guard (diff-touches-lockfile.sh) that refuses green gates when a dependency lockfile/manifest is touched but no rows are extracted

Why

Closes a silent security downgrade. Before this PR, Dependabot bumps to uv.lock / poetry.lock / Cargo.lock produced DEPS_TSV="", which cascaded to GATE_STATE=success + auto-merge with the comment "No known exploits found affecting the target versions" — when zero versions had actually been scanned. See issue #52 for the full failure-mode analysis and nexus-mcp#170 for the observed instance.

Cargo.lock / package-lock.json remain unparsed for now — the fail-loud guard refuses green gates when they are touched, which is strictly safer than the current silent-success path.

Test plan

  • `bats tests/extract-deps.bats` — 7 tests, including 2 new (uv-lock-multi, poetry-lock)
  • `bats tests/diff-touches-lockfile.bats` — 7 new tests
  • `bats tests/pr-body-to-deps.bats` — 8 new tests
  • `scripts/check-inline-sync.sh` — 5 pairs, all OK
  • Post-merge: re-run dependency-cooldown on deps: bump the minor-and-patch group across 1 directory with 5 updates nexus-mcp#170 (or a fresh uv.lock Dependabot PR) and verify the comment renders all 6 packages with Release-Age and OpenSSF-Scorecard sections populated

Fixes #52

j7an added 7 commits April 20, 2026 22:38
Part of #52. Detects dependency lockfile/manifest edits in a unified diff
so the workflow can refuse green gates when the dep extractor returns zero
rows on a lockfile bump. Covers *.lock, requirements*.txt, pyproject.toml,
Pipfile, go.mod, Cargo.toml, package.json, package-lock.json, yarn.lock,
pnpm-lock.yaml. Primitive only — workflow wiring lands in a later commit.
Part of #52. Scrapes Dependabot PR bodies into the extract-deps.sh TSV
schema as a defense-in-depth fallback when diff parsing returns zero rows.
Recognizes three Dependabot body shapes: single-dep Bumps, prose Updates,
grouped-update markdown tables. Pattern B anchored at column 0 to avoid
false positives from release-notes blockquotes. Primitive only — workflow
wiring lands in a later commit.
Fixes #52 (partial — primary parser layer). Adds a section-aware parser
that tracks the current file (via diff --git headers), current package
name (from context or + lines inside a [[package]] stanza), and emits a
row when a + version = "..." line is encountered. Covers uv.lock and
poetry.lock (both tagged pypi ecosystem). Cargo.lock and package-lock.json
remain unparsed — the fail-loud guard (landing in the next commit) will
refuse green gates when they are touched.

Renames large-uv-lock fixture to large-valid — the #50 SIGPIPE regression
test was mislabeled as uv.lock content; it is actually pip-style lines
with padding. Content unchanged.

Inline copy in dependency-cooldown.yml kept in lockstep via
check-inline-sync.sh.
Fixes #52. Composes the three-layer defense end-to-end in dependency-
cooldown.yml:

- Inlines diff_touches_lockfile() and pr_body_to_deps() as function-
  scoped bash blocks matching the existing inline-sync convention.
- After DEPS_TSV extraction, tries the PR-body fallback when empty and
  the touched files map to a supported ecosystem (pypi or actions).
- If DEPS_TSV is still empty AND the diff touches a lockfile/manifest,
  sets GUARD_TRIGGERED, populates EXTRACTION_WARNING with the list of
  touched paths, and forces GATE_STATE=error + AUTO_MERGE_OK=false via
  a new topmost branch in the gate state machine.
- Gates the existing action-count mismatch check so it cannot overwrite
  a guard-set EXTRACTION_WARNING.
- Extends check-inline-sync.sh's INLINE_PAIRS to cover the two new
  inline blocks, keeping every commit self-consistent.
Previously, the guard primitive's allowlist only matched lockfile and
manifest basenames, so Actions-only Dependabot PRs (which touch
.github/workflows/*.yml but no lockfile) could slip past Layer 2/3 of
the fallback/guard if extract-deps ever returned zero rows — the exact
silent-green failure mode #52 was meant to close, just for a different
ecosystem.

Add a second case branch that matches .github/workflows/*.yml and
.yaml by full path (to avoid over-triggering on any *.yml). Mirror
into the inline copy and add two regression tests.

Refs #52.
The Pattern A regex in pr-body-to-deps.sh used [^f]* between the `]`
and `from`, which silently dropped any Dependabot bump whose URL
contained `f` — including common names like `ruff`, `fastapi`, and
anything hosted under orgs such as `astral-sh` or `facebook`.

Replace [^f]* with the explicit \([^)]+\) URL match (same construct
already used by Pattern C). Switch the single-bumps fixture to the
`ruff` case so the test is now a regression for this exact bug.

Refs #52.
When Layer 3's guard triggered (extractor returned zero rows on a
dependency-relevant diff and the PR-body fallback also failed), the
final comment still flowed through the clean-scan branch — posting
"No known exploits found" + "Auto-merge has been enabled" even as
the gate state machine below flipped the status check to `error`.

Add a GUARD_TRIGGERED branch at the top of the results if/elif chain
so guard-triggered runs compose a comment that matches the red gate:
a "not scanned" results table and an auto-merge-not-enabled footer.
The EXTRACTION_WARNING_SECTION prepended further down still carries
the list of touched paths on top of this body.

Refs #52.
@j7an j7an merged commit 365119e into main Apr 21, 2026
7 checks passed
@j7an j7an deleted the fix/cooldown-lockfile-extraction branch April 21, 2026 14:23
j7an added a commit to j7an/nexus-mcp that referenced this pull request Apr 22, 2026
Upstream j7an/shared-workflows#53 fixes a silent security downgrade where
dependency-cooldown.yml's extract-deps.sh parser returned DEPS_TSV="" for
TOML lockfiles (uv.lock, poetry.lock, Cargo.lock), cascading to
GATE_STATE=success and auto-merge with the comment "No known exploits
found" when zero versions had been scanned.

Observed on nexus-mcp#170 (Dependabot uv.lock bump of pydantic, mypy,
pytest, respx, ruff) — the scan rendered "(no dependencies extracted
from diff)" and enabled auto-merge despite scanning nothing.

v2.5.2 adds a section-aware TOML parser, a PR-body fallback primitive,
and a fail-loud guard that refuses green gates when a lockfile is
touched but no rows are extracted.

Bumps all three consumer workflows to keep the fleet aligned at a single
shared-workflows SHA (consistent with d51d30d).

Refs: j7an/shared-workflows#53
Refs: j7an/shared-workflows#52
j7an added a commit to j7an/nexus-mcp that referenced this pull request Apr 22, 2026
…#173)

Upstream j7an/shared-workflows#53 fixes a silent security downgrade where
dependency-cooldown.yml's extract-deps.sh parser returned DEPS_TSV="" for
TOML lockfiles (uv.lock, poetry.lock, Cargo.lock), cascading to
GATE_STATE=success and auto-merge with the comment "No known exploits
found" when zero versions had been scanned.

Observed on nexus-mcp#170 (Dependabot uv.lock bump of pydantic, mypy,
pytest, respx, ruff) — the scan rendered "(no dependencies extracted
from diff)" and enabled auto-merge despite scanning nothing.

v2.5.2 adds a section-aware TOML parser, a PR-body fallback primitive,
and a fail-loud guard that refuses green gates when a lockfile is
touched but no rows are extracted.

Bumps all three consumer workflows to keep the fleet aligned at a single
shared-workflows SHA (consistent with d51d30d).

Refs: j7an/shared-workflows#53
Refs: j7an/shared-workflows#52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

fix(cooldown): extract-deps skips uv.lock & TOML lockfile bumps, silently marking scan clean

1 participant