New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

@fullerstack/nax-ipware

Package Overview
Dependencies
Maintainers
1
Versions
23
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@fullerstack/nax-ipware - npm Package Compare versions

Comparing version 0.0.2 to 0.0.3-dev-38256e136c

10

package.json
{
"version": "0.0.3-dev-38256e136c",
"name": "@fullerstack/nax-ipware",
"version": "0.0.2",
"license": "MIT",
"description": "A node library for server applications retrieving user's real IP address",

@@ -24,4 +25,4 @@ "homepage": "https://github.com/neekware/fullerstack/blob/main/libs/nax-ipware/README.md",

"dependencies": {
"tslib": "^2.0.0",
"ts-essentials": "^7.0.2"
"ts-essentials": "^7.0.2",
"tslib": "^2.0.0"
},

@@ -31,3 +32,2 @@ "main": "./src/index.js",

"author": "Val Neekman",
"license": "MIT",
"repository": {

@@ -40,2 +40,2 @@ "type": "git",

}
}
}

@@ -35,3 +35,3 @@ # NAX IPware (A Node Application Agnostic Library)

console.log(clientIp);
// { ip: '177.139.100.100'', routable: true, trustedProxies: false }
// { ip: '177.139.100.100'', isPublic: true, isRouteTrusted: false }
// do something with the ip address (e.g. pass it within the request)

@@ -42,3 +42,5 @@ // note: ip address doesn't change often, so better cache it for performance

// Order of precedence is (Public, Private, Loopback, None)
// `publicOnly` is `false` by default, if so, the order of precedence is (Public, Private, Loopback, None)
// if `publicOnly` is set to `true`, then a public ip is returned or an empty IpwareIpInfo
// `isRouteTrusted` is set to `true` if the proxy info matched the proxy configurations
```

@@ -93,3 +95,3 @@

A default list that holds the private address prefixes is called `IPWARE_PRIVATE_IP_PREFIX`.
This list is used to determine if an IP address is `public & routable` or `private`.
This list is used to determine if an IP address is `public` or `private`.

@@ -136,6 +138,5 @@ ```typescript

// In the above scenario, use your load balancer IP address as a way to filter out unwanted requests.
const ipInfo = ipware.getClientIpByTrustedProxies(request, {
const ipInfo = ipware.getClientIP(request, {
proxy: {
enabled: true,
proxyIpPrefixes: ['177.139.233.132']
proxyList: ['177.139.233.132']
},

@@ -145,6 +146,5 @@ });

// If you have multiple proxies, simply add them to the list
const ipInfo = ipware.getClientIpByTrustedProxies(request, {
const ipInfo = ipware.getClientIP(request, {
proxy: {
enabled: true,
proxyIpPrefixes: ['177.139.233.100', '177.139.233.132']
proxyList: ['177.139.233.100', '177.139.233.132']
},

@@ -154,13 +154,11 @@ });

// For proxy servers with fixed sub-domain and dynamic IP, use the following pattern.
const ipInfo = ipware.getClientIpByTrustedProxies(request, {
const ipInfo = ipware.getClientIP(request, {
proxy: {
enabled: true,
proxyIpPrefixes: ['177.139.', '177.140']
proxyList: ['177.139.', '177.140']
},
});
const ipInfo = ipware.getClientIpByTrustedProxies(request, {
const ipInfo = ipware.getClientIP(request, {
proxy: {
enabled: true,
proxyIpPrefixes: ['177.139.233.', '177.139.240']
proxyList: ['177.139.233.', '177.139.240']
},

@@ -170,9 +168,17 @@ });

// For proxy by ip address, count will be ignored
const ipInfo = ipware.getClientIpByTrustedProxies(request, {
const ipInfo = ipware.getClientIP(request, {
proxy: {
enabled: true,
proxyIpPrefixes: ['177.139.', '177.140'],
proxyList: ['177.139.', '177.140'],
proxyCount: 2 // will be ignored
},
});
// For strict mode, we either return the ip that matches the proxy info, or none
const ipInfo = ipware.getClientIP(request, {
proxy: {
strict: true,
proxyList: ['177.139.233.', '177.139.240']
},
});
```

@@ -198,6 +204,5 @@

// In the above scenario, the total number of proxies can be used as a way to filter out unwanted requests.
const ipInfo = ipware.getClientIpByProxyCount(request, {
const ipInfo = ipware.getClientIP(request, {
proxy: {
enabled: true,
proxyCount: 1
count: 1
},

@@ -207,9 +212,17 @@ });

// For proxy by count, proxy prefixes will be ignored
const ipInfo = ipware.getClientIpByProxyCount(request, {
const ipInfo = ipware.getClientIP(request, {
proxy: {
enabled: true,
proxyCount: 1,
proxyIpPrefixes: ['177.139.233.'] // will be ignored
count: 1
proxyList: ['177.139.233.'] // will be ignored
},
});
// For strict mode, we either return the ip that matches the proxy info, or none
const ipInfo = ipware.getClientIP(request, {
proxy: {
strict: true,
count: 1
},
});
```

@@ -226,2 +239,12 @@

### Public IP Address ONLY (routable on the internet)
```typescript
// For publicOnly mode, we either return the first public IP address based on order or none
const ipInfo = ipware.getClientIP(request, {
publicOnly: true
});
```
### Originating Request

@@ -228,0 +251,0 @@

@@ -18,7 +18,2 @@ /**

/**
* Given two IP addresses, it returns the the best match ip
* Best match order: precedence is (Public, Private, Loopback, null)
*/
private bestMatched;
/**
* Determines if IP is loopback

@@ -42,16 +37,2 @@ * @param {string} ip Ip address

/**
* Return the client IP address as per proxies count configuration
* @param request HTTP request
* @param options ipware call options
* @returns IpwareIpInfo
*/
getClientIpByProxyCount(request: any, callOptions?: IpwareCallOptions): IpwareIpInfo;
/**
* Return the client IP address as per proxies ip prefixes configuration
* @param request HTTP request
* @param options ipware call options
* @returns IpwareIpInfo
*/
getClientIpByTrustedProxies(request: any, callOptions?: IpwareCallOptions): IpwareIpInfo;
/**
* Return the client IP address as per best matched IP address

@@ -58,0 +39,0 @@ * @param request HTTP request

@@ -19,7 +19,1 @@ /**

export declare const IpwareCallOptionsDefault: DeepReadonly<IpwareCallOptions>;
export declare const IPWARE_ERROR_MESSAGE: {
proxyDisabledOnProxyAwareApi: string;
proxyEnabledWithoutProxyCount: string;
proxyEnabledWithoutTrustedProxies: string;
proxyEnabledOnNonProxyAwareApi: string;
};

@@ -10,3 +10,3 @@ "use strict";

Object.defineProperty(exports, "__esModule", { value: true });
exports.IPWARE_ERROR_MESSAGE = exports.IpwareCallOptionsDefault = exports.IpwareConfigOptionsDefault = exports.IpwareProxyOptionsDefault = exports.IPWARE_DEFAULT_IP_INFO = exports.IPWARE_CLIENT_IP_ORDER_DEFAULT = exports.IPWARE_NON_PUBLIC_IP_PREFIX = exports.IPWARE_LOOPBACK_PREFIX = exports.IPWARE_PRIVATE_IP_PREFIX = exports.IPWARE_HEADERS_IP_ATTRIBUTES_ORDER = void 0;
exports.IpwareCallOptionsDefault = exports.IpwareConfigOptionsDefault = exports.IpwareProxyOptionsDefault = exports.IPWARE_DEFAULT_IP_INFO = exports.IPWARE_CLIENT_IP_ORDER_DEFAULT = exports.IPWARE_NON_PUBLIC_IP_PREFIX = exports.IPWARE_LOOPBACK_PREFIX = exports.IPWARE_PRIVATE_IP_PREFIX = exports.IPWARE_HEADERS_IP_ATTRIBUTES_ORDER = void 0;
// Search for the real IP address in the following order (user configurable)

@@ -203,10 +203,10 @@ // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Forwarded-For

ip: '',
routable: false,
trustedRoute: false,
isPublic: false,
isRouteTrusted: false,
};
exports.IpwareProxyOptionsDefault = {
enabled: false,
proxyIpPrefixes: [],
proxyList: [],
count: 0,
order: exports.IPWARE_CLIENT_IP_ORDER_DEFAULT,
strict: false,
};

@@ -218,2 +218,3 @@ exports.IpwareConfigOptionsDefault = {

proxy: exports.IpwareProxyOptionsDefault,
publicOnly: false,
};

@@ -223,9 +224,4 @@ exports.IpwareCallOptionsDefault = {

proxy: exports.IpwareProxyOptionsDefault,
publicOnly: false,
};
exports.IPWARE_ERROR_MESSAGE = {
proxyDisabledOnProxyAwareApi: 'Proxy check disabled, on calls to proxy-aware api',
proxyEnabledWithoutProxyCount: 'Proxy check enabled, yet no proxy count not provided',
proxyEnabledWithoutTrustedProxies: 'Proxy check enabled, yet no proxy prefixes provided',
proxyEnabledOnNonProxyAwareApi: 'Proxy check enabled, yet wrong API called',
};
//# sourceMappingURL=ipware.default.js.map

@@ -17,4 +17,4 @@ "use strict";

constructor(options) {
this.options = lodash_1.cloneDeep(ipware_default_1.IpwareConfigOptionsDefault);
this.options = lodash_1.mergeWith(this.options, options, (dest, src) => Array.isArray(dest) ? src : undefined);
this.options = ipware_default_1.IpwareConfigOptionsDefault;
this.options = lodash_1.mergeWith(lodash_1.cloneDeep(this.options), options, (dest, src) => Array.isArray(dest) ? src : undefined);
}

@@ -27,4 +27,4 @@ /**

if (ipware_util_1.isValidIP(cleanedIp)) {
const routable = this.isPublic(cleanedIp);
return { ip: cleanedIp, routable, trustedRoute: false };
const isPublic = this.isPublic(cleanedIp);
return { ip: cleanedIp, isPublic, isRouteTrusted: false };
}

@@ -34,18 +34,2 @@ return ipware_default_1.IPWARE_DEFAULT_IP_INFO;

/**
* Given two IP addresses, it returns the the best match ip
* Best match order: precedence is (Public, Private, Loopback, null)
*/
bestMatched(lastIP, nextIp) {
if (!lastIP) {
return nextIp;
}
if (this.isPublic(lastIP) && this.isPrivate(nextIp)) {
return lastIP;
}
if (this.isPrivate(lastIP) && this.isLoopback(nextIp)) {
return lastIP;
}
return nextIp;
}
/**
* Determines if IP is loopback

@@ -89,3 +73,3 @@ * @param {string} ip Ip address

/**
* Return the client IP address as per proxies count configuration
* Return the client IP address as per best matched IP address
* @param request HTTP request

@@ -95,4 +79,6 @@ * @param options ipware call options

*/
getClientIpByProxyCount(request, callOptions) {
getClientIP(request, callOptions) {
const options = lodash_1.mergeWith(lodash_1.cloneDeep(this.options), callOptions, (dest, src) => Array.isArray(dest) ? src : undefined);
const privateIPList = [];
const loopbackIPList = [];
let ipInfo;

@@ -103,110 +89,45 @@ for (const key of options.requestHeadersOrder) {

// process the header attribute, we can have multiple ip addresses in the same attribute
const ipData = ipware_util_1.getIPsFromString(ipString);
// proxy options not configured, we can't continue
if (!options.proxy.enabled) {
throw new Error(ipware_default_1.IPWARE_ERROR_MESSAGE.proxyDisabledOnProxyAwareApi);
const ipData = ipware_util_1.getIPsFromString(ipString, options.proxy.order);
// we are expecting at least `1` ip address
if (ipData.count < 1) {
continue;
}
// proxy check enabled, but count is not configured properly, we can't continue
if (options.proxy.count < 1) {
throw new Error(ipware_default_1.IPWARE_ERROR_MESSAGE.proxyEnabledWithoutProxyCount);
}
// we are expecting requests via `x` number of proxies, but the IP counts don't match
const clientIp = ipData.ips[0];
// we are expecting `x` number of ips as per `proxy.count`
if (options.proxy.count > 0 && options.proxy.count !== ipData.count - 1) {
continue;
}
// some configuration may be `custom` & reverse in direction (`proxy2, proxy1, client`)
// the default configuration for most servers is `left-most` (`client, <proxy1, proxy2`)
if (options.proxy.order !== ipware_default_1.IPWARE_CLIENT_IP_ORDER_DEFAULT) {
ipData.ips = ipData.ips.reverse();
}
// we matched the proxy information, however, the client IP still may be private
// we let the caller to decide what to do with a private client IP
ipInfo = this.getInfo(ipData.ips[0]);
ipInfo.trustedRoute = true;
return ipInfo;
}
}
// we did not find any ip address based on the caller requirement
return ipware_default_1.IPWARE_DEFAULT_IP_INFO;
}
/**
* Return the client IP address as per proxies ip prefixes configuration
* @param request HTTP request
* @param options ipware call options
* @returns IpwareIpInfo
*/
getClientIpByTrustedProxies(request, callOptions) {
const options = lodash_1.mergeWith(lodash_1.cloneDeep(this.options), callOptions, (dest, src) => Array.isArray(dest) ? src : undefined);
let ipInfo;
for (const key of options.requestHeadersOrder) {
const ipString = ipware_util_1.getHeadersAttribute(request.headers, key);
if (ipString) {
// process the header attribute, we can have multiple ip addresses in the same attribute
const ipData = ipware_util_1.getIPsFromString(ipString);
// proxy options not configured, we can't continue
if (!options.proxy.enabled) {
throw new Error(ipware_default_1.IPWARE_ERROR_MESSAGE.proxyDisabledOnProxyAwareApi);
}
// proxy check enabled, but not configured properly, we can't continue
if (options.proxy.proxyIpPrefixes.length < 1) {
throw new Error(ipware_default_1.IPWARE_ERROR_MESSAGE.proxyEnabledWithoutTrustedProxies);
}
// we are expecting requests via specific trusted proxies, but specified proxies are more available IP addresses
if (options.proxy.proxyIpPrefixes.length > ipData.count - 1) {
// we are expecting at least `1` ip address as per `proxy.proxyList`
if (options.proxy.proxyList.length > 0 && ipData.count < 2) {
continue;
}
// some configuration may be `custom` & reverse in direction (`proxy2, proxy1, client`)
// the default configuration for most servers is `left-most` (`client, <proxy1, proxy2`)
if (options.proxy.order !== ipware_default_1.IPWARE_CLIENT_IP_ORDER_DEFAULT) {
ipData.ips = ipData.ips.reverse();
}
for (let idx = options.proxy.proxyIpPrefixes.length - 1; idx > 1; idx--) {
// using startWith to allow for partial matches (e.g. `10.`, `10.0.`)
// we match all proxy prefixes in the array, if so we can take the first ip as client IP
if (!ipData.ips[idx].startsWith(options.proxy.proxyIpPrefixes[idx])) {
return ipware_default_1.IPWARE_DEFAULT_IP_INFO;
if (options.proxy.proxyList.length > 0) {
for (const proxy of options.proxy.proxyList) {
// the right most ip address is the most trusted proxy
// ip spoofing is always possible if the hacker guess our proxy's ip address or subnet, and sends in a fake ip address
// to prevent ip spoofing, ipware must be combined with ip filtering at firewall level
// alternatively you can configure your proxy to send a customer header attribute that is hard to guess, but your server is aware of it
if (ipData.ips[ipData.count - 1].startsWith(proxy)) {
ipInfo = this.getInfo(clientIp);
if (ipInfo.ip) {
ipInfo.isRouteTrusted = true;
// configuration is strictly looking for a public ip address only, or none at all, continue processing ...
if (options.publicOnly && !ipInfo.isPublic) {
continue;
}
return ipInfo;
}
}
}
}
// we matched the proxy information, however, the client IP still may be private
// we let the caller to decide what to do a private client IP
ipInfo = this.getInfo(ipData.ips[0]);
ipInfo.trustedRoute = true;
return ipInfo;
}
}
// we did not find any ip address based on the caller requirement
return ipware_default_1.IPWARE_DEFAULT_IP_INFO;
}
/**
* Return the client IP address as per best matched IP address
* @param request HTTP request
* @param options ipware call options
* @returns IpwareIpInfo
*/
getClientIP(request, callOptions) {
const options = lodash_1.mergeWith(lodash_1.cloneDeep(this.options), callOptions, (dest, src) => Array.isArray(dest) ? src : undefined);
let ipInfo;
for (const key of options.requestHeadersOrder) {
const ipString = ipware_util_1.getHeadersAttribute(request.headers, key);
if (ipString) {
// process the header attribute, we can have multiple ip addresses in the same attribute
const ipData = ipware_util_1.getIPsFromString(ipString);
// expecting at least one IP address, let's look for the next header
if (ipData.count < 1) {
continue;
}
// proxy check enabled, but not wrong api is called, better not continue for maximum security
if (options.proxy.enabled) {
throw new Error(ipware_default_1.IPWARE_ERROR_MESSAGE.proxyEnabledOnNonProxyAwareApi);
}
// handle custom ip order
if (options.proxy.order !== ipware_default_1.IPWARE_CLIENT_IP_ORDER_DEFAULT && ipData.count > 1) {
ipData.ips = ipData.ips.reverse();
}
// we return the first public and routable IP address, based on headers precedence order
for (const ip of ipData.ips) {
ipInfo = this.getInfo(ip);
if (ipInfo.ip && ipInfo.routable) {
ipInfo.trustedRoute = true;
return ipInfo;
else {
ipInfo = this.getInfo(clientIp);
if (ipInfo.ip) {
// configuration is strictly looking for a public ip address only, or none at all
if (options.publicOnly && !ipInfo.isPublic) {
this.isLoopback(ipInfo.ip) ? loopbackIPList.push(ipInfo) : privateIPList.push(ipInfo);
}
else {
return ipInfo;
}
}

@@ -216,8 +137,31 @@ }

}
// in strict mode, we either return an ip that comes through the matching proxy/count or none
if (options.proxy.strict && (options.proxy.proxyList.length > 0 || options.proxy.count > 0)) {
return ipware_default_1.IPWARE_DEFAULT_IP_INFO;
}
// no ip address from headers, let's fallback to the request itself
const reqIp = ipware_util_1.getIpFromRequest(request);
ipInfo = this.getInfo(reqIp);
if (ipInfo.ip && ipInfo.routable) {
return ipInfo;
if (ipInfo.ip) {
// configuration is strictly looking for a public ip address only, or none at all
if (options.publicOnly && ipInfo.isPublic) {
return ipInfo;
}
else {
this.isLoopback(ipInfo.ip) ? loopbackIPList.push(ipInfo) : privateIPList.push(ipInfo);
}
}
// no public ip address at this point, return empty ip info if configuration is publicOnly
if (options.publicOnly) {
return ipware_default_1.IPWARE_DEFAULT_IP_INFO;
}
// the best private ip address is the first one in the list
if (privateIPList.length > 0) {
return privateIPList[0];
}
// the best loopback ip address is the first one in the list
if (loopbackIPList.length > 0) {
return loopbackIPList[0];
}
// unable to find any ip, return empty and let the caller decide what to do
return ipware_default_1.IPWARE_DEFAULT_IP_INFO;

@@ -224,0 +168,0 @@ }

@@ -13,4 +13,4 @@ /**

ip: string;
routable: boolean;
trustedRoute?: boolean;
isPublic: boolean;
isRouteTrusted?: boolean;
}

@@ -23,6 +23,6 @@ export interface IpwareData {

export interface IpwareProxyOptions {
enabled: boolean;
proxyIpPrefixes?: string[];
proxyList?: string[];
count?: number;
order?: string;
order?: IpwareClientIpOrder;
strict?: boolean;
}

@@ -34,2 +34,3 @@ export interface IpwareConfigOptions {

proxy?: IpwareProxyOptions;
publicOnly?: boolean;
}

@@ -39,2 +40,3 @@ export interface IpwareCallOptions {

proxy?: IpwareProxyOptions;
publicOnly?: boolean;
}

@@ -8,3 +8,3 @@ /**

*/
import { IpwareData, IpwareHeaders } from './ipware.model';
import { IpwareClientIpOrder, IpwareData, IpwareHeaders } from './ipware.model';
/**

@@ -28,4 +28,6 @@ * Check the validity of an IPv4 address

* Given a string, it returns a list of one or more valid IP addresses
* @param str - string to be parsed
* @param order - client ip order (default is `left-most`)
*/
export declare function getIPsFromString(str: string): IpwareData;
export declare function getIPsFromString(str: string, order?: IpwareClientIpOrder): IpwareData;
/**

@@ -32,0 +34,0 @@ * Returns HTTP request headers attribute by key

@@ -47,9 +47,15 @@ "use strict";

* Given a string, it returns a list of one or more valid IP addresses
* @param str - string to be parsed
* @param order - client ip order (default is `left-most`)
*/
function getIPsFromString(str) {
function getIPsFromString(str, order = 'left-most') {
const ipList = { ips: [], count: 0 };
for (const ip of str.toLowerCase().split(',').map(cleanUpIP).filter(isValidIP)) {
ipList.ips.push(ip);
for (const ip of str
.toLowerCase()
.split(',')
.map(cleanUpIP)
.filter((ip) => ip)) {
order === 'left-most' ? ipList.ips.push(ip) : ipList.ips.unshift(ip);
}
ipList.count = ipList.ips.length || 0;
ipList.count = ipList.ips.length;
return ipList;

@@ -56,0 +62,0 @@ }

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

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