ssrf-agent-guard 0.1.3 → 0.1.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # ssrf-agent-guard
2
2
 
3
- #### SSRF-Guard is a Node.js module for protecting your HTTP/HTTPS requests against SSRF (Server-Side Request Forgery) attacks. It wraps http.Agent and https.Agent to enforce pre and post DNS host/IP checks, block access to cloud metadata endpoints, private IPs, and unsafe domains.
3
+ #### `ssrf-agent-guard` is a Node.js module for protecting your HTTP/HTTPS requests against SSRF (Server-Side Request Forgery) attacks. It wraps http.Agent and https.Agent to enforce pre and post DNS host/IP checks, block access to cloud metadata endpoints, private IPs, and unsafe domains.
4
4
  ---
5
5
 
6
6
  ## Features
@@ -35,7 +35,10 @@ const isValidDomainOptions = {
35
35
  subdomain: true,
36
36
  wildcard: true
37
37
  };
38
- axios.get(url, {httpAgent: ssrfAgentGuard(url), httpsAgent: ssrfAgentGuard(url)})
38
+ axios.get(
39
+ url, {
40
+ httpAgent: ssrfAgentGuard(url, isValidDomainOptions), httpsAgent: ssrfAgentGuard(url, isValidDomainOptions)
41
+ })
39
42
  .then((response) => {
40
43
  console.log(`Success`);
41
44
  })
package/dist/index.cjs.js CHANGED
@@ -27,25 +27,16 @@ function isIp(input) {
27
27
  function isPublicIp(ip) {
28
28
  return ipaddr.parse(ip).range() === 'unicast';
29
29
  }
30
- /**
31
- * Validates whether a domain is syntactically valid.
32
- */
33
- function isSafeIp(hostname) {
34
- // Case 1: IP address
35
- if (isIp(hostname)) {
36
- return isPublicIp(hostname); // only allow public IPs
37
- }
38
- return true;
39
- }
40
30
  /**
41
31
  * High-level validation for hostnames (domains + public IPs).
42
32
  */
43
33
  function isSafeHost(hostname, isValidDomainOptions) {
44
34
  // Block cloud metadata IP/domains
45
- if (CLOUD_METADATA_HOSTS.indexOf(hostname))
46
- return false;
47
- if (!isSafeIp(hostname))
35
+ if (CLOUD_METADATA_HOSTS.indexOf(hostname) !== -1)
48
36
  return false;
37
+ // Case 1: IP address
38
+ if (isIp(hostname))
39
+ return isPublicIp(hostname);
49
40
  // Case 2: Domain name
50
41
  return isValidDomain(hostname, {
51
42
  allowUnicode: false,
@@ -90,7 +81,7 @@ const ssrfAgentGuard = function (url, isValidDomainOptions) {
90
81
  // The original createConnection function from the Agent
91
82
  const createConnection = finalAgent.createConnection;
92
83
  // Patch the createConnection method on the agent
93
- finalAgent.createConnection = (options, fn) => {
84
+ finalAgent.createConnection = function (options, fn) {
94
85
  const { host: address } = options;
95
86
  // --- 1. Pre-DNS Check (Host/Address Check) ---
96
87
  // If the 'host' option is an IP address, check it immediately.
@@ -99,22 +90,19 @@ const ssrfAgentGuard = function (url, isValidDomainOptions) {
99
90
  throw new Error(`DNS lookup ${address} is not allowed.`);
100
91
  }
101
92
  // Call the original createConnection
102
- // @ts-expect-error 'this' is not assignable to type 'HttpAgent | HttpsAgent'
103
93
  const client = createConnection.call(this, options, fn);
104
94
  // --- 2. Post-DNS Check (Lookup Event Check) ---
105
95
  // The 'lookup' event fires after the DNS lookup is complete
106
96
  // and provides the resolved IP address.
107
97
  client?.on('lookup', (err, resolvedAddress) => {
108
- // If there was an error in lookup, or if the resolved IP is allowed, do nothing.
109
- if (err) {
110
- return;
111
- }
112
98
  // Ensure resolvedAddress is a string for the check (it's typically a string for simple lookups)
113
99
  const ipToCheck = Array.isArray(resolvedAddress) ? resolvedAddress[0] : resolvedAddress;
114
- if (!isSafeHost(ipToCheck)) {
115
- // If the resolved IP is NOT allowed (e.g., a private IP), destroy the connection.
116
- return client?.destroy(new Error(`DNS lookup ${ipToCheck} is not allowed.`));
100
+ // If there was an error in lookup, or if the resolved IP is allowed, do nothing.
101
+ if (err || isSafeHost(ipToCheck, isValidDomainOptions)) {
102
+ return false;
117
103
  }
104
+ // If the resolved IP is NOT allowed (e.g., a private IP), destroy the connection.
105
+ return client?.destroy(new Error(`DNS lookup ${ipToCheck} is not allowed.`));
118
106
  });
119
107
  return client;
120
108
  };
package/dist/index.esm.js CHANGED
@@ -23,25 +23,16 @@ function isIp(input) {
23
23
  function isPublicIp(ip) {
24
24
  return ipaddr.parse(ip).range() === 'unicast';
25
25
  }
26
- /**
27
- * Validates whether a domain is syntactically valid.
28
- */
29
- function isSafeIp(hostname) {
30
- // Case 1: IP address
31
- if (isIp(hostname)) {
32
- return isPublicIp(hostname); // only allow public IPs
33
- }
34
- return true;
35
- }
36
26
  /**
37
27
  * High-level validation for hostnames (domains + public IPs).
38
28
  */
39
29
  function isSafeHost(hostname, isValidDomainOptions) {
40
30
  // Block cloud metadata IP/domains
41
- if (CLOUD_METADATA_HOSTS.indexOf(hostname))
42
- return false;
43
- if (!isSafeIp(hostname))
31
+ if (CLOUD_METADATA_HOSTS.indexOf(hostname) !== -1)
44
32
  return false;
33
+ // Case 1: IP address
34
+ if (isIp(hostname))
35
+ return isPublicIp(hostname);
45
36
  // Case 2: Domain name
46
37
  return isValidDomain(hostname, {
47
38
  allowUnicode: false,
@@ -86,7 +77,7 @@ const ssrfAgentGuard = function (url, isValidDomainOptions) {
86
77
  // The original createConnection function from the Agent
87
78
  const createConnection = finalAgent.createConnection;
88
79
  // Patch the createConnection method on the agent
89
- finalAgent.createConnection = (options, fn) => {
80
+ finalAgent.createConnection = function (options, fn) {
90
81
  const { host: address } = options;
91
82
  // --- 1. Pre-DNS Check (Host/Address Check) ---
92
83
  // If the 'host' option is an IP address, check it immediately.
@@ -95,22 +86,19 @@ const ssrfAgentGuard = function (url, isValidDomainOptions) {
95
86
  throw new Error(`DNS lookup ${address} is not allowed.`);
96
87
  }
97
88
  // Call the original createConnection
98
- // @ts-expect-error 'this' is not assignable to type 'HttpAgent | HttpsAgent'
99
89
  const client = createConnection.call(this, options, fn);
100
90
  // --- 2. Post-DNS Check (Lookup Event Check) ---
101
91
  // The 'lookup' event fires after the DNS lookup is complete
102
92
  // and provides the resolved IP address.
103
93
  client?.on('lookup', (err, resolvedAddress) => {
104
- // If there was an error in lookup, or if the resolved IP is allowed, do nothing.
105
- if (err) {
106
- return;
107
- }
108
94
  // Ensure resolvedAddress is a string for the check (it's typically a string for simple lookups)
109
95
  const ipToCheck = Array.isArray(resolvedAddress) ? resolvedAddress[0] : resolvedAddress;
110
- if (!isSafeHost(ipToCheck)) {
111
- // If the resolved IP is NOT allowed (e.g., a private IP), destroy the connection.
112
- return client?.destroy(new Error(`DNS lookup ${ipToCheck} is not allowed.`));
96
+ // If there was an error in lookup, or if the resolved IP is allowed, do nothing.
97
+ if (err || isSafeHost(ipToCheck, isValidDomainOptions)) {
98
+ return false;
113
99
  }
100
+ // If the resolved IP is NOT allowed (e.g., a private IP), destroy the connection.
101
+ return client?.destroy(new Error(`DNS lookup ${ipToCheck} is not allowed.`));
114
102
  });
115
103
  return client;
116
104
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ssrf-agent-guard",
3
- "version": "0.1.3",
3
+ "version": "0.1.5",
4
4
  "description": "A TypeScript SSRF protection library for Node.js (express/axios) with advanced policies, DNS rebinding detection and cloud metadata protection.",
5
5
  "main": "dist/index.cjs.js",
6
6
  "module": "dist/index.esm.js",
Binary file