Hint 1: Pickle reconstructs objects; JSON does not
pickle.loads can call arbitrary code through __reduce__ as it rebuilds objects. Parse the token with json.loads and remove every path that unpickles.
Hint 1 unlocks after your first failed run.
Hint 2: Validate, and do not trust a signature
After parsing, enforce a strict schema: exactly user and role, both strings, an allowed role, no extra keys. A valid HMAC would authenticate the sender, not make a pickle safe to load.
Hint 2 unlocks after 2 failed runs.
Reveal the reference patch and explanation
import json
ALLOWED_ROLES = {"member", "admin", "viewer"}
def load_token(token):
data = json.loads(token) # never pickle; a non-JSON token raises here
if not isinstance(data, dict):
raise ValueError("token must be a JSON object")
if set(data) != {"user", "role"}:
raise ValueError("unexpected fields in token")
if not isinstance(data["user"], str) or not isinstance(data["role"], str):
raise ValueError("user and role must be strings")
if data["role"] not in ALLOWED_ROLES:
raise ValueError("unknown role")
return {"user": data["user"], "role": data["role"]}A JSON token such as {"user":"ana","role":"member"} loads to its two fields. A non-JSON token — a base64 pickle, or a pickle with a valid HMAC appended — raises at json.loads and is rejected, so nothing is unpickled and no __reduce__ runs. The schema rejects an unknown role, a non-string field and an extra field like is_admin, so a client cannot change its own role or smuggle in state. Signing does not help here: the signed-token case carries a correct HMAC, yet a loader that verifies the signature and then unpickles still executes the payload, because the signature attests to the sender, not to the safety of the content; only not unpickling untrusted bytes is safe.
Compare it with your own fix, then rerun the tests. The editor is not changed when you open this explanation.
The reference patch unlocks after you run code you have edited.
Inspect the actual test harness
The browser and the lab download run the same cases, including cases that catch common wrong fixes, such as comparing with one fixture value instead of the real rule. The download includes the complete test adapter and cryptographic fixtures.
Download the grader, python/cases.mjsHow the browser runs your Python
Your code runs in CPython 3.14.2 compiled to WebAssembly (Pyodide 314.0.7), in a Web Worker of its own. The test cases and the pass or fail verdict run in a second worker, which only sends your functions their inputs and reads back the values they return, so changing built-ins or the harness inside Python cannot change the result. All the cases together have a 8-second limit.
The first run downloads about 6.3 MB from this site, which your browser then keeps; later runs start in a second or two. Other pages never load it.
Available: the Python standard library, including sqlite3, ipaddress, pathlib, os.path and urllib.parse, and print(), whose output appears with your results.
Not available: network access, subprocesses and threads, and other packages from PyPI. The lab download runs the same cases with your local python3 (3.12 or newer).
This lab models one token. The same rule applies to any format that can reconstruct objects or call code — Python pickle, PyYAML’s unsafe loader, Java/.NET native serialization — for which the fix is a data-only format (JSON, or yaml.safe_load) plus schema validation. If you must accept a self-produced blob, an HMAC detects tampering but still must not gate an unsafe deserializer; keep the format data-only regardless.
Your next task: Where does your system load a session, cache entry, cookie or message that an attacker can influence, and is any of it deserialized with a format that can construct objects rather than plain data?
Python pickle: the module documentation’s security warning
Related guide: Parse bounded JSON and XML into an explicit application schema