ssrf-agent-guard 0.1.1 → 0.1.2
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/dist/index.cjs.js +6 -5
- package/dist/index.d.ts +14 -0
- package/dist/index.esm.js +6 -5
- package/dist/lib/types.d.ts +27 -0
- package/dist/lib/utils.d.ts +9 -0
- package/index.ts +5 -2
- package/lib/utils.ts +1 -1
- package/package.json +1 -1
- package/tsconfig.json +3 -1
package/dist/index.cjs.js
CHANGED
|
@@ -49,7 +49,7 @@ function isSafeHost(hostname, isValidDomainOptions) {
|
|
|
49
49
|
// Case 2: Domain name
|
|
50
50
|
return isValidDomain(hostname, {
|
|
51
51
|
allowUnicode: false,
|
|
52
|
-
subdomain:
|
|
52
|
+
subdomain: true,
|
|
53
53
|
...isValidDomainOptions,
|
|
54
54
|
});
|
|
55
55
|
}
|
|
@@ -80,7 +80,7 @@ const CREATE_CONNECTION = Symbol('createConnection');
|
|
|
80
80
|
* @param isValidDomainOptions Options for validating domain names.
|
|
81
81
|
* @returns The patched CustomAgent instance.
|
|
82
82
|
*/
|
|
83
|
-
function
|
|
83
|
+
const ssrfAgentGuard = function (url, isValidDomainOptions) {
|
|
84
84
|
const finalAgent = getAgent(url);
|
|
85
85
|
// Prevent patching the agent multiple times
|
|
86
86
|
if (finalAgent[CREATE_CONNECTION]) {
|
|
@@ -111,7 +111,7 @@ function index (url, isValidDomainOptions) {
|
|
|
111
111
|
}
|
|
112
112
|
// Ensure resolvedAddress is a string for the check (it's typically a string for simple lookups)
|
|
113
113
|
const ipToCheck = Array.isArray(resolvedAddress) ? resolvedAddress[0] : resolvedAddress;
|
|
114
|
-
if (!
|
|
114
|
+
if (!isSafeHost(ipToCheck)) {
|
|
115
115
|
// If the resolved IP is NOT allowed (e.g., a private IP), destroy the connection.
|
|
116
116
|
return client?.destroy(new Error(`DNS lookup ${ipToCheck} is not allowed.`));
|
|
117
117
|
}
|
|
@@ -119,6 +119,7 @@ function index (url, isValidDomainOptions) {
|
|
|
119
119
|
return client;
|
|
120
120
|
};
|
|
121
121
|
return finalAgent;
|
|
122
|
-
}
|
|
122
|
+
};
|
|
123
|
+
module.exports = ssrfAgentGuard;
|
|
123
124
|
|
|
124
|
-
exports.default =
|
|
125
|
+
exports.default = ssrfAgentGuard;
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { Agent as HttpAgent } from 'http';
|
|
2
|
+
import { Agent as HttpsAgent } from 'https';
|
|
3
|
+
import { IsValidDomainOptions } from './lib/types';
|
|
4
|
+
type CustomAgent = HttpAgent | HttpsAgent;
|
|
5
|
+
/**
|
|
6
|
+
* Patches an http.Agent or https.Agent to enforce an HOST/IP check
|
|
7
|
+
* before and after a DNS lookup.
|
|
8
|
+
*
|
|
9
|
+
* @param url The URL or another input to determine the agent type.
|
|
10
|
+
* @param isValidDomainOptions Options for validating domain names.
|
|
11
|
+
* @returns The patched CustomAgent instance.
|
|
12
|
+
*/
|
|
13
|
+
declare const ssrfAgentGuard: (url: string, isValidDomainOptions?: IsValidDomainOptions) => CustomAgent;
|
|
14
|
+
export default ssrfAgentGuard;
|
package/dist/index.esm.js
CHANGED
|
@@ -45,7 +45,7 @@ function isSafeHost(hostname, isValidDomainOptions) {
|
|
|
45
45
|
// Case 2: Domain name
|
|
46
46
|
return isValidDomain(hostname, {
|
|
47
47
|
allowUnicode: false,
|
|
48
|
-
subdomain:
|
|
48
|
+
subdomain: true,
|
|
49
49
|
...isValidDomainOptions,
|
|
50
50
|
});
|
|
51
51
|
}
|
|
@@ -76,7 +76,7 @@ const CREATE_CONNECTION = Symbol('createConnection');
|
|
|
76
76
|
* @param isValidDomainOptions Options for validating domain names.
|
|
77
77
|
* @returns The patched CustomAgent instance.
|
|
78
78
|
*/
|
|
79
|
-
function
|
|
79
|
+
const ssrfAgentGuard = function (url, isValidDomainOptions) {
|
|
80
80
|
const finalAgent = getAgent(url);
|
|
81
81
|
// Prevent patching the agent multiple times
|
|
82
82
|
if (finalAgent[CREATE_CONNECTION]) {
|
|
@@ -107,7 +107,7 @@ function index (url, isValidDomainOptions) {
|
|
|
107
107
|
}
|
|
108
108
|
// Ensure resolvedAddress is a string for the check (it's typically a string for simple lookups)
|
|
109
109
|
const ipToCheck = Array.isArray(resolvedAddress) ? resolvedAddress[0] : resolvedAddress;
|
|
110
|
-
if (!
|
|
110
|
+
if (!isSafeHost(ipToCheck)) {
|
|
111
111
|
// If the resolved IP is NOT allowed (e.g., a private IP), destroy the connection.
|
|
112
112
|
return client?.destroy(new Error(`DNS lookup ${ipToCheck} is not allowed.`));
|
|
113
113
|
}
|
|
@@ -115,6 +115,7 @@ function index (url, isValidDomainOptions) {
|
|
|
115
115
|
return client;
|
|
116
116
|
};
|
|
117
117
|
return finalAgent;
|
|
118
|
-
}
|
|
118
|
+
};
|
|
119
|
+
module.exports = ssrfAgentGuard;
|
|
119
120
|
|
|
120
|
-
export {
|
|
121
|
+
export { ssrfAgentGuard as default };
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export interface Options {
|
|
2
|
+
protocal?: string;
|
|
3
|
+
metadataHosts?: string[];
|
|
4
|
+
mode?: 'block' | 'report' | 'allow';
|
|
5
|
+
policy?: PolicyOptions;
|
|
6
|
+
blockCloudMetadata?: boolean;
|
|
7
|
+
detectDnsRebinding?: boolean;
|
|
8
|
+
logger?: (level: 'info' | 'warn' | 'error', msg: string, meta?: any) => void;
|
|
9
|
+
}
|
|
10
|
+
export interface PolicyOptions {
|
|
11
|
+
allowDomains?: string[];
|
|
12
|
+
denyDomains?: string[];
|
|
13
|
+
denyTLD?: string[];
|
|
14
|
+
}
|
|
15
|
+
export interface BlockEvent {
|
|
16
|
+
url: string;
|
|
17
|
+
reason: string;
|
|
18
|
+
ip?: string;
|
|
19
|
+
timestamp: number;
|
|
20
|
+
}
|
|
21
|
+
export interface IsValidDomainOptions {
|
|
22
|
+
subdomain?: boolean;
|
|
23
|
+
wildcard?: boolean;
|
|
24
|
+
allowUnicode?: boolean;
|
|
25
|
+
topLevel?: boolean;
|
|
26
|
+
}
|
|
27
|
+
export declare const CLOUD_METADATA_HOSTS: string[];
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { IsValidDomainOptions } from './types';
|
|
2
|
+
/**
|
|
3
|
+
* Validates whether a domain is syntactically valid.
|
|
4
|
+
*/
|
|
5
|
+
export declare function isSafeIp(hostname: string): boolean;
|
|
6
|
+
/**
|
|
7
|
+
* High-level validation for hostnames (domains + public IPs).
|
|
8
|
+
*/
|
|
9
|
+
export declare function isSafeHost(hostname: string, isValidDomainOptions?: IsValidDomainOptions): boolean;
|
package/index.ts
CHANGED
|
@@ -38,7 +38,7 @@ const CREATE_CONNECTION = Symbol('createConnection');
|
|
|
38
38
|
* @param isValidDomainOptions Options for validating domain names.
|
|
39
39
|
* @returns The patched CustomAgent instance.
|
|
40
40
|
*/
|
|
41
|
-
|
|
41
|
+
const ssrfAgentGuard = function (url: string, isValidDomainOptions?: IsValidDomainOptions): CustomAgent {
|
|
42
42
|
const finalAgent = getAgent(url);
|
|
43
43
|
|
|
44
44
|
// Prevent patching the agent multiple times
|
|
@@ -79,7 +79,7 @@ export default function (url: string, isValidDomainOptions?: IsValidDomainOption
|
|
|
79
79
|
// Ensure resolvedAddress is a string for the check (it's typically a string for simple lookups)
|
|
80
80
|
const ipToCheck = Array.isArray(resolvedAddress) ? resolvedAddress[0] : resolvedAddress;
|
|
81
81
|
|
|
82
|
-
if (!
|
|
82
|
+
if (!isSafeHost(ipToCheck)) {
|
|
83
83
|
// If the resolved IP is NOT allowed (e.g., a private IP), destroy the connection.
|
|
84
84
|
return client?.destroy(new Error(`DNS lookup ${ipToCheck} is not allowed.`));
|
|
85
85
|
}
|
|
@@ -90,3 +90,6 @@ export default function (url: string, isValidDomainOptions?: IsValidDomainOption
|
|
|
90
90
|
|
|
91
91
|
return finalAgent;
|
|
92
92
|
}
|
|
93
|
+
|
|
94
|
+
export default ssrfAgentGuard;
|
|
95
|
+
module.exports = ssrfAgentGuard;
|
package/lib/utils.ts
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ssrf-agent-guard",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
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",
|
package/tsconfig.json
CHANGED
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
"target": "ES2020",
|
|
4
4
|
"module": "ESNext",
|
|
5
5
|
"declaration": true,
|
|
6
|
+
"emitDeclarationOnly": false,
|
|
7
|
+
"declarationDir": "./dist",
|
|
6
8
|
"outDir": "dist",
|
|
7
9
|
"strict": true,
|
|
8
10
|
"esModuleInterop": true,
|
|
@@ -10,7 +12,7 @@
|
|
|
10
12
|
"skipLibCheck": true
|
|
11
13
|
},
|
|
12
14
|
"include": [
|
|
13
|
-
|
|
15
|
+
"lib/**/*",
|
|
14
16
|
"rollup.config.mjs"
|
|
15
17
|
]
|
|
16
18
|
}
|