Back to Blog
high SEVERITY8 min read

How Inconsistent IP Address Parsing Happens in JavaScript and How to Fix It

A high-severity vulnerability in the `ip-address` npm package (CVE-2026-69192) allowed attackers to craft IPv4 addresses with leading-zero octets that the library decoded as decimal while system resolvers decoded them as octal — creating a dangerous parsing discrepancy that could enable Server-Side Request Forgery (SSRF) and trust-boundary bypass. The fix upgrades `ip-address` from version 10.1.0 to 10.3.1 in the `core/http/react-ui` frontend dependency tree, eliminating the inconsistency and en

O
By Orbis AppSec
Published August 20, 2026Reviewed August 20, 2026

Answer Summary

CVE-2026-69192 is a high-severity SSRF vulnerability (CWE-918) in the `ip-address` npm package affecting versions before 10.3.1. The `Address4` class decoded IPv4 octets with leading zeros as decimal numbers, while OS-level and many network resolvers interpret them as octal — a classic parser differential that attackers exploit to bypass IP allowlists and reach internal services. The fix is to upgrade `ip-address` to version 10.3.1, which adds an explicit override entry in `bun.lock` and `package.json` to ensure the corrected version is resolved throughout the dependency tree.

Vulnerability at a Glance

cweCWE-918
fixUpgrade ip-address to 10.3.1 and add an explicit override in bun.lock
riskAttackers bypass IP allowlists to reach internal/loopback services
languageJavaScript / TypeScript (Node.js / Bun)
root causeAddress4 parsed leading-zero octets as decimal; resolvers parse them as octal
vulnerabilityServer-Side Request Forgery via inconsistent IP address parsing

How Inconsistent IP Address Parsing Happens in JavaScript and How to Fix It


At a Glance

Field Detail
CVE CVE-2026-69192
Package ip-address < 10.3.1
Severity High
CWE CWE-918 — Server-Side Request Forgery
Impact SSRF, trust-boundary bypass
Fix Upgrade to ip-address@10.3.1

Introduction

The core/http/react-ui frontend depends on @modelcontextprotocol/sdk, which in turn pulls in the ip-address npm package for IPv4/IPv6 address validation. On the surface, this looks like a routine utility dependency — but version 10.1.0 of ip-address contains a subtle, high-severity flaw in how its Address4 class handles octets with leading zeros.

When Address4 encounters an address like 010.0.0.1, it reads the leading-zero octet as decimal 10. But POSIX-compliant system resolvers — and many network stacks — interpret a leading zero as an octal prefix, making 010 equal to decimal 8. This one-digit difference is the entire attack surface.

If your application uses ip-address to validate or allowlist IPv4 addresses before passing them to an HTTP client or system resolver, an attacker can craft an address that passes your validation (because the library sees 10.0.0.1) but resolves differently at the network layer (because the OS sees 8.0.0.1 — or, more dangerously, routes to a private/loopback address entirely).


The Vulnerability Explained

The Octal Trap in IPv4 Notation

In C and many Unix-derived systems, numeric literals with a leading zero are octal. This convention leaked into early socket APIs and remains in POSIX inet_aton(). Consider these two interpretations of the same string:

Address string:  010.0.0.1

ip-address 10.1.0 (Address4):  10.0.0.1    decimal interpretation
POSIX inet_aton / many resolvers:   8.0.0.1    octal interpretation

Now imagine an attacker wants to reach 127.0.0.1 (loopback) on a server that blocks 127.x.x.x in its allowlist. They can try:

0177.0.0.1
  • ip-address 10.1.0 sees: 177.0.0.1not loopback, passes the allowlist check ✅
  • System resolver sees: 0177 = octal 127 → 127.0.0.1is loopback, connects to internal service ✅

The validation says "safe." The network says "internal." That gap is SSRF.

Where This Lives in the Dependency Tree

The vulnerable package entered the project through core/http/react-ui/bun.lock. Before the fix, the lock file resolved ip-address to version 10.1.0 as a transitive dependency of @modelcontextprotocol/sdk@1.27.1:

# bun.lock (before fix) — relevant excerpt
"@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.27.1", "", {
  "dependencies": {
    "@hono/node-server": "^1.19.9",
    ...
    "hono": "^4.11.4",
    ...
  }
}]

The ip-address package was not pinned with an override, so Bun resolved whatever version satisfied the semver range — which landed on the vulnerable 10.1.0.

Attack Scenario

Here's a concrete exploitation path for this application:

  1. The React UI communicates with a backend Go service (noted in the PR threat model as "a Go service — vulnerabilities in HTTP handlers are remotely exploitable").
  2. The frontend or its BFF (Backend for Frontend) uses @modelcontextprotocol/sdk to validate or route MCP (Model Context Protocol) endpoint URLs.
  3. An attacker supplies a crafted IP in a tool/resource URL: http://0177.0.0.1:8080/admin
  4. Address4 from ip-address 10.1.0 parses 0177 as decimal 177177.0.0.1 — not in the private range blocklist.
  5. The SDK forwards the request. The OS resolver interprets 0177 as octal → connects to 127.0.0.1:8080/admin — the local admin interface.
  6. The attacker has achieved SSRF to a loopback service that was never meant to be externally reachable.

The Fix

What Changed

The fix makes two targeted edits to enforce ip-address@10.3.1 across the entire dependency tree:

1. core/http/react-ui/bun.lock — Added an explicit override

  "overrides": {
-   "hono": "4.12.25",
+   "hono": "4.12.34",
+   "ip-address": "10.3.1",
  },

The overrides field in Bun's lock file forces every package in the tree that depends on ip-address to resolve to exactly 10.3.1, regardless of what semver range they declare. Without this override, a transitive dependency could silently re-introduce the vulnerable version.

2. core/http/react-ui/package.json — Updated @modelcontextprotocol/sdk range

- "@modelcontextprotocol/sdk": "^1.25.1",
+ "@modelcontextprotocol/sdk": "^1.30.0",

Bumping to ^1.30.0 also picks up the SDK's own internal dependency updates, reducing the chance that the SDK itself re-pins to an older ip-address.

Why the Override Is the Critical Part

Simply upgrading the SDK might not be enough. If any other package in the tree declares "ip-address": "^10.0.0", Bun could still resolve 10.1.0 for that package. The "ip-address": "10.3.1" override entry acts as a fleet-wide pin — it is the authoritative, non-negotiable version for every consumer in this project.

What Version 10.3.1 Actually Fixes

ip-address 10.3.1 updates the Address4 parser to detect leading-zero octets and either:
- Reject them as invalid (strict mode), or
- Normalize them by stripping the leading zero before numeric conversion

Either behavior eliminates the decimal/octal discrepancy, ensuring that whatever IP string passes Address4 validation is also what the downstream resolver will connect to.


Prevention & Best Practices

1. Always Pin Transitive IP-Parsing Dependencies

IP address parsing is a security-sensitive operation. Any library that validates, normalizes, or routes based on IP addresses should be pinned to a known-good version using your package manager's override/resolution mechanism:

// package.json
"overrides": {
  "ip-address": "10.3.1"
}
// For npm users, use "resolutions" (yarn) or "overrides" (npm 8.3+)
"resolutions": {
  "ip-address": "10.3.1"
}

2. Test Leading-Zero Octets Explicitly

Add unit tests that assert your IP validation rejects or canonicalizes octal-style addresses:

import { Address4 } from 'ip-address';

// These should either throw or normalize — never silently pass as different IPs
const suspiciousAddresses = [
  '0177.0.0.1',   // octal 127 → loopback
  '010.0.0.1',    // octal 8
  '0x7f.0.0.1',   // hex 127 → loopback
];

for (const addr of suspiciousAddresses) {
  try {
    const parsed = new Address4(addr);
    // Ensure the parsed address matches what a resolver would actually use
    console.assert(parsed.address !== '127.0.0.1', `SSRF risk: ${addr}`);
  } catch (e) {
    // Rejection is also acceptable
  }
}

3. Implement Defense-in-Depth for SSRF

IP parsing fixes are necessary but not sufficient. Layer your SSRF defenses:

  • Allowlist only known-good IP ranges rather than blocklisting private ranges
  • Use a dedicated SSRF-safe HTTP client that performs its own address validation after DNS resolution
  • Validate at the network layer using egress firewall rules that block RFC-1918 and loopback ranges regardless of what the application layer allows

4. Run SCA Scanners in CI

This vulnerability was caught by Trivy's Software Composition Analysis (SCA) scanner, which matched the installed ip-address version against its CVE database. Integrate SCA into your CI pipeline so that vulnerable transitive dependencies are flagged before they reach production:

# Example GitHub Actions step
- name: Run Trivy vulnerability scanner
  uses: aquasecurity/trivy-action@master
  with:
    scan-type: 'fs'
    scan-ref: '.'
    severity: 'HIGH,CRITICAL'

Relevant Standards

  • OWASP SSRF Prevention Cheat Sheet: https://cheatsheetseries.owasp.org/cheatsheets/Server_Side_Request_Forgery_Prevention_Cheat_Sheet.html
  • CWE-918: Server-Side Request Forgery
  • OWASP Top 10 — A10:2021: Server-Side Request Forgery

Key Takeaways

  • Leading-zero octets are a parsing landmine: 0177.0.0.1 is 177.0.0.1 to Address4 in ip-address < 10.3.1 but 127.0.0.1 (loopback) to the OS resolver — a gap wide enough to drive SSRF through.
  • Transitive dependencies need explicit overrides: The vulnerable ip-address version entered through @modelcontextprotocol/sdk, not a direct dependency. The "ip-address": "10.3.1" override in bun.lock is what actually enforces the safe version fleet-wide.
  • Upgrading the direct dependency alone is not always enough: Bumping @modelcontextprotocol/sdk to ^1.30.0 helps, but without the overrides entry, other packages could still resolve the old version.
  • SSRF in MCP/BFF layers is high-impact: The Model Context Protocol SDK routes to external tool endpoints — if an attacker can influence those URLs, SSRF gives them access to any service reachable from the server's network, including internal admin APIs.
  • SCA tooling catches what code review misses: No human reviewer scanning a bun.lock diff would spot a vulnerable transitive dependency version; automated scanners like Trivy are essential for this class of issue.

How Orbis AppSec Detected This

  • Source: User-influenced IP address strings passed to Address4 via @modelcontextprotocol/sdk URL routing logic
  • Sink: Address4 constructor in ip-address@10.1.0 — the point where the octal/decimal discrepancy manifests before the address is forwarded to a network resolver
  • Missing control: No version override for ip-address in bun.lock, allowing the vulnerable 10.1.0 to be resolved as a transitive dependency; no normalization of leading-zero octets before network calls
  • CWE: CWE-918 — Server-Side Request Forgery (SSRF)
  • Fix: Added "ip-address": "10.3.1" to the overrides block in bun.lock and upgraded @modelcontextprotocol/sdk to ^1.30.0 in package.json, forcing the safe parser version across the entire dependency tree

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

CVE-2026-69192 is a reminder that security vulnerabilities don't always look like buffer overflows or SQL injections — sometimes they hide in the gap between two components that both think they're parsing the same string correctly. The ip-address library's Address4 class and the system resolver were each internally consistent; the danger lived in their disagreement about what a leading zero means.

The fix is surgical: a two-line change to bun.lock and package.json that pins ip-address to 10.3.1 and ensures no future dependency resolution can silently downgrade it. But the broader lesson is about defense in depth — IP allowlist validation is only as strong as the parser implementing it, and parsers need to be held to the same standard as any other security control: pinned, tested, and monitored for CVEs.

For any application that routes requests based on user-supplied IP addresses or URLs — especially those integrating with protocol SDKs like MCP — this class of parser-differential vulnerability deserves explicit attention in your threat model.


References

Frequently Asked Questions

What is an IP address parsing inconsistency vulnerability?

It occurs when two components in the same request pipeline interpret the same IP string differently — for example, one treating "010" as decimal 10 and another as octal 8 — letting attackers craft addresses that pass validation but resolve to unintended hosts.

How do you prevent SSRF from IP parsing bugs in JavaScript?

Pin IP-parsing libraries to versions that normalize leading-zero octets, use explicit overrides in your lock file to enforce a single resolved version, and add integration tests that assert leading-zero addresses are rejected or canonicalized before any network call.

What CWE is this IP address parsing vulnerability?

CWE-918 (Server-Side Request Forgery), because the root impact is the server making unintended outbound requests to internal resources on behalf of an attacker.

Is input validation alone enough to prevent this SSRF variant?

No. If your validation library and your resolver disagree on what an IP string means, validation can pass while the actual connection goes somewhere else. You need both a correctly-implemented parsing library and consistent resolver behavior.

Can static analysis detect this IP parsing vulnerability?

Yes — Trivy's SCA scanner flagged this exact issue by matching the installed version of `ip-address` against its CVE database. Keeping SCA tooling in your CI pipeline is the most reliable way to catch transitive dependency vulnerabilities like this one.

View the Security Fix

Check out the pull request that fixed this vulnerability

View PR #11632

Related Articles

critical

How Server-Side Request Forgery (SSRF) Happens in Node.js and How to Fix It

A critical Server-Side Request Forgery (SSRF) vulnerability in `src/fetch.js` allowed the `fetchPage()` function to access internal network addresses, private IP ranges, and cloud metadata endpoints without any validation. This fix hardens input validation to block requests to RFC 1918 private addresses, localhost, and cloud metadata endpoints, preventing attackers from exploiting the function to probe internal infrastructure.

high

How SSRF via IP Address Parsing Inconsistency happens in Node.js and how to fix it

A critical parsing inconsistency in the ip-address npm package (versions before 10.3.1) allowed Server-Side Request Forgery (SSRF) and trust-boundary bypass. The library decoded IP addresses with leading-zero octets as decimal (e.g., 0127.0.0.1 as 127.0.0.1), while DNS resolvers and system libraries interpreted them as octal (e.g., 0127 as 87 decimal), enabling attackers to bypass IP allowlists and access internal resources.

high

How Octal IP Address Parsing Inconsistency Enables SSRF in Node.js and How to Fix It

A critical parsing inconsistency in the `ip-address` npm package (version 10.2.0) allowed attackers to bypass SSRF protections by exploiting how leading-zero octets are interpreted differently—decimal by the library versus octal by system resolvers. This vulnerability (CVE-2026-69192) was fixed by upgrading to version 10.3.1 using an npm override, ensuring consistent IP address validation across the application.

high

How NO_PROXY bypass via crafted URL happens in Node.js axios and how to fix it

A high-severity vulnerability (CVE-2026-42043) in the axios HTTP client library allowed attackers to bypass NO_PROXY environment variable restrictions using specially crafted URLs. This could route sensitive internal traffic through attacker-controlled proxy servers. The fix upgrades axios from 1.13.6 to 1.18.0, which includes a rewritten proxy resolution mechanism using `proxy-from-env` v2.1.0 and the `https-proxy-agent` package.

high

How Server-Side Request Forgery (SSRF) happens in Node.js through inconsistent IP address parsing and how to fix it

A high-severity Server-Side Request Forgery (SSRF) vulnerability (CVE-2026-69192) was discovered in the ip-address package version 10.2.0, where inconsistent IP address parsing allowed attackers to bypass trust boundaries and access internal resources. The fix upgrades ip-address from 10.2.0 to 10.3.1 across the dependency tree, with explicit pinning in package.json and strategic version management in bun.lock to prevent both direct and transitive exploitation paths.

high

How Regular Expression Denial of Service happens in JavaScript and how to fix it

CVE-2026-33671 is a Regular Expression Denial of Service (ReDoS) vulnerability in the picomatch glob-matching library, triggered by specially crafted extglob patterns that cause catastrophic regex backtracking. The fix upgrades picomatch to version 4.0.4 (with overrides pinning all transitive copies) in the client's dependency tree, eliminating the vulnerable regex evaluation path. Left unpatched, any code path that passes user-influenced glob patterns to picomatch could be weaponized to stall a