Skip to content

chore(storage): restrict auth token cache deserialization - #73372

Merged
Piccirello merged 1 commit into
masterfrom
pickle-harden-auth-cache
Jul 24, 2026
Merged

chore(storage): restrict auth token cache deserialization#73372
Piccirello merged 1 commit into
masterfrom
pickle-harden-auth-cache

Conversation

@Piccirello

Copy link
Copy Markdown
Member

Problem

The periodic auth-token cache verifier deserialized Redis values with a bare pickle.loads. Pickle runs whatever the byte stream tells it to during unpickling, so any path that could write to those cache keys becomes a code-execution vector. This is an internal cache, so it's defense-in-depth rather than an open door, but a bare pickle.loads is more trust than this path needs: the cache only ever holds a JSON string or a plain dict, so no class ever needs to be reconstructed here.

Changes

  • Swap the bare pickle.loads in the verifier for a restricted unpickler whose find_class always raises. JSON-shaped values (str, dict, list, number, bool, None) deserialize through pickle opcodes that never call find_class, so this loads exactly today's data and refuses to construct any class or callable. The three production formats it reads (pickled JSON string, pickled dict, zstd + pickle) are unchanged. It mirrors the existing _CatalogUnpickler in posthog/hogql/database/database.py.
  • Add a short "Deserialization (pickle)" section to .agents/security.md: don't add new pickle, use JSON / pydantic instead, and if it's genuinely unavoidable (an existing cross-language format contract), keep it to internal data behind a restricted unpickler with a justified nosemgrep. It also notes the implicit pickle path through Django's default cache backend.

How did you test this code?

I (Claude) wrote the test first. TestAuthTokenCacheDeserializationHardening feeds the deserializer a __reduce__ payload wired to a marker function. Against the old code the marker fired (the payload executed) while the function still returned None — the exact problem. After the change the marker never runs and the payload is rejected. I also confirmed all three production cache formats still deserialize. Ran that SimpleTestCase locally: passed. The DB-backed verifier tests need Postgres/Redis and run in CI.

Automatic notifications

  • Publish to changelog?
  • Alert Sales and Marketing teams?

🤖 Agent context

Autonomy: Human-driven (agent-assisted)

Authored by Claude (Opus 4.8), directed by the assignee. Skills invoked: superpowers:brainstorming, superpowers:test-driven-development, writing-tests.

This came out of a broader look at pickle usage in the repo. The AI conversation stream, which could actually move off pickle, is being migrated to JSON in a separate three-PR stack. This cache can't move unilaterally because its format is a cross-language contract, so hardening the read was the right call here instead. The deny-all unpickler is stronger than an allowlist for this site because it needs zero classes.

The periodic auth-token cache verifier unpickled Redis values with a bare
pickle.loads. The cache only ever holds a JSON string or a plain dict, so
this switches to a restricted unpickler that constructs no classes, plus a
short repo policy on pickle usage.
@Piccirello Piccirello self-assigned this Jul 23, 2026
@Piccirello
Piccirello marked this pull request as ready for review July 23, 2026 21:51
@pr-assigner-resolver-posthog
pr-assigner-resolver-posthog Bot requested a review from a team July 23, 2026 21:52
@greptile-apps

greptile-apps Bot commented Jul 23, 2026

Copy link
Copy Markdown
Contributor
Prompt To Fix All With AI
Fix the following 1 code review issue. Work through them one at a time, proposing concise fixes.

---

### Issue 1 of 1
posthog/storage/test/test_auth_token_cache_verifier.py:913
**Unsuppressed pickle blocks security CI**

When the security job scans this test, the new `pickle.dumps` call matches the blocking `avoid-pickle` rule without a `nosemgrep` suppression, causing the CI job to fail; the suppression below applies only to the separate `pickle.loads` call.

Reviews (1): Last reviewed commit: "chore(storage): restrict auth token cach..." | Re-trigger Greptile

Comment thread posthog/storage/test/test_auth_token_cache_verifier.py
@github-actions

Copy link
Copy Markdown
Contributor

🤖 CI report

Playwright — all passed

All tests passed.

View test results →

@Piccirello
Piccirello merged commit 1920144 into master Jul 24, 2026
314 of 336 checks passed
@Piccirello
Piccirello deleted the pickle-harden-auth-cache branch July 24, 2026 15:44
@deployment-status-posthog

deployment-status-posthog Bot commented Jul 24, 2026

Copy link
Copy Markdown

Deploy status

Environment Status Deployed At Workflow
dev ✅ Deployed 2026-07-24 16:24 UTC Run
prod-us ✅ Deployed 2026-07-24 16:40 UTC Run
prod-eu ✅ Deployed 2026-07-24 16:44 UTC Run

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants