Socket
Socket
Sign inDemoInstall

@arcjet/ip

Package Overview
Dependencies
Maintainers
2
Versions
27
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@arcjet/ip - npm Package Compare versions

Comparing version 1.0.0-alpha.13 to 1.0.0-alpha.14

6

index.d.ts

@@ -19,3 +19,7 @@ interface PartialSocket {

}
declare function findIP(request: RequestLike, headers: Headers): string;
export type Platform = "cloudflare" | "fly-io";
export interface Options {
platform?: Platform;
}
declare function findIP(request: RequestLike, headers: Headers, options?: Options): string;
export default findIP;

157

index.js

@@ -256,20 +256,16 @@ function parseXForwardedFor(value) {

}
// TODO: Evaluate if we need to allow other IPv4 addresses in development,
// such as "This network"
if (process.env["NODE_ENV"] === "production") {
// Private IPv4 address ranges
if (octets[0] === 10) {
return false;
}
if (octets[0] === 172 && octets[1] >= 16 && octets[1] <= 31) {
return false;
}
if (octets[0] === 192 && octets[1] === 168) {
return false;
}
// Loopback address
if (octets[0] === 127) {
return false;
}
// Private IPv4 address ranges
if (octets[0] === 10) {
return false;
}
if (octets[0] === 172 && octets[1] >= 16 && octets[1] <= 31) {
return false;
}
if (octets[0] === 192 && octets[1] === 168) {
return false;
}
// Loopback address
if (octets[0] === 127) {
return false;
}
// Shared range

@@ -346,26 +342,22 @@ if (octets[0] === 100 && (octets[1] & 0b1100_0000) === 0b0100_0000) {

}
// TODO: Evaluate if we need to allow other IPv6 addresses in development,
// such as "Unique local range" or "Unspecified address"
if (process.env["NODE_ENV"] === "production") {
// Loopback address
if (segments[0] === 0 &&
segments[1] === 0 &&
segments[2] === 0 &&
segments[3] === 0 &&
segments[4] === 0 &&
segments[5] === 0 &&
segments[6] === 0 &&
segments[7] === 0x1) {
return false;
}
// IPv4-mapped Address (`::ffff:0:0/96`)
if (segments[0] === 0 &&
segments[1] === 0 &&
segments[2] === 0 &&
segments[3] === 0 &&
segments[4] === 0 &&
segments[5] === 0xffff) {
return false;
}
// Loopback address
if (segments[0] === 0 &&
segments[1] === 0 &&
segments[2] === 0 &&
segments[3] === 0 &&
segments[4] === 0 &&
segments[5] === 0 &&
segments[6] === 0 &&
segments[7] === 0x1) {
return false;
}
// IPv4-mapped Address (`::ffff:0:0/96`)
if (segments[0] === 0 &&
segments[1] === 0 &&
segments[2] === 0 &&
segments[3] === 0 &&
segments[4] === 0 &&
segments[5] === 0xffff) {
return false;
}
// IPv4-IPv6 Translat. (`64:ff9b:1::/48`)

@@ -465,6 +457,47 @@ if (segments[0] === 0x64 && segments[1] === 0xff9b && segments[2] === 1) {

// SOFTWARE.
function findIP(request, headers) {
function findIP(request, headers, options = {}) {
// Prefer anything available via the platform over headers since headers can
// be set by users. Only if we don't have an IP available in `request` do we
// search the `headers`.
if (isGlobalIP(request.ip)) {
return request.ip;
}
const socketRemoteAddress = request.socket?.remoteAddress;
if (isGlobalIP(socketRemoteAddress)) {
return socketRemoteAddress;
}
const infoRemoteAddress = request.info?.remoteAddress;
if (isGlobalIP(infoRemoteAddress)) {
return infoRemoteAddress;
}
// AWS Api Gateway + Lambda
const requestContextIdentitySourceIP = request.requestContext?.identity?.sourceIp;
if (isGlobalIP(requestContextIdentitySourceIP)) {
return requestContextIdentitySourceIP;
}
// Platform-specific headers should only be accepted when we can determine
// that we are running on that platform. For example, the `CF-Connecting-IP`
// header should only be accepted when running on Cloudflare; otherwise, it
// can be spoofed.
const { platform } = options;
if (platform === "cloudflare") {
// CF-Connecting-IPv6: https://developers.cloudflare.com/fundamentals/reference/http-request-headers/#cf-connecting-ipv6
const cfConnectingIPv6 = headers.get("cf-connecting-ipv6");
if (isGlobalIPv6(cfConnectingIPv6)) {
return cfConnectingIPv6;
}
// CF-Connecting-IP: https://developers.cloudflare.com/fundamentals/reference/http-request-headers/#cf-connecting-ip
const cfConnectingIP = headers.get("cf-connecting-ip");
if (isGlobalIP(cfConnectingIP)) {
return cfConnectingIP;
}
}
// Fly.io: https://fly.io/docs/machines/runtime-environment/#fly_app_name
if (platform === "fly-io") {
// Fly-Client-IP: https://fly.io/docs/networking/request-headers/#fly-client-ip
const flyClientIP = headers.get("fly-client-ip");
if (isGlobalIP(flyClientIP)) {
return flyClientIP;
}
}
// Standard headers used by Amazon EC2, Heroku, and others.

@@ -480,6 +513,6 @@ const xClientIP = headers.get("x-client-ip");

// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Forwarded-For
// We may find more than one IP in the `x-forwarded-for` header. We want to
// iterate left-to-right, since left-most IP will be closest to the client,
// and we'll return the first public IP in the list.
for (const item of xForwardedForItems) {
// We may find more than one IP in the `x-forwarded-for` header. Since the
// first IP will be closest to the user (and the most likely to be spoofed),
// we want to iterate tail-to-head so we reverse the list.
for (const item of xForwardedForItems.reverse()) {
if (isGlobalIP(item)) {

@@ -489,9 +522,2 @@ return item;

}
// Cloudflare.
// CF-Connecting-IP: https://developers.cloudflare.com/fundamentals/reference/http-request-headers/#cf-connecting-ip
const cfConnectingIP = headers.get("cf-connecting-ip");
if (isGlobalIP(cfConnectingIP)) {
return cfConnectingIP;
}
// TODO: CF-Connecting-IPv6: https://developers.cloudflare.com/fundamentals/reference/http-request-headers/#cf-connecting-ipv6
// DigitalOcean.

@@ -509,3 +535,3 @@ // DO-Connecting-IP: https://www.digitalocean.com/community/questions/app-platform-client-ip

}
// Akamai and Cloudflare
// Akamai
// True-Client-IP

@@ -516,8 +542,2 @@ const trueClientIP = headers.get("true-client-ip");

}
// Fly.io
// Fly-Client-IP: https://fly.io/docs/networking/request-headers/#fly-client-ip
const flyClientIP = headers.get("fly-client-ip");
if (isGlobalIP(flyClientIP)) {
return flyClientIP;
}
// Default nginx proxy/fcgi; alternative to x-forwarded-for, used by some proxies

@@ -552,21 +572,2 @@ // X-Real-IP

}
const socketRemoteAddress = request.socket?.remoteAddress;
if (isGlobalIP(socketRemoteAddress)) {
return socketRemoteAddress;
}
const infoRemoteAddress = request.info?.remoteAddress;
if (isGlobalIP(infoRemoteAddress)) {
return infoRemoteAddress;
}
// AWS Api Gateway + Lambda
const requestContextIdentitySourceIP = request.requestContext?.identity?.sourceIp;
if (isGlobalIP(requestContextIdentitySourceIP)) {
return requestContextIdentitySourceIP;
}
// Cloudflare fallback
// Cf-Pseudo-IPv4: https://blog.cloudflare.com/eliminating-the-last-reasons-to-not-enable-ipv6/#introducingpseudoipv4
const cfPseudoIPv4 = headers.get("cf-pseudo-ipv4");
if (isGlobalIP(cfPseudoIPv4)) {
return cfPseudoIPv4;
}
return "";

@@ -573,0 +574,0 @@ }

@@ -307,20 +307,16 @@ function parseXForwardedFor(value?: string | null): string[] {

// TODO: Evaluate if we need to allow other IPv4 addresses in development,
// such as "This network"
if (process.env["NODE_ENV"] === "production") {
// Private IPv4 address ranges
if (octets[0] === 10) {
return false;
}
if (octets[0] === 172 && octets[1] >= 16 && octets[1] <= 31) {
return false;
}
if (octets[0] === 192 && octets[1] === 168) {
return false;
}
// Private IPv4 address ranges
if (octets[0] === 10) {
return false;
}
if (octets[0] === 172 && octets[1] >= 16 && octets[1] <= 31) {
return false;
}
if (octets[0] === 192 && octets[1] === 168) {
return false;
}
// Loopback address
if (octets[0] === 127) {
return false;
}
// Loopback address
if (octets[0] === 127) {
return false;
}

@@ -415,30 +411,26 @@

// TODO: Evaluate if we need to allow other IPv6 addresses in development,
// such as "Unique local range" or "Unspecified address"
if (process.env["NODE_ENV"] === "production") {
// Loopback address
if (
segments[0] === 0 &&
segments[1] === 0 &&
segments[2] === 0 &&
segments[3] === 0 &&
segments[4] === 0 &&
segments[5] === 0 &&
segments[6] === 0 &&
segments[7] === 0x1
) {
return false;
}
// Loopback address
if (
segments[0] === 0 &&
segments[1] === 0 &&
segments[2] === 0 &&
segments[3] === 0 &&
segments[4] === 0 &&
segments[5] === 0 &&
segments[6] === 0 &&
segments[7] === 0x1
) {
return false;
}
// IPv4-mapped Address (`::ffff:0:0/96`)
if (
segments[0] === 0 &&
segments[1] === 0 &&
segments[2] === 0 &&
segments[3] === 0 &&
segments[4] === 0 &&
segments[5] === 0xffff
) {
return false;
}
// IPv4-mapped Address (`::ffff:0:0/96`)
if (
segments[0] === 0 &&
segments[1] === 0 &&
segments[2] === 0 &&
segments[3] === 0 &&
segments[4] === 0 &&
segments[5] === 0xffff
) {
return false;
}

@@ -566,2 +558,8 @@

export type Platform = "cloudflare" | "fly-io";
export interface Options {
platform?: Platform;
}
// Heavily based on https://github.com/pbojinov/request-ip

@@ -588,3 +586,10 @@ //

// SOFTWARE.
function findIP(request: RequestLike, headers: Headers): string {
function findIP(
request: RequestLike,
headers: Headers,
options: Options = {},
): string {
// Prefer anything available via the platform over headers since headers can
// be set by users. Only if we don't have an IP available in `request` do we
// search the `headers`.
if (isGlobalIP(request.ip)) {

@@ -594,2 +599,49 @@ return request.ip;

const socketRemoteAddress = request.socket?.remoteAddress;
if (isGlobalIP(socketRemoteAddress)) {
return socketRemoteAddress;
}
const infoRemoteAddress = request.info?.remoteAddress;
if (isGlobalIP(infoRemoteAddress)) {
return infoRemoteAddress;
}
// AWS Api Gateway + Lambda
const requestContextIdentitySourceIP =
request.requestContext?.identity?.sourceIp;
if (isGlobalIP(requestContextIdentitySourceIP)) {
return requestContextIdentitySourceIP;
}
// Platform-specific headers should only be accepted when we can determine
// that we are running on that platform. For example, the `CF-Connecting-IP`
// header should only be accepted when running on Cloudflare; otherwise, it
// can be spoofed.
const { platform } = options;
if (platform === "cloudflare") {
// CF-Connecting-IPv6: https://developers.cloudflare.com/fundamentals/reference/http-request-headers/#cf-connecting-ipv6
const cfConnectingIPv6 = headers.get("cf-connecting-ipv6");
if (isGlobalIPv6(cfConnectingIPv6)) {
return cfConnectingIPv6;
}
// CF-Connecting-IP: https://developers.cloudflare.com/fundamentals/reference/http-request-headers/#cf-connecting-ip
const cfConnectingIP = headers.get("cf-connecting-ip");
if (isGlobalIP(cfConnectingIP)) {
return cfConnectingIP;
}
}
// Fly.io: https://fly.io/docs/machines/runtime-environment/#fly_app_name
if (platform === "fly-io") {
// Fly-Client-IP: https://fly.io/docs/networking/request-headers/#fly-client-ip
const flyClientIP = headers.get("fly-client-ip");
if (isGlobalIP(flyClientIP)) {
return flyClientIP;
}
}
// Standard headers used by Amazon EC2, Heroku, and others.

@@ -606,6 +658,6 @@ const xClientIP = headers.get("x-client-ip");

// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Forwarded-For
// We may find more than one IP in the `x-forwarded-for` header. We want to
// iterate left-to-right, since left-most IP will be closest to the client,
// and we'll return the first public IP in the list.
for (const item of xForwardedForItems) {
// We may find more than one IP in the `x-forwarded-for` header. Since the
// first IP will be closest to the user (and the most likely to be spoofed),
// we want to iterate tail-to-head so we reverse the list.
for (const item of xForwardedForItems.reverse()) {
if (isGlobalIP(item)) {

@@ -616,11 +668,2 @@ return item;

// Cloudflare.
// CF-Connecting-IP: https://developers.cloudflare.com/fundamentals/reference/http-request-headers/#cf-connecting-ip
const cfConnectingIP = headers.get("cf-connecting-ip");
if (isGlobalIP(cfConnectingIP)) {
return cfConnectingIP;
}
// TODO: CF-Connecting-IPv6: https://developers.cloudflare.com/fundamentals/reference/http-request-headers/#cf-connecting-ipv6
// DigitalOcean.

@@ -640,3 +683,3 @@ // DO-Connecting-IP: https://www.digitalocean.com/community/questions/app-platform-client-ip

// Akamai and Cloudflare
// Akamai
// True-Client-IP

@@ -648,9 +691,2 @@ const trueClientIP = headers.get("true-client-ip");

// Fly.io
// Fly-Client-IP: https://fly.io/docs/networking/request-headers/#fly-client-ip
const flyClientIP = headers.get("fly-client-ip");
if (isGlobalIP(flyClientIP)) {
return flyClientIP;
}
// Default nginx proxy/fcgi; alternative to x-forwarded-for, used by some proxies

@@ -691,26 +727,2 @@ // X-Real-IP

const socketRemoteAddress = request.socket?.remoteAddress;
if (isGlobalIP(socketRemoteAddress)) {
return socketRemoteAddress;
}
const infoRemoteAddress = request.info?.remoteAddress;
if (isGlobalIP(infoRemoteAddress)) {
return infoRemoteAddress;
}
// AWS Api Gateway + Lambda
const requestContextIdentitySourceIP =
request.requestContext?.identity?.sourceIp;
if (isGlobalIP(requestContextIdentitySourceIP)) {
return requestContextIdentitySourceIP;
}
// Cloudflare fallback
// Cf-Pseudo-IPv4: https://blog.cloudflare.com/eliminating-the-last-reasons-to-not-enable-ipv6/#introducingpseudoipv4
const cfPseudoIPv4 = headers.get("cf-pseudo-ipv4");
if (isGlobalIP(cfPseudoIPv4)) {
return cfPseudoIPv4;
}
return "";

@@ -717,0 +729,0 @@ }

{
"name": "@arcjet/ip",
"version": "1.0.0-alpha.13",
"version": "1.0.0-alpha.14",
"description": "Arcjet utilities for finding the originating IP of a request",

@@ -44,7 +44,7 @@ "license": "Apache-2.0",

"devDependencies": {
"@arcjet/eslint-config": "1.0.0-alpha.13",
"@arcjet/rollup-config": "1.0.0-alpha.13",
"@arcjet/tsconfig": "1.0.0-alpha.13",
"@arcjet/eslint-config": "1.0.0-alpha.14",
"@arcjet/rollup-config": "1.0.0-alpha.14",
"@arcjet/tsconfig": "1.0.0-alpha.14",
"@jest/globals": "29.7.0",
"@rollup/wasm-node": "4.17.2",
"@rollup/wasm-node": "4.18.0",
"@types/node": "18.18.0",

@@ -51,0 +51,0 @@ "jest": "29.7.0",

<a href="https://arcjet.com" target="_arcjet-home">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="https://arcjet.com/arcjet-logo-dark-planet-arrival.svg">
<img src="https://arcjet.com/arcjet-logo-light-planet-arrival.svg" alt="Arcjet Logo" height="144" width="auto">
<source media="(prefers-color-scheme: dark)" srcset="https://arcjet.com/logo/arcjet-dark-lockup-voyage-horizontal.svg">
<img src="https://arcjet.com/logo/arcjet-light-lockup-voyage-horizontal.svg" alt="Arcjet Logo" height="128" width="auto">
</picture>

@@ -6,0 +6,0 @@ </a>

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc