#137 moved the sandbox→host return value decode behind a restricted unpickler. One adjacent decode surface remains: side-channel payloads (/trace, /log, plugin namespaces) are msgpack, and the shared codec registers two extension types — _EXT_NDARRAY and _EXT_PYDANTIC — that the host decodes on every unpack of a sandbox-emitted event.
Two robustness gaps under #116's threat model (sandbox runs less-trusted workloads):
_decode_ext for ndarray runs np.frombuffer + reshape on an unvalidated header (dtype string, shape) — malformed input raises mid-decode, and decoding forces a numpy import on the host even for consumers that never use arrays.
- The pydantic ext decode recursively re-enters
msgpack.unpackb, so deeply nested ext payloads can drive host-side recursion to a RecursionError.
PR #122 addressed this by deleting the ext types entirely (plain msgpack), which was rejected as lossy — numpy/pydantic round-trip on side channels is a real feature. The follow-up is validated decode, not removal: bound recursion depth, validate the ndarray header (dtype allowlist — notably no object dtype — and shape/size sanity against the buffer), and fail closed with a typed error instead of an arbitrary exception from inside numpy.
Related: #116, #137, #122.
🤖 Generated with Claude Code
#137 moved the sandbox→host return value decode behind a restricted unpickler. One adjacent decode surface remains: side-channel payloads (
/trace,/log, plugin namespaces) are msgpack, and the shared codec registers two extension types —_EXT_NDARRAYand_EXT_PYDANTIC— that the host decodes on every unpack of a sandbox-emitted event.Two robustness gaps under #116's threat model (sandbox runs less-trusted workloads):
_decode_extfor ndarray runsnp.frombuffer+reshapeon an unvalidated header (dtype string, shape) — malformed input raises mid-decode, and decoding forces a numpy import on the host even for consumers that never use arrays.msgpack.unpackb, so deeply nested ext payloads can drive host-side recursion to aRecursionError.PR #122 addressed this by deleting the ext types entirely (plain msgpack), which was rejected as lossy — numpy/pydantic round-trip on side channels is a real feature. The follow-up is validated decode, not removal: bound recursion depth, validate the ndarray header (dtype allowlist — notably no
objectdtype — and shape/size sanity against the buffer), and fail closed with a typed error instead of an arbitrary exception from inside numpy.Related: #116, #137, #122.
🤖 Generated with Claude Code