security-patches
Version:
Latest Security Patches
790 lines (761 loc) • 26.7 kB
JavaScript
var md5EncriptPatch = `
import * as cookie from 'cookie';
import * as crypto from 'crypto';
import { Request } from 'express';
import got from 'got';
import * as hashSum from 'hash-sum';
import { HttpProxyAgent, HttpsProxyAgent } from 'hpagent';
import * as multer from 'multer';
import * as querystring from 'query-string';
import * as tough from 'tough-cookie';
import { URL, URLSearchParams } from 'url';
import * as uuid from 'uuid';
import config from '../config/config';
import configNodes from '../config/configNodes';
import { Middlewares } from '../middleware/GlobalMiddlewares';
import { StatusCodes } from 'http-status-codes';
const statusCodes = Object.values(StatusCodes).filter(
s => typeof s === 'number'
);
export class SDBaseService {
public configNodes = configNodes;
__constructDefault(bh, ...webVars) {
const system: any = {};
const web: any = {};
try {
system.environment = process.env;
if (!bh.input) {
bh.input = {};
}
if (!bh.local) {
bh.local = {};
}
if (!bh.model) {
bh.model = {};
}
if (webVars[0]) {
web.req = webVars[0];
bh.input.params = web.req.params;
bh.input.query = web.req.query;
bh.input.body = web.req.body;
bh.input.files = web.req.files;
bh.input.cookies = web.req.cookies;
bh.input.headers = web.req.headers;
bh.input.hostname = web.req.hostname;
bh.input.method = web.req.method;
bh.input.path = web.req.path;
bh.input.signedCookies = web.req.signedCookies;
}
if (webVars[1]) {
web.res = webVars[1];
}
if (webVars[2]) {
web.next = webVars[2];
}
Object.defineProperty(bh, 'system', {
value: system,
writable: false,
});
Object.defineProperty(bh, 'web', {
value: web,
writable: true,
});
return bh;
} catch (e) {
console.error(e);
throw e;
}
}
addDMPropertiesToBh(bh, req: Request, entityName?: string) {
if (entityName) {
bh.model = {};
bh.model[entityName] = req.body[entityName];
} else {
bh.model = undefined;
}
bh.filter = req.body.filter;
bh.pageSize = req.body.pageSize;
bh.offSet = req.body.offSet;
bh.orderBy = req.body.orderBy;
bh.data = req.body.data;
bh.result = req.body.result;
}
operators = {
eq(a, b, c, d) {
return a == b;
},
se(a, b, c, d) {
return a === b;
},
neq(a, b, c, d) {
return a != b;
},
sne(a, b, c, d) {
return a !== b;
},
lt(a, b, c, d) {
return a < b;
},
lte(a, b, c, d) {
return a <= b;
},
gt(a, b, c, d) {
return a > b;
},
gte(a, b, c, d) {
return a >= b;
},
btwn(a, b, c, d) {
return a >= b && a <= c;
},
cont(a, b, c, d) {
return (a + '').indexOf(b) !== -1;
},
regex(a, b, c, d) {
return (a + '').match(new RegExp(b, d ? 'i' : ''));
},
true(a, b, c, d) {
return a === true;
},
false(a, b, c, d) {
return a === false;
},
null(a, b, c, d) {
return typeof a === 'undefined' || a === null;
},
nnull(a, b, c, d) {
return typeof a !== 'undefined' && a !== null;
},
empty(a, b, c, d) {
if (typeof a === 'string' || Array.isArray(a)) {
return a.length === 0;
} else if (typeof a === 'object' && a !== null) {
return Object.keys(a).length === 0;
}
return false;
},
nempty(a, b, c, d) {
if (typeof a === 'string' || Array.isArray(a)) {
return a.length !== 0;
} else if (typeof a === 'object' && a !== null) {
return Object.keys(a).length !== 0;
}
return false;
},
istype(a, b, c, d) {
if (b === 'array') {
return Array.isArray(a);
} else if (b === 'json') {
try {
JSON.parse(a);
return true;
} catch (e) {
// or maybe ??? a !== null; }
return false;
}
} else if (b === 'null') {
return a === null;
} else {
return typeof a === b && !Array.isArray(a) && a !== null;
}
},
isStatusCode(a, b, c, d) {
const mayBeNum = Number(a);
return isNaN(mayBeNum) ? false : statusCodes.includes(mayBeNum);
},
else(a, b, c, d) {
return a === true;
},
};
/**
*
* construct http request options and make a request
*
*/
httpRequest(
url,
timeout,
method,
headers,
followRedirects,
cookies,
authType,
body,
paytoqs,
proxyConfig,
tlsConfig,
ret,
params,
rejectUnauthorized,
username,
password,
token
) {
let opts = {};
opts['url'] = url;
if (ret == 'arraybuffer') {
opts['responseType'] = 'buffer';
} else {
opts['responseType'] = ret;
}
opts['timeout'] = {
send: timeout,
response: timeout,
};
opts['method'] = method;
opts['headers'] = {};
opts['maxRedirects'] = 21;
opts['cookieJar'] = new tough.CookieJar();
opts['proxy'] = null;
opts['https'] = { rejectUnauthorized: rejectUnauthorized };
const preRequestTimestamp = process.hrtime();
const originalHeaderMap = {};
const redirectList = [];
opts['hooks'] = {
beforeRequest: [
options => {
// Whilst HTTP headers are meant to be case-insensitive,
// in the real world, there are servers that aren't so compliant.
// GOT will lower case all headers given a chance, so we need
// to restore the case of any headers the user has set.
Object.keys(options.headers).forEach(h => {
if (
originalHeaderMap[h] &&
originalHeaderMap[h] !== h
) {
options.headers[originalHeaderMap[h]] =
options.headers[h];
delete options.headers[h];
}
});
},
],
beforeRedirect: [
(options, response) => {
let redirectInfo = {
location: response.headers.location,
};
if (response.headers.hasOwnProperty('set-cookie')) {
redirectInfo['cookies'] = this.extractCookies(
response.headers['set-cookie']
);
}
redirectList.push(redirectInfo);
},
],
};
// known headers to normalize
const ctSet = 'Content-Type'; // set default camel case
const clSet = 'Content-Length';
opts = this.setHeaders(opts, headers, clSet, ctSet);
opts = this.setRedirects(opts, followRedirects);
opts = this.setCookies(opts, cookies, url);
opts = this.addRequestCredentials(
opts,
authType,
username,
password,
token
);
opts = this.constructBody(opts, method, body, clSet, ctSet);
opts = this.setPaytoqs(opts, method, body, paytoqs);
opts = this.revertCapitalisation(opts, clSet, ctSet);
opts = this.addProxyConfig(opts, proxyConfig, url);
opts = this.addTLSOptions(opts, tlsConfig);
opts = this.addQueryParams(opts, params);
return got(opts).then(res => {
const msg = {};
msg['statusCode'] = res.statusCode;
msg['headers'] = res.headers;
msg['responseUrl'] = res.url;
msg['payload'] = res.body;
msg['redirectList'] = redirectList;
if (msg['headers'].hasOwnProperty('set-cookie')) {
msg['responseCookies'] = this.extractCookies(
msg['headers']['set-cookie']
);
}
msg['headers']['x-neutrinos-request-node'] = hashSum(
msg['headers']
);
// Calculate request time
const diff = process.hrtime(preRequestTimestamp);
const ms = diff[0] * 1e3 + diff[1] * 1e-6;
const metricRequestDurationMillis = ms.toFixed(3);
msg['requestDuration'] = metricRequestDurationMillis;
if (res['client']?.bytesRead) {
msg['bytesRead'] = res['client'].bytesRead;
}
// Convert the payload to the required return type
if (ret !== 'arraybuffer') {
// txt
if (ret === 'json') {
msg['payload'] = JSON.parse(JSON.stringify(msg['payload']));
} else {
msg['payload'] = msg['payload'].toString('utf8');
}
}
return msg;
});
}
setHeaders(opts, headers, clSet, ctSet) {
// headers is an object, each key is treated as a header
if (headers) {
for (const v in headers) {
if (headers.hasOwnProperty(v)) {
// normalize known headers
let name = v.toLowerCase();
if (name !== 'content-type' && name !== 'content-length') {
// only normalise the known headers used later in this
// function. Otherwise leave them alone.
name = v;
}
opts.headers[name] = headers[v];
}
}
}
return opts;
}
setRedirects(opts, followRedirects) {
opts.followRedirect = !!followRedirects;
return opts;
}
setCookies(opts, cookies, url) {
if (opts.headers.hasOwnProperty('cookie')) {
cookies = cookie.parse(opts.headers.cookie, { decode: String });
for (const name in cookies) {
opts.cookieJar.setCookie(
cookie.serialize(name, cookies[name], { encode: String }),
url,
{
ignoreError: true,
}
);
}
delete opts.headers.cookie;
}
// cookie is an object, each key is treated as a new cookie
if (!cookies) {
return opts;
}
Object.keys(cookies).forEach(name => {
if (cookies[name] === null || cookies[name].value === null) {
// This case clears a cookie for HTTP In/Response nodes.
// Ignore for this node.
return;
}
if (typeof cookies[name] === 'object') {
if (cookies[name].encode === false) {
// If the encode option is false, the value is not encoded.
opts.cookieJar.setCookie(
cookie.serialize(name, cookies[name].value, {
encode: String,
}),
url,
{ ignoreError: true }
);
return;
}
// The value is encoded by encodeURIComponent().
opts.cookieJar.setCookie(
cookie.serialize(name, cookies[name].value),
url,
{
ignoreError: true,
}
);
return;
}
opts.cookieJar.setCookie(
cookie.serialize(name, cookies[name]),
url,
{ ignoreError: true }
);
});
return opts;
}
addRequestCredentials(opts, authType, user, password, token) {
if (user || password || token) {
if (authType === 'basic') {
let cred = '';
if (user) {
cred = user;
}
if (password) {
cred += ':' + password;
}
opts.headers.Authorization =
'Basic ' + Buffer.from(cred).toString('base64');
} else if (authType === 'digest') {
/**
* Code Change from required to got
*/
let digestCreds = {
user,
password: password,
};
let sentCreds = false;
opts['hooks'].afterResponse = [
(response, retry) => {
if (response.statusCode === 401) {
if (sentCreds) {
return response;
}
const requestUrl = new URL(
response.request.requestUrl
);
const options = response.request.options;
const normalisedHeaders = {};
Object.keys(response.headers).forEach(k => {
normalisedHeaders[k.toLowerCase()] =
response.headers[k];
});
sentCreds = true;
return retry(options);
}
return response;
},
];
} else if (authType === 'bearer') {
opts.headers.Authorization = \`Bearer \${token || ''} \`;
}
}
return opts;
}
setPaytoqs(opts, method, body, paytoqs) {
if (method === 'GET' && typeof body !== 'undefined' && paytoqs) {
if (typeof body === 'object') {
try {
if (opts.url.indexOf('?') !== -1) {
opts.url +=
(opts.url.endsWith('?') ? '' : '&') +
querystring.stringify(body);
} else {
opts.url += '?' + querystring.stringify(body);
}
} catch (err) {
throw new Error('Invalid Payload');
}
} else {
throw new Error('Invalid Payload');
}
}
return opts;
}
addQueryParams(opts, params) {
if (typeof params === 'object' && Object.entries(params).length > 0) {
try {
const paramsMap = [];
for (const [key, value] of Object.entries(params)) {
paramsMap.push([key, value]);
}
const searchParams = new URLSearchParams(paramsMap);
opts.url = opts.url + '?' + searchParams.toString();
} catch (err) {
throw new Error('Invalid params');
}
}
return opts;
}
revertCapitalisation(opts, clSet, ctSet) {
// revert to user supplied Capitalisation if needed.
if (
opts.headers.hasOwnProperty('content-type') &&
ctSet !== 'content-type'
) {
opts.headers[ctSet] = opts.headers['content-type'];
delete opts.headers['content-type'];
}
if (
opts.headers.hasOwnProperty('content-length') &&
clSet !== 'content-length'
) {
opts.headers[clSet] = opts.headers['content-length'];
delete opts.headers['content-length'];
}
return opts;
}
constructBody(opts, method, body, clSet, ctSet) {
let payload = null;
if (
method !== 'GET' &&
method !== 'HEAD' &&
typeof body !== 'undefined'
) {
if (
opts.headers['content-type'] === 'multipart/form-data' &&
typeof body === 'object'
) {
opts.formData = {};
for (const opt in body) {
if (body.hasOwnProperty(opt)) {
const val = body[opt];
if (val !== undefined && val !== null) {
if (
typeof val === 'string' ||
Buffer.isBuffer(val)
) {
opts.formData[opt] = val;
} else if (
typeof val === 'object' &&
val.hasOwnProperty('value')
) {
// Treat as file to upload - ensure it has an options object
// as request complains if it doesn't
if (!val.hasOwnProperty('options')) {
val.options = {};
}
opts.formData[opt] = val;
} else {
opts.formData[opt] = JSON.stringify(val);
}
}
}
}
} else {
if (typeof body === 'string' || Buffer.isBuffer(body)) {
payload = body;
} else if (typeof body === 'number') {
payload = body + '';
} else {
if (
opts.headers['content-type'] ===
'application/x-www-form-urlencoded'
) {
payload = querystring.stringify(body);
} else {
payload = JSON.stringify(body);
if (opts.headers['content-type'] == null) {
opts.headers[ctSet] = 'application/json';
}
}
}
if (opts.headers['content-length'] == null) {
if (Buffer.isBuffer(payload)) {
opts.headers[clSet] = payload.length;
} else {
opts.headers[clSet] = Buffer.byteLength(payload);
}
}
opts.body = payload;
}
}
return opts;
}
addProxyConfig(opts, proxyConfig, url) {
let prox = null;
let noprox = null;
let noproxy;
if (proxyConfig) {
prox = proxyConfig.url;
noprox = proxyConfig.noproxy;
}
if (noprox) {
for (const i in noprox) {
if (url.indexOf(noprox[i]) !== -1) {
noproxy = true;
}
}
}
if (prox && !noproxy) {
const match = prox.match(/^(https?:\/\/)?(.+)?:([0-9]+)?/i);
if (match) {
let proxyURL = new URL(prox);
//set username/password to null to stop empty creds header
let proxyOptions = {
proxy: proxyURL,
maxFreeSockets: 256,
maxSockets: 256,
keepAlive: true,
};
if (proxyConfig?.credentials) {
let proxyUsername = proxyConfig.credentials.username || '';
let proxyPassword = proxyConfig.credentials.password || '';
if (proxyUsername || proxyPassword) {
proxyOptions.proxy.username = proxyUsername;
proxyOptions.proxy.password = proxyPassword;
}
} else if (proxyURL.username || proxyURL.password) {
proxyOptions.proxy.username = proxyURL.username;
proxyOptions.proxy.password = proxyURL.password;
}
//need both incase of http -> https redirect
opts.agent = {
http: new HttpProxyAgent(proxyOptions),
https: new HttpsProxyAgent(proxyOptions),
};
} else {
opts.proxy = null;
throw new Error('Invalid proxy url');
}
}
return opts;
}
addTLSOptions(opts, tlsConfig) {
if (tlsConfig) {
opts.https = {};
tlsConfig.addTLSOptions(opts.https);
if (opts.https.ca) {
opts.https.certificateAuthority = opts.https.ca;
delete opts.https.ca;
}
if (opts.https.cert) {
opts.https.certificate = opts.https.cert;
delete opts.https.cert;
}
return opts;
}
if (opts.hasOwnProperty('rejectUnauthorized')) {
opts.https = { rejectUnauthorized: opts.rejectUnauthorized };
}
return opts;
}
extractCookies(setCookie) {
const cookies = {};
setCookie.forEach(c => {
const parsedCookie = cookie.parse(c);
const eqIdx = c.indexOf('=');
const key = c.substr(0, eqIdx).trim();
parsedCookie.value = parsedCookie[key];
delete parsedCookie[key];
cookies[key] = parsedCookie;
});
return cookies;
}
getMiddlesWaresBySequenceId(
sequenceId: string,
type: string,
generatedMiddleWares: any
) {
const returnedMws = [];
const middleWares =
config?.['middleware']?.['sequences']?.['sequenceId']?.['type'];
if (Array.isArray(middleWares)) {
middleWares.forEach(middleware => {
const serviceName = Object.getOwnPropertyNames(middleware)[0];
const mwName = middleware[serviceName];
if (
serviceName === '__ssdGlobalMiddlewares__' &&
typeof Middlewares[mwName] === 'function'
) {
returnedMws.push(Middlewares[mwName]());
return;
}
if (generatedMiddleWares?.[serviceName][mwName]) {
returnedMws.push(
generatedMiddleWares[serviceName][mwName].functionDef
);
}
});
}
return returnedMws;
}
multipartParser(fileUploadOptions) {
let o: any = {
storage: multer.memoryStorage(),
};
if (fileUploadOptions.path) {
o = {
dest: fileUploadOptions.path,
};
}
let mp;
if (fileUploadOptions.options instanceof Array) {
mp = multer(o).fields(fileUploadOptions.options);
} else {
mp = multer(o).any();
}
return function (req, res, next) {
mp(req, res, function (err) {
req._body = true;
next(err);
});
};
}
/**
*
* @param configType
* @param id
*
* Based on config type and id return respective config object
*/
getConfigObj(configType, id): any {
if (!configType || !id) {
return;
}
if (!this.configNodes) {
throw new Error('Cannot find the config nodes');
}
if (
!this.configNodes.hasOwnProperty(configType) ||
!this.configNodes[configType].hasOwnProperty(id)
) {
throw new Error(
\`Cannot find the \${configType} config type or id \${id}\`
);
}
return this.configNodes[configType][id];
}
/**
*
* @param bh
* @param c - cookies should be object
*
* sets the cookies to the response
*/
cookieSetter(bh, c) {
if (typeof c === 'object') {
const cs = Object.keys(c);
for (const i of cs) {
bh.web.res.cookie(i, c[i].value, c[i]);
}
}
return bh;
}
}
`
var uploadFileLimitsPatch = `
multipartParser(fileUploadOptions) {
const MULTER_OPTION_FILESIZE = parseInt(process.env.MULTER_OPTION_FILESIZE);
const MULTER_OPTION_FIELDS = parseInt(process.env.MULTER_OPTION_FIELDS);
const MULTER_OPTION_FILES = parseInt(process.env.MULTER_OPTION_FILES);
const MULTER_OPTION_PARTS = parseInt(process.env.MULTER_OPTION_PARTS);
let o: multer.Options = {
storage: multer.memoryStorage(),
limits: {
fileSize: !isNaN(MULTER_OPTION_FILESIZE) ? MULTER_OPTION_FILESIZE : 1024 * 1024 * 1024,
fields: !isNaN(MULTER_OPTION_FIELDS) ? MULTER_OPTION_FIELDS : 1000,
files: !isNaN(MULTER_OPTION_FILES) ? MULTER_OPTION_FILES : 1000,
parts: !isNaN(MULTER_OPTION_PARTS) ? MULTER_OPTION_PARTS : 100,
},
};
if (fileUploadOptions.path) {
o = {
dest: fileUploadOptions.path,
};
}
let mp;
if (fileUploadOptions.options instanceof Array) {
mp = multer(o).fields(fileUploadOptions.options);
} else {
mp = multer(o).any();
}
return function (req, res, next) {
mp(req, res, function (err) {
req._body = true;
next(err);
});
};
}
/**
*
* @param configType
* @param id
*
* Based on config type and id return respective config object
*/
`
module.exports = {
md5EncriptPatch,
uploadFileLimitsPatch
}