Repository navigation
fix(deps): close the advisories on the auth path and fail CI on new ones (D-01) - #228
Merged
Merged
Conversation
…nes (D-01) pip-audit reported nine advisories against the shipped lockfile. The load- bearing one: PyJWT 2.10.1 carries PYSEC-2026-176, a verifier-side algorithm allow-list bypass in jwt.decode() -- which is the exact call at src/pmcp/auth.py:512 and the exact control SECURITY.md promises. Also closed PYSEC-2026-175 (PyJWKClient -> urlopen) and PYSEC-2026-177 (unknown-kid JWKS refetch, this review's own S-07), plus cryptography (does the signature verification), aiohttp (the JWKS fetch client), starlette, python-multipart, python-dotenv, click and pytest. Floors raised, not just the lock: aiohttp>=3.14.2 and pyjwt>=2.13.0. A floor that still admits the vulnerable range is not a pin -- the same unbounded- dependency class that has bitten this repo before. Upgrades are TARGETED, not 'uv lock --upgrade'. The blanket form also pulled ruff 0.14.10 -> 0.16.6, whose new default rules failed ~40 pre-existing files in scripts/probes -- unrelated churn in a security fix. ruff is held. New blocking 'audit' job runs pip-audit --strict on the resolved environment. Blocking rather than advisory: this repo's guard culture is fail-closed, and the whole lesson of D-01 is that an advisory on the auth path sat unnoticed. Its two new remote uses: raise the pinned-ref count guard 30 -> 32, which is the deliberate step that guard exists to force.
Board finding (codex, blocking): the audit job ran 'uv sync --all-extras --dev'
and 'uv run --with pip-audit', neither with --locked. Both silently RE-RESOLVE
when uv.lock is stale against pyproject.toml, so the job would audit a freshly
resolved graph, report clean, and leave the vulnerable lock that actually ships
untouched -- a false green in the one job whose entire purpose is to fail
closed. Verified: with a drifted floor, 'uv sync --locked' refuses ('To update
the lockfile, run uv lock') instead of re-resolving.
pip-audit is now pinned (==2.9.0). An unpinned tool fetched at run time is the
same supply-chain shape #217 closed for actions; the advisory DATA is still
fetched fresh, which is the part that must not be pinned.
This was referenced Sep 5, 2026
ViperJuice
added a commit
that referenced
this pull request
Sep 10, 2026
Six advisories were published against the pinned httpx2/httpcore2 2.9.1 in the last two days, so `main` started failing its own audit gate without anyone changing code — which is exactly what that gate (added in #228) exists to do. Every open PR inherits the red X, including the TRUST phase in #240. httpx2 2.9.1 PYSEC-2026-3845/3846/3847/3848/3849 fixes 2.10.0/2.11.0/2.12.0 httpcore2 2.9.1 PYSEC-2026-3844 fix 2.10.0 Lockfile only. `pyproject.toml:86` already declares `httpx2>=2.5.0,<3.0.0` and 2.12.0 sits inside it, so no constraint moves. Scoped with `--upgrade-package` for each flagged package rather than a bare `uv lock --upgrade`, which over-reached in this repo before and dragged ruff forward, breaking ~40 unrelated files under scripts/probes/. Exactly three lock entries changed: httpcore2 2.9.1 -> 2.12.0 httpx2 2.9.1 -> 2.12.0 httpx2-jsfetch (new) 1.0 The new transitive was checked rather than waved through — a novel package appearing under an HTTP library is the shape this repo's current programme is about. It is declared by httpx2 2.12.0 itself, comes from PyPI with sdist and wheel hashes, and is gated `sys_platform == 'emscripten'`, so it never installs off Pyodide. Verified empirically: not importable after `uv sync --locked`. Verification: - `pip-audit --strict` on the resolved environment: "No known vulnerabilities found" (was six). - Full suite, 3456 collected — identical population to main, no new tests. 3215 passed + 107 failed + 106 errors = 3428, exactly main's passed count. - The failures are the known worktree-location artifact: `node_modules` at the volume root trips npm's local-prefix rule and the resolver refuses by design (#195). Every failure reason traces to that refusal or its downstream UpdateServerOutput cascade, and the output contains zero occurrences of httpx, httpcore, ConnectError, ReadError or TransportError — so the transport bump introduced none of them. A checkout outside /mnt runs clean, as CI does. Claude-Session: https://claude.ai/code/session_018FncHYooSXexECJ2DYcnu6 Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
First item from the 2026-09-01 codebase review (#227), D-01. See #224.
Why this is first, not tenth. The review lists D-01 as "refresh
uv.lock", butpip-auditon the shipped lockfile reported nine advisories, and the load-bearing one is PYSEC-2026-176 in PyJWT 2.10.1 — a verifier-side algorithm allow-list bypass injwt.decode(). That is the exact call atsrc/pmcp/auth.py:512(jwt.decode(..., algorithms=list(allowed_algorithms))), and the exact control SECURITY.md promises:Also closed, all adjacent to this repo's own auth work:
pyjwt2.10.1 → 2.13.0PyJWKClientpasses its URI straight tourllib.request.urlopen(next door to #210/#211); 177 = unknown-kidforces a JWKS refetch — this review's own S-07, reachable through the dependency toocryptography46.0.3 → 50.0.1aiohttp3.13.2 → 3.14.3starlette0.50.0 → 1.6.0python-multipart,python-dotenv,click,pytestpip-auditnow reports No known vulnerabilities found.Floors, not just the lock.
aiohttp>=3.14.2andpyjwt>=2.13.0are declared. A floor that still admits the vulnerable range is not a pin — the same unbounded-dependency class that has bitten this repo before.Targeted upgrades, deliberately not
uv lock --upgrade. The blanket form also pulled ruff 0.14.10 → 0.16.6, whose new default rules failed ~40 pre-existing files underscripts/probes/— unrelated churn inside a security fix. ruff is held at 0.14.10; only the eight flagged packages moved.New blocking
auditjob.pip-audit --strictagainst the resolved environment (uv.lockis what ships and what advisories are scored against). Blocking rather than advisory because this repo's guard culture is fail-closed (#187, #189, #202, #217) and the whole lesson of D-01 is that an advisory on the auth path went unnoticed until a manual review. The cost — a newly published advisory can redden an unrelated PR — is the decision we want forced.Its two new remote
uses:raise the pinned-ref count guard from 30 to 32. That guard firing was correct: a new remote action is meant to be spelled twice.Verification (each run in the step it is reported):
pip-auditclean; full suite 3199 passed, with failures/errors/skips/deselects identical tomain— including across the starlette 0.x→1.x and cryptography 46→50 major bumps (the 107 failed / 106 errors are the documented/tmp/package.jsonnpm-identity host quirk, unrelated);ruff check,ruff format --check,mypy src/, andscripts/check_workflows.pyall exit 0; workflow-guard suite 194 passed.🤖 Generated with Claude Code
https://claude.ai/code/session_018NR2LKqZgn7CXVH2816gaa
Need help on this PR? Tag
@codesmith-botwith what you need. Autofix is disabled.