Back to Blog
high SEVERITY4 min read

requestInput() Type Confusion: NaN and Object Bypass in JavaScript

The `requestInput()` utility function lacked validation on its `type` parameter and failed to handle `NaN` results from float conversions, creating a type confusion weakness. An attacker could supply malformed inputs that propagate unhandled `NaN` values or unexpected object types through the type system. The fix adds explicit guards against `NaN` type parameters and rejects non-primitive type values.

O
By Orbis AppSec
•Technically reviewed by Anupam Mediratta•Published October 2, 2026•Reviewed October 2, 2026

Answer Summary

The `requestInput(de, du, field, type)` utility function in affected first-party code handles type conversion for boolean and numeric inputs without strict validation. An attacker could cause `requestInput()` to return unhandled `NaN` values from float conversions or pass unexpected object types as the `type` parameter, leading to arithmetic errors or type confusion in downstream code. The fix adds validation to reject `NaN` type parameters and non-string/number/boolean type values at `src/utils.js:350`. CWE-843 (Access of Resource Using Incompatible Type).

Vulnerability at a Glance

cweCWE-843 (Access of Resource Using Incompatible Type)
fixExplicit `Number.isNaN(type)` check and whitelist validation for `typeof type`
riskUnhandled NaN propagation causing arithmetic errors; unexpected object types bypassing type guards
languageJavaScript
root causeMissing validation on `type` parameter and unchecked `NaN` from `parseFloat()` equivalent conversion
vulnerabilityType Confusion / Type Validation Bypass

Affected Versions

Affected not applicable (first-party code)
Fixed in not applicable (first-party code) — see commit details
Ecosystem N/A
CVE / GHSA not assigned
CWE CWE-843 (Access of Resource Using Incompatible Type)

Introduction

A type confusion weakness in the requestInput() utility function created a subtle but dangerous path for malformed data to propagate through JavaScript's type system. The function, which handles type conversion for boolean and numeric inputs, assumed its type parameter would always be a valid primitive and that float conversions would succeed—assumptions that fail under adversarial input.

The vulnerability sits at the boundary between dynamic JavaScript and typed expectations. When requestInput(de, du, field, type) receives a type value that is NaN or an unexpected object, or when a float conversion produces NaN, the function's previous implementation would dutifully return these poisoned values to callers. Downstream code expecting clean numbers or booleans would then face arithmetic surprises or type confusion.

The Vulnerability Explained

The vulnerable code in requestInput() performed type conversion without validating either the conversion result or the type parameter itself:

export function requestInput(de, du, field, type) {
  // ... earlier code ...
  if (typeof type === 'undefined') {
    console.error(`requestInput: type is not defined for ${field}`);
    return null;
  }
  // No validation of whether type is NaN or an unexpected object
  du[field] = type;
  return de[field];
}

The function's contract promises to return converted values, but it had two critical gaps:

NaN type parameter: If type itself was NaN (perhaps from a previous failed calculation), typeof type === 'number' would pass, yet NaN is not a valid type indicator. The function would assign NaN to du[field] and return the raw input.

Unexpected object types: An attacker could pass {} or any object as type. Since typeof {} === 'object', the initial undefined check would pass, and the object would propagate through the system.

Float conversion to NaN: For float types, conversion of non-numeric strings produces NaN. Without explicit handling, this NaN would be returned to callers who might use it in comparisons (NaN === NaN is false) or arithmetic operations that silently poison further calculations.

Consider this attack scenario: A service uses requestInput() to parse a rate-limiting parameter from user input:

const rateLimit = requestInput(dataEnv, dataUsed, 'maxRequests', 'float');
// Attacker sends: { maxRequests: 'unlimited' }
// rateLimit becomes NaN
if (rateLimit > 1000) {  // false—NaN comparisons are always false
  applyStrictLimits();   // skipped!
}

The Fix

The defense-in-depth fix adds explicit validation at three critical points. First, it rejects NaN as a type parameter:

if (typeof type === 'number' && Number.isNaN(type)) {
  console.error(`requestInput: type is NaN for ${field}`);
  return null;
}

Second, it whitelists acceptable type values:

if (!['string', 'number', 'boolean'].includes(typeof type)) {
  console.error(`requestInput: type has an unexpected type (${typeof type}) for ${field}`);
  return null;
}

These changes work together to ensure that requestInput() maintains its security invariant: it returns null, a valid primitive, or a defined value—but never unhandled NaN or unexpected object types that could propagate through calling code.

The fix is intentionally conservative. Rather than attempting to coerce or sanitize unexpected inputs, it fails closed with null, forcing callers to handle the error case explicitly.

Key Takeaways

  • Never trust the typeof check alone for numeric parameters—NaN has typeof 'number' and requires Number.isNaN() detection
  • Whitelist acceptable type values when a parameter must be one of a fixed set of primitives; blacklist approaches miss exotic object types
  • Make failure modes explicit and bounded rather than allowing poisoned values to propagate; returning null on validation failure is safer than returning NaN
  • Float conversions require NaN handling—any code path that parses user input to floating-point numbers must explicitly check Number.isNaN() before using the result
  • Defense-in-depth at utility boundaries prevents vulnerabilities from becoming exploitable even when the immediate caller appears safe

How Orbis AppSec Detected This

Source: The type parameter and input values passed to requestInput() from upstream request handlers

Sink: The requestInput() function's return of unvalidated NaN values and unexpected object types to calling code

Missing control: No validation that type is a valid primitive (not NaN, not an object) and no check that float conversions produce valid numbers rather than NaN

CWE: CWE-843 — Access of Resource Using Incompatible Type

Fix: Added explicit Number.isNaN(type) check and whitelist validation for typeof type being 'string', 'number', or 'boolean'

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

Type confusion vulnerabilities in JavaScript often hide in the gap between dynamic flexibility and developer assumptions. The requestInput() fix demonstrates that even "internal" utility functions need rigorous validation—especially when they sit at the boundary between untrusted input and application logic. By making the failure mode explicit and returning null rather than propagating NaN or unexpected objects, this defense-in-depth change prevents a class of arithmetic and type-bypass attacks before they can reach exploitable code paths.

Prevention and further reading

Frequently Asked Questions

Can `requestInput()` return `NaN` for a float type with the fix applied?

No. The fix ensures that if a float conversion would produce `NaN`, the function returns `null` instead. The security invariant guarantees `Number.isNaN(result) === false` for any non-null numeric return.

What happens if I pass an object like `{}` as the `type` parameter to `requestInput()`?

Before the fix, the object would be assigned to `du[field]` and returned. After the fix, the function detects `typeof type === 'object'` and returns `null` after logging an error.

Does the fix change how `requestInput()` handles valid boolean string inputs like "true"?

No. Valid boolean conversions remain unchanged. The fix only affects the failure mode when inputs cannot be converted—making it explicit and bounded rather than propagating unexpected types.

View the Security Fix

Check out the pull request that fixed this vulnerability

View PR #209

Related Articles

high

adm-zip 0.6.0 Preserves SUID Bits From ZIPs: CVE-2026-102282

The `adm-zip` dependency resolved to 0.6.0 in this project's dependency tree, a version affected by CVE-2026-102282: during extraction it applies the Unix permission bits stored in each ZIP entry's external file attributes verbatim, including the setuid (`04000`), setgid (`02000`), and sticky bits. An attacker who controls an archive passed to `extractAllTo()` or `extractEntryTo()` can therefore have the extractor create a setuid binary owned by whatever user the extraction process runs as. The

critical

No Rate Limit on /api/uploads/presign Enables DoS

The `/api/uploads/presign` endpoint accepted unlimited concurrent requests to generate storage presigned URLs, giving an attacker a free lever to exhaust storage-provider quotas and server resources. The fix adds an `express-rate-limit` middleware capping each client to 30 requests per minute on that route.

high

CVE-2026-54673: builder-util-runtime Leaks Auth Headers on Redirect

electron-updater and electron-builder rely on builder-util-runtime to fetch update manifests and artifacts over HTTP. A flaw in that shared HTTP executor allowed credential headers attached to the original update-feed request to be re-sent after a redirect, exposing them to any host the redirect pointed to. The project fixes this by upgrading builder-util-runtime to 9.7.0 and collapsing a duplicate, older copy of the package that electron-updater had pinned on its own.

high

image-size 1.2.1 DoS: Zero-Valued Dimensions in Image Buffer Parser

A high-severity denial-of-service vulnerability in image-size 1.2.1 allows attackers to crash Node.js services using malicious image buffers with zero-valued dimensions. The fix removes the vulnerable `queue` dependency and tightens dimension validation in version 2.0.3.

high

linkify-it 5.0.1 mailto: Link Parsing Causes DoS

linkify-it versions up to 5.0.1 can be forced into excessive processing time when autolinking a specially crafted mailto: link, allowing a remote attacker to degrade or stall the parsing thread. Upgrading to linkify-it 5.0.2 closes the issue; any application that runs linkify-it (directly or via markdown-it) against untrusted text should update immediately.

high

undici 8.10.0 Cache Poisoning: CVE-2026-85152 Auth Bypass

undici, the HTTP client used by Node.js's `fetch()` implementation, shipped a caching layer that did not properly isolate cached responses by origin, letting a response poisoned on one origin be served to requests for another. This created a path to cross-origin authentication bypass, tracked as CVE-2026-85152 and fixed in undici 8.10.2.