n8n-nodes-dominusnode 1.0.0
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/CHANGELOG.md +21 -0
- package/LICENSE +21 -0
- package/README.md +99 -0
- package/dist/credentials/DominusNodeApi.credentials.d.ts +7 -0
- package/dist/credentials/DominusNodeApi.credentials.js +42 -0
- package/dist/credentials/DominusNodeApi.credentials.js.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +12 -0
- package/dist/index.js.map +1 -0
- package/dist/nodes/DominusNodeProxy/DominusNodeProxy.node.d.ts +24 -0
- package/dist/nodes/DominusNodeProxy/DominusNodeProxy.node.js +436 -0
- package/dist/nodes/DominusNodeProxy/DominusNodeProxy.node.js.map +1 -0
- package/dist/nodes/DominusNodeUsage/DominusNodeUsage.node.d.ts +13 -0
- package/dist/nodes/DominusNodeUsage/DominusNodeUsage.node.js +105 -0
- package/dist/nodes/DominusNodeUsage/DominusNodeUsage.node.js.map +1 -0
- package/dist/nodes/DominusNodeWallet/DominusNodeWallet.node.d.ts +33 -0
- package/dist/nodes/DominusNodeWallet/DominusNodeWallet.node.js +656 -0
- package/dist/nodes/DominusNodeWallet/DominusNodeWallet.node.js.map +1 -0
- package/dist/shared/auth.d.ts +74 -0
- package/dist/shared/auth.js +264 -0
- package/dist/shared/auth.js.map +1 -0
- package/dist/shared/constants.d.ts +9 -0
- package/dist/shared/constants.js +13 -0
- package/dist/shared/constants.js.map +1 -0
- package/dist/shared/ssrf.d.ts +42 -0
- package/dist/shared/ssrf.js +252 -0
- package/dist/shared/ssrf.js.map +1 -0
- package/package.json +41 -0
- package/src/credentials/DominusNodeApi.credentials.ts +39 -0
- package/src/index.ts +4 -0
- package/src/nodes/DominusNodeProxy/DominusNodeProxy.node.ts +459 -0
- package/src/nodes/DominusNodeUsage/DominusNodeUsage.node.ts +130 -0
- package/src/nodes/DominusNodeWallet/DominusNodeWallet.node.ts +898 -0
- package/src/shared/auth.ts +272 -0
- package/src/shared/constants.ts +11 -0
- package/src/shared/ssrf.ts +257 -0
- package/tests/DominusNodeProxy.test.ts +281 -0
- package/tests/DominusNodeUsage.test.ts +250 -0
- package/tests/DominusNodeWallet.test.ts +591 -0
- package/tests/ssrf.test.ts +238 -0
- package/tsconfig.json +18 -0
- package/vitest.config.ts +8 -0
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
import { describe, it, expect } from "vitest";
|
|
2
|
+
import { normalizeIpv4, isPrivateIp, validateUrl } from "../src/shared/ssrf";
|
|
3
|
+
|
|
4
|
+
// ===========================================================================
|
|
5
|
+
// normalizeIpv4
|
|
6
|
+
// ===========================================================================
|
|
7
|
+
|
|
8
|
+
describe("normalizeIpv4", () => {
|
|
9
|
+
it("normalizes decimal integer to dotted-decimal", () => {
|
|
10
|
+
expect(normalizeIpv4("2130706433")).toBe("127.0.0.1");
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
it("normalizes hex to dotted-decimal", () => {
|
|
14
|
+
expect(normalizeIpv4("0x7f000001")).toBe("127.0.0.1");
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
it("normalizes octal octets", () => {
|
|
18
|
+
expect(normalizeIpv4("0177.0.0.1")).toBe("127.0.0.1");
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
it("normalizes mixed-radix hex octets", () => {
|
|
22
|
+
expect(normalizeIpv4("0xC0.0xA8.0x01.0x01")).toBe("192.168.1.1");
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
it("returns null for hostnames", () => {
|
|
26
|
+
expect(normalizeIpv4("example.com")).toBeNull();
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it("handles zero", () => {
|
|
30
|
+
expect(normalizeIpv4("0")).toBe("0.0.0.0");
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it("handles max uint32", () => {
|
|
34
|
+
expect(normalizeIpv4("4294967295")).toBe("255.255.255.255");
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it("returns null for out-of-range", () => {
|
|
38
|
+
expect(normalizeIpv4("4294967296")).toBeNull();
|
|
39
|
+
});
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
// ===========================================================================
|
|
43
|
+
// isPrivateIp
|
|
44
|
+
// ===========================================================================
|
|
45
|
+
|
|
46
|
+
describe("isPrivateIp", () => {
|
|
47
|
+
// IPv4 private ranges
|
|
48
|
+
it("detects 127.0.0.1 as private", () => {
|
|
49
|
+
expect(isPrivateIp("127.0.0.1")).toBe(true);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it("detects 10.0.0.0 as private", () => {
|
|
53
|
+
expect(isPrivateIp("10.0.0.0")).toBe(true);
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
it("detects 172.16.0.1 as private", () => {
|
|
57
|
+
expect(isPrivateIp("172.16.0.1")).toBe(true);
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
it("allows 172.15.0.1 (not private)", () => {
|
|
61
|
+
expect(isPrivateIp("172.15.0.1")).toBe(false);
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
it("detects 192.168.0.1 as private", () => {
|
|
65
|
+
expect(isPrivateIp("192.168.0.1")).toBe(true);
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
it("detects 169.254.169.254 (link-local/cloud metadata) as private", () => {
|
|
69
|
+
expect(isPrivateIp("169.254.169.254")).toBe(true);
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
it("detects 0.0.0.0 as private", () => {
|
|
73
|
+
expect(isPrivateIp("0.0.0.0")).toBe(true);
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
// CGNAT
|
|
77
|
+
it("detects 100.64.0.1 (CGNAT) as private", () => {
|
|
78
|
+
expect(isPrivateIp("100.64.0.1")).toBe(true);
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
it("detects 100.127.255.255 (CGNAT upper) as private", () => {
|
|
82
|
+
expect(isPrivateIp("100.127.255.255")).toBe(true);
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
it("allows 100.63.255.255 (below CGNAT)", () => {
|
|
86
|
+
expect(isPrivateIp("100.63.255.255")).toBe(false);
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
// Multicast
|
|
90
|
+
it("detects 224.0.0.1 (multicast) as private", () => {
|
|
91
|
+
expect(isPrivateIp("224.0.0.1")).toBe(true);
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
it("detects 255.255.255.255 (broadcast) as private", () => {
|
|
95
|
+
expect(isPrivateIp("255.255.255.255")).toBe(true);
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
// Public IPs
|
|
99
|
+
it("allows 8.8.8.8 (public)", () => {
|
|
100
|
+
expect(isPrivateIp("8.8.8.8")).toBe(false);
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
it("allows 1.1.1.1 (public)", () => {
|
|
104
|
+
expect(isPrivateIp("1.1.1.1")).toBe(false);
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
// IPv6
|
|
108
|
+
it("detects ::1 as private", () => {
|
|
109
|
+
expect(isPrivateIp("::1")).toBe(true);
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
it("detects :: as private", () => {
|
|
113
|
+
expect(isPrivateIp("::")).toBe(true);
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
it("detects fc00::1 as private (ULA)", () => {
|
|
117
|
+
expect(isPrivateIp("fc00::1")).toBe(true);
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
it("detects fe80::1 as private (link-local)", () => {
|
|
121
|
+
expect(isPrivateIp("fe80::1")).toBe(true);
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
// IPv4-mapped IPv6
|
|
125
|
+
it("detects ::ffff:127.0.0.1 as private", () => {
|
|
126
|
+
expect(isPrivateIp("::ffff:127.0.0.1")).toBe(true);
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
it("detects ::ffff:7f00:0001 as private (hex form)", () => {
|
|
130
|
+
expect(isPrivateIp("::ffff:7f00:0001")).toBe(true);
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
// IPv4-compatible IPv6
|
|
134
|
+
it("detects ::127.0.0.1 (IPv4-compatible) as private", () => {
|
|
135
|
+
expect(isPrivateIp("::127.0.0.1")).toBe(true);
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
// Bracketed IPv6
|
|
139
|
+
it("handles [::1] bracketed form", () => {
|
|
140
|
+
expect(isPrivateIp("[::1]")).toBe(true);
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
// Zone ID
|
|
144
|
+
it("strips IPv6 zone ID", () => {
|
|
145
|
+
expect(isPrivateIp("fe80::1%eth0")).toBe(true);
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
// Teredo (2001:0000::/32) — inverted IPv4 in last 32 bits
|
|
149
|
+
it("detects Teredo embedding private IPv4", () => {
|
|
150
|
+
// Teredo inverts bits: to embed 127.0.0.1 (7f.00.00.01), inverted = 80.ff.ff.fe
|
|
151
|
+
// But we check the INVERTED result, so embedding 80ff:fffe should decode to 127.0.0.1
|
|
152
|
+
expect(isPrivateIp("2001:0000:0000:0000:0000:0000:80ff:fffe")).toBe(true);
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
// 6to4 (2002::/16)
|
|
156
|
+
it("detects 6to4 embedding private IPv4 (127.0.0.1)", () => {
|
|
157
|
+
// 127.0.0.1 = 7f00:0001
|
|
158
|
+
expect(isPrivateIp("2002:7f00:0001::1")).toBe(true);
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
it("detects 6to4 embedding 10.0.0.1", () => {
|
|
162
|
+
// 10.0.0.1 = 0a00:0001
|
|
163
|
+
expect(isPrivateIp("2002:0a00:0001::1")).toBe(true);
|
|
164
|
+
});
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
// ===========================================================================
|
|
168
|
+
// validateUrl
|
|
169
|
+
// ===========================================================================
|
|
170
|
+
|
|
171
|
+
describe("validateUrl", () => {
|
|
172
|
+
it("accepts valid https URL", () => {
|
|
173
|
+
const parsed = validateUrl("https://httpbin.org/ip");
|
|
174
|
+
expect(parsed.hostname).toBe("httpbin.org");
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
it("accepts valid http URL", () => {
|
|
178
|
+
const parsed = validateUrl("http://example.com/path");
|
|
179
|
+
expect(parsed.hostname).toBe("example.com");
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
it("rejects invalid URL", () => {
|
|
183
|
+
expect(() => validateUrl("not-a-url")).toThrow(/Invalid URL/);
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
it("rejects file:// protocol", () => {
|
|
187
|
+
expect(() => validateUrl("file:///etc/passwd")).toThrow(/protocols/);
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
it("rejects ftp:// protocol", () => {
|
|
191
|
+
expect(() => validateUrl("ftp://ftp.example.com")).toThrow(/protocols/);
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
it("rejects localhost", () => {
|
|
195
|
+
expect(() => validateUrl("http://localhost/secret")).toThrow(/localhost/);
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
it("rejects 0.0.0.0", () => {
|
|
199
|
+
expect(() => validateUrl("http://0.0.0.0/")).toThrow(/localhost/);
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
it("rejects private IPs", () => {
|
|
203
|
+
expect(() => validateUrl("http://192.168.1.1/admin")).toThrow(/private/i);
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
it("rejects .localhost TLD", () => {
|
|
207
|
+
expect(() => validateUrl("http://evil.localhost/")).toThrow(/localhost/);
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
it("rejects .local hostname", () => {
|
|
211
|
+
expect(() => validateUrl("http://printer.local/")).toThrow(/internal/);
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
it("rejects .internal hostname", () => {
|
|
215
|
+
expect(() => validateUrl("http://db.internal/")).toThrow(/internal/);
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
it("rejects .arpa hostname", () => {
|
|
219
|
+
expect(() => validateUrl("http://1.168.192.in-addr.arpa/")).toThrow(/internal/);
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
it("rejects embedded credentials", () => {
|
|
223
|
+
expect(() => validateUrl("http://user:pass@example.com/")).toThrow(/credentials/);
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
it("rejects cloud metadata endpoint", () => {
|
|
227
|
+
expect(() => validateUrl("http://169.254.169.254/latest/meta-data/")).toThrow(/blocked/i);
|
|
228
|
+
expect(() => validateUrl("http://metadata.google.internal/computeMetadata/v1/")).toThrow(/blocked/i);
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
it("rejects hex-encoded loopback", () => {
|
|
232
|
+
expect(() => validateUrl("http://0x7f000001/")).toThrow(/private/i);
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
it("rejects decimal-encoded loopback", () => {
|
|
236
|
+
expect(() => validateUrl("http://2130706433/")).toThrow(/private/i);
|
|
237
|
+
});
|
|
238
|
+
});
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"module": "CommonJS",
|
|
5
|
+
"moduleResolution": "node",
|
|
6
|
+
"declaration": true,
|
|
7
|
+
"outDir": "dist",
|
|
8
|
+
"rootDir": "src",
|
|
9
|
+
"strict": true,
|
|
10
|
+
"esModuleInterop": true,
|
|
11
|
+
"skipLibCheck": true,
|
|
12
|
+
"forceConsistentCasingInFileNames": true,
|
|
13
|
+
"resolveJsonModule": true,
|
|
14
|
+
"sourceMap": true
|
|
15
|
+
},
|
|
16
|
+
"include": ["src/**/*.ts"],
|
|
17
|
+
"exclude": ["node_modules", "dist", "tests"]
|
|
18
|
+
}
|