Skip to content

Evaluate replacing custom IP validation with ipaddr.js library #11

@jonnyspicer

Description

@jonnyspicer

Background

PR #6 (Add adsb.lol network feed capability) includes custom IP address validation code for SSRF protection. This code uses regex patterns to identify private and reserved IP ranges.

Current implementation:

  • Custom isPrivateIP() function in src/server.js (lines 31-69)
  • Regex-based validation for IPv4 and IPv6 private ranges
  • Handles IPv4-mapped IPv6 addresses
  • Zero runtime dependencies

Relevant code:

function isPrivateIP(ip) {
  const ipv4PrivateRanges = [
    /^127\./, /^10\./, /^172\.(1[6-9]|2[0-9]|3[0-1])\./,
    /^192\.168\./, /^169\.254\./, /^0\.0\.0\.0$/,
  ];
  const ipv6PrivateRanges = [
    /^::1$/, /^fe80:/i, /^fc00:/i, /^fd00:/i, /^ff00:/i, /^::ffff:/i,
  ];
  // ... validation logic
}

The Tradeoff

Zero Dependencies (Current):

  • ✅ No supply chain attack risk
  • ✅ Smaller Docker image
  • ✅ Faster deployments (no npm registry dependency)
  • ✅ No dependency vulnerabilities
  • ❌ Security expertise needed to avoid bypasses
  • ❌ May miss edge cases
  • ❌ More maintenance burden

External Library (ipaddr.js):

  • ✅ Battle-tested by thousands of projects
  • ✅ Handles all edge cases (decimal notation, octal, hex, etc.)
  • ✅ Security experts have reviewed it
  • ✅ Actively maintained
  • ❌ Adds 1 runtime dependency (~1.9KB)
  • ❌ Supply chain risk (minimal for well-established library)
  • ❌ Need to monitor for updates

Recommended Library: ipaddr.js

Why ipaddr.js:

  • No known SSRF bypass vulnerabilities (unlike ip, private-ip, nossrf)
  • Small footprint (1.9KB minified + gzipped)
  • Correct classification of reserved/private ranges
  • Used by Express.js and other major projects
  • Active maintenance

Usage example:

import ipaddr from 'ipaddr.js';

function isPrivateIP(ip) {
  try {
    const addr = ipaddr.process(ip);
    return addr.range() !== 'unicast';  // Anything non-public
  } catch {
    return true;  // Invalid IP, block it
  }
}

Edge Cases Our Code May Miss

  1. Decimal IP notation: 2130706433 = 127.0.0.1
  2. Octal notation: 0177.0.0.1 = 127.0.0.1
  3. Hex notation: 0x7f.0.0.1 = 127.0.0.1
  4. Mixed notation: 127.1 = 127.0.0.1
  5. IPv6 zone IDs: fe80::1%eth0
  6. IPv6 compressed forms: Various compression patterns
  7. URL encoding: %3A%3A1 (encoded ::1)

Some of these are already blocked by our numeric format check, but a library provides comprehensive coverage.

Decision Framework

Consider the library if:

  • Security is critical (SSRF protection is security-critical ✓)
  • The code may be internet-facing (possible ✓)
  • Edge cases exist that could be missed (yes ✓)
  • Maintenance burden is a concern (small team ✓)

Keep custom code if:

  • Zero dependencies is a hard requirement (currently valued)
  • The code is simple and well-tested (moderately complex)
  • Performance is critical (not a bottleneck here)
  • You have security expertise in-house (unknown)

Recommendation

Add ipaddr.js as the single runtime dependency because:

  1. SSRF protection is security-critical
  2. The custom regex approach required 5+ iterations to get right in PR Add adsb.lol network feed capability #6
  3. Going from 0→1 dependency is different than 1→many
  4. 1.9KB is negligible on Raspberry Pi 5
  5. The security benefits outweigh the dependency cost

This maintains the "minimal dependencies" philosophy while using proven code for the one security-critical component.

Implementation (if approved)

npm install ipaddr.js

Files to modify:

  • package.json: Add "ipaddr.js": "^2.2.0" to dependencies
  • src/server.js: Replace isPrivateIP() function with ipaddr.js implementation
  • test/server.test.js: Update tests to cover edge cases

Estimated effort: 1-2 hours

Alternative: Keep Current Implementation

If the decision is to maintain zero dependencies, consider:

  1. Adding comprehensive test cases for edge case IP formats
  2. Documenting known limitations
  3. Security audit of the validation logic
  4. Regular review of SSRF bypass techniques

References


Label suggestions: enhancement, security, dependencies, needs-decision

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions