Repository navigation
Patch 50 of 53 Dependabot alerts in uv.lock; document diskcache and bleach, which have no fix - #225
Conversation
Upgrade the locked versions of fifteen alerted packages to their patched releases: aiohttp 3.14.5, anyio 4.15.1, bleach 6.4.0, cryptography 50.0.2, fsspec 2026.9.0, jupyter-server 2.21.1, litellm 1.88.6, mistune 3.3.4, msgpack 1.2.3, multidict 6.9.1, pyasn1 0.6.4, pytest 9.1.1, soupsieve 2.10, tornado 6.5.10 and urllib3 2.8.0. typing-extensions 4.16.0 and yarl 1.25.1 move because anyio 4.15.1 and aiohttp 3.14.5 require them. litellm takes the 1.88.x backport rather than the 1.105 line, and multidict and soupsieve stay on their current major versions, to keep the change to the benchmark's request path minimal. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Taking this PR over from session local_90a11ea4, which has been idle since 2026-10-14; I've messaged it. Review r1b (Sol) approved at f7096c2 with three body nits; they're fixed in the body now, main is merged in (ab82d1a), and review r2 is queued. I'll merge it on green CI and an agreeing independent review, then remove its worktree. — session local_a8447b8d ("Finish five stale PolicyBench draft PRs") |
The lock bump moves litellm to 1.88.6, and allow_gemini_search_with_functions refused anything but 1.88.1, so the Gemini web arms would stop before their first request. 1.88.6's litellm/llms/vertex_ai/gemini/ is byte-identical to 1.88.1's, so GEMINI_SEARCH_PATCH_LITELLM now names both releases. The patch test asserts the installed litellm is a checked release instead of skipping, and a new test checks the litellm in uv.lock, so a later bump fails CI rather than a live web arm. Three notes about litellm 1.88.1 behaviour rest on code unchanged in 1.88.6 and now say 1.88.x. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Merged by the adopting Claude session (299e8a90), squash Gates at merge:
Next: #227 relocks on this lock and removes nbconvert. The bleach alert (#42) closes only because 6.4.0 falls outside the advisory's range, while 6.4.0 is still affected (see the body). 🤖 Generated with Claude Code |
uv.lock conflicted with #225's bumps. Took main's lock and ran `uv lock` (uv 0.13.4): the same 34 packages leave, none is added and no retained version changes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Post-merge alert check (Dependabot's scan of
🤖 Generated with Claude Code |
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
PolicyBench had 44 open Dependabot alerts when this PR opened on 2026-10-14, all on Python packages pinned in
uv.lock. This PR upgrades the locked versions of the 15 packages behind them to patched releases. bleach's GHSA-g75f should also close, but only because its advisory names a single version; the bug is not fixed (see below).Nine more alerts opened while this PR waited, #84–#92 (count on 2026-10-25: 53 open, the 44 plus these nine):
uv.lock. Bumping it here would be undone a commit later.So after merge the count should drop to 2: diskcache #1, which has no fixed release, and nbconvert #92, which #227 removes. After #227 it should be 1 (diskcache).
pyproject.tomlis unchanged.policyengine,policyengine-us,policyengine-ukandpolicyengine-coredo not move, so reference outputs, scores and every published number stay where they are.What changed in
uv.lockEach package moved with
uv lock --upgrade-package <name>.Two packages move only because upgraded packages require them: typing-extensions 4.15.0 → 4.16.0 (anyio 4.15.1 needs
typing_extensions>=4.16.0below Python 3.15) and yarl 1.22.0 → 1.25.1 (aiohttp 3.14.5 needsyarl>=1.25.1). Nothing else in the lock changes: no source, hash-set or marker changes outside these 17 entries.Three choices kept the bump narrow:
completion/responses(policybench/eval_no_tools.py); batch runs call the provider SDKs directly (policybench/batch_eval.py). A jump of 17 minor versions would change provider adapters under future runs; that belongs in its own PR. The advisory itself concerns the litellm proxy server, which PolicyBench does not run.llms/vertex_ai/gemini/,llms/gemini/,main.py,responses/,litellm_core_utils/prompt_templates/factory.pyandlitellm_core_utils/llm_response_utils/are byte-identical.utils.pyadds only a Bedrock Mantle responses route.llms/anthropic/:chat/transformation.pychanges only how sampling parameters are sent.temperatureother than 1,top_pandtop_know go through_apply_sampling_param, which refuses them, or drops them underdrop_params, for a model whose map entry setssupports_sampling_params: false(or, with no flag, whose name is a Fable or Opus 4.7/4.8 one). PolicyBench doesn't setLITELLM_LOCAL_MODEL_COST_MAP, so litellm loads its model map from GitHub at import. That map flags nine of PolicyBench's Claude ids: Fable 5 and 5.1, Opus 4.7, 4.8, 5 and 5.5, Sonnet 5 and 5.5, and Haiku 5.5. The bundled fallback map flags Fable 5, Opus 4.7 and Opus 4.8, and the name rule adds Fable 5.1.common_utils.pyadds those helpers and changes how_is_adaptive_thinking_modelreads the map (it now also strips Bedrock and Vertex prefixes). The adapter consults it only when a request setsreasoning_effort, and no PolicyBench request sends one to Claude (REASONING_EFFORT_OVERRIDESis empty).cost_calculation.pyis the web-search cost change below.experimental_pass_through/messages/interceptors/advisor.pygates caller-supplied advisor credentials, in the messages pass-through that PolicyBench doesn't use.claude-fable-5entry with the input, output and cache prices PolicyBench registers for it.policybench.config.MODELS(the board request with and without explanations) and forclaude-haiku-4-5andclaude-haiku-4-5-20251001attemperature=0.0, the request the reference explainer and five audit scripts send. All 24 bodies are identical between 1.88.1 and 1.88.6, with the remote map and with the bundled one. Neither the benchmark requests nor the tool pilot send sampling parameters, and Haiku 4.5 is unflagged in both maps, so itstemperature=0.0is still forwarded.server_tool_use.web_search_requestsfrom a dict as well as an object, and streamed chunks coerce it.scripts/tool_pilot_argon.py(from Ready the tool pilot's v2-pilot run: prompt fixes, a no-tools arm, web timeout #239) patches litellm's Gemini search-tool handling and refused to run on any litellm but 1.88.1, so this bump would have stopped its Gemini web arms (review r2). Since the adapter it patches is byte-identical in 1.88.6,GEMINI_SEARCH_PATCH_LITELLMnow names both releases.test_the_gemini_patch_keeps_search_beside_functionsasserts the installed litellm is one of them instead of skipping, and runs on 1.88.6: litellm dropsgoogleSearchwithout the patch and keeps it with the patch. A new test checks that the litellm inuv.lockis one of them, so a later lock bump fails CI rather than a live web arm. With the gate left at 1.88.1 only, both tests fail. Three other notes about litellm 1.88.1 behaviour (policybench/config.py,docs/runbook.mdand the pilot's deferred-result note) rest on code that is unchanged in 1.88.6, so they now say 1.88.x.uv lock --upgrade-package multidictpicks by default.No constraint blocked a patch. (
policyengine-core3.32.29 allowspytest<10, so pytest 9 needs no override.)The two alerts with no patched release
diskcache, GHSA-w8v5-vhqr-4h9v (#1): reachable, needs local write access, kept
How it gets in. diskcache now enters only through litellm's
cachingextra (litellm[caching]in our dependencies).policyengine6.1.2 no longer requires it (its METADATA has no diskcache line), and no other installed package imports it.The path is reachable.
policybench/cli.pycallspolicybench.cache.enable_cache()foreval-no-tools,eval-no-tools-repeatedandeval-no-tools-chunked, and forretry-failed-responses/repair-failed-rowswith--use-cache. That setslitellm.cache = Cache(type="disk", ...), which wrapsdiskcache.Cache(dir)(litellm/caching/disk_cache.py).diskcache/core.pyDisk.fetchunpickles any row stored asMODE_PICKLE. The mode is read from the cache's own sqlite file, so whoever can write the cache directory decides what gets unpickled.What an attacker needs. A way to write or replace what diskcache opens. The default directory is
.policybench_cache, resolved against the process's current directory (policybench/cache.py), so it sits wherever the command was launched, not necessarily inside the checkout. Its permissions are whatever existed or the operator's umask produced. They need not match the source tree's, so write access to the cache does not imply write access topolicybench/or.venv/. An attacker could get in through:cache.dband its journals, the hex subdirectories, the.valvalue files);These scenarios are inferred from the permission model; what is verified is that a row diskcache reads with
MODE_PICKLEreachespickle.load.Where it runs in CI. CI starts diskcache only in private temporary directories:
tests/test_supervisor.py::test_supervised_run_keeps_policyengine_out_of_workersstarts realpolicybench runworkers (each runspolicybench.cli eval-no-tools, which callsenable_cache()) withPOLICYBENCH_CACHE_DIRunder pytest'stmp_path. Provider calls there are faked, so CI exercises cache setup, not real provider responses stored and replayed.tests/test_cache.pypatchesCache. The Vercel build is Bun-only.The residual risk is running with a cache directory, or a parent of it, that another account can write or replace. Trusted cache contents and safe filesystem permissions are assumptions of every eval run until a guard enforces them.
Why not drop it. Removing diskcache means dropping the
cachingextra, andenable_cache()would then raise (litellm requireslitellm[caching]for disk caching). Eval runs would lose the response cache that keeps resumed runs from re-billing provider calls.Decision. Keep it and document it here. Refuse a LiteLLM disk-cache directory another account could write (GHSA-w8v5-vhqr-4h9v) #226 (in review) adds a guard to
enable_cache(). On Linux and macOS it refuses a cache when another account could write the cache or anything in it, or could replace the cache through a parent directory. A sticky parent such as/tmpis accepted, since only an entry's owner can rename it there. It judges this by ownership, permission bits, ACLs and the umask. It does not check Windows. That change touches the runner, so it is its own PR.bleach, GHSA-g75f-g53v-794x (#42): not reachable; 6.4.0 is still affected
bleach.linkify(..., parse_email=True)and lists the range as= 6.3.0with no fix. I diffedbleach/linkifier.pybetween 6.3.0 and 6.4.0.handle_email_addressesis identical, and the email regex changed only in formatting ("""...""".format(...)line wrapping). The advisory's PoC,bleach.linkify(("a." * 15000) + "a", parse_email=True), takes 6.40 s on 6.3.0 and 5.71 s on 6.4.0, against 0.034 s withparse_email=Falseon both. Dependabot will likely close Scoring rename: Approximate -> Continuous; document Within 1% in paper #42 after this bump because 6.4.0 is outside the stated range. Read that as a range artifact, not a fix. Take jupyter-book out of the paper render environment and uv.lock #227 then takes bleach out of the lock.jupyter-book→jupyter-server→nbconvert[css]in thedocsextra. In the installed environment, onlynbconvert/filters/strings.pyandnbconvert/preprocessors/sanitize.pyimport bleach, and both call onlybleach.clean. No package callsbleach.linkifyor usesLinker/LinkifyFilter(tornado'slinkifyis its own implementation). Quarto's Jupyter engine (share/jupyter/*.pyin Quarto 1.9.36) imports neither nbconvert nor bleach. PolicyBench code never imports bleach.docs/(docs/myst.yml,docs/requirements.txt). Take jupyter-book out of the paper render environment and uv.lock #227 takes it out of thedocsextra, leavingdocs/requirements.txtas its only declaration, and so takes jupyter-server, nbconvert, bleach, mistune and soupsieve out ofuv.lock: 18 of the 44 alerts open on 2026-10-14, plus nbconvert scenario_121 encodes disability in inputs PE does not map to USDA disability, zeroing SNAP #92. That is a docs-toolchain change, so it is its own PR.Verification
I ran everything at the lock commit (
0b4d91a1). After main's release #221 re-rendered the paper, I merged main in (headf7096c23) and re-ran the render comparison there. On 2026-10-25 I merged main again (ab82d1a5) for #238's certifi 2026.10.19. That merge took main's lockfile revision 5, written by uv 0.13.4, and main'spexpectentry; it changed no other package. Every run used a venv synced from this lock (uv sync --frozen --extra dev --extra docs; CPython 3.14.4 on macOS arm64). The logs are kept outside the repo.Full test suite:
OPENBLAS_NUM_THREADS=1 uv run --frozen pytest -qgives 4606 passed, 51 skipped, exit 0 (43m46s) at0b4d91a1. The local full run atf7096c23was cut off at 82% with no failures when its session restarted, and I did not repeat it. CI's suite (pytest -m "not slow", Python 3.12,uv sync --locked) passed atf7096c23and again atab82d1a5(run 40149555608), and is the check of record at the current head. The 14 warnings all come from packages this PR doesn't move: a divide warning in policyengine-us, policyengine-core'sdpath.utildeprecation, and pandas dtype notices. None are pytest 9 deprecations.Lint:
ruff check .passes;ruff format --check .reports 163 files already formatted.Lock:
uv lock --lockedpasses, and at the current head passes under both uv 0.13.4 (the version that wrote main's lock) and 0.13.5. A field-by-field comparison with main's lock shows changes only in the 17 entries listed above; every same-version entry and the lock metadata are identical.Paper: I re-rendered with
uv run --frozen python paper/render_paper.py(HTML and PDF, Quarto 1.9.36) and compared the output with the committedapp/public/paper/:web/index.htmldiffers on one line, a Quarto per-render cell id (<div id="00dcfe5d" class="cell" ...>→id="1bf227ac"). Its visible text is identical.policybench.pdfhas identical text (pdftotext -layout) and the same size (260,225 bytes). Its 66 differing bytes all sit in CreationDate/ModDate and the trailer/ID; with those masked, the files are byte-identical.At the merged head
f7096c23, compared against Add Gemini 4 Argon and Muse Spark 1.3 (release, 50 models) [WIP] #221's committed paper, the result is the same: 17 of 19 files byte-identical; one HTML line differs, again a cell id (7f7f2048→4f934fb6); visible text and PDF text are identical; and the PDF (260,940 bytes) is byte-identical once its dates and/IDare masked.So the paper doesn't move. I restored the committed render, left the
rendered_paper_artifactspins alone, and did not runfreeze_snapshot.py --rendered-only.App:
cd app && bun run prepare-data && bun test && bun run lint && bun run build: 173 pass / 0 fail, lint exit 0, build exit 0, with a clean worktree afterwards.CI on this head: lint, test, paper (hermetic HTML render), app and the Vercel preview all pass.
Reviews
f7096c23): APPROVE, with three body findings, answered atab82d1a5: the diskcache threat model, where CI runs diskcache, and a placeholder.ab82d1a5): REQUEST_CHANGES. It had one blocking finding: the tool pilot's Gemini patch refused litellm 1.88.6, and its test skipped the mismatch. It also had two body findings: 53 open alerts, not 54, and Refuse a LiteLLM disk-cache directory another account could write (GHSA-w8v5-vhqr-4h9v) #226's coverage stated too broadly. All three are answered atce3a83bc(above).ce3a83bc): APPROVE. It tracedcompletionthrough to the Gemini mapping and found both patched methods unchanged, and it found the alert arithmetic right. It had two non-blocking body findings, both answered above. First, the Anthropic adapter changes more than sampling parameters. Checking that also showed that the earlier list of no-sampling models was incomplete, so the bullet now gives the full list and the cross-version request-body comparison. Second, Refuse a LiteLLM disk-cache directory another account could write (GHSA-w8v5-vhqr-4h9v) #226 accepts sticky parents such as/tmp.Invariants
This PR changes locked third-party versions, the tool pilot's litellm gate and its tests, and two comments. The properties it must preserve, and how each was checked:
policyengine,policyengine-us,policyengine-ukandpolicyengine-corekeep the exact lock entries from main (lock diff).uv sync --lockedaccepts the lock (uv lock --lockedpasses; CI syncs with--locked).uv.lockis one the tool pilot's Gemini patch was checked against (test_the_locked_litellm_is_one_the_gemini_patch_was_checked_against).axiom: n/a: dependency security bump, no policy logic🤖 Generated with Claude Code