Skip to content

OpenHands recipe lock: PyJWT 2.14.0 relock closes ten advisories (unblocks the required osv-scanner check) - #525

Merged
seathatflowsinourveins merged 6 commits into
mainfrom
res-openhands-pyjwt-2140-20260930
Sep 30, 2026
Merged

seathatflowsinourveins merged 6 commits into
mainfrom
res-openhands-pyjwt-2140-20260930

Conversation

@seathatflowsinourveins

@seathatflowsinourveins seathatflowsinourveins commented Sep 30, 2026 •

Copy link
Copy Markdown
Owner

Scope

  • What this PR changes: relocks the OpenHands recipe lock from PyJWT 2.13.0 to 2.14.0 (three lines). Ten PyJWT advisories published 2026-09-29 affect 2.13.0; nine of them fail the required osv-scanner check (one pull_request log read; main push runs from 23:33Z on also concluded failure, logs not read), and the tenth (GHSA-w6j9-cwv2-h6wq) was ignored. OSV and GitHub list 2.14.0 as the first patched release for all ten. The change deletes that ignore and its IGNORE_SCOPES entry and rebinds the lock's IGNORE_ALLOWED_LOCKS entry (the two oauthlib advisories) to the relocked sha256, with a new receipt that carries the 2026-09-29 reachability review forward. oauthlib stays 3.3.1 (4.0.0 was published 2026-09-28T06:01Z, inside the 7-day window; its advisories stay ignored until 2026-10-13); its relock is tracked in Relock OpenHands onto oauthlib 4.0.0 / PyJWT 2.15.1 by 2026-10-08 (osv ignores from #517 expire 2026-10-13) #518 (owner bc, session native-agent-stack-bc; joint relock with PyJWT 2.15.1 earliest 2026-10-05T18:40:43Z, due 2026-10-08; a decision point at 2026-09-30T16:56:01Z, when PyJWT 2.15.0 leaves the window). Every window statement in the record is as of 2026-09-30T03:27:54Z.
  • Authorization: the lock's owner session could not be reached on 2026-09-29 and is gone; the Gate A owner (2d) and the pi-practice session (a2) each asked the coordinator for this relock, and session 21, which had claimed it, withdrew and reviewed.
  • Base commit: e45328d3 (main).
  • Lane: lane:foundation (registering the files in manifests/evidence.json through the hot-file protocol does not make it shared).
  • Owned paths touched: blueprints/runtime-workers/openhands/, .github/osv-scanner.toml, tests/test_osv_lockfile_coverage.py, docs/decisions/2026-09-22-github-automation-closure.md, evidence/receipts/, manifests/evidence.json (last commit).

SOTA sources

Evidence-class table

Claim Evidence class Command / receipt
The new lock differs from the previous one in exactly lines 530-532 (the pyjwt line and its two hashes); both PyJWT 2.14.0 hashes equal PyPI's and the release is not yanked source_review (structural comparison and registry read; not an execution) evidence/relock-2026-09-30.txt sections C and D
The lock is reproducible: a fresh single-command run from the pinned SDK archive gives the same uv.lock and requirements.lock, byte for byte; uv lock, uv lock --check and uv export exit 0, cmp exits 0 local_integration (documented method, uv 0.12.17) receipt locks[0].method; section C
The workflow's command over the 49 lockfiles exits 0 with no PyJWT ignore left; the previous lock under the same config exits 1 with the ten advisories local_integration (local runs of the pinned scanner, not CI; this PR's own osv-scanner check is the CI run) receipt osv_scanner.runs; section E
The recipe's hashed install sequence installs the new lock in a scratch venv on the host network, uv pip check passes and the SDK import check passes local_integration (host scratch venv; no model request; not the image; not an OpenHands run) receipt locks[0].install_check; section F
The 2026-09-29 oauthlib reachability review still holds at the new digest: PyJWT 2.14.0's wheel and sdist have no oauthlib reference (0 matches; the same search over requests-oauthlib's wheel gives 80) local_integration (search with a positive control) plus source_review (unchanged inputs) receipt carried_forward_review; section G
The same outcome for each test under oauthlib 3.3.1 and 4.0.0: import requests_oauthlib, google_auth_oauthlib.flow works and requests-oauthlib 2.0.0's own unit tests give 72 passed and the same 2 failed (timing assertions that also fail under 3.3.1) unchanged upstream test code run by us on a scratch tree (pre-check for the follow-up relock; not applied) receipt oauthlib_4_0_0_precheck; section J (full pytest output)

Local commands run

$ osv-scanner scan source --config .github/osv-scanner.toml --no-resolve <49 inventory lockfiles>   (2.6.0, no PyJWT ignore)
No issues found                                                                                       exit 0
$ osv-scanner scan source --config .github/osv-scanner.toml --no-resolve --lockfile=requirements.txt:<previous lock>   (control)
Total 1 package affected by 10 known vulnerabilities (1 Critical, 5 High, 4 Medium, 0 Low, 0 Unknown)  exit 1
$ uv lock --upgrade-package anyio==4.14.2 --upgrade-package click==8.5.0 --upgrade-package pypdf==6.19.0 --upgrade-package soupsieve==2.9.2 --upgrade-package pyjwt==2.14.0
Updated pyjwt v2.13.0 -> v2.14.0 (and the 09-27 updates)                                              exit 0; uv lock --check exit 0; cmp exit 0
$ python3 -B -m unittest tests.test_osv_lockfile_coverage tests.test_runtime_worker_openhands
Ran 151 tests ... OK                                                                                  exit 0
$ python3 scripts/validate.py
{"components": 69, "hashed_files": 8242, "profiles": 4, "receipts": 173, "status": "passed"}         exit 0
$ python3 scripts/evidence_manifest.py --check
{"files": 8242, "status": "passed"}                                                                   exit 0

The full unittest discovery runs in CI's validate check.

Decision record

docs/decisions/2026-09-22-github-automation-closure.md, dated addendum 2026-09-30 (alternatives: PyJWT 2.15.1 or 2.15.0, relocking oauthlib 4.0.0 now, extending the ignores to the nine; overturn: an advisory only a later PyJWT release fixes, an OpenHands run that fails with 2.14.0, a recorded reason for staying on 2.14.0 that no longer holds when PyJWT 2.15.0 leaves the window (2026-09-30T16:56:01Z), oauthlib 4.0.0 or PyJWT 2.15.1 clearing their windows on 2026-10-05; the follow-up relock has an owner and a due date of 2026-10-08 in #518).

Host evidence

This PR adds a receipt under evidence/receipts/, not evidence/hosts/.

  • scripts/validate.py (exit 0 above) validates the receipt and its registration.
  • Independent review: an Opus evidence-reviewer read head 11706fa (no defect in the lock change; two medium and six low record findings) and session 21 ran an evidence-reviewer and a stack-verifier that re-ran all seven commands (approve with nits; two medium and several low findings), then a delta check on the first repair (all text: as-of times, an owner name, a decision point for PyJWT 2.15.0, output labels). All findings are addressed in two repair rounds; session 21 was asked to check the second.
  • No platform_status change.

Residuals

  • The agent-server image's PyInstaller server binary is built from upstream's uv.lock, which still pins PyJWT 2.13.0, and the grader venv (OpenHands/benchmarks@405bae7's uv.lock, synced by install-grader.sh) pins PyJWT 2.10.1; neither is in the OSV inventory, and the 2026-09-28 grype record of the image predates these advisories. The relock covers the recipe venv only.
  • No OpenHands task ran with the relocked venv; the install check ran on the host network.
  • An installation that recorded the old lock digest must re-run install (host.py refuses to run it otherwise); this host has none.
  • The oauthlib 4.0.0 (and PyJWT 2.15.1) relock is tracked in Relock OpenHands onto oauthlib 4.0.0 / PyJWT 2.15.1 by 2026-10-08 (osv ignores from #517 expire 2026-10-13) #518: earliest 2026-10-05T18:40:41Z, due 2026-10-08; on 2026-10-13 the two oauthlib ignores need renewal for the frozen Lumibot lock or that lock goes.
  • The OSV database is live: a later advisory for any pinned package fails the check again.

Checklist

  • No workflows or Actions are changed.
  • No secrets are printed, logged or committed.
  • No new paid hosting, subscription or billing surface.
  • Peer-owned untracked files and worktrees were preserved.

🤖 Generated with Claude Code

Scout and others added 2 commits September 29, 2026 22:41
…p the PyJWT ignore and rebind the oauthlib allowance

Nine PyJWT advisories published 2026-09-29 (one Critical, five High, three Medium) failed the required osv-scanner check
on blueprints/runtime-workers/openhands/requirements.lock (CI run 36657687493). PyJWT 2.14.0 fixes them and
GHSA-w6j9-cwv2-h6wq, which the 2026-09-29 ignore hid.

- requirements.lock: three lines (pyjwt 2.13.0 -> 2.14.0 and its two hashes), made with the recipe's own method (uv
  0.12.17, the pinned SDK archive, one `uv lock` with the four 09-27 upgrades plus pyjwt==2.14.0); a fresh single-command
  run is byte-identical. pins.json requirements_sha256 rebound.
- The GHSA-w6j9-cwv2-h6wq ignore and IGNORE_SCOPES entry are deleted; the lock's IGNORE_ALLOWED_LOCKS entry keeps the two
  oauthlib advisories at the relocked sha256 and names the new record, which carries the 09-29 review forward.
- oauthlib stays 3.3.1 (4.0.0 is inside the 7-day window until 2026-10-05T06:01Z).
- Checks: the pinned osv-scanner 2.6.0 CI command over 49 lockfiles exits 0 with no PyJWT ignore; the previous lock under
  the same config exits 1 with ten advisories; the recipe's hashed install sequence and SDK import check pass in a scratch
  venv. Receipt, returned outputs, research and README notes and the decision addendum are included.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…protocol, last commit)

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@seathatflowsinourveins seathatflowsinourveins added the lane:foundation Foundation lane: Claude/Codex setup, hosts, memory, RAG, research, workers label Sep 30, 2026
@socket-security

socket-security Bot commented Sep 30, 2026 •

Copy link
Copy Markdown

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Updatedpypi/​pyjwt@​2.13.0 ⏵ 2.14.0100 +1100 +75100100100

View full report

Scout and others added 2 commits September 29, 2026 23:10
…idence-reviewer, session 21's evidence-reviewer and stack-verifier)

Neither review found a defect in the lock change; the findings were about the record. Fixed here:

- 2.14.0 "fixes all ten" is now attributed to the OSV and GitHub ranges (all ten first patched 2.14.0, section H of the
  returned outputs); the changelog names nine, the tenth's fix sits under an id neither database serves.
- oauthlib carry-forward: the PyJWT wheel (the installed artifact) and sdist are searched for oauthlib with the command
  shown and a positive control (requests-oauthlib's wheel: 80 hits); "one replaced artifact" corrected to two files of one
  release.
- as-of time, one identical overturn list in the receipt, research.md and the decision, and the follow-up trigger for
  PyJWT 2.15.1 with oauthlib 4.0.0; the oauthlib relock now has an owner and a date (#518: due 2026-10-08).
- oauthlib 4.0.0 pre-check: imports and requests-oauthlib 2.0.0's unit tests give identical results under 3.3.1 and 4.0.0.
- limitations added: the grader venv (PyJWT 2.10.1), the reinstall requirement, the host-network import check, the
  unreconciled "Filtered 7" count; stale wording fixed (README, the decision's present-tense list, "every artifact").
- returned outputs rebuilt: no truncated lines, exit lines for uv lock and uv lock --check, '(derived)' labels, the push runs.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…st commit)

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Scout and others added 2 commits September 29, 2026 23:31
…s, owner name, PyJWT 2.15.0 decision point, honest output labels)

All text and evidence-form; the lock, pins.json and the test file are unchanged.

- #518 now says what this record claims (owner bc, session native-agent-stack-bc; exact window times; the 2.15.0 decision point; the relock
  command with oauthlib added and pyjwt 2.14.0 replaced by 2.15.1); its updatedAt is recorded in the receipt.
- Every window statement carries an as-of time and the exit times (PyJWT 2.15.0 2026-09-30T16:56:01Z, oauthlib 4.0.0 2026-10-05T06:01:19Z,
  PyJWT 2.15.1 2026-10-05T18:40:43Z, 7 days after the later file upload on PyPI); the toml no longer speaks in the present tense.
- 2.15.0's Security item (recursion errors from deeply nested payloads, the payload counterpart of GHSA-8wjv) gets a recorded reason for
  staying on 2.14.0 (no decode of attacker-controlled tokens found in the recipe venv) and a decision point when 2.15.0 leaves the window;
  one identical overturn list carries the triggers.
- Returned outputs rebuilt so no line claims more than it is: each command as run, sections G, H and J with the real commands, the full
  pytest output (-rf --tb=line) for both oauthlib versions, the requests-oauthlib sdist hash (one of the lock's), the test dependency
  versions, '(derived)' and '(abridged)' labels; 'identical' reworded to 'the same outcome for each test' (the two failures are timing
  assertions that also fail under oauthlib 3.3.1).
- README: grype's advisories are for the four 09-27 versions only; the receipt's evidence class names the run of unchanged upstream tests;
  'cannot have failed' is 'most likely did not fail'.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…t commit)

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@seathatflowsinourveins
seathatflowsinourveins merged commit f77612b into main Sep 30, 2026
25 checks passed
@seathatflowsinourveins
seathatflowsinourveins deleted the res-openhands-pyjwt-2140-20260930 branch September 30, 2026 04:00
seathatflowsinourveins added a commit that referenced this pull request Sep 30, 2026
…outputs (D2), honest output labels (#529)

* PyJWT relock record: follow-up for the second review's residuals (redis decode search, timing range from the outputs, honest output labels)

Text and evidence only; the lock, pins.json, tests and .github/osv-scanner.toml are unchanged (the toml text was already correct at #525).

- D1: the reason for staying on PyJWT 2.14.0 until the joint relock now covers every PyJWT decode found in the relocked venv
  (section K of the returned outputs, a static search of 10,746 installed .py files with its script published): google-auth
  (oauth2/id_token.py:153, a module nothing outside google-auth imports), LiteLLM (proxy modules, not run), oauthlib
  (common.py:221, no caller outside oauthlib), redis 7.2.1 (auth/token.py:89, verify_signature false on a token its configured
  credential provider supplies; nothing outside redis references redis.auth.token or JWToken) and mcp (encode only); the recipe's
  own code imports neither redis nor jwt.
- D2: the two failing requests-oauthlib tests' miss (assertAlmostEqual of an integer expiry against a float one) is computed from
  the retained outputs (0.09 to 0.39 s) and explained: it follows the fractional second of the run's clock, so they fail on almost
  any run under oauthlib 3.3.1 and 4.0.0 alike.
- Nits: the returned outputs' header rule now names its exceptions ('(abridged)', '(derived)', '#', excerpts, the ~ substitution and
  the rounding of window times), section D shows its real command, section G shows each command as run, and the builder script
  and the section K script are published (evidence/relock-2026-09-30.builder.sh.txt, .jwt-callers.py.txt).

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* Register the follow-up's files in the evidence manifest (hot file, last commit)

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* PyJWT relock follow-up, repair round for session 21's read (aliased imports, google-auth :146, LiteLLM :1516, honest checks)

Text, script and evidence only; the lock, pins.json, tests and .github/osv-scanner.toml are unchanged.

- N1: section K's script now follows every PyJWT import form (`import jwt`, `import jwt as X`, `from jwt import ...`), counts only
  names bound to the jwt module, and so cites google-auth's PyJWT decode at oauth2/id_token.py:146 (the :153 `jwt.decode` beside it is
  google-auth's own jwt module) and adds LiteLLM's `import jwt as _jwt` decode at mcp_management_endpoints.py:1516. The conclusion
  (no decode of an attacker-controlled token reachable in the recipe venv) is unchanged.
- N3: the recipe is searched recursively (12 files, not 8), the google-auth outside-reference check excludes only google/auth and
  google/oauth2, litellm.proxy gets its own outside-reference check, and the "10,746 files" wording says what it leaves out (PyJWT's
  own files and installer tooling). Section K reproduces byte for byte from the published script.
- N2: the 0.38-0.43 s figure is stated as a first run's terminal output that was not retained; the 0.22-0.28 s figure is the one in
  this file's git history (f77612b).
- N4: the outputs header names every exception (abridged commands, derived lines, comments, excerpts, path placeholders), command
  lines show the real loops and paths, `osv-scanner --version` is its own command, and the script and builder names agree.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* Re-register the follow-up's repaired files in the evidence manifest (hot file, last commit)

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Scout <scout@local>
Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

lane:foundation Foundation lane: Claude/Codex setup, hosts, memory, RAG, research, workers

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant