-
Notifications
You must be signed in to change notification settings - Fork 50
/
Copy pathrequest-filters.js
75 lines (63 loc) · 2.64 KB
/
request-filters.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
export const localIpFilter = (_req, headersData) => {
//https://stackoverflow.com/a/62925185, but without 127.0.0.1 range
const localIpRegex = /\b(0?10\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)|172\.0?1[6-9]\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)|172\.0?2[0-9]\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)|172\.0?3[01]\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)|192\.168\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)|169\.254\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)|::1|[fF][cCdD][0-9a-fA-F]{2}(?:[:][0-9a-fA-F]{0,4}){0,7}|[fF][eE][89aAbB][0-9a-fA-F](?:[:][0-9a-fA-F]{0,4}){0,7})(?:\/([789]|1?[0-9]{2}))?\b/;
const host = headersData.targetApiServer.host || '';
if (localIpRegex.test(host) || host.endsWith('.cluster.local')) {
throw Error('Local IP addresses are not allowed.');
}
};
export const pathWhitelistFilter = req => {
const path = req.originalUrl.replace(/^\/backend/, '');
// list comes from "/" call to API server
const whitelist = [
'/.well-known',
'/api',
'/apis',
'/healthz',
'/livez',
'/logs',
'/metrics',
'/openapi',
'/openid',
'/readyz',
'/version',
];
if (!whitelist.some(e => path.startsWith(e))) {
throw Error(`Path ${path} is not whitelisted.`);
}
};
export const pathInvalidCharacterFilter = req => {
const encodedPath = req.originalUrl;
let decodedPath;
try {
decodedPath = decodeURIComponent(encodedPath);
} catch (err) {
throw Error('Path contains invalid encoding', { cause: err });
}
// Check if the decoded path still contains encoded characters (i.e., '%' symbol)
if (decodedPath.includes('%')) {
throw Error('Decoded path contains illegal % characters');
}
// Check if the decoded path contains any non-printable or control characters
// eslint-disable-next-line no-control-regex
const controlCharRegex = /[\x00-\x1F\x7F]/;
if (controlCharRegex.test(decodedPath) || decodedPath.includes('..')) {
throw Error('Path contains invalid characters');
}
};
export const invalidRequestMethodFilter = req => {
const path = req.originalUrl;
const method = req.method;
if (
method === 'TRACE' ||
(['OPTIONS', 'HEAD'].includes(method) && !path.includes('proxy'))
) {
throw Error(`Invalid request method`);
}
};
export const filters = [
invalidRequestMethodFilter,
localIpFilter,
pathWhitelistFilter,
pathInvalidCharacterFilter,
];