Skip to content

PyJWT relock record: redis decode search (D1), timing range from the outputs (D2), honest output labels - #529

Merged
seathatflowsinourveins merged 4 commits into
mainfrom
res-openhands-pyjwt-followup-20260930
Sep 30, 2026
Merged

seathatflowsinourveins merged 4 commits into
mainfrom
res-openhands-pyjwt-followup-20260930

Conversation

@seathatflowsinourveins

@seathatflowsinourveins seathatflowsinourveins commented Sep 30, 2026 •

Copy link
Copy Markdown
Owner

Scope

  • What this PR changes: the record of OpenHands recipe lock: PyJWT 2.14.0 relock closes ten advisories (unblocks the required osv-scanner check) #525's PyJWT 2.14.0 relock, after the second review's residuals (D1, D2 and nits): the reason for staying on 2.14.0 until the joint relock now covers every PyJWT decode found in the relocked venv (a static search of its 10,746 installed .py files, script published), the two failing requests-oauthlib tests' timing miss is computed from the retained outputs and explained, and the returned outputs' header rule names its exceptions. No lock, pin, test or config change (requirements.lock, pins.json, tests/, .github/osv-scanner.toml are byte-unchanged from main).
  • Base commit: f77612b6 (main, after OpenHands recipe lock: PyJWT 2.14.0 relock closes ten advisories (unblocks the required osv-scanner check) #525).
  • 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/ (research.md and evidence/), 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
No decode of an attacker-controlled token through PyJWT is reachable in the recipe venv: the PyJWT importers are google-auth (id_token.py:146, through import jwt as jwt_lib; no importer of that module outside google/auth and google/oauth2), LiteLLM (seven proxy modules, one through import jwt as _jwt at mcp_management_endpoints.py:1516; nothing outside litellm references litellm.proxy; the proxy is not run), oauthlib (common.py:221, no caller outside oauthlib), redis (auth/token.py:89, verify_signature false on a token its credential provider supplies; nothing outside redis references redis.auth.token or JWToken) and mcp (encode only); none of the recipe's 12 .py files (searched recursively) imports redis or jwt local_integration (static text search of installed .py files, import aliases and from-imports followed; a name assembled at run time would not match; not a runtime trace) receipt release_choice.decision_point_2_15_0; returned outputs section K; evidence/relock-2026-09-30.jwt-callers.py.txt
The two failing requests-oauthlib tests fail on an integer-versus-float timestamp comparison off by 0.09 to 0.39 s in the four retained failures, which follows the run's fractional second; they fail under oauthlib 3.3.1 and 4.0.0 alike unchanged upstream test code run by us on a scratch tree receipt oauthlib_4_0_0_precheck; returned outputs section J (full pytest output)
The returned outputs' commands are shown as run, with abridged and derived lines labelled, and the builder that wrote them is published source_review (reading the file against its builder) evidence/relock-2026-09-30.txt, evidence/relock-2026-09-30.builder.sh.txt

Local commands run

$ python3 scripts/validate.py
{"components": 69, "hashed_files": 8244, "profiles": 4, "receipts": 173, "status": "passed"}         exit 0
$ python3 scripts/evidence_manifest.py --check
{"files": 8244, "status": "passed"}                                                                   exit 0
$ python3 -B -m unittest tests.test_osv_lockfile_coverage tests.test_runtime_worker_openhands
Ran 151 tests ... OK                                                                                  exit 0
$ python3 relock-2026-09-30.jwt-callers.py <scratch venv site-packages> blueprints/runtime-workers/openhands   (section K)
10746 files searched; PyJWT importers and callers as in the table; output equals section K byte for byte  exit 0

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

Decision record

docs/decisions/2026-09-22-github-automation-closure.md, the dated 2026-09-30 addendum (rewritten in place for D1, with the as-of times refreshed); the overturn list is unchanged, and trigger (c) still fires at 2026-09-30T16:56:01Z if the recorded reason stops holding.

Host evidence

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

  • scripts/validate.py (exit 0 above) validates the receipt and its registration.
  • Independent review: session 21 read head 1ac08865 (needs changes: aliased PyJWT imports missed by section K, the google-auth decode cited at the wrong line, an unretained figure cited as retained, unnamed exceptions in the outputs header); all are fixed in the repair round (head 3f18398c), and section K reproduces byte for byte from the published script. Session 21 was asked to check the new head.
  • No platform_status change.

Residuals

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 30, 2026 00:05
…is 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>
…st 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
Scout and others added 2 commits September 30, 2026 00:34
…mports, 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>
…hot file, last commit)

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