npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@bybrave/ip2

v3.0.0

Published

Maintained fork of ip — IP address utilities with the unpatched SSRF advisory (CVE-2024-29415 / GHSA-2p57-rm9w-gvfp) fixed

Readme

@bybrave/ip2

CI npm

Maintained fork of ip — IP address utilities for Node.js — with the unpatched SSRF advisory fixed.

The original package has ~38M downloads/month, no release since February 2024, and a HIGH-severity advisory (CVE-2024-29415 / GHSA-2p57-rm9w-gvfp) that affects every published version and makes npm audit fail on every install. This fork fixes the root cause instead of patching regexes.

npm install @bybrave/ip2
// CommonJS — drop-in
const ip = require('@bybrave/ip2');

// ESM — named imports
import { isPrivate, isPublic, cidrSubnet } from '@bybrave/ip2';

// TypeScript types built in

The vulnerability, and how it is fixed

The original isPrivate() / isPublic() / isLoopback() ran regexes over the raw input string. Any spelling of an address the regexes didn't anticipate — octal or hex octets, abbreviated IPv4, uncompressed IPv6 — was silently classified as public:

// original [email protected] — all of these are loopback/private, all reported PUBLIC:
ip.isPublic('127.1')            // true 💥
ip.isPublic('012.1.2.3')        // true 💥  (octal 10.1.2.3)
ip.isPublic('01200034567')      // true 💥  (integer 10.0.14.119)
ip.isPublic('000:0:0000::01')   // true 💥  (that's ::1)

If those answers guard outgoing requests, that's an SSRF hole (CVE-2024-29415, incomplete fix of CVE-2023-42282).

This fork:

  1. Parses first, classifies second. Every classification function parses the address into bytes with a strict parser (same grammar as net.isIP, plus IPv6 zone IDs). Classification runs on bytes, never on strings.
  2. Rejects ambiguous input. Non-canonical spellings (127.1, 0x7f.1, 0177.0.0.1, 2130706433) throw instead of being guessed at. Fail closed, not open. If you need to accept legacy notations, normalize explicitly first: ip.fromLong(ip.normalizeToLong(addr)).
  3. Covers the full IANA special-purpose registries (IPv4, IPv6), not four regexes. isPrivate() now means "not globally reachable" — the same convention as Python's ipaddress. That adds CGNAT (100.64/10), TEST-NETs, benchmarking, multicast, reserved, ULA, documentation ranges and more — see the table below.
  4. Classifies transition addresses by their embedded IPv4. ::ffff:a.b.c.d (v4-mapped), 64:ff9b::a.b.c.d (NAT64) and 2002:xxxx:xxxx:: (6to4) are private/public according to the IPv4 address inside them.
// @bybrave/ip2
ip.isPublic('127.1')             // throws Error: Invalid ip address: 127.1
ip.isPrivate('000:0:0000::01')   // true (parsed as ::1)
ip.isLoopback('::fFFf:127.0.0.1')// true
ip.isPublic('64:ff9b::8.8.8.8')  // true  (NAT64 of a public address)
ip.isPrivate('100.64.0.1')       // true  (CGNAT — original said public)

All fixes over [email protected]

| Fixed | Original issue | |---|---| | CVE-2024-29415 / GHSA-2p57-rm9w-gvfp — SSRF via non-canonical addresses | #136, #150, #153, #158 | | isPrivate() misses CGNAT, TEST-NETs, benchmarking, multicast, reserved, documentation ranges | — | | cidrSubnet(...).contains() returns false for IPv4-mapped IPv6 addresses | #104 | | isV6Format() returns true for IPv4 addresses | #67 | | isV4Format() accepts octets ≥ 256 | #105 | | address('public') returned private addresses and vice versa (filter was inverted) | — | | loopback('ipv6') returned fe80::1, which is link-local, not loopback → now ::1 | — | | cidr()/cidrSubnet() silently produced garbage for invalid prefix lengths (/33, /x) → now throw | — | | address(name) crashed with TypeError for unknown interface names → now returns undefined | — | | TypeScript types built in (no @types/ip needed), ESM named exports | — |

Breaking changes (v3)

Version 3.0.0 because strictness is the fix — if you fed this library non-canonical strings, v2 was giving you wrong answers, not compatible ones:

  • isPrivate / isPublic / isLoopback / toBuffer / toLong throw on malformed or non-canonical input (octal/hex octets, 127.1, integer strings). Use normalizeToLong() first if you must accept legacy notations.
  • isLoopback('fe80::1') is now false (link-local ≠ loopback; still isPrivate). isLoopback('::') is now false (unspecified ≠ loopback; still isPrivate).
  • loopback('ipv6') returns '::1' instead of 'fe80::1'.
  • isPrivate() returns true for more ranges (CGNAT, TEST-NETs, multicast, …) — it now means "not globally reachable".
  • address('public') actually returns public addresses now. If you relied on the inverted behaviour to get a LAN address, use address('private') — that's what it was giving you.
  • Node.js ≥ 18.

API

Everything the original exports, plus isValid():

ip.address('public', 'ipv6')          // address of a network interface
ip.isPrivate('10.0.0.1')              // true — not globally reachable
ip.isPublic('8.8.8.8')                // true
ip.isLoopback('127.8.8.8')            // true
ip.isValid('300.1.2.3')               // false (never throws)
ip.isV4Format('192.168.0.1')          // true
ip.isV6Format('2001:db8::1')          // true
ip.isEqual('::ffff:7f00:1', '127.0.0.1') // true

ip.toBuffer('127.0.0.1')              // Buffer([127, 0, 0, 1])
ip.toString(buf, offset, length)      // '127.0.0.1'
ip.toLong('127.0.0.1')                // 2130706433
ip.fromLong(2130706433)               // '127.0.0.1'
ip.normalizeToLong('127.1')           // 2130706433 (lenient, -1 on error)

ip.fromPrefixLen(24)                  // '255.255.255.0'
ip.mask('192.168.1.134', '255.255.255.0') // '192.168.1.0'
ip.cidr('192.168.1.134/26')           // '192.168.1.128'
ip.not('255.255.255.0')               // '0.0.0.255'
ip.or('0.0.0.255', '192.168.1.10')    // '192.168.1.255'

const s = ip.cidrSubnet('192.168.1.134/26')
s.networkAddress                      // '192.168.1.128'
s.firstAddress                        // '192.168.1.129'
s.lastAddress                         // '192.168.1.190'
s.broadcastAddress                    // '192.168.1.191'
s.subnetMaskLength                    // 26
s.numHosts                            // 62
s.contains('::ffff:192.168.1.180')    // true (v4-mapped handled)

Ranges treated as private (not globally reachable)

IPv4: 0.0.0.0/8, 10.0.0.0/8, 100.64.0.0/10 (CGNAT), 127.0.0.0/8, 169.254.0.0/16, 172.16.0.0/12, 192.0.0.0/24, 192.0.2.0/24, 192.88.99.0/24, 192.168.0.0/16, 198.18.0.0/15, 198.51.100.0/24, 203.0.113.0/24, 224.0.0.0/4, 240.0.0.0/4 (incl. broadcast).

IPv6: ::/96 (unspecified, loopback, v4-compatible), 64:ff9b:1::/48, 100::/64, 2001::/23, 2001:db8::/32, 3fff::/20, 5f00::/16, fc00::/7, fe80::/10, fec0::/10, ff00::/8. Transition ranges ::ffff:0:0/96, 64:ff9b::/96 and 2002::/16 follow their embedded IPv4 address.

Support

If this package saves you time, you can support maintenance:

Ko-fi Bitcoin

Bitcoin (BTC): bc1q37557q5jpeaxqydzwvf3jgj7zhnfpn2td3q40q

Credits & license

MIT. Based on node-ip by Fedor Indutny.