@push.rocks/smartproxy 29.1.0 → 30.0.1
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 +30 -0
- package/dist_rust/rustproxy_linux_amd64_musl +0 -0
- package/dist_rust/rustproxy_linux_amd64_musl.tsrust-build.json +5 -5
- package/dist_rust/rustproxy_linux_arm64_musl +0 -0
- package/dist_rust/rustproxy_linux_arm64_musl.tsrust-build.json +5 -5
- package/dist_ts/00_commitinfo_data.js +1 -1
- package/dist_ts/proxies/smart-proxy/smart-proxy.js +10 -2
- package/dist_ts/proxies/smart-proxy/utils/index.d.ts +1 -1
- package/dist_ts/proxies/smart-proxy/utils/index.js +2 -2
- package/dist_ts/proxies/smart-proxy/utils/ip-entry-validation.d.ts +14 -0
- package/dist_ts/proxies/smart-proxy/utils/ip-entry-validation.js +200 -0
- package/dist_ts/proxies/smart-proxy/utils/route-validation-error.d.ts +9 -0
- package/dist_ts/proxies/smart-proxy/utils/route-validation-error.js +19 -1
- package/dist_ts/proxies/smart-proxy/utils/route-validator.d.ts +9 -12
- package/dist_ts/proxies/smart-proxy/utils/route-validator.js +84 -81
- package/package.json +2 -2
- package/readme.md +15 -5
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/proxies/smart-proxy/smart-proxy.ts +10 -1
- package/ts/proxies/smart-proxy/utils/index.ts +1 -1
- package/ts/proxies/smart-proxy/utils/ip-entry-validation.ts +201 -0
- package/ts/proxies/smart-proxy/utils/route-validation-error.ts +20 -0
- package/ts/proxies/smart-proxy/utils/route-validator.ts +94 -83
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
import * as plugins from '../../../plugins.js';
|
|
2
|
+
/**
|
|
3
|
+
* Validation of configured IP entries, one rule per Rust parser that reads them.
|
|
4
|
+
*
|
|
5
|
+
* Every check accepts exactly what its Rust reader can match and answers why it refuses anything
|
|
6
|
+
* else, so a configuration that validates never carries an entry the engine silently ignores:
|
|
7
|
+
*
|
|
8
|
+
* - Security lists (`security.ipAllowList`, `security.ipBlockList`, `defaults.security`,
|
|
9
|
+
* `securityPolicy.blockedIps`/`blockedCidrs`) are read by `rustproxy-security` `IpPattern::parse`:
|
|
10
|
+
* `*`, an address or an `ipnet` prefix, compared as parsed addresses with the client converted
|
|
11
|
+
* from IPv4-mapped IPv6 to IPv4. Anything else becomes `0.0.0.0` and never matches.
|
|
12
|
+
* - Source matches (`match.clientIp`, `trustedProxyIPs` of a route policy or the settings) are read
|
|
13
|
+
* by `rustproxy-routing` `matchers::ip_matches`: `*`, an `ipnet` prefix, a four-part IPv4 wildcard,
|
|
14
|
+
* or an exact address compared as text against the client's Rust `Display` form, after a leading
|
|
15
|
+
* `::ffff:` is stripped from both sides.
|
|
16
|
+
* - VPN assigned addresses (`security.vpn.allowedAssignedIps`) are compared as text against the
|
|
17
|
+
* address the VPN metadata carries.
|
|
18
|
+
*
|
|
19
|
+
* A check answers `null` for an entry it accepts, else the reason it refuses it.
|
|
20
|
+
*/
|
|
21
|
+
const ipv4MappedPrefixLength = 96;
|
|
22
|
+
/** An address without a zone index, in any textual form `node:net` and Rust both read. */
|
|
23
|
+
const ipFamilyOf = (value) => {
|
|
24
|
+
if (value.includes('%'))
|
|
25
|
+
return 0;
|
|
26
|
+
return plugins.net.isIP(value);
|
|
27
|
+
};
|
|
28
|
+
/** The eight 16-bit segments of an IPv6 address `node:net` already accepted. */
|
|
29
|
+
const ipv6SegmentsOf = (address) => {
|
|
30
|
+
const toSegments = (text) => {
|
|
31
|
+
if (text === '')
|
|
32
|
+
return [];
|
|
33
|
+
const segments = [];
|
|
34
|
+
for (const part of text.split(':')) {
|
|
35
|
+
if (part.includes('.')) {
|
|
36
|
+
const [a, b, c, d] = part.split('.').map(Number);
|
|
37
|
+
segments.push((a << 8) | b, (c << 8) | d);
|
|
38
|
+
}
|
|
39
|
+
else {
|
|
40
|
+
segments.push(parseInt(part, 16));
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
return segments;
|
|
44
|
+
};
|
|
45
|
+
const compressionAt = address.indexOf('::');
|
|
46
|
+
if (compressionAt === -1) {
|
|
47
|
+
return toSegments(address);
|
|
48
|
+
}
|
|
49
|
+
const head = toSegments(address.slice(0, compressionAt));
|
|
50
|
+
const tail = toSegments(address.slice(compressionAt + 2));
|
|
51
|
+
return [...head, ...new Array(8 - head.length - tail.length).fill(0), ...tail];
|
|
52
|
+
};
|
|
53
|
+
/** The IPv4 address an IPv4-mapped IPv6 address (`::ffff:a.b.c.d`) carries, else `null`. */
|
|
54
|
+
const ipv4MappedOf = (segments) => {
|
|
55
|
+
if (segments.slice(0, 5).some((segment) => segment !== 0) || segments[5] !== 0xffff) {
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
return [segments[6] >> 8, segments[6] & 0xff, segments[7] >> 8, segments[7] & 0xff].join('.');
|
|
59
|
+
};
|
|
60
|
+
/**
|
|
61
|
+
* The address as Rust's `Display` writes it: IPv4 unchanged (`node:net` admits only plain decimal
|
|
62
|
+
* octets), IPv4-mapped IPv6 as `::ffff:a.b.c.d`, any other IPv6 in lowercase hex without leading
|
|
63
|
+
* zeros and with the first longest run of two or more zero segments written as `::`.
|
|
64
|
+
*/
|
|
65
|
+
export const formatIpAddress = (address) => {
|
|
66
|
+
if (ipFamilyOf(address) !== 6) {
|
|
67
|
+
return address;
|
|
68
|
+
}
|
|
69
|
+
const segments = ipv6SegmentsOf(address);
|
|
70
|
+
const mapped = ipv4MappedOf(segments);
|
|
71
|
+
if (mapped !== null) {
|
|
72
|
+
return `::ffff:${mapped}`;
|
|
73
|
+
}
|
|
74
|
+
let longestStart = 0;
|
|
75
|
+
let longestLength = 0;
|
|
76
|
+
let currentStart = 0;
|
|
77
|
+
let currentLength = 0;
|
|
78
|
+
segments.forEach((segment, index) => {
|
|
79
|
+
if (segment === 0) {
|
|
80
|
+
if (currentLength === 0)
|
|
81
|
+
currentStart = index;
|
|
82
|
+
currentLength += 1;
|
|
83
|
+
if (currentLength > longestLength) {
|
|
84
|
+
longestStart = currentStart;
|
|
85
|
+
longestLength = currentLength;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
else {
|
|
89
|
+
currentLength = 0;
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
const hex = (part) => part.map((segment) => segment.toString(16)).join(':');
|
|
93
|
+
if (longestLength > 1) {
|
|
94
|
+
return `${hex(segments.slice(0, longestStart))}::${hex(segments.slice(longestStart + longestLength))}`;
|
|
95
|
+
}
|
|
96
|
+
return hex(segments);
|
|
97
|
+
};
|
|
98
|
+
/**
|
|
99
|
+
* An address, `/` and a prefix length as `ipnet` parses it: up to two digits and at most 32 for
|
|
100
|
+
* IPv4, up to three digits and at most 128 for IPv6. Host bits may be set.
|
|
101
|
+
*/
|
|
102
|
+
const parsePrefix = (value) => {
|
|
103
|
+
const parts = value.split('/');
|
|
104
|
+
if (parts.length !== 2)
|
|
105
|
+
return null;
|
|
106
|
+
const [address, lengthText] = parts;
|
|
107
|
+
const family = ipFamilyOf(address);
|
|
108
|
+
if (family === 4 && /^\d{1,2}$/.test(lengthText) && Number(lengthText) <= 32) {
|
|
109
|
+
return { address, family, length: Number(lengthText) };
|
|
110
|
+
}
|
|
111
|
+
if (family === 6 && /^\d{1,3}$/.test(lengthText) && Number(lengthText) <= 128) {
|
|
112
|
+
return { address, family, length: Number(lengthText) };
|
|
113
|
+
}
|
|
114
|
+
return null;
|
|
115
|
+
};
|
|
116
|
+
/** The IPv4 prefix an IPv4-mapped IPv6 prefix of at least 96 bits covers, else `null`. */
|
|
117
|
+
const ipv4PrefixOfMapped = (prefix) => {
|
|
118
|
+
if (prefix.family !== 6 || prefix.length < ipv4MappedPrefixLength)
|
|
119
|
+
return null;
|
|
120
|
+
const mapped = ipv4MappedOf(ipv6SegmentsOf(prefix.address));
|
|
121
|
+
return mapped === null ? null : `${mapped}/${prefix.length - ipv4MappedPrefixLength}`;
|
|
122
|
+
};
|
|
123
|
+
/** A four-part IPv4 wildcard with plain decimal octets first and only `*` parts after them. */
|
|
124
|
+
const isIpv4TrailingWildcard = (value) => {
|
|
125
|
+
const parts = value.split('.');
|
|
126
|
+
if (parts.length !== 4)
|
|
127
|
+
return false;
|
|
128
|
+
const firstWildcard = parts.indexOf('*');
|
|
129
|
+
if (firstWildcard === -1)
|
|
130
|
+
return false;
|
|
131
|
+
return parts.slice(0, firstWildcard).every((part) => /^(0|[1-9]\d{0,2})$/.test(part) && Number(part) <= 255)
|
|
132
|
+
&& parts.slice(firstWildcard).every((part) => part === '*');
|
|
133
|
+
};
|
|
134
|
+
const zoneIndexReason = 'a zone index (%…) is not supported';
|
|
135
|
+
/** Checks an entry of a security list: `ipAllowList`, `ipBlockList` or a global block list. */
|
|
136
|
+
export const checkSecurityIpEntry = (entry) => {
|
|
137
|
+
if (typeof entry !== 'string')
|
|
138
|
+
return 'must be a string';
|
|
139
|
+
if (entry === '*')
|
|
140
|
+
return null;
|
|
141
|
+
if (entry.includes('%'))
|
|
142
|
+
return zoneIndexReason;
|
|
143
|
+
const family = ipFamilyOf(entry);
|
|
144
|
+
if (family !== 0) {
|
|
145
|
+
const mapped = family === 6 ? ipv4MappedOf(ipv6SegmentsOf(entry)) : null;
|
|
146
|
+
return mapped === null
|
|
147
|
+
? null
|
|
148
|
+
: `an IPv4-mapped address never matches, because clients are compared as IPv4; write ${mapped}`;
|
|
149
|
+
}
|
|
150
|
+
const prefix = parsePrefix(entry);
|
|
151
|
+
if (prefix) {
|
|
152
|
+
const ipv4Prefix = ipv4PrefixOfMapped(prefix);
|
|
153
|
+
return ipv4Prefix === null
|
|
154
|
+
? null
|
|
155
|
+
: `an IPv4-mapped prefix never matches, because clients are compared as IPv4; write ${ipv4Prefix}`;
|
|
156
|
+
}
|
|
157
|
+
return "must be '*', an IPv4 or IPv6 address, or an address prefix such as 192.0.2.0/24";
|
|
158
|
+
};
|
|
159
|
+
/** Checks a source-match entry: `match.clientIp` or a `trustedProxyIPs` entry. */
|
|
160
|
+
export const checkSourceMatchIpEntry = (entry) => {
|
|
161
|
+
if (typeof entry !== 'string')
|
|
162
|
+
return 'must be a string';
|
|
163
|
+
if (entry === '*')
|
|
164
|
+
return null;
|
|
165
|
+
if (entry.includes('%'))
|
|
166
|
+
return zoneIndexReason;
|
|
167
|
+
if (ipFamilyOf(entry) !== 0) {
|
|
168
|
+
const canonical = formatIpAddress(entry);
|
|
169
|
+
return canonical === entry
|
|
170
|
+
? null
|
|
171
|
+
: `an exact address is compared as text with the client address; write ${canonical}`;
|
|
172
|
+
}
|
|
173
|
+
const prefix = parsePrefix(entry);
|
|
174
|
+
if (prefix) {
|
|
175
|
+
const ipv4Prefix = ipv4PrefixOfMapped(prefix);
|
|
176
|
+
return ipv4Prefix === null
|
|
177
|
+
? null
|
|
178
|
+
: `an IPv4-mapped prefix never matches, because clients are compared as IPv4; write ${ipv4Prefix}`;
|
|
179
|
+
}
|
|
180
|
+
if (isIpv4TrailingWildcard(entry))
|
|
181
|
+
return null;
|
|
182
|
+
return "must be '*', an IPv4 or IPv6 address, an address prefix such as 192.0.2.0/24, "
|
|
183
|
+
+ 'or a four-part IPv4 wildcard such as 192.0.2.*';
|
|
184
|
+
};
|
|
185
|
+
/** Checks a `security.vpn.allowedAssignedIps` entry. */
|
|
186
|
+
export const checkAssignedIpEntry = (entry) => {
|
|
187
|
+
if (typeof entry !== 'string')
|
|
188
|
+
return 'must be a string';
|
|
189
|
+
if (entry.includes('%'))
|
|
190
|
+
return zoneIndexReason;
|
|
191
|
+
if (ipFamilyOf(entry) === 0)
|
|
192
|
+
return 'must be one IPv4 or IPv6 address';
|
|
193
|
+
const canonical = formatIpAddress(entry);
|
|
194
|
+
return canonical === entry
|
|
195
|
+
? null
|
|
196
|
+
: `an assigned address is compared as text with the VPN client's address; write ${canonical}`;
|
|
197
|
+
};
|
|
198
|
+
/** Formats one refused entry for a validation message. */
|
|
199
|
+
export const describeRefusedIpEntry = (labelArg, entryArg, reasonArg) => `${labelArg}: ${String(entryArg)} (${reasonArg})`;
|
|
200
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaXAtZW50cnktdmFsaWRhdGlvbi5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uL3RzL3Byb3hpZXMvc21hcnQtcHJveHkvdXRpbHMvaXAtZW50cnktdmFsaWRhdGlvbi50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUssT0FBTyxNQUFNLHFCQUFxQixDQUFDO0FBRS9DOzs7Ozs7Ozs7Ozs7Ozs7Ozs7R0FrQkc7QUFFSCxNQUFNLHNCQUFzQixHQUFHLEVBQUUsQ0FBQztBQUVsQywwRkFBMEY7QUFDMUYsTUFBTSxVQUFVLEdBQUcsQ0FBQyxLQUFhLEVBQWEsRUFBRTtJQUM5QyxJQUFJLEtBQUssQ0FBQyxRQUFRLENBQUMsR0FBRyxDQUFDO1FBQUUsT0FBTyxDQUFDLENBQUM7SUFDbEMsT0FBTyxPQUFPLENBQUMsR0FBRyxDQUFDLElBQUksQ0FBQyxLQUFLLENBQWMsQ0FBQztBQUM5QyxDQUFDLENBQUM7QUFFRixnRkFBZ0Y7QUFDaEYsTUFBTSxjQUFjLEdBQUcsQ0FBQyxPQUFlLEVBQVksRUFBRTtJQUNuRCxNQUFNLFVBQVUsR0FBRyxDQUFDLElBQVksRUFBWSxFQUFFO1FBQzVDLElBQUksSUFBSSxLQUFLLEVBQUU7WUFBRSxPQUFPLEVBQUUsQ0FBQztRQUMzQixNQUFNLFFBQVEsR0FBYSxFQUFFLENBQUM7UUFDOUIsS0FBSyxNQUFNLElBQUksSUFBSSxJQUFJLENBQUMsS0FBSyxDQUFDLEdBQUcsQ0FBQyxFQUFFLENBQUM7WUFDbkMsSUFBSSxJQUFJLENBQUMsUUFBUSxDQUFDLEdBQUcsQ0FBQyxFQUFFLENBQUM7Z0JBQ3ZCLE1BQU0sQ0FBQyxDQUFDLEVBQUUsQ0FBQyxFQUFFLENBQUMsRUFBRSxDQUFDLENBQUMsR0FBRyxJQUFJLENBQUMsS0FBSyxDQUFDLEdBQUcsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxNQUFNLENBQUMsQ0FBQztnQkFDakQsUUFBUSxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUMsSUFBSSxD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
|
|
@@ -22,3 +22,12 @@ export declare class RouteValidationError extends Error {
|
|
|
22
22
|
readonly errors: IRouteValidationErrorEntry[];
|
|
23
23
|
constructor(summaryArg: string, errorsArg: IRouteValidationErrorEntry[]);
|
|
24
24
|
}
|
|
25
|
+
/**
|
|
26
|
+
* Thrown when SmartProxy refuses settings outside its routes because they failed validation.
|
|
27
|
+
*
|
|
28
|
+
* `messages` lists every reason. The message names the first reasons only.
|
|
29
|
+
*/
|
|
30
|
+
export declare class SettingsValidationError extends Error {
|
|
31
|
+
readonly messages: string[];
|
|
32
|
+
constructor(summaryArg: string, messagesArg: string[]);
|
|
33
|
+
}
|
|
@@ -38,4 +38,22 @@ export class RouteValidationError extends Error {
|
|
|
38
38
|
this.errors = errorsArg;
|
|
39
39
|
}
|
|
40
40
|
}
|
|
41
|
-
|
|
41
|
+
/**
|
|
42
|
+
* Thrown when SmartProxy refuses settings outside its routes because they failed validation.
|
|
43
|
+
*
|
|
44
|
+
* `messages` lists every reason. The message names the first reasons only.
|
|
45
|
+
*/
|
|
46
|
+
export class SettingsValidationError extends Error {
|
|
47
|
+
messages;
|
|
48
|
+
constructor(summaryArg, messagesArg) {
|
|
49
|
+
const shownMessages = messagesArg.slice(0, MAX_MESSAGES_PER_ROUTE_IN_MESSAGE).map(truncateText);
|
|
50
|
+
const hiddenMessageCount = messagesArg.length - shownMessages.length;
|
|
51
|
+
if (hiddenMessageCount > 0) {
|
|
52
|
+
shownMessages.push(`and ${hiddenMessageCount} more`);
|
|
53
|
+
}
|
|
54
|
+
super(`${summaryArg}: ${shownMessages.join('; ')}`);
|
|
55
|
+
this.name = 'SettingsValidationError';
|
|
56
|
+
this.messages = messagesArg;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicm91dGUtdmFsaWRhdGlvbi1lcnJvci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uL3RzL3Byb3hpZXMvc21hcnQtcHJveHkvdXRpbHMvcm91dGUtdmFsaWRhdGlvbi1lcnJvci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFlQSxNQUFNLHFCQUFxQixHQUFHLEVBQUUsQ0FBQztBQUNqQyxNQUFNLGlDQUFpQyxHQUFHLENBQUMsQ0FBQztBQUM1QyxNQUFNLDBCQUEwQixHQUFHLEdBQUcsQ0FBQztBQUV2QyxTQUFTLFlBQVksQ0FBQyxPQUFlO0lBQ25DLE9BQU8sT0FBTyxDQUFDLE1BQU0sR0FBRywwQkFBMEI7UUFDaEQsQ0FBQyxDQUFDLEdBQUcsT0FBTyxDQUFDLEtBQUssQ0FBQyxDQUFDLEVBQUUsMEJBQTBCLENBQUMsR0FBRztRQUNwRCxDQUFDLENBQUMsT0FBTyxDQUFDO0FBQ2QsQ0FBQztBQUVELFNBQVMsV0FBVyxDQUFDLFFBQW9DO0lBQ3ZELE1BQU0sYUFBYSxHQUFHLFFBQVEsQ0FBQyxRQUFRO1NBQ3BDLEtBQUssQ0FBQyxDQUFDLEVBQUUsaUNBQWlDLENBQUM7U0FDM0MsR0FBRyxDQUFDLFlBQVksQ0FBQyxDQUFDO0lBQ3JCLE1BQU0sa0JBQWtCLEdBQUcsUUFBUSxDQUFDLFFBQVEsQ0FBQyxNQUFNLEdBQUcsYUFBYSxDQUFDLE1BQU0sQ0FBQztJQUMzRSxJQUFJLGtCQUFrQixHQUFHLENBQUMsRUFBRSxDQUFDO1FBQzNCLGFBQWEsQ0FBQyxJQUFJLENBQUMsT0FBTyxrQkFBa0IsT0FBTyxDQUFDLENBQUM7SUFDdkQsQ0FBQztJQUNELE9BQU8sVUFBVSxZQUFZLENBQUMsUUFBUSxDQUFDLEtBQUssQ0FBQyxNQUFNLGFBQWEsQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLEVBQUUsQ0FBQztBQUNoRixDQUFDO0FBRUQsU0FBUyxhQUFhLENBQUMsVUFBa0IsRUFBRSxTQUF1QztJQUNoRixNQUFNLFdBQVcsR0FBRyxTQUFTLENBQUMsS0FBSyxDQUFDLENBQUMsRUFBRSxxQkFBcUIsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxXQUFXLENBQUMsQ0FBQztJQUMvRSxNQUFNLGdCQUFnQixHQUFHLFNBQVMsQ0FBQyxNQUFNLEdBQUcsV0FBVyxDQUFDLE1BQU0sQ0FBQztJQUMvRCxJQUFJLGdCQUFnQixHQUFHLENBQUMsRUFBRSxDQUFDO1FBQ3pCLFdBQVcsQ0FBQyxJQUFJLENBQUMsT0FBTyxnQkFBZ0IsZ0JBQWdCLENBQUMsQ0FBQztJQUM1RCxDQUFDO0lBQ0QsT0FBTyxHQUFHLFVBQVUsS0FBSyxTQUFTLENBQUMsTUFBTSwwQkFBMEIsV0FBVyxDQUFDLElBQUksQ0FBQyxLQUFLLENBQUMsRUFBRSxDQUFDO0FBQy9GLENBQUM7QUFFRDs7Ozs7R0FLRztBQUNILE1BQU0sT0FBTyxvQkFBcUIsU0FBUSxLQUFLO0lBQzdCLE1BQU0sQ0FBK0I7SUFFckQsWUFBWSxVQUFrQixFQUFFLFNBQXVDO1FBQ3JFLEtBQUssQ0FBQyxhQUFhLENBQUMsVUFBVSxFQUFFLFNBQVMsQ0FBQyxDQUFDLENBQUM7UUFDNUMsSUFBSSxDQUFDLElBQUksR0FBRyxzQkFBc0IsQ0FBQztRQUNuQyxJQUFJLENBQUMsTUFBTSxHQUFHLFNBQVMsQ0FBQztJQUMxQixDQUFDO0NBQ0Y7QUFFRDs7OztHQUlHO0FBQ0gsTUFBTSxPQUFPLHVCQUF3QixTQUFRLEtBQUs7SUFDaEMsUUFBUSxDQUFXO0lBRW5DLFlBQVksVUFBa0IsRUFBRSxXQUFxQjtRQUNuRCxNQUFNLGFBQWEsR0FBRyxXQUFXLENBQUMsS0FBSyxDQUFDLENBQUMsRUFBRSxpQ0FBaUMsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxZQUFZLENBQUMsQ0FBQztRQUNoRyxNQUFNLGtCQUFrQixHQUFHLFdBQVcsQ0FBQyxNQUFNLEdBQUcsYUFBYSxDQUFDLE1BQU0sQ0FBQztRQUNyRSxJQUFJLGtCQUFrQixHQUFHLENBQUMsRUFBRSxDQUFDO1lBQzNCLGFBQWEsQ0FBQyxJQUFJLENBQUMsT0FBTyxrQkFBa0IsT0FBTyxDQUFDLENBQUM7UUFDdkQsQ0FBQztRQUNELEtBQUssQ0FBQyxHQUFHLFVBQVUsS0FBSyxhQUFhLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxFQUFFLENBQUMsQ0FBQztRQUNwRCxJQUFJLENBQUMsSUFBSSxHQUFHLHlCQUF5QixDQUFDO1FBQ3RDLElBQUksQ0FBQyxRQUFRLEdBQUcsV0FBVyxDQUFDO0lBQzlCLENBQUM7Q0FDRiJ9
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { IRouteConfig, IRouteMatch, IRouteAction } from '../models/route-types.js';
|
|
2
|
+
import type { ISmartProxyOptions, ISmartProxySecurityPolicy } from '../models/interfaces.js';
|
|
2
3
|
import { type IRouteValidationErrorEntry } from './route-validation-error.js';
|
|
3
4
|
/**
|
|
4
5
|
* Validates route configurations for correctness and safety
|
|
@@ -22,6 +23,14 @@ export declare class RouteValidator {
|
|
|
22
23
|
* A name or id that several routes share gets `[<index>]` appended, so every label is unique.
|
|
23
24
|
*/
|
|
24
25
|
static getRouteLabels(routes: IRouteConfig[]): string[];
|
|
26
|
+
/**
|
|
27
|
+
* Validate the IP entries of the settings outside routes: the global `trustedProxyIPs`, the
|
|
28
|
+
* `defaults.security` lists the engine copies into routes without security, and the global
|
|
29
|
+
* `securityPolicy`. Answers every reason, empty when the settings are valid.
|
|
30
|
+
*/
|
|
31
|
+
static validateSettings(settings: Pick<ISmartProxyOptions, 'trustedProxyIPs' | 'defaults' | 'securityPolicy'>): string[];
|
|
32
|
+
/** Validate the IP entries of a global ingress security policy. */
|
|
33
|
+
static validateSecurityPolicy(policy: ISmartProxySecurityPolicy): string[];
|
|
25
34
|
/**
|
|
26
35
|
* Validate multiple route configurations.
|
|
27
36
|
*
|
|
@@ -54,18 +63,6 @@ export declare class RouteValidator {
|
|
|
54
63
|
* Validate path pattern
|
|
55
64
|
*/
|
|
56
65
|
private static isValidPath;
|
|
57
|
-
/**
|
|
58
|
-
* Validate IP pattern
|
|
59
|
-
*/
|
|
60
|
-
private static isValidIPPattern;
|
|
61
|
-
/**
|
|
62
|
-
* Validate IPv4 address
|
|
63
|
-
*/
|
|
64
|
-
private static isValidIPv4;
|
|
65
|
-
/**
|
|
66
|
-
* Validate IPv6 address
|
|
67
|
-
*/
|
|
68
|
-
private static isValidIPv6;
|
|
69
66
|
/**
|
|
70
67
|
* Log validation errors
|
|
71
68
|
*/
|