Load a session token without pickle

A session loader parses JSON but falls back to pickle for “legacy” tokens, so a crafted pickle runs code when it is loaded. Repair load_token to parse JSON only and validate it against a fixed schema, and leave no path that unpickles untrusted bytes.

Requires Node.js 22 or newer, which runs the tests, and Python 3.12 or newer, which runs your code (the command python3, or the program named in the PYTHON environment variable). No package installation or server needed. To work in a container instead, open the folder with VS Code Dev Containers or GitHub Codespaces: .devcontainer/devcontainer.json uses the Dev Containers Node.js 24 image, pinned by digest.
1. Unzip all files into one folder.
2. In that folder, run: node --test exercise.test.mjs
3. Edit session.py, then run the same command again.

The starter deliberately fails some tests. A successful repair passes every case, including cases that catch common wrong fixes. The browser runs this same grader with your code in Pyodide (CPython compiled to WebAssembly); locally it runs your code with python3. If you completed the optional first stage in the browser, Download my work adds your input as stage1-input.json and the tests include it. Only run code you wrote or reviewed.

1. Reproduce code execution
Run the starter. A base64-encoded pickle is not JSON, so the loader falls back to pickle.loads, and the payload’s __reduce__ runs code as the token is loaded.

2. Parse JSON only
Remove the pickle fallback. Parse the token with json.loads and let a non-JSON token raise. Data formats like JSON describe values; they do not reconstruct arbitrary objects, so loading them runs no code.

3. Validate against a schema
Dropping pickle is necessary but not sufficient: check that the result is an object with exactly user and role, both strings, and an allowed role. Reject extra fields so a client cannot smuggle in state such as is_admin.

4. Understand why signing is not enough
An HMAC over the bytes proves who sent them, not that they are safe to deserialize. If the key leaks, or the server signs data an attacker influenced, a verify-then-unpickle loader still runs the code. The signed-token case shows this.

SUPPLIED CONTRACT
load_token(token) receives a token string and returns the session as {"user": str, "role": str}. Parse it with json.loads only — never pickle. Reject anything that is not a JSON object with exactly the keys "user" and "role", both strings, and a role in the allowed set {"member", "admin", "viewer"}; raise ValueError for anything else, including a JSON value that is not an object. A non-JSON token (including a base64 pickle or a signed pickle) must be rejected, not decoded.

REFERENCE
reference.py contains an explained solution: 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.

SCOPE
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.

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?

SOURCE
https://docs.python.org/3/library/pickle.html#module-pickle
