Back to Blog
high SEVERITY4 min read

Anthropic API Adapter Prototype Pollution in parseToolCallInput

The `parseToolCallInput` function in the Anthropic API adapter used `JSON.parse` without protecting against prototype pollution keys. An attacker who could manipulate API responses—through a man-in-the-middle attack, compromised Codex backend, or DNS spoofing—could inject `__proto__`, `constructor`, or `prototype` properties to pollute JavaScript's Object prototype and affect extension runtime behavior.

O
By Orbis AppSec
•Published September 27, 2026•Reviewed September 27, 2026

Answer Summary

The `parseToolCallInput` function in the Anthropic API adapter used `JSON.parse` without a reviver function to filter dangerous keys. An attacker intercepting or spoofing Codex backend responses could inject `__proto__`, `constructor`, or `prototype` properties that pollute the JavaScript Object prototype, potentially altering behavior of all objects in the extension runtime. The fix adds a `reviver` callback that returns `undefined` for these three keys, explicitly bounding the failure mode. CWE-502 (Deserialization of Untrusted Data).

Vulnerability at a Glance

cweCWE-502
fixAdded reviver callback returning `undefined` for prototype pollution keys
riskMan-in-the-middle or compromised backend could alter extension runtime behavior through prototype pollution
languageTypeScript
root cause`JSON.parse` without reviver filtering `__proto__`, `constructor`, `prototype`
vulnerabilityPrototype Pollution via JSON Deserialization

Affected Versions

Affected not applicable (first-party code)
Fixed in commit-based fix (see PR)
Ecosystem N/A
CVE / GHSA not assigned
CWE CWE-502 (Deserialization of Untrusted Data)

Introduction

The parseToolCallInput function sits at a trust boundary most developers rarely examine: where your application receives structured data from an external API and transforms it into executable instructions. In the Anthropic API adapter, this function receives tool call arguments as JSON strings from the Codex backend and parses them into JavaScript objects. The vulnerability wasn't in using JSON.parse—it was in using it bare, without accounting for what happens when an attacker controls the string being parsed.

This is a defense-in-depth fix. No exploit has been demonstrated in the wild. But the pattern it closes—trusting API responses to contain only benign property names—is exactly where supply-chain and infrastructure attacks increasingly target.

The Vulnerability Explained

The vulnerable code in parseToolCallInput was straightforward:

function parseToolCallInput(argumentsJson: string): object {
  // ... validation ...
  try {
    const parsed = JSON.parse(argumentsJson);
    if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {
      return parsed;
    }
  }
  // ...
}

The problematic line: JSON.parse(argumentsJson) without a reviver function.

JavaScript's JSON.parse creates objects with the default prototype chain. When parsing {"__proto__": {"polluted": true}}, the __proto__ key becomes a property assignment that, in many contexts, can overwrite Object.prototype. If parseToolCallInput returns this object and subsequent code merges it elsewhere (via Object.assign, spread syntax, or recursive merging), the pollution spreads.

Attack scenario: An attacker with MITM position or compromised Codex backend returns a tool call with arguments like:

{"__proto__": {"toString": "polluted", "isAdmin": true}}

If the extension later checks user.isAdmin on any object without that property, the polluted prototype chain returns true. The impact depends entirely on what checks exist downstream—logging bypasses, authorization flaws, or unexpected type coercion.

The parseToolCallInput function specifically handles tool call arguments, meaning this pollution vector could affect any extension using the Anthropic adapter to execute tool calls based on model-generated responses.

The Fix

The fix adds a reviver callback to JSON.parse that explicitly neutralizes the three dangerous keys:

const parsed = JSON.parse(argumentsJson, (key, value) => {
  if (key === '__proto__' || key === 'constructor' || key === 'prototype') {
    return undefined;
  }
  return value;
});

Before: JSON.parse(argumentsJson) — accepts all keys, including prototype pollution vectors.

After: The reviver intercepts every key-value pair during parsing. For __proto__, constructor, or prototype, it returns undefined, effectively stripping these properties from the result. All other keys pass through unchanged.

This change is surgical. It doesn't alter the function's signature or return type. It doesn't add dependencies. It simply bounds the deserialization to safe property names, making the failure mode explicit: if an attacker tries to pollute the prototype, those properties vanish rather than propagate.

Key Takeaways

  • The reviver parameter in JSON.parse is your last line of defense when parsing untrusted JSON. Most developers ignore it; security-critical code should use it.

  • Tool call arguments from LLM APIs are untrusted data regardless of TLS. Backend compromise, DNS hijacking, or supply-chain attacks on the API provider can all inject malicious payloads.

  • __proto__, constructor, and prototype are not valid data keys in any reasonable JSON schema. Rejecting them at parse time has no legitimate downside.

  • Defense-in-depth at deserialization points prevents cascading failures. Even if upstream validation fails, the parser itself enforces safety.

  • TypeScript's object return type provides no protection against prototype pollution. The parsed result passes type checking while still being dangerous.

How Orbis AppSec Detected This

Source: The argumentsJson parameter to parseToolCallInput, originating from HTTP responses to the Anthropic/Codex API.

Sink: JSON.parse() invoked without a reviver function to filter dangerous keys.

Missing control: No validation that parsed object keys exclude __proto__, constructor, or prototype before returning the object to callers.

CWE: CWE-502 (Deserialization of Untrusted Data)

Fix: Added a reviver callback returning undefined for the three prototype pollution keys, explicitly bounding the deserialization failure mode.

Orbis AppSec automatically detected this vulnerability and opened a pull request with the fix. Try Orbis AppSec on your repositories to find and fix issues like this automatically.

Conclusion

The parseToolCallInput fix demonstrates that deserialization hardening doesn't require massive refactoring. A nine-line change—adding a reviver callback with three specific key checks—eliminates a class of prototype pollution attacks against this code path. For developers integrating LLM APIs, this pattern should be standard: every JSON.parse of untrusted data deserves scrutiny of what keys it might create, and whether those keys could poison the objects everyone else relies on.

Prevention and further reading

Frequently Asked Questions

Does the fix in `parseToolCallInput` change what valid tool call arguments are accepted?

No. The reviver only blocks the three literal keys `__proto__`, `constructor`, and `prototype`. Any legitimate tool call arguments using these as property names would be extremely unusual and now explicitly rejected as a security boundary.

Is this vulnerability exploitable without network-level access to the Codex backend or Anthropic API traffic?

No direct exploitation path is known without MITM, compromised backend, or DNS spoofing. The fix is defense-in-depth that makes the failure mode explicit and bounded rather than relying on transport security alone.

Why was `JSON.parse` with a reviver chosen over a schema validation library like Zod or ajv?

The PR description notes this is "defence-in-depth" at a specific line rather than a demonstrated exploit. The minimal reviver approach bounds the immediate risk without adding dependency weight, though schema validation would provide stronger guarantees for future hardening.

View the Security Fix

Check out the pull request that fixed this vulnerability

View PR #104

Related Articles

high

load_localStorage.js JSON Parse: Prototype Pollution via __proto__

The load_localStorage.js utility parsed JSON configuration without validating keys, permitting prototype pollution through malicious `__proto__`, `constructor`, or `prototype` properties. An attacker with filesystem access could poison downstream JavaScript execution by injecting these special keys into the loaded data structure.

critical

How Arbitrary Code Execution Happens in protobufjs and How to Fix It

CVE-2026-41242 is a critical vulnerability in protobufjs versions 8.0.0 and earlier that allows attackers to execute arbitrary code by injecting malicious type fields into protobuf definitions. The fix upgrades the dependency from `^8.0.0` to `^8.6.6` in `core/package.json`, eliminating the unsafe code path that processed attacker-controlled type metadata without validation.

high

How Quadratic CPU Consumption Happens in JS-YAML and How to Fix It

A critical vulnerability in JS-YAML versions 3.x and 4.x allowed attackers to trigger quadratic CPU consumption through maliciously crafted YAML input using the `!!omap` tag resolver. The vulnerability stems from inefficient array operations in the ordered map resolution logic, which could be exploited for denial-of-service attacks. Upgrading to JS-YAML 4.3.1 or 3.15.1 patches this attack surface by optimizing the computational complexity of ordered map processing.

critical

How Type Confusion Vulnerabilities Happen in JavaScript Dependencies and How to Fix Them

A critical type confusion vulnerability (CVE-2021-23436) was discovered in immer 9.0.7, a popular immutable state management library used in the client application. By upgrading to immer 9.0.6, the vulnerability was patched, eliminating a flaw that could have allowed attackers to bypass previous security fixes (CVE-2020-28477). This fix demonstrates why keeping dependencies current is essential for maintaining application security.

critical

How Prototype Pollution Happens in i18next-fs-backend and How to Fix It

A critical prototype pollution vulnerability (CVE-2026-48713) was discovered in i18next-fs-backend versions prior to 2.6.6, where specially crafted missing-key strings could pollute the JavaScript object prototype. This fix upgrades the dependency to patch the vulnerability and prevent attackers from injecting malicious properties into application objects.

high

js-yaml 4.3.1 Denial of Service: Malformed Input Hangs YAML Parser

A denial of service vulnerability in js-yaml versions 4.3.1 and earlier allows attackers to hang the YAML parser indefinitely by providing specially crafted malformed input. The fix, released in versions 4.3.2 and 3.15.2, patches the parsing logic to prevent unbounded processing. Upgrading is recommended for all applications parsing untrusted YAML data.