Skip to content

astral-sh/setup-uv silently omitted from Packages Scanned list in dependency-cooldown.yml #27

@j7an

Description

@j7an

What

In both v2.0.0 and v2.0.1 of dependency-cooldown.yml, astral-sh/setup-uv is silently excluded from the "Packages scanned" list in the scan comment, even when the triggering PR's diff clearly includes a uses: astral-sh/setup-uv@... bump.

Observed on j7an/nexus-mcp#160 across both scanner versions:

The PR diff includes bumps for three actions:

  • step-security/harden-runner (appears in scan ✓)
  • trufflesecurity/trufflehog (appears in scan ✓)
  • astral-sh/setup-uv (absent from scan ❌)

The v2.0.0 scan comment header read:

Packages scanned: step-security/harden-runner, trufflesecurity/trufflehog

After the v2.0.1 rebase it still read:

Packages scanned: step-security/harden-runner (v2.17.0), trufflesecurity/trufflehog (v3.94.3)

In both cases astral-sh/setup-uv is missing despite being in the same diff. This is a silent data loss — a consumer could have a genuine advisory against setup-uv and the scan would report "no known exploits" while never actually asking GHSA or OSV about it.

Why

The likely root cause is in the diff-parsing pipeline that extracts action names from + uses: lines. Current v2.0.1 code (approximately):

while IFS= read -r _uses_line; do
  [ -z "$_uses_line" ] && continue
  _action_name=$(echo "$_uses_line" | sed -E 's/^\+\s+uses:\s+//' | sed -E 's/@.*//' | sed -E 's/\s*$//')
  [ -z "$_action_name" ] && continue
  echo "$_action_name" | grep -qE '^(\./|docker://)' && continue
  echo "$ACTIONS" | grep -qxF "$_action_name" 2>/dev/null && continue
  _action_ver=$(echo "$_uses_line" | grep -oE '#\s*v[0-9][0-9.]*' | sed -E 's/#\s*v//' || true)
  ACTIONS="$(printf '%s\n%s' "$ACTIONS" "$_action_name")"
  ACTION_VERSIONS["$_action_name"]="$_action_ver"
done <<< "$(echo \"\$DIFF\" | grep -E '^\+\s+uses:' || true)"

Hypotheses (prioritized, all need verification):

  1. Indentation or step-level context differs. setup-uv is typically used inside jobs.<job>.steps[].uses at a deeper indent than top-level reusable-workflow uses: calls. The grep -E '^\+\s+uses:' tolerates any leading whitespace, but the sed -E 's/^\+\s+uses:\s+//' assumes a specific shape and might produce a malformed _action_name (with leading whitespace) that then gets caught by a later filter.
  2. The diff's + line has a BOM or non-ASCII whitespace. Unlikely but possible if Dependabot's diff generator varies across action sources.
  3. Subshell scope on the while loop. The <<< here-string should execute the loop body in the current shell (bash), so ACTIONS modifications persist. But if a consumer is running this under a non-bash POSIX shell, the entire loop could run in a subshell and discard additions — though we'd see all actions dropped, not just setup-uv. Rule out by inspecting the shell used in CI.
  4. Dedup false-positive. grep -qxF "$_action_name" is a full-line fixed-string match so a substring collision is unlikely, but worth ruling out if any other action name in the repo is a prefix or suffix of astral-sh/setup-uv.
  5. Silent filter via grep -v -E '^(\./|docker://)' (v2.0.0 code path). Does not apply — astral-sh/setup-uv doesn't start with ./ or docker://. Listed for completeness.

The root cause needs empirical confirmation, not speculation. Step 1 of the fix is reproduction.

How

  1. Reproduce. Use gh pr diff 160 --repo j7an/nexus-mcp (or a saved snapshot from before its merge) to capture the actual diff. Run the extraction pipeline locally against the captured diff and observe where astral-sh/setup-uv drops out. Save the reproducer diff as a fixture in the shared-workflows repo.
  2. Isolate the stage. Test each pipeline stage (grep -E '^\+\s+uses:', sed 's/^\+\s+uses:\s+//', sed 's/@.*//', sed 's/\s*$//', grep -qE '^(\./|docker://)', grep -qxF dedup) individually against the setup-uv input line. Identify which stage removes it.
  3. Fix the identified stage with the minimum change. If it's a regex issue, fix the regex; if it's a subshell issue, restructure the loop to run in the current shell.
  4. Add a regression fixture. Create a fixture diff in tests/fixtures/ (or wherever shared-workflows keeps test data) that includes an astral-sh/setup-uv bump alongside step-security/harden-runner and trufflesecurity/trufflehog. Add a test that runs the extraction pipeline against the fixture and asserts all three are returned in the packages list.
  5. Add a sanity-check guard to the main workflow. After extraction, count + uses: lines in the diff and compare against the length of $ACTIONS. If they differ, render a ⚠️ Extraction warning section in the scan comment listing the expected-vs-found counts. This makes future silent drops visible instead of silent.

Acceptance Criteria

  • Root cause of setup-uv being dropped is identified and documented in the fix PR description
  • A captured diff from j7an/nexus-mcp#160 (or equivalent) is committed as a test fixture
  • The extraction pipeline is updated so that a diff containing + uses: astral-sh/setup-uv@<sha> # v<version> produces astral-sh/setup-uv in the extracted list
  • A regression test (shell test, bats, or equivalent) runs the extractor against the fixture and asserts that all three actions are returned
  • A re-scan of a comparable 3-action bump PR lists astral-sh/setup-uv in the Packages Scanned section of the scan comment
  • Sanity-check guard: if the count of + uses: lines in the input diff does not equal the count of extracted actions, the scan comment includes a warning section with both counts so future silent drops are immediately visible
  • No regressions in extraction for the other two actions or for Python dependencies

Related

  • Observed during the same 2026-04-12 verification that found the cooldown-enforcement regression and the stale-label issue (separate issues)
  • Risk magnitude: a silently-unscanned dependency could ship with a genuine advisory while the scan reports "no known exploits" — this is a correctness issue, not a cosmetic one

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions