@itentialopensource/adapter-utils 5.10.23 → 5.10.25
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/lib/connectorRest.js +54 -2
- package/lib/restHandler.js +2 -2
- package/package.json +3 -2
package/lib/connectorRest.js
CHANGED
|
@@ -32,6 +32,7 @@ const { HttpsProxyAgent } = require('https-proxy-agent');
|
|
|
32
32
|
const { SocksProxyAgent } = require('socks-proxy-agent');
|
|
33
33
|
const AsyncLockCl = require('async-lock');
|
|
34
34
|
const FormData = require('form-data');
|
|
35
|
+
const validator = require('validator');
|
|
35
36
|
|
|
36
37
|
const ThrottleCl = require(path.join(__dirname, '/throttle.js'));
|
|
37
38
|
|
|
@@ -134,6 +135,45 @@ let sslCertFilePath = null;
|
|
|
134
135
|
|
|
135
136
|
const mfaStepsResults = []; // keeps requested result for each step
|
|
136
137
|
|
|
138
|
+
// SSRF Protection: Validate requests to prevent malicious requests
|
|
139
|
+
function validateRequestSecurity(requestHeader, options) {
|
|
140
|
+
const { hostname, port: requestPort, path: requestPath } = requestHeader;
|
|
141
|
+
if (!hostname) {
|
|
142
|
+
throw new Error('Hostname is required');
|
|
143
|
+
}
|
|
144
|
+
if (!requestPort) {
|
|
145
|
+
throw new Error('Port is required');
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
const isValidIP = validator.isIP(hostname);
|
|
149
|
+
let isValidDomain = false;
|
|
150
|
+
if (!isValidIP) {
|
|
151
|
+
// Only validate as FQDN if it's NOT an IP address
|
|
152
|
+
const fqdnOptions = {
|
|
153
|
+
require_tld: false,
|
|
154
|
+
allow_underscores: true,
|
|
155
|
+
ignore_max_length: true
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
isValidDomain = validator.isFQDN(hostname, fqdnOptions);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
if (!isValidIP && !isValidDomain) {
|
|
162
|
+
throw new Error('Hostname must be a valid IP address or domain name');
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
if (!validator.isPort(String(requestPort))) {
|
|
166
|
+
throw new Error('Invalid port number');
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
if (requestPath && options.maxPathLength && options.maxPathLength > 0) {
|
|
170
|
+
if (requestPath.length > options.maxPathLength) {
|
|
171
|
+
throw new Error(`Path too long (${requestPath.length} > ${options.maxPathLength})`);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
return requestHeader;
|
|
175
|
+
}
|
|
176
|
+
|
|
137
177
|
/* CONNECTOR ENGINE INTERNAL FUNCTIONS */
|
|
138
178
|
/** Wait for adapter-mongo to be available.
|
|
139
179
|
* @summary adapter may load before adapter-mongo but it requires UPDATE: test if dbUtil object can connect.
|
|
@@ -644,7 +684,8 @@ function makeRequest(request, entitySchema, callProperties, startTrip, attempt,
|
|
|
644
684
|
}
|
|
645
685
|
|
|
646
686
|
// make the call to System
|
|
647
|
-
const
|
|
687
|
+
const validatedHeader = validateRequestSecurity(request.header, {});
|
|
688
|
+
const httpRequest = useProt.request(validatedHeader, (res) => {
|
|
648
689
|
let respStr = '';
|
|
649
690
|
if (res.headers['content-encoding'] !== 'gzip') {
|
|
650
691
|
res.setEncoding('utf8');
|
|
@@ -869,7 +910,18 @@ function makeRequest(request, entitySchema, callProperties, startTrip, attempt,
|
|
|
869
910
|
// handle any exception
|
|
870
911
|
const tripDiff = process.hrtime(roundTTime);
|
|
871
912
|
const tripEnd = `${Math.round(((tripDiff[0] * NS_PER_SEC) + tripDiff[1]) / 1000000)}ms`;
|
|
872
|
-
|
|
913
|
+
|
|
914
|
+
// Check if this is a validation error from validateRequestSecurity
|
|
915
|
+
const isValidationError = e.message && (
|
|
916
|
+
e.message.includes('Hostname is required')
|
|
917
|
+
|| e.message.includes('Port is required')
|
|
918
|
+
|| e.message.includes('Hostname must be a valid IP address or domain name')
|
|
919
|
+
|| e.message.includes('Invalid port number')
|
|
920
|
+
|| e.message.includes('Path length exceeds maximum allowed')
|
|
921
|
+
);
|
|
922
|
+
|
|
923
|
+
const errorMessage = isValidationError ? 'Request security validation failed' : 'Issue during make request';
|
|
924
|
+
const errorObj = transUtilInst.checkAndReturn(e, origin, errorMessage);
|
|
873
925
|
errorObj.metrics = {
|
|
874
926
|
code: 'translate_error',
|
|
875
927
|
redirects: attempt,
|
package/lib/restHandler.js
CHANGED
|
@@ -209,7 +209,7 @@ function handleRestRequest(request, entityId, entitySchema, callProperties, filt
|
|
|
209
209
|
retError = JSON.parse(perror.response.trim());
|
|
210
210
|
} catch (ex) {
|
|
211
211
|
// otherwise log parse failure but still return the unparsed error
|
|
212
|
-
log.warn(`${origin}: An error occurred parsing the error JSON: ${ex}: Error Response is: ${perror}`);
|
|
212
|
+
log.warn(`${origin}: An error occurred parsing the error JSON: ${ex}: Error Response is: ${perror.response}`);
|
|
213
213
|
}
|
|
214
214
|
}
|
|
215
215
|
}
|
|
@@ -454,7 +454,7 @@ function handleRestRequest(request, entityId, entitySchema, callProperties, filt
|
|
|
454
454
|
retResponse = JSON.parse(resObj.response.trim());
|
|
455
455
|
} catch (ex) {
|
|
456
456
|
// otherwise log parse failure and return the unparsed response
|
|
457
|
-
log.warn(`${origin}: An error occurred parsing the resulting JSON: ${ex}: Actual Response is: ${resObj}`);
|
|
457
|
+
log.warn(`${origin}: An error occurred parsing the resulting JSON: ${ex}: Actual Response is: ${resObj.response}`);
|
|
458
458
|
return callback(retObject);
|
|
459
459
|
}
|
|
460
460
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@itentialopensource/adapter-utils",
|
|
3
|
-
"version": "5.10.
|
|
3
|
+
"version": "5.10.25",
|
|
4
4
|
"description": "Itential Adapter Utility Libraries",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"postinstall": "node utils/setup.js",
|
|
@@ -37,11 +37,12 @@
|
|
|
37
37
|
"json-query": "^2.2.2",
|
|
38
38
|
"jsontoxml": "^1.0.1",
|
|
39
39
|
"jsonwebtoken": "^9.0.1",
|
|
40
|
-
"mongodb": "^3.7.4",
|
|
41
40
|
"mocha": "^10.7.3",
|
|
41
|
+
"mongodb": "^3.7.4",
|
|
42
42
|
"readline-sync": "^1.4.10",
|
|
43
43
|
"socks-proxy-agent": "^8.0.1",
|
|
44
44
|
"uuid": "^9.0.0",
|
|
45
|
+
"validator": "^13.15.15",
|
|
45
46
|
"xml2js": "^0.6.0"
|
|
46
47
|
},
|
|
47
48
|
"devDependencies": {
|