Skip to content

Commit

Permalink
feat(options): add allowMetaIPAddress option
Browse files Browse the repository at this point in the history
  • Loading branch information
azu committed Aug 29, 2019
1 parent 9663c5a commit 188668e
Show file tree
Hide file tree
Showing 3 changed files with 75 additions and 20 deletions.
26 changes: 20 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,12 +59,25 @@ It will prevent [DNS rebinding](https://en.wikipedia.org/wiki/DNS_rebinding)

## API


```ts
export interface RequestFilteringAgentOptions {
allowPrivateIP?: boolean;
// Allow to connect private IP address
// Example, http://127.0.0.1/, http://localhost/
// Default: false
allowPrivateIPAddress?: boolean;
// Allow to connect meta address 0.0.0.0
// 0.0.0.0 (IPv4) and :: (IPv6) a meta address that routing another address
// https://en.wikipedia.org/wiki/Reserved_IP_addresses
// https://tools.ietf.org/html/rfc6890
// Default: false
allowMetaIPAddress?: boolean;
// Allow address list
// This values are preferred than denyAddressList
// Default: []
allowIPAddressList?: string[];
denyIPAddressList?: string[];
// Deny address list
// Default: []
denyIPAddressList?: string[]
}
/**
* Apply request filter to http(s).Agent instance
Expand All @@ -89,6 +102,7 @@ export declare const globalHttpsAgent: RequestFilteringHttpsAgent;
* @param url
*/
export declare const useAgent: (url: string) => RequestFilteringHttpAgent | RequestFilteringHttpsAgent;

```

### Example: Create an Agent with options
Expand All @@ -101,8 +115,8 @@ const { RequestFilteringHttpAgent } = require("request-filtering-agent");

// Create http agent that allow 127.0.0.1, but it disallow other private ip
const agent = new RequestFilteringHttpAgent({
allowIPAddressList: ["127.0.0.1"], // it is preferred than allowPrivateIP option
allowPrivateIP: false, // Default: false
allowIPAddressList: ["127.0.0.1"], // it is preferred than allowPrivateIPAddress option
allowPrivateIPAddress: false, // Default: false
});
// 127.0.0.1 is private ip address, but it is allowed
const url = 'http://127.0.0.1:8080/';
Expand All @@ -128,7 +142,7 @@ const agent = new http.Agent({
});
// Apply request filtering to http.Agent
const agentWithFiltering = applyRequestFilter(agent, {
allowPrivateIP: false // Default: false
allowPrivateIPAddress: false // Default: false
});
// 127.0.0.1 is private ip address
const url = 'http://127.0.0.1:8080/';
Expand Down
25 changes: 19 additions & 6 deletions src/request-filtering-agent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,16 @@ declare module "http" {
}

export interface RequestFilteringAgentOptions {
// allow to connect private IP address
// Allow to connect private IP address
// Example, http://127.0.0.1/, http://localhost/
//Default: false
allowPrivateIP?: boolean;
// Default: false
allowPrivateIPAddress?: boolean;
// Allow to connect meta address 0.0.0.0
// 0.0.0.0 (IPv4) and :: (IPv6) a meta address that routing another address
// https://en.wikipedia.org/wiki/Reserved_IP_addresses
// https://tools.ietf.org/html/rfc6890
// Default: false
allowMetaIPAddress?: boolean;
// Allow address list
// This values are preferred than denyAddressList
// Default: []
Expand All @@ -39,7 +45,12 @@ const validateAddress = ({ address, host, family }: { address: string; host?: st
return;
}

if (!options.allowPrivateIP && ip.isPrivate(address)) {
if (!options.allowMetaIPAddress) {
if (address === "0.0.0.0" || address == "::") {
return new Error(`DNS lookup ${address}(family:${family}, host:${host}) is not allowed. Because, It is meta IP address.`);
}
}
if (!options.allowPrivateIPAddress && ip.isPrivate(address)) {
return new Error(`DNS lookup ${address}(family:${family}, host:${host}) is not allowed. Because, It is private IP address.`);
}

Expand All @@ -65,6 +76,7 @@ const addDropFilterSocket = (options: Required<RequestFilteringAgentOptions>, so
// public
// prevent twice apply
const appliedAgentSet = new Set<http.Agent | https.Agent>();

/**
* Apply request filter to http(s).Agent instance
*/
Expand All @@ -74,7 +86,8 @@ export function applyRequestFilter<T extends http.Agent | http.Agent>(agent: T,
}
appliedAgentSet.add(agent);
const requestFilterOptions: Required<RequestFilteringAgentOptions> = {
allowPrivateIP: options && options.allowPrivateIP !== undefined ? options.allowPrivateIP : false,
allowPrivateIPAddress: options && options.allowPrivateIPAddress !== undefined ? options.allowPrivateIPAddress : false,
allowMetaIPAddress: options && options.allowMetaIPAddress !== undefined ? options.allowMetaIPAddress : false,
allowIPAddressList: options && options.allowIPAddressList ? options.allowIPAddressList : [],
denyIPAddressList: options && options.denyIPAddressList ? options.denyIPAddressList : []
};
Expand Down Expand Up @@ -105,7 +118,7 @@ export function applyRequestFilter<T extends http.Agent | http.Agent>(agent: T,
return socket;
};
return agent;
};
}

/**
* A subclsss of http.Agent with request filtering
Expand Down
44 changes: 36 additions & 8 deletions test/request-filtering-agent.test.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
import * as assert from "assert";
import fetch from "node-fetch";
import { globalHttpAgent, RequestFilteringHttpAgent, useAgent, applyRequestFilter } from "../src/request-filtering-agent";
import {
globalHttpAgent,
RequestFilteringHttpAgent,
useAgent,
applyRequestFilter
} from "../src/request-filtering-agent";
import * as http from "http";

const TEST_PORT = 12456;
describe("request-filtering-agent", function () {
describe("request-filtering-agent", function() {
let close = () => {
return Promise.resolve();
};
Expand Down Expand Up @@ -38,7 +43,7 @@ describe("request-filtering-agent", function () {
});
it("should request local ip address with allowPrivateIP: true", async () => {
const agent = new RequestFilteringHttpAgent({
allowPrivateIP: true
allowPrivateIPAddress: true
});
const privateIPs = [
`http://127.0.0.1:${TEST_PORT}`
Expand All @@ -59,7 +64,7 @@ describe("request-filtering-agent", function () {
keepAlive: true
});
const agentWithFiltering = applyRequestFilter(agent, {
allowPrivateIP: true
allowPrivateIPAddress: true
});
const privateIPs = [
`http://127.0.0.1:${TEST_PORT}`
Expand All @@ -71,17 +76,39 @@ describe("request-filtering-agent", function () {
timeout: 2000
});
} catch (error) {
assert.fail(new Error("should fetch, because it is allow"));
assert.fail(new Error("should fetch, because it is allow, error" + error));
}
}
});
it("0.0.0.0 and :: is metaAddress, it is disabled by default", async () => {
const agent = new RequestFilteringHttpAgent();
const disAllowedIPs = [
`http://0.0.0.0:${TEST_PORT}`,
`http://[::]:${TEST_PORT}`
];
for (const ipAddress of disAllowedIPs) {
try {
await fetch(ipAddress, {
agent,
timeout: 2000
});
throw new ReferenceError("SHOULD NOT BE CALLED:" + ipAddress);
} catch (error) {
if (error instanceof ReferenceError) {
assert.fail(error);
}
}
}
});

it("should allow http://127.0.0.1, but other private ip is disallowed", async () => {
const agent = new RequestFilteringHttpAgent({
allowIPAddressList: ["127.0.0.1"],
allowPrivateIP: false
allowPrivateIPAddress: false
});
const privateIPs = [
`http://127.0.0.1:${TEST_PORT}`
`http://127.0.0.1:${TEST_PORT}`,
`http://localhost:${TEST_PORT}`
];
for (const ipAddress of privateIPs) {
try {
Expand All @@ -90,7 +117,7 @@ describe("request-filtering-agent", function () {
timeout: 2000
});
} catch (error) {
assert.fail(new Error("should fetch, because it is allow"));
assert.fail(new Error("should fetch, because it is allow, error" + error));
}
}
const disAllowedPrivateIPs = [
Expand All @@ -112,6 +139,7 @@ describe("request-filtering-agent", function () {
});
it("should not request because Socket is closed", async () => {
const privateIPs = [
`http://0.0.0.0:${TEST_PORT}`, // 0.0.0.0 is special
`http://127.0.0.1:${TEST_PORT}`, //
`http://[email protected]:${TEST_PORT}` //
];
Expand Down

0 comments on commit 188668e

Please sign in to comment.