Skip to content

Add SafePickleEncoder with restricted unpickler; deprecate PickleEncoder#849

Open
eddieran wants to merge 1 commit into
Bogdanp:masterfrom
eddieran:fix/safe-pickle-encoder
Open

Add SafePickleEncoder with restricted unpickler; deprecate PickleEncoder#849
eddieran wants to merge 1 commit into
Bogdanp:masterfrom
eddieran:fix/safe-pickle-encoder

Conversation

@eddieran
Copy link
Copy Markdown

Summary

Fixes #848PickleEncoder calls pickle.loads() without restrictions, enabling remote code execution via crafted pickle payloads when an attacker has broker access.

This PR:

  • Adds SafePickleEncoder — a drop-in replacement that uses a RestrictedUnpickler subclass, limiting deserialization to a fixed allow-list of safe built-in types (int, str, dict, list, datetime, UUID, Decimal, etc.)
  • Supports extension via extra_allowed parameter for projects that need custom types
  • Deprecates PickleEncoderdecode() now emits a DeprecationWarning recommending SafePickleEncoder, with removal planned for v3.0.0
  • Exports SafePickleEncoder from the top-level dramatiq package

Migration

# Before
import dramatiq
dramatiq.set_encoder(dramatiq.PickleEncoder())

# After
import dramatiq
dramatiq.set_encoder(dramatiq.SafePickleEncoder())

# If you need custom types:
dramatiq.set_encoder(dramatiq.SafePickleEncoder(
    extra_allowed={"mymodule.MyClass"}
))

Test plan

  • Roundtrip encode/decode of basic Python types (int, float, str, bytes, list, dict, tuple, set, None, bool)
  • Malicious pickle payloads (e.g. os.system) are blocked with DecodeError
  • Arbitrary classes not in the allow-list are rejected
  • extra_allowed parameter correctly extends the allow-list
  • PickleEncoder.decode() emits DeprecationWarning
  • Integration test with stub broker and worker
  • SafePickleEncoder is importable from dramatiq top-level

PickleEncoder calls pickle.loads() without restrictions, enabling remote
code execution when a broker is compromised (issue Bogdanp#848).

This commit:
- Adds SafePickleEncoder that uses a RestrictedUnpickler subclass,
  limiting deserialization to a fixed allow-list of safe built-in types
  (int, str, dict, list, datetime, UUID, Decimal, etc.).
- Supports extension via extra_allowed parameter for custom types.
- Adds a DeprecationWarning to PickleEncoder.decode() recommending
  SafePickleEncoder, with removal planned for dramatiq v3.0.0.
- Exports SafePickleEncoder from the dramatiq top-level package.
- Adds tests covering roundtrip encoding, RCE payload blocking,
  deprecation warning, extra_allowed, and stub broker integration.

Closes Bogdanp#848
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.

PickleEncoder: unrestricted pickle.loads enables RCE via broker message injection

1 participant