How IP Address Parsing Inconsistency Happens in Node.js and How to Fix It
The Vulnerability Explained
In the CanvaLight plugin's dependency tree, a subtle but critical flaw existed in the ip-address npm package (version 10.2.0). The Address4 class—responsible for parsing IPv4 address strings—had a parsing inconsistency that could silently bypass IP-based trust boundaries.
The Core Problem:
The ip-address library decoded leading-zero octets as decimal numbers, while standard DNS resolvers and most operating systems decode them as octal. This created a dangerous divergence:
- What the
ip-addresslibrary saw:010.0.0.1→ parsed as10.0.0.1(decimal interpretation of010) - What DNS resolvers saw:
010.0.0.1→ parsed as8.0.0.1(octal interpretation of010)
For applications using the ip-address library to validate or filter requests against IP whitelists, an attacker could craft a malicious request with a leading-zero IP address that would:
1. Pass validation checks in the application (because ip-address decoded it as expected)
2. Resolve to a different IP address at the DNS/resolver level (because resolvers use octal)
3. Reach an internal or restricted service that the application thought it was blocking
Why This Matters for CanvaLight
The CanvaLight plugin (in plugins/canvasight/package-lock.json) depends on ip-address for network operations. If CanvaLight uses this library to:
- Validate client IP addresses against a whitelist
- Construct URLs for internal service calls
- Parse proxy headers
- Implement IP-based rate limiting
...then an attacker could exploit the parsing mismatch to:
- Bypass IP-based access controls
- Perform Server-Side Request Forgery (SSRF) attacks against internal services
- Access restricted resources that should have been blocked
Real-World Attack Scenario
Imagine CanvaLight has a whitelist: ["192.168.1.100", "192.168.1.101"]. An attacker crafts a request with the IP 192.0168.1.100 (leading zero in the second octet):
GET /api/internal HTTP/1.1
X-Forwarded-For: 192.0168.1.100
-
CanvaLight's validation (using
ip-address10.2.0):
- Parses192.0168.1.100→192.8.1.100(octal:0168=8in decimal... wait, no)
- Actually:192.0168.1.100→192.168.1.100(decimal interpretation of0168=168)
- ✅ Matches whitelist → Request allowed -
DNS resolver (standard behavior):
- Parses192.0168.1.100→192.8.1.100(octal:0168=8)
- ❌ Does not match whitelist → But resolver already resolved to192.8.1.100
- Request reaches192.8.1.100instead of the expected service
This is a trust-boundary bypass: the application and the resolver disagree on what IP address was requested.
The Fix
The fix upgrades the ip-address package from 10.2.0 to 10.3.1 in both package.json and package-lock.json:
Before (Vulnerable)
"node_modules/ip-address": {
"version": "10.2.0",
"resolved": "https://registry.npmmirror.com/ip-address/-/ip-address-10.2.0.tgz",
"integrity": "sha512-/+S6j4E9AHvW9SWMSEY9Xfy66O5PWvVEJ08O0y5JGyEKQpojb0K0GKpz/v5HJ/G0vi3D2sjGK78119oXZeE0qA==",
"license": "MIT",
"peer": true,
"engines": {
After (Fixed)
"node_modules/ip-address": {
"version": "10.3.1",
"resolved": "https://registry.npmmirror.com/ip-address/-/ip-address-10.3.1.tgz",
"integrity": "sha512-1e9d3kb97NHJTIJDZW9rKqW2h6+dFa50Dy0fpPSMQp2ADje5gvKsXmdiK6dwY5t76TaTt5+P5N1Y/LoToIxP6g==",
"license": "MIT",
"peer": true,
"engines": {
What Changed in Version 10.3.1
The ip-address 10.3.1 release corrected the Address4 parser to:
- Properly interpret leading-zero octets as octal, aligning with RFC standards and resolver behavior
- Validate that octal octets don't exceed 255 (since
0377in octal =255in decimal, the maximum valid octet value) - Reject invalid octal sequences that would produce out-of-range values
Now, when the parser encounters 010.0.0.1:
- It correctly interprets 010 as octal → 8 in decimal
- Result: 8.0.0.1 (matches DNS resolver behavior)
- Applications using this library now have consistent parsing across the entire stack
Why This Specific Fix Works
By upgrading to 10.3.1, the CanvaLight plugin now:
- ✅ Parses IP addresses consistently with DNS resolvers and OS-level network stacks
- ✅ Prevents attackers from crafting octal-encoded IPs to bypass validation
- ✅ Maintains backward compatibility for valid IP addresses (no leading zeros or standard decimal notation)
- ✅ Closes the trust-boundary gap that enabled SSRF attacks
Prevention & Best Practices
1. Always Validate IP Address Parsing Consistency
When using any IP parsing library, verify it matches your DNS resolver and network stack behavior:
// Test that your parser matches resolver behavior
const Address4 = require('ip-address').Address4;
const testCases = [
'192.168.1.1', // Standard
'010.0.0.1', // Octal (should parse to 8.0.0.1)
'0377.0377.0377.0377' // Max octal (should parse to 255.255.255.255)
];
testCases.forEach(ip => {
const parsed = new Address4(ip);
console.log(`${ip} -> ${parsed.address}`);
// Compare against: nslookup or dig output
});
2. Keep Dependencies Updated
IP parsing is security-critical. Subscribe to security advisories for your IP libraries:
- Use npm audit regularly
- Enable Dependabot or similar tools
- Review and apply patches for high-severity vulnerabilities
3. Never Trust IP-Based Access Controls Alone
Combine IP validation with other security measures:
- Use cryptographic authentication (JWT, OAuth, mTLS)
- Implement request signing
- Validate request origins through multiple channels
4. Use Canonical IP Representation
When storing or comparing IPs, normalize them first:
const Address4 = require('ip-address').Address4;
function normalizeIP(ipString) {
try {
const addr = new Address4(ipString);
return addr.address; // Returns canonical form
} catch (e) {
throw new Error(`Invalid IP: ${ipString}`);
}
}
// Now compare normalized forms
const whitelist = ['192.168.1.100', '192.168.1.101'].map(normalizeIP);
const clientIP = normalizeIP(req.headers['x-forwarded-for']);
5. Leverage Static Analysis
Use security scanners to detect known vulnerable versions:
- Trivy: Detects CVE-2026-69192 in package-lock.json
- npm audit: Built-in vulnerability detection
- Snyk: Continuous monitoring for dependency vulnerabilities
CWE References
- CWE-1025: Comparison Using Wrong Factors (the parser and resolver use different interpretation rules)
- CWE-436: Interpretation Conflict (same input, different meanings)
- CWE-918: Server-Side Request Forgery (SSRF) - the consequence of the parsing mismatch
Key Takeaways
-
Leading-zero octets are octal, not decimal: The
ip-address10.2.0 library incorrectly parsed010as10instead of8, breaking trust boundaries. Always verify your parser matches standard resolver behavior. -
Trust-boundary misalignment is a security risk: When your application and your network stack disagree on what an IP address means, attackers can exploit the gap. This specific vulnerability allowed SSRF by making whitelisted IPs resolve to different addresses.
-
Upgrade
ip-addressto 10.3.1 or later: The fix corrects octal interpretation and closes the parsing inconsistency. If your project depends onip-address, update immediately if you're on 10.2.0 or earlier. -
Test IP parsing against real resolvers: Don't assume your library matches DNS behavior. Write tests that compare your parser's output to actual DNS resolution for edge cases like leading-zero octets.
-
IP-based security requires consistency across the stack: Whether you're validating whitelists, parsing proxy headers, or constructing internal URLs, ensure all components (application code, libraries, DNS, OS) interpret IP addresses the same way.
How Orbis AppSec Detected This
Source: Dependency manifest (plugins/canvasight/package-lock.json) containing ip-address version 10.2.0
Sink: Any code path in the CanvaLight plugin that uses Address4 to parse IP addresses for validation, filtering, or access control decisions
Missing Control: The ip-address 10.2.0 library lacked proper octal interpretation for leading-zero octets, creating a divergence from standard resolver behavior
CWE: CWE-1025 (Comparison Using Wrong Factors) and CWE-436 (Interpretation Conflict)
Fix: Upgrade ip-address from 10.2.0 to 10.3.1 to correct the Address4 parser's handling of leading-zero octets, aligning it with RFC standards and DNS resolver behavior
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 demonstrates a subtle but critical class of vulnerabilities: semantic divergence between components that should agree on data interpretation. When an application's IP parser and a DNS resolver interpret the same IP string differently, attackers can exploit the gap to bypass security controls and execute SSRF attacks.
The fix—upgrading ip-address to 10.3.1—restores consistency by correcting octal interpretation. However, the broader lesson is clear: always verify that your security-critical libraries match the behavior of the systems they interact with. For IP addresses, this means testing against real DNS resolvers. For other data types (URLs, file paths, JSON), the principle remains the same.
By keeping dependencies updated, validating parsing consistency, and combining IP-based controls with cryptographic authentication, you can prevent similar vulnerabilities in your own code.
References
- CWE-1025: Comparison Using Wrong Factors
- CWE-436: Interpretation Conflict
- CWE-918: Server-Side Request Forgery (SSRF)
- OWASP Server-Side Request Forgery (SSRF) Prevention Cheat Sheet
- RFC 3986: Uniform Resource Identifier (URI) Generic Syntax
- npm ip-address Package
- Semgrep Rule: Dependency Version Check
- fix: upgrade ip-address to 10.3.1 (CVE-2026-69192)