Back to Blog
high SEVERITY6 min read

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

The `ip-address` npm package (version 10.2.0) parsed IPv4 addresses with leading-zero octets as decimal numbers, while operating system resolvers interpret them as octal. This inconsistency (CVE-2026-69192) allows attackers to bypass SSRF protections and trust-boundary checks by crafting IP addresses that appear safe to the library but resolve to internal network addresses. The fix upgrades `ip-address` to version 10.3.1, which correctly rejects or normalizes ambiguous octal notation.

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

Answer Summary

CVE-2026-69192 is a high-severity SSRF and trust-boundary bypass vulnerability in the Node.js `ip-address` npm package (versions prior to 10.3.1). The `Address4` class decodes leading-zero octets as decimal integers while OS-level DNS resolvers decode them as octal, creating a parsing differential (CWE-918). The fix is to upgrade `ip-address` from 10.2.0 to 10.3.1, which eliminates the ambiguous parsing behavior by rejecting or correctly interpreting octal-notation octets.

Vulnerability at a Glance

cweCWE-918
fixUpgrade ip-address from 10.2.0 to 10.3.1
riskAttackers can bypass allow/deny lists to reach internal services
languageJavaScript/Node.js
root causeAddress4 treats leading-zero octets as decimal while resolvers treat them as octal
vulnerabilityServer-Side Request Forgery (SSRF) via IP address parsing inconsistency

Introduction

In the Argo project—a Next.js-based AI SaaS platform—the dependency ip-address at version 10.2.0 introduced a subtle but dangerous parsing inconsistency. The library's Address4 class decoded IPv4 octets with leading zeros (e.g., 0177) as decimal values, while the underlying operating system resolver and most network stacks interpret them as octal. This mismatch, tracked as CVE-2026-69192, means an attacker could craft an IP address like 0177.0.0.01 that the library validates as the harmless address 177.0.0.1, but the actual network request resolves to 127.0.0.1—the loopback interface.

This vulnerability was flagged by Trivy in package-lock.json where ip-address was pinned at version 10.2.0 as a peer dependency. Although the project's PR notes the dependency as "not confirmed reachable," the presence of this library in any request-validation or URL-filtering pipeline creates a high-risk attack surface for SSRF.

The Vulnerability Explained

The Octal Parsing Differential

In most POSIX systems and network stacks, an IPv4 octet with a leading zero is interpreted as an octal number:

0177 (octal) = 127 (decimal)
0300 (octal) = 192 (decimal)
0250 (octal) = 168 (decimal)

However, ip-address version 10.2.0's Address4 class used standard JavaScript parseInt() or equivalent decimal parsing, treating 0177 as simply 177. This creates a parsing oracle:

Input ip-address 10.2.0 sees OS Resolver sees
0177.0.0.01 177.0.0.1 (public) 127.0.0.1 (loopback)
0300.0250.0.01 300.250.0.1 (invalid) 192.168.0.1 (private)
010.0.0.01 10.0.0.1 (private ✓ blocked) 8.0.0.1 (public)

Attack Scenario

Consider a typical SSRF protection pattern in a Node.js application:

const { Address4 } = require('ip-address');

function isInternalIP(ipString) {
  const addr = new Address4(ipString);
  // Check if the IP is in private ranges
  return addr.isInSubnet(new Address4('127.0.0.0/8')) ||
         addr.isInSubnet(new Address4('10.0.0.0/8')) ||
         addr.isInSubnet(new Address4('172.16.0.0/12')) ||
         addr.isInSubnet(new Address4('192.168.0.0/16'));
}

// Attacker supplies: "0177.0.0.01"
if (!isInternalIP(userSuppliedIP)) {
  // Library says "177.0.0.1" — not internal, allow the request!
  fetch(`http://${userSuppliedIP}/admin/secrets`);
  // But the OS resolves 0177.0.0.01 → 127.0.0.1 — SSRF to localhost!
}

An attacker targeting the Argo platform could use this to:
1. Access internal metadata endpoints (e.g., cloud provider metadata at 0251.0250.0251.0376169.254.169.254)
2. Reach internal microservices behind the firewall
3. Exfiltrate secrets from the local environment

Why This Is High Severity

The Argo project handles AI agent orchestration and likely makes outbound HTTP requests as part of its MCP (Model Context Protocol) SDK integration (@modelcontextprotocol/sdk). Any URL or IP validation using ip-address 10.2.0 could be bypassed, giving attackers access to internal infrastructure.

The Fix

The fix upgrades ip-address from 10.2.0 to 10.3.1, which correctly handles leading-zero octets by either rejecting them as ambiguous or interpreting them consistently with OS resolver behavior.

Changes Made

1. package.json — Pin the fixed version as a direct dependency:

-    "sharp": "^0.35.0"
+    "sharp": "^0.35.0",
+    "ip-address": "10.3.1"

By adding ip-address as a direct dependency pinned to exactly 10.3.1 (no caret or tilde), the project ensures that regardless of what peer dependencies request, the resolved version will always be the patched one.

2. package-lock.json — Lock the resolved version:

 "node_modules/ip-address": {
-  "version": "10.2.0",
-  "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.2.0.tgz",
-  "integrity": "sha512-/+S6j4E9AHvW9SWMSEY9Xfy66O5PWvVEJ08O0y5JGyEKQpojb0K0GKpz/v5HJ/G0vi3D2sjGK78119oXZeE0qA==",
+  "version": "10.3.1",
+  "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.3.1.tgz",
+  "integrity": "sha512-1e9d3kb97NHJTIJDZW9rKqW2h6+dFa50Dy0fpPSMQp2ADje5gvKsXmdiK6dwY5t76TaTt5+P5N1Y/LoToIxP6g==",

How 10.3.1 Fixes the Issue

Version 10.3.1 of ip-address modifies the Address4 parsing logic to:
1. Reject octets with leading zeros as invalid input, or
2. Parse them as octal to match OS resolver behavior

Either approach eliminates the parsing differential. The PR notes that "it only tightens handling of untrusted input and leaves valid inputs unaffected"—meaning standard decimal IPv4 addresses like 192.168.1.1 continue to work identically.

Why a Direct Dependency Pin?

Notice in the original package-lock.json, ip-address was marked as "peer": true—it was pulled in transitively. By adding it as a direct dependency in package.json with an exact version pin ("ip-address": "10.3.1" without ^), the project takes explicit control over which version is resolved, preventing future regressions from transitive dependency updates.

Prevention & Best Practices

1. Defense in Depth for SSRF

Never rely solely on pre-request IP validation. Implement a multi-layer approach:

// Layer 1: Reject ambiguous formats before parsing
if (/^0\d/.test(octet)) {
  throw new Error('Leading zeros in IP octets are not permitted');
}

// Layer 2: Validate the resolved address, not just the input
const resolved = await dns.resolve4(hostname);
if (isPrivateIP(resolved[0])) {
  throw new Error('Resolved to internal address');
}

// Layer 3: Network-level controls (firewall egress rules)

2. Audit Transitive Dependencies

Use npm audit, Trivy, or Snyk to continuously scan your lockfile. The vulnerability existed in a peer dependency—not something directly imported—making it easy to miss in code review.

3. Pin Critical Security Dependencies

For libraries that handle security-sensitive parsing (IP addresses, URLs, certificates), use exact version pins rather than semver ranges to prevent unexpected changes.

4. Relevant Standards

  • OWASP SSRF Prevention Cheat Sheet: Recommends validating resolved IPs, not input strings
  • CWE-918: Server-Side Request Forgery
  • CWE-1389: Incorrect Parsing of Numbers with Different Radixes

Key Takeaways

  • Leading zeros in IPv4 octets are ambiguous: 0177 means 177 in some parsers and 127 in others—never trust a single parser's interpretation for security decisions.
  • The ip-address npm package at 10.2.0 had a critical parsing differential in its Address4 class that made SSRF bypass trivial with crafted octal-notation addresses.
  • Peer dependencies can introduce high-severity vulnerabilities silently—the vulnerable ip-address version was pulled transitively, not directly imported by Argo.
  • Pinning "ip-address": "10.3.1" as a direct dependency overrides the peer dependency resolution and ensures the patched version is always used.
  • Post-resolution validation is essential: Even with a fixed parser, always validate the actual resolved IP address against deny lists before making outbound requests.

How Orbis AppSec Detected This

  • Source: User-influenced input (URLs or IP addresses) entering the application through API endpoints, potentially processed via the @modelcontextprotocol/sdk or @hono/node-server request handlers.
  • Sink: The Address4 constructor in ip-address 10.2.0 (node_modules/ip-address), which parses IPv4 addresses with leading-zero octets as decimal, creating a trust-boundary bypass before outbound HTTP requests.
  • Missing control: No rejection or consistent octal interpretation of leading-zero IPv4 octets; no post-resolution IP validation.
  • CWE: CWE-918 (Server-Side Request Forgery)
  • Fix: Upgraded ip-address from 10.2.0 to 10.3.1, which eliminates the decimal/octal parsing inconsistency in Address4, and pinned it as a direct dependency to prevent transitive regression.

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 textbook example of how parsing differentials create security vulnerabilities. The ip-address library and the OS resolver disagreed on what 0177.0.0.01 means, and that disagreement is all an attacker needs to bypass SSRF protections. The fix—upgrading to 10.3.1 and pinning the dependency—is minimal in code changes but critical in security impact.

For any Node.js application that validates IP addresses before making outbound requests, this vulnerability is a reminder: your validator and your resolver must agree on the interpretation of every input, or your security boundary doesn't exist.

References

Frequently Asked Questions

What is an IP address parsing inconsistency vulnerability?

It occurs when two components in a system interpret the same IP address string differently—one may see "0177.0.0.1" as 177.0.0.1 (decimal) while another interprets it as 127.0.0.1 (octal), allowing security checks to be bypassed.

How do you prevent SSRF via IP parsing in Node.js?

Use libraries that strictly reject ambiguous IP formats (like leading zeros), validate resolved addresses against deny lists after DNS resolution, and keep IP-parsing dependencies updated.

What CWE is SSRF?

CWE-918: Server-Side Request Forgery (SSRF). Related CWEs include CWE-1389 (Incorrect Parsing of Numbers with Different Radixes).

Is URL validation enough to prevent SSRF?

No. URL validation alone is insufficient because parsing differentials between validators and resolvers can allow crafted addresses to bypass checks. You must also validate the resolved IP address.

Can static analysis detect IP parsing SSRF?

Yes. Tools like Trivy, Semgrep, and Snyk can detect known vulnerable library versions and flag patterns where user-supplied URLs are fetched without post-resolution validation.

View the Security Fix

Check out the pull request that fixed this vulnerability

View PR #308

Related Articles

critical

How Server-Side Request Forgery happens in Node.js and how to fix it

The order-flow service in a Node.js e-commerce backend built an outbound fetch() URL by directly concatenating a configurable `sendingOrder.url` value with a query string, with no validation of protocol or destination. This allowed order data—including customer and payment-adjacent information—to be silently redirected to an attacker-controlled endpoint simply by changing a config value or environment variable.

medium

How gitlab.bandit.B501 happens in Python and how to fix it

The `proverbia-scraper.py` script disabled TLS certificate verification on its `requests.get()` call and silenced the resulting security warnings, exposing the scraper to man-in-the-middle attacks. The fix removes the `verify=False` flag and the warning suppression, restoring proper certificate validation while keeping the existing 30-second timeout intact.

high

How Server-Side Request Forgery (SSRF) happens in Node.js and how to fix it

A critical Server-Side Request Forgery (SSRF) vulnerability in the ldfetch CLI tool allowed attackers to access internal cloud metadata services and local files through unvalidated URL arguments. The fix introduces strict protocol validation with an explicit opt-in flag for local file access, transforming a dangerous default into a secure-by-design implementation.

high

How Server-Side Request Forgery (SSRF) happens in Go HTTP handlers and how to fix it

A Server-Side Request Forgery (SSRF) vulnerability was discovered in `internal/web/controller/server.go` where the `applySubTemplate` endpoint accepted arbitrary URLs from user input and passed them directly to `serverService.ApplySubTemplateFromGithub()` without any host validation. An attacker could exploit this to make the server issue HTTP requests to internal network resources, cloud metadata endpoints, or redirect-controlled destinations. The fix introduces a strict allowlist that restrict

critical

How SSRF via Vulnerable Dependency Versions Happens in Node.js and How to Fix It

A permissive semver range in `package.json` allowed npm to install axios versions vulnerable to SSRF (CVE-2024-39338). By bumping the minimum version from `^1.6.0` to `^1.7.4`, all downstream consumers of this SDK are now protected from server-side request forgery attacks. This critical fix required changing just one line in the dependency manifest.

critical

How Server-Side Request Forgery (SSRF) happens in JavaScript and how to fix it

A critical Server-Side Request Forgery (SSRF) vulnerability was discovered in playground.html where the `__forEachRdfMessageChunkFromUrl` function fetched user-controlled URLs without validating against private IP ranges or internal network addresses. The fix introduces a comprehensive `__isBlockedFetchUrl` validation function that blocks requests to localhost, private IP ranges, and link-local addresses before any fetch occurs.