Inferensys

Prompt

Tool Input Deserialization Safety Prompt

A practical prompt playbook for security engineers preventing deserialization attacks from agent-supplied payloads in production AI workflows.
Developer demonstrating multi-agent tool use, agent tool selection interface on laptop, casual tech demo moment.
PROMPT PLAYBOOK

When to Use This Prompt

Define the job, reader, and constraints for the Tool Input Deserialization Safety Prompt.

This prompt is for security engineers and platform developers who are building the gateway between an AI agent and any tool that accepts serialized object payloads—such as Python pickles, YAML configs, or JSON with embedded type hints. The job-to-be-done is preventing deserialization attacks when an agent supplies or modifies a serialized payload before it reaches a tool's execution environment. You need this when your agent can construct arguments that will be deserialized by a downstream service, worker, or library, and you cannot trust the model to avoid dangerous type constructions on its own. The ideal user is someone who understands that prompt injection is not the only attack surface: a model coerced into producing a malicious serialized object can trigger remote code execution, denial-of-service, or data exfiltration at the deserialization layer, entirely outside the prompt's text boundary.

Do not use this prompt as a replacement for sandboxing, capability-based security, or hardened deserialization libraries at the infrastructure level. It is a defense-in-depth layer, not a primary security boundary. The prompt is appropriate when you control the pre-invocation validation pipeline and can insert a structured safety check between the model's output and the tool's deserializer. It is not appropriate when the agent directly writes files to disk, calls an unvalidated eval() equivalent, or operates in an environment where the deserialization target is outside your observability perimeter. You also should not rely on this prompt alone if the downstream tool uses a deserialization format that is inherently unsafe by design and cannot be made safe through structural checks—in those cases, the correct answer is replacing the format or wrapping the tool in a hardened sandbox, not prompting your way around it.

Before using this prompt, you must have defined a concrete allowlist of safe types, a maximum object depth, a maximum payload size, and a set of known-dangerous patterns for your specific deserialization format. The prompt template expects these as input variables. If you cannot enumerate what 'safe' means for your tool's serialization format, stop and define that contract first. Once you have that contract, this prompt becomes a reusable validation harness that you can wire into your agent's tool-calling middleware. The next section provides the copy-ready template, and the implementation harness section explains how to integrate it with retries, logging, and human escalation when the safety check rejects a payload.

PRACTICAL GUARDRAILS

Use Case Fit

Where the Tool Input Deserialization Safety Prompt works and where it introduces unacceptable risk or complexity.

01

Good Fit: Agent-to-Internal-API Gateways

Use when: An AI agent constructs serialized payloads (JSON, XML, Pickle) that are deserialized by an internal service. Guardrail: Deploy this prompt as a pre-deserialization safety layer that rejects dangerous types before the payload reaches the application runtime.

02

Good Fit: Multi-Tenant Platforms with Untrusted Agent Input

Use when: You run a platform where multiple tenants or users configure agents that can submit arbitrary serialized objects to shared tool infrastructure. Guardrail: Enforce a strict allowlist of deserializable types and a maximum object depth at the gateway, validated by this prompt's output.

03

Bad Fit: Performance-Critical, High-Throughput Pipelines

Avoid when: Every millisecond of latency matters and you process millions of small, trusted payloads per second. Guardrail: Use a native, compiled deserialization firewall (e.g., a Rust/WASM filter) instead. Reserve this prompt for asynchronous, lower-volume, or high-risk integration points.

04

Bad Fit: Already-Sandboxed or Strongly-Typed Runtimes

Avoid when: Your tool runtime already enforces memory-safe deserialization with a strict schema and no support for polymorphic types or custom gadget chains. Guardrail: Rely on the runtime's native guarantees. Adding an LLM-based check introduces a new failure mode without reducing material risk.

05

Required Input: A Complete Type Allowlist

Risk: Without an explicit allowlist, the prompt cannot reliably distinguish safe from dangerous types, leading to bypasses. Guardrail: Always provide a concrete, version-controlled list of permitted classes, modules, or type identifiers as part of the prompt's [CONSTRAINTS] input.

06

Operational Risk: Prompt Drift on Deserialization Logic

Risk: A model update may change how it interprets type safety rules, silently allowing a previously blocked gadget chain. Guardrail: Pin your model version and run a regression suite of known dangerous payloads (billion laughs, gadget chains) as a pre-deployment gate for any prompt or model change.

PROMPT PLAYBOOK

Copy-Ready Prompt Template

A reusable prompt template for safely deserializing agent-supplied payloads with strict type, depth, and size constraints.

This prompt template is designed to be placed directly in front of a deserialization step where an agent or upstream system provides a serialized payload. It instructs the model to act as a secure deserialization gate, producing a safe object graph while rejecting dangerous types, circular references, and resource-exhaustion attacks. The template uses square-bracket placeholders for all dynamic inputs, making it easy to integrate into a Python, Node.js, or Go application harness.

text
You are a secure deserialization guard. Your only job is to safely deserialize the provided payload into a strict in-memory representation and report what you did.

## Input Payload
[PAYLOAD]

## Deserialization Format
[FORMAT]

## Allowed Types
[ALLOWED_TYPES]

## Constraints
- Maximum object depth: [MAX_DEPTH]
- Maximum total nodes: [MAX_NODES]
- Maximum string length: [MAX_STRING_LENGTH]
- Maximum array length: [MAX_ARRAY_LENGTH]
- Reject all non-whitelisted types immediately.
- Detect and reject circular references.
- Detect and reject exponential expansion attacks (e.g., billion laughs, zip bombs in encoded form).
- Do not execute, evaluate, or instantiate any code, classes, or constructors referenced in the payload.

## Output Schema
Return a strict JSON object with the following structure:
{
  "status": "success" | "rejected",
  "safe_object": <deserialized object graph, only if status is success>,
  "rejection_reason": "<detailed reason if status is rejected, otherwise null>",
  "warnings": ["<list of non-fatal concerns, e.g., depth near limit>"]
}

## Examples of Rejection
- Type not in allowed list: reject with reason "Disallowed type: [type_name]"
- Circular reference detected: reject with reason "Circular reference at path: [path]"
- Depth exceeds [MAX_DEPTH]: reject with reason "Max depth exceeded at path: [path]"
- Node count exceeds [MAX_NODES]: reject with reason "Max node count exceeded"

## Final Instruction
Process the payload exactly once. Do not retry on rejection. Do not attempt to fix or coerce the payload. If you are uncertain about safety, reject.

To adapt this template, replace each square-bracket placeholder with concrete values from your application context. [PAYLOAD] should be the raw serialized string. [FORMAT] should be the explicit format name (e.g., JSON, YAML, XML, Python pickle). [ALLOWED_TYPES] must be an exhaustive, minimal list of permitted types (e.g., str, int, float, bool, list, dict, None). Set numeric limits based on your application's memory and performance budget. If your system uses a custom deserialization library, mention its name and version in the [FORMAT] field to help the model reason about known vulnerabilities. Always run the model's output through a structural validator before trusting the safe_object field in your application code.

IMPLEMENTATION TABLE

Prompt Variables

Required inputs for the Tool Input Deserialization Safety Prompt. Each placeholder must be populated before the prompt is assembled and sent to the model. Validation notes describe how to verify the input is safe and well-formed before it reaches the prompt.

PlaceholderPurposeExampleValidation Notes

[RAW_PAYLOAD]

The untrusted serialized data string received from the agent tool call

{"$type":"System.IO.FileInfo","path":"/etc/passwd"}

Must be a non-empty string. Check for known dangerous type indicators before passing to the prompt. Reject if payload exceeds configured max depth or size limit.

[ALLOWED_TYPES]

Whitelist of fully qualified type names permitted for deserialization

["System.String", "System.Int32", "System.Collections.Generic.List`1[[System.String]]"]

Must be a valid JSON array of strings. Each entry must be a fully qualified type name. Reject if empty or contains wildcards. Validate against a canonical type registry.

[MAX_DEPTH]

Maximum allowed nesting depth for the deserialized object graph

5

Must be a positive integer between 1 and 20. Lower values are safer. Validate as integer and enforce upper bound. Reject if null or non-numeric.

[MAX_OBJECT_COUNT]

Maximum total number of objects allowed in the deserialized graph

1000

Must be a positive integer. Used to prevent billion-laughs and expansion attacks. Validate as integer. Reject if null, zero, or exceeds system resource limits.

[FORBIDDEN_PATTERNS]

Regex patterns or type name substrings that trigger immediate rejection

["System.Runtime", "System.Reflection", "System.Diagnostics.Process"]

Must be a valid JSON array of strings. Each entry is a case-insensitive substring match against type names. Validate array structure. Reject if empty when deserialization is enabled.

[CIRCULAR_REFERENCE_POLICY]

Policy for handling circular references: reject, prune, or replace-with-null

"reject"

Must be one of: reject, prune, replace-with-null. reject is safest for untrusted input. Validate against allowed enum values. Reject if missing or unrecognized.

[OUTPUT_SCHEMA]

Expected structure for the deserialization safety report

{"safe_object": {}, "rejected_types": [], "depth_exceeded": false, "circular_refs_detected": false, "anomalies": []}

Must be a valid JSON Schema or example structure. Validate that the schema includes fields for rejected items and safety flags. Reject if schema is missing required safety-report fields.

PROMPT PLAYBOOK

Implementation Harness Notes

How to wire the deserialization safety prompt into an agent-to-tool gateway with validation, logging, and circuit-breaking.

The Tool Input Deserialization Safety Prompt is not a standalone utility; it is a pre-invocation security gate that must sit between the agent's tool call and the actual deserialization library. In a production harness, you will typically deploy this prompt inside a thin API middleware or a sidecar that intercepts tool invocation requests. The agent emits a serialized payload (JSON, YAML, XML, or a custom format), and before that payload reaches json.loads(), pickle.loads(), or yaml.safe_load(), the prompt inspects the raw string and the intended target type. The prompt's job is to produce a structured safety verdict: a deserialized object for safe payloads, or a rejection block listing dangerous types, depth violations, circular references, and type-confusion indicators. This verdict must be machine-readable so the harness can act on it without human parsing.

Implement the harness as a deterministic wrapper around the LLM call. First, apply static pre-checks that do not require the model: reject payloads exceeding a maximum byte size (e.g., 1 MB), scan for known dangerous magic bytes if the format supports them, and enforce a maximum nesting depth via a fast streaming parser before the prompt ever runs. If the payload passes static checks, construct the prompt with the raw payload in [INPUT], the expected schema or type in [TARGET_TYPE], and a strict [OUTPUT_SCHEMA] that demands a JSON object with fields status ("safe" or "rejected"), deserialized (the safe object or null), and rejection_reasons (an array of strings). After the model responds, validate the output JSON strictly: confirm status is an allowed enum value, rejection_reasons is present when status is "rejected", and deserialized conforms to [TARGET_TYPE] when status is "safe". If the model output fails structural validation, retry once with the validation error injected into [CONSTRAINTS]. If it fails again, escalate to a human review queue and block the tool invocation. Log every verdict—safe or rejected—with the payload hash, model response, and harness decision for audit and regression testing.

For high-throughput systems, consider model choice and caching carefully. A fast, instruction-tuned model (e.g., GPT-4o-mini, Claude Haiku) is usually sufficient for deserialization safety checks because the task is pattern recognition, not deep reasoning. Cache verdicts by payload hash to avoid redundant LLM calls for repeated payloads. Instrument the harness with a circuit breaker: if the model returns malformed responses for more than 5% of requests in a rolling window, or if latency exceeds a threshold, fail closed and route all tool invocations to a manual approval queue. Never allow a deserialization operation to proceed when the safety harness is degraded. Finally, maintain a regression suite of known attack payloads—gadget chains, billion-laughs XML bombs, circular references, and type-confusion samples—and run them through the harness on every prompt or model change to catch regressions before they reach production.

IMPLEMENTATION TABLE

Expected Output Contract

Fields, format, and validation rules for the safe deserialization report produced by the prompt. Use this contract to validate the model's output before passing the deserialized object to downstream systems.

Field or ElementType or FormatRequiredValidation Rule

safe_object

object

Must be a valid JSON object. Parse check required. Must not contain any keys listed in rejected_dangerous_types. Depth must not exceed [MAX_DEPTH].

rejected_dangerous_types

array of strings

Each element must be a string matching a known dangerous type (e.g., 'datetime', 'subprocess.Popen'). Array must be empty if no dangerous types found. Schema check: array items must be strings.

circular_references_detected

array of strings

Each element must be a JSONPath string (e.g., '$.a.b') identifying a detected cycle. Array must be empty if no cycles found. Null not allowed; use empty array.

depth_limit_exceeded

boolean

Must be true if the input payload exceeded [MAX_DEPTH] during traversal. If true, safe_object must be truncated at the limit. No null values allowed.

type_confusion_warnings

array of objects

Each object must contain 'field' (string), 'expected_type' (string), and 'actual_type' (string). Array must be empty if no mismatches. Schema check: required keys present in each element.

transformation_log

array of strings

Each entry must describe a safety transformation applied (e.g., 'Replaced class key at $.config'). Array must be empty if no transformations applied. Human-readable strings required.

deserialization_success

boolean

Must be true only if safe_object was produced without errors and rejected_dangerous_types is empty. If false, safe_object must be an empty object {}. No null values allowed.

error_message

string or null

Must be null if deserialization_success is true. If deserialization_success is false, must contain a non-empty string describing the primary failure reason. Null allowed only on success.

PRACTICAL GUARDRAILS

Common Failure Modes

Deserialization attacks are a primary vector for remote code execution in agent-tool pipelines. These cards cover the most common failure modes when agents supply serialized payloads and how to prevent them before the deserializer runs.

01

Unsafe Type Deserialization

What to watch: Agent-supplied payloads that specify dangerous types (e.g., java.util.Runtime, Python pickle with __reduce__, .NET ObjectDataProvider) can trigger code execution during deserialization. Attackers exploit type hints in JSON, YAML, or binary formats to instantiate arbitrary classes. Guardrail: Maintain an explicit allowlist of permitted types. Reject any payload that references a type outside the allowlist before the deserializer processes it. Never use blocklist-based type filtering.

02

Billion-Laughs Entity Expansion

What to watch: XML and YAML payloads with recursive entity definitions that expand exponentially during parsing, consuming memory and CPU until the process crashes. A small payload can expand to gigabytes of data. Guardrail: Enforce strict entity expansion limits (e.g., maximum 1000 expansions, depth limit of 5). Disable DTD processing and external entity resolution entirely unless explicitly required and validated.

03

Circular Reference Stack Overflow

What to watch: JSON or YAML payloads with circular references ($ref loops) that cause infinite recursion during deserialization, leading to stack overflow and process termination. Attackers embed self-referencing objects that naive resolvers cannot detect. Guardrail: Implement reference tracking with a visited-node set during deserialization. Reject any payload where a reference points to an already-traversed node. Enforce a maximum reference depth (e.g., 20 levels).

04

Gadget Chain Injection

What to watch: Payloads that chain together otherwise-safe classes already on the classpath to achieve code execution. Gadget chains exploit property setters, constructors, and finalizers in libraries like Commons Collections or Spring. Guardrail: Use a deserialization library that supports type allowlisting at the field level, not just the root object. Run gadget chain detection tools against your classpath during CI. Reject payloads with unexpected nested object graphs.

05

Type Confusion and Polymorphic Attacks

What to watch: Payloads that declare a safe base type but supply a malicious subclass, bypassing type checks that only inspect the declared type. Polymorphic deserialization without explicit subtype control is the most common bypass vector. Guardrail: Require explicit subtype registration for all polymorphic fields. Reject any payload where the actual type does not match a pre-registered subtype mapping. Never use @JsonTypeInfo without @JsonSubTypes in Jackson.

06

Depth and Size Exhaustion

What to watch: Deeply nested payloads (1000+ levels) or excessively large payloads that exhaust stack space or memory before type checks complete. Attackers bypass type validation by triggering resource exhaustion at the parser level. Guardrail: Enforce maximum nesting depth (e.g., 20 levels) and maximum payload size (e.g., 1MB) at the input boundary before any parsing begins. Reject oversized or over-nested payloads with a clear error before deserialization.

IMPLEMENTATION TABLE

Evaluation Rubric

Use this rubric to test the Tool Input Deserialization Safety Prompt before deployment. Each criterion targets a specific failure mode in deserialization safety, including gadget chains, resource exhaustion, and type confusion.

CriterionPass StandardFailure SignalTest Method

Standard Object Deserialization

Safe JSON object deserializes correctly with all fields preserved and no modifications

Missing fields, altered values, or rejection of safe payload

Provide a simple {"key": "value"} payload and verify output matches input exactly

Dangerous Type Rejection

Payloads containing dangerous types (e.g., 'java.lang.Runtime', 'subprocess.Popen') are rejected with a clear reason

Dangerous type accepted and deserialized, or rejection without explanation

Inject payloads with known gadget chain classes and verify rejection with type name in rejection reason

Billion-Laughs Attack Prevention

XML entity expansion attacks are detected and rejected before resource exhaustion occurs

System hangs, timeout, or memory spike during deserialization

Submit a billion-laughs XML payload and verify rejection within [MAX_DESERIALIZATION_MS] milliseconds

Circular Reference Detection

Objects with circular references are detected and either rejected or safely handled with depth-limited output

Stack overflow, infinite loop, or unterminated processing

Provide JSON with circular references (e.g., object referencing itself) and verify clean rejection or bounded output

Depth Limit Enforcement

Nested objects exceeding [MAX_NESTING_DEPTH] are rejected with a depth violation message

Deeply nested payload accepted without truncation or rejection

Submit a payload with nesting depth of [MAX_NESTING_DEPTH] + 1 and verify rejection with depth value in message

Type Confusion Detection

Payloads where declared type does not match actual structure are flagged and rejected

Type confusion accepted and processed as declared type

Submit {"type": "SafeType", "data": "malicious_payload"} where actual structure mismatches and verify rejection

Size Limit Enforcement

Payloads exceeding [MAX_PAYLOAD_SIZE_BYTES] are rejected before full parsing

Large payload processed without size check, causing memory pressure

Submit payload of [MAX_PAYLOAD_SIZE_BYTES] + 1KB and verify rejection with size information in message

Safe Output Contract

Deserialized output contains only allowed primitive types, arrays, and flat objects with no executable constructs

Output contains function references, class instances, or code objects

Deserialize a complex but safe payload and verify output contains only JSON-serializable primitives, arrays, and objects

ADAPTATION OPTIONS

Adapt This Prompt

How to adapt

Add a two-stage pipeline: first validate the raw payload structure, then deserialize with strict type whitelisting. Include depth limits (default 10), circular reference detection, and size caps (1MB). Log every rejection with the rejected type, depth, and source agent ID.

Extend the prompt:

code
[SYSTEM_INSTRUCTION]
You are a production deserialization safety filter. Given a serialized [INPUT_FORMAT] payload from agent [AGENT_ID]:
1. Validate the payload is well-formed [INPUT_FORMAT]
2. Parse with max depth [MAX_DEPTH] and max size [MAX_SIZE_BYTES]
3. Reject any type not in [ALLOWED_TYPES]
4. Detect circular references and reject if found
5. Return:
{
  "safe_object": <parsed object or null>,
  "rejected_types": ["<type>", ...],
  "circular_reference_detected": true/false,
  "depth_exceeded": true/false,
  "size_exceeded": true/false,
  "rejection_reasons": ["<reason>", ...]
}

Watch for

  • Billion-laughs XML expansion exhausting memory before size check
  • JSON prototype pollution via __proto__ or constructor keys
  • Silent failures when the model returns malformed JSON instead of the schema
Prasad Kumkar

About the author

Prasad Kumkar

CEO & MD, Inference Systems

Prasad Kumkar is the CEO & MD of Inference Systems and writes about AI systems architecture, LLM infrastructure, model serving, evaluation, and production deployment. Over 5+ years, he has worked across computer vision models, L5 autonomous vehicle systems, and LLM research, with a focus on taking complex AI ideas into real-world engineering systems.

His work and writing cover AI systems, large language models, AI agents, multimodal systems, autonomous systems, inference optimization, RAG, evaluation, and production AI engineering.