Modern Deserialization V33 · v1.6
Request bodies that look like serialized-object payloads for runtimes where deserialization equals code execution. Detection-only, because a forgiving parser will still deserialize the remainder if you strip the head bytes. The caller chooses how to refuse.
What it catches
- Python pickle: head bytes
\x80+ protocol byte0x02-0x05at position 0 - Ruby Marshal: head bytes
\x04\x08at position 0 - .NET BinaryFormatter: head bytes
\x00\x01\x00\x00\x00at position 0 - Java FastJSON: embedded
"@type":"<class>"autotype marker anywhere - PHP
unserialize:O:<len>:"<ClassName>":<count>:{shape anywhere
Head-byte markers take precedence over embedded markers (byte-precise wins over substring).
Sample
import { detectDeserialization, isSerializedPayload } from '@arcis/node';
const runtime = detectDeserialization(body_string);
// "python_pickle" | "java_fastjson" | "php_unserialize" | "ruby_marshal" | "dotnet_binary_formatter" | null
if (runtime) {
res.status(400).json({ error: 'serialized payload not allowed', runtime });
return;
}
// Or simpler: just check the boolean
if (isSerializedPayload(body_string)) {
res.status(400).send();
}
Configuration
No options. The function is a pure marker check. Run it on every endpoint that accepts a string body before any deserialization library touches the content.
When you actually do want serialization
If your endpoint legitimately accepts pickle (a Python-only RPC channel, for instance), do not call this detector on that route. The detector is a per-route opt-in, not a global middleware.
References
Refuse, do not strip. Detection only. A forgiving deserializer might still parse the remainder of a payload after the magic bytes are removed. The caller has to refuse the request; sanitization is not safe for this class.