Project-relative cache-dir allows unrestricted pickle.load of attacker-controlled data

Open
#5,546 1 comment 0 reactions 0 assignees View on GitHub

Maintainers usually reply within 1 day

@rinopatrick is already working on this.

Since Nov 1, 2026.

  • #5547 by @dannyward630 — closed without merging
  • #5648 by @rinopatrick — open

Assessment

Difficulty
4/5
Estimated time
3-5 days
Newbie friendliness
22/100
Issue type
Bug
Clarity
Mostly clear
Activity status
Active
Tech stack
python
Domain
security

Research direction

Start with how a relative cache-dir from [tool.black] reaches the cache code: src/black/files.py finds the project pyproject.toml, src/black/init.py passes the directory on, and src/black/cache.py and src/blib2to3/pgen2/driver.py load pickles from it. A pull request (#5547) is already open against this, so check it and the thread before starting. Done means the chosen remediation is agreed with maintainers and the tests cover that project-relative caches are ignored or rejected while --no-cache and an explicit CLI cache dir still work.

Written by the indexing model from the issue text.

Description

S: needs discussion
Summary

Black resolves a relative cache-dir from [tool.black] inside the project checkout and then calls pickle.load() on files under that path. Both the metadata cache and the grammar caches are affected.

A repo author can drop crafted pickle files with the expected names. If someone later runs Black on that checkout with caching enabled, the process deserializes them.

This was also shared with Tidelift security, who confirmed the tracing. It was treated as a hardening issue rather than a CVE, which matches Black not claiming untrusted-checkout safety.

Affected code
  • src/black/files.py — finds the project pyproject.toml
  • src/black/__init__.py — accepts a relative cache-dir from project config and passes it into grammar init (unless --no-cache)
  • src/black/cache.py — metadata cache does unrestricted pickle.load
  • src/blib2to3/pgen2/driver.py + grammar.py — grammar cache uses a predictable filename + mtime check, then pickle.load
Preconditions
  • Project sets a relative cache-dir
  • Matching cache files exist for the Black version/mode/Python runtime
  • Someone runs Black on the checkout with caching enabled
  • --no-cache is not used

Cloning alone is not enough.

Impact

Arbitrary Python code can run as the Black process. Likelihood is limited because the victim has to actually run Black with caching on, and the attacker needs the right filenames.

--no-cache blocks both paths.

Suggested remediation
  • Don’t accept cache-dir from project config (or only accept absolute paths from a trusted source)
  • Switch both caches to a non-executable format, or check that the writer is the same authority as the Black process
  • Treat existing project-relative pickle caches as invalid
  • Keep an explicit CLI cache dir and --no-cache working

Static tracing only. Default cache is still per-user when there’s no project override.

Dominant language
Python
Stars
42.2k
Forks
3k
Avg merge
2d 14h
Merged PRs (30d)
98

Getting set up

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from psf/black

All issues in psf/black

Similar issues

More Python issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.