Back to Blog
critical SEVERITY7 min read

How JSON request validation bypass happens in Node.js API handlers and how to fix it

A critical validation bypass in the `/api/agents/jobs` endpoint allowed attackers to send malformed JSON with arbitrary properties that could trigger prototype pollution or unexpected behavior. The fix added comprehensive validation checks including array detection and property existence verification to prevent malicious payloads from reaching downstream processing logic.

O
By Orbis AppSec
Published July 24, 2026Reviewed July 24, 2026

Answer Summary

This vulnerability is a JSON request validation bypass (CWE-1284) in a Node.js API handler that accepts POST requests. The `/api/agents/jobs` endpoint in `review-agent-handlers.js` only validated `body.provider` but didn't check for arrays, missing properties, or malformed objects, allowing prototype pollution attacks. The fix added four validation layers: type checking, array rejection, property existence verification with `Object.hasOwn()`, and value validation—preventing arbitrary properties from reaching downstream code.

Vulnerability at a Glance

cweCWE-1284 (Improper Validation of Specified Quantity in Input)
fixAdded array check, `Object.hasOwn()` verification, and comprehensive type validation before processing
riskAttackers can inject arbitrary properties into request objects, potentially polluting prototypes or causing unexpected behavior in downstream processing
languageJavaScript (Node.js)
root causeInsufficient validation only checked `body.provider` value without validating object structure or property existence
vulnerabilityJSON request validation bypass enabling prototype pollution

Introduction

In a Node.js workspace API handler, we discovered a critical JSON validation bypass in src/ui/workspace/routes/api/review-agent-handlers.js at line 83. The /api/agents/jobs endpoint accepted POST requests with JSON bodies but performed only minimal validation—checking if body.provider equaled "guide". This incomplete validation left the door wide open for attackers to send malformed JSON objects with arbitrary properties, arrays instead of objects, or payloads designed to trigger prototype pollution.

The vulnerable code pattern looked innocuous at first glance:

if (!body || typeof body !== "object" || body.provider !== "guide") {

But this check had three critical gaps: it didn't reject arrays (which pass typeof === "object"), it accessed body.provider without verifying the property exists (allowing undefined to slip through in certain edge cases), and it didn't prevent additional malicious properties from being included in the payload. For developers building API endpoints that process JSON, this vulnerability demonstrates why comprehensive input validation is non-negotiable.

The Vulnerability Explained

The vulnerable code in review-agent-handlers.js at line 84 shows the problematic validation pattern:

if (!body || typeof body !== "object" || body.provider !== "guide") {
    return Response.json({ error: "Only the guide provider is available in RunWield code review." }, {
        status: 400,
    });
}

This validation has four critical weaknesses:

1. Arrays Pass Type Checking

In JavaScript, typeof [] returns "object", so an attacker could send:

["malicious", "array", "data"]

This array would pass the typeof body !== "object" check and proceed to downstream processing where code might iterate over array indices or access properties, causing unexpected behavior.

2. Property Access Without Existence Check

The code directly accesses body.provider without verifying the property exists. While undefined !== "guide" would fail the check, this pattern is fragile. If downstream code uses the body object assuming provider exists, it could lead to errors or security issues.

3. No Protection Against Prototype Pollution

An attacker could send:

{
  "provider": "guide",
  "__proto__": {
    "isAdmin": true
  }
}

This payload passes all existing checks but injects properties into Object.prototype, potentially affecting the entire application's behavior. Any subsequent code checking someObject.isAdmin would unexpectedly find true.

4. No Schema Validation

The validation doesn't check for unexpected properties. An attacker could include:

{
  "provider": "guide",
  "constructor": {"prototype": {"polluted": true}},
  "maliciousPayload": "...",
  "arbitraryData": "..."
}

These additional properties could exploit vulnerabilities in downstream processing logic that iterates over object keys or passes the entire body to other functions.

Real-World Attack Scenario

Consider how this endpoint might be exploited in the RunWield code review system:

  1. Reconnaissance: The attacker discovers the /api/agents/jobs endpoint accepts JSON POST requests
  2. Payload crafting: They send a prototype pollution payload:
POST /api/agents/jobs
Content-Type: application/json

{
  "provider": "guide",
  "__proto__": {
    "isAdmin": true,
    "bypassAuth": true
  }
}
  1. Exploitation: The payload passes validation and gets processed. If downstream code checks authorization with:
if (user.isAdmin) {
  // Grant access to sensitive operations
}

The polluted prototype means every object in the application now has isAdmin: true, potentially granting unauthorized access to privileged functionality.

  1. Persistence: Prototype pollution affects the entire Node.js process, so the attacker's injected properties persist across subsequent requests until the server restarts.

The Fix

The security patch adds four layers of validation to create comprehensive protection:

Before (Vulnerable Code):

if (!body || typeof body !== "object" || body.provider !== "guide") {

After (Secure Code):

if (!body || typeof body !== "object" || Array.isArray(body) || !Object.hasOwn(body, "provider") || body.provider !== "guide") {

Let's break down each addition:

1. Array Rejection: Array.isArray(body)

Explicitly rejects arrays, preventing attackers from sending ["data"] instead of {"provider": "guide"}. This closes the typeof loophole where arrays pass object type checking.

2. Property Existence Check: Object.hasOwn(body, "provider")

Uses the secure Object.hasOwn() method (added in ES2022) to verify provider exists as an own property of the body object, not inherited from the prototype chain. This is critical because:

  • It prevents prototype pollution from affecting validation
  • It ensures the property is explicitly present in the request
  • It's safer than body.hasOwnProperty("provider") which can be overridden

3. Maintained Type Validation

The existing typeof body !== "object" check remains to reject primitives (strings, numbers, null).

4. Maintained Value Validation

The body.provider !== "guide" check ensures only the expected provider value is accepted.

Why This Multi-Layered Approach Works

The fix creates a validation chain where each check serves a specific security purpose:

!body                           // Reject falsy values (null, undefined)
typeof body !== "object"        // Reject primitives (string, number, boolean)
Array.isArray(body)            // Reject arrays (close typeof loophole)
!Object.hasOwn(body, "provider") // Verify required property exists as own property
body.provider !== "guide"      // Validate expected value

An attacker must pass all five checks to reach downstream code. This defense-in-depth approach means:

  • Prototype pollution payloads fail the Object.hasOwn() check
  • Array payloads fail the Array.isArray() check
  • Objects missing provider fail the existence check
  • Objects with wrong provider values fail the value check

While this fix doesn't implement full schema validation (which would be the gold standard), it provides robust protection against the most common JSON validation attacks.

Prevention & Best Practices

1. Use Schema Validation Libraries

For production APIs, implement comprehensive schema validation:

import Joi from 'joi';

const jobSchema = Joi.object({
  provider: Joi.string().valid('guide').required(),
  // Define all expected properties explicitly
}).strict(); // Reject unknown properties

const { error, value } = jobSchema.validate(body);
if (error) {
  return Response.json({ error: error.details[0].message }, { status: 400 });
}

2. Freeze Object Prototypes

Prevent prototype pollution at the application level:

Object.freeze(Object.prototype);
Object.freeze(Array.prototype);

3. Use Safe Property Access

Always verify property existence before access:

// Unsafe
if (body.provider === "guide") { }

// Safe
if (Object.hasOwn(body, "provider") && body.provider === "guide") { }

4. Implement Content-Type Validation

Enforce strict content-type headers:

if (request.headers.get("content-type") !== "application/json") {
  return Response.json({ error: "Content-Type must be application/json" }, { status: 415 });
}

5. Set JSON Parse Limits

Prevent large payload attacks:

const body = await request.json({ 
  maxSize: 1024 * 100 // 100KB limit
}).catch(() => ({}));

6. Use Static Analysis

Deploy tools like Semgrep with rules that detect incomplete validation patterns. The rule that caught this vulnerability looks for:
- JSON parsing followed by insufficient property checks
- Missing Array.isArray() checks after typeof === "object"
- Property access without hasOwn() verification

Key Takeaways

  • The review-agent-handlers.js endpoint only validated body.provider value, leaving it vulnerable to arrays, missing properties, and prototype pollution attacks
  • JavaScript's typeof [] === "object" quirk requires explicit Array.isArray() checks to prevent array payloads from passing validation
  • Object.hasOwn() is essential for secure property verification because it checks own properties without being affected by prototype pollution
  • Multi-layered validation creates defense-in-depth: each check in the chain blocks a different attack vector
  • This vulnerability affected a publicly accessible API endpoint in a Node.js workspace, making it a critical risk for all downstream consumers of this package

How Orbis AppSec Detected This

Source: JSON request body from POST request to /api/agents/jobs endpoint, parsed at line 83 via await request.json()

Sink: The body object passed to downstream processing logic without comprehensive validation

Missing control: No array rejection check, no explicit property existence verification with Object.hasOwn(), and no protection against arbitrary properties that could enable prototype pollution

CWE: CWE-1284 (Improper Validation of Specified Quantity in Input)

Fix: Added Array.isArray(body) check and Object.hasOwn(body, "provider") verification to create a four-layer validation chain that blocks malformed objects, arrays, and prototype pollution attempts

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

This JSON validation bypass in the /api/agents/jobs endpoint demonstrates why comprehensive input validation is critical for API security. A seemingly simple check for body.provider left the application vulnerable to prototype pollution and malformed payload attacks. The fix—adding array detection and Object.hasOwn() verification—creates a robust validation chain that blocks multiple attack vectors.

For developers building Node.js APIs, this vulnerability serves as a reminder: never trust JSON input without thorough validation. Use schema validation libraries for complex endpoints, always check for arrays after type validation, and verify property existence with Object.hasOwn() before accessing values. These practices, combined with automated security scanning, create the defense-in-depth approach needed to protect modern web applications.

References

Frequently Asked Questions

What is JSON request validation bypass?

A security flaw where an API endpoint accepts JSON payloads without properly validating their structure, type, or content, allowing attackers to send malformed objects with arbitrary properties that can exploit downstream code through prototype pollution or logic manipulation.

How do you prevent JSON validation bypass in Node.js?

Use comprehensive validation including type checks (`typeof body !== "object"`), array rejection (`!Array.isArray(body)`), explicit property existence verification (`Object.hasOwn(body, "property")`), and schema validation libraries like Joi or Ajv to enforce strict input structure before processing.

What CWE is JSON validation bypass?

CWE-1284 (Improper Validation of Specified Quantity in Input) and related to CWE-1321 (Improperly Controlled Modification of Object Prototype Attributes / Prototype Pollution) when the bypass enables prototype pollution attacks.

Is checking `body.provider !== "guide"` enough to prevent JSON validation bypass?

No. Checking only a single property value doesn't validate object structure. Attackers can send arrays, objects without the required property, or objects with additional malicious properties like `__proto__` that bypass single-property validation and exploit downstream code.

Can static analysis detect JSON validation bypass?

Yes. Static analysis tools like Semgrep can detect incomplete validation patterns by identifying JSON parsing followed by insufficient property checks, missing type validation, or absence of schema validation before object properties are accessed or passed to other functions.

View the Security Fix

Check out the pull request that fixed this vulnerability

View PR #33

Related Articles

critical

How arbitrary code execution via injected protobuf definition type fields happens in Node.js and how to fix it

A critical arbitrary code execution vulnerability (CVE-2026-41242) was discovered in protobufjs versions prior to 7.5.5 and 8.0.1, allowing attackers to inject malicious code through crafted protobuf definition type fields. The fix upgrades the dependency from the vulnerable version 6.11.4 to 7.5.5, which properly sanitizes type field inputs during protobuf parsing. This vulnerability is especially dangerous because protobufjs is widely used in Node.js applications for serialization, meaning a s

high

How command injection happens in Node.js child_process and how to fix it

A high-severity command injection vulnerability was discovered in `bootstrap.js` at line 34, where the `exec()` function was used to run shell commands constructed from a `folder` variable. By replacing `exec()` with `execFile()` and passing arguments as an array, the fix eliminates the shell interpolation entirely, closing the door on command injection attacks that could have affected all downstream consumers of this Node.js library.

high

How unsafe pickle deserialization happens in NumPy's np.load() and how to fix it

A high-severity arbitrary code execution vulnerability was discovered in `tools/ardy-engine/retarget.py` where `np.load()` was called with `allow_pickle=True`, enabling attackers to embed malicious pickle payloads in `.npz` files. The fix was a single-character change—switching `allow_pickle=True` to `allow_pickle=False`—that eliminates the deserialization attack vector while preserving the file's legitimate array data loading functionality.

high

How SSRF and Credential Leakage via Absolute URLs happens in axios and how to fix it

A high-severity vulnerability (CVE-2025-27152) in axios versions prior to 1.8.2 allowed Server-Side Request Forgery (SSRF) attacks and credential leakage when making HTTP requests with absolute URLs. This vulnerability was fixed by upgrading from axios 1.7.4 to 1.8.2 in both package.json and package-lock.json, eliminating the attack vector that could have exposed authentication tokens and enabled unauthorized server-side requests.

high

How pickle-based arbitrary code execution happens in PyTorch and how to fix it

A high-severity arbitrary code execution vulnerability was discovered in `scripts/export_joyvasa_audio.py` where `torch.load()` was called with `weights_only=False`, allowing any pickle-serialized Python object — including malicious code — to execute during checkpoint loading. The fix switches to `weights_only=True` and explicitly allowlists only the two non-standard classes the checkpoint actually requires: `argparse.Namespace` and `pathlib.PosixPath`. This closes a real code execution path tha

critical

How WebSocket protocol length header abuse happens in Node.js and how to fix it

A critical vulnerability (CVE-2026-54466) in websocket-driver versions prior to 0.7.5 allows attackers to corrupt WebSocket messages by manipulating protocol length headers. This can lead to data integrity issues, denial of service, or potentially arbitrary code execution in affected Node.js applications. The fix involves upgrading the websocket-driver dependency to version 0.7.5.