@contrast/protect 1.2.1 → 1.4.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/lib/error-handlers/constants.js +15 -0
- package/lib/error-handlers/index.js +17 -0
- package/lib/error-handlers/install/express4.js +89 -0
- package/lib/error-handlers/install/fastify3.js +17 -4
- package/lib/error-handlers/install/koa2.js +16 -2
- package/lib/esm-loader.mjs +15 -0
- package/lib/get-source-context.js +33 -0
- package/lib/hardening/constants.js +20 -0
- package/lib/hardening/handlers.js +65 -0
- package/lib/hardening/index.js +29 -0
- package/lib/hardening/install/node-serialize0.js +59 -0
- package/lib/index.d.ts +127 -19
- package/lib/index.js +19 -0
- package/lib/input-analysis/constants.js +20 -0
- package/lib/input-analysis/handlers.js +201 -16
- package/lib/input-analysis/index.js +40 -3
- package/lib/input-analysis/install/body-parser1.js +122 -0
- package/lib/input-analysis/install/cookie-parser1.js +80 -0
- package/lib/input-analysis/install/express4.js +103 -0
- package/lib/input-analysis/install/fastify3.js +51 -24
- package/lib/input-analysis/install/formidable1.js +72 -0
- package/lib/input-analysis/install/http.js +30 -4
- package/lib/input-analysis/install/koa-body5.js +63 -0
- package/lib/input-analysis/install/koa-bodyparser4.js +64 -0
- package/lib/input-analysis/install/koa2.js +38 -48
- package/lib/input-analysis/install/multer1.js +88 -0
- package/lib/input-analysis/install/qs6.js +57 -0
- package/lib/input-analysis/install/universal-cookie4.js +52 -0
- package/lib/input-analysis/ip-analysis.js +76 -0
- package/lib/input-analysis/virtual-patches.js +109 -0
- package/lib/input-tracing/constants.js +15 -0
- package/lib/input-tracing/handlers/index.js +225 -66
- package/lib/input-tracing/index.js +25 -2
- package/lib/input-tracing/install/child-process.js +28 -7
- package/lib/input-tracing/install/eval.js +60 -0
- package/lib/input-tracing/install/fs.js +21 -4
- package/lib/input-tracing/install/http.js +63 -0
- package/lib/input-tracing/install/mongodb.js +233 -0
- package/lib/input-tracing/install/mysql.js +21 -4
- package/lib/input-tracing/install/postgres.js +20 -4
- package/lib/input-tracing/install/sequelize.js +22 -5
- package/lib/input-tracing/install/sqlite3.js +21 -4
- package/lib/input-tracing/install/vm.js +132 -0
- package/lib/make-response-blocker.js +15 -0
- package/lib/make-source-context.js +22 -1
- package/lib/security-exception.js +15 -0
- package/lib/semantic-analysis/handlers.js +160 -0
- package/lib/semantic-analysis/index.js +38 -0
- package/lib/throw-security-exception.js +17 -6
- package/package.json +10 -12
- package/lib/cli-rewriter.js +0 -20
- package/lib/input-analysis/install/co-body.js +0 -51
- package/lib/input-analysis/install/cookie-parser.js +0 -48
- package/lib/input-analysis/install/formidable.js +0 -53
- package/lib/input-analysis/install/multer.js +0 -52
- package/lib/input-analysis/install/qs.js +0 -40
- package/lib/input-analysis/install/universal-cookie.js +0 -34
- package/lib/input-tracing/handlers/nosql-injection-mongo.js +0 -48
- package/lib/utils.js +0 -88
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright: 2022 Contrast Security, Inc
|
|
3
|
+
* Contact: support@contrastsecurity.com
|
|
4
|
+
* License: Commercial
|
|
5
|
+
|
|
6
|
+
* NOTICE: This Software and the patented inventions embodied within may only be
|
|
7
|
+
* used as part of Contrast Security’s commercial offerings. Even though it is
|
|
8
|
+
* made available through public repositories, use of this Software is subject to
|
|
9
|
+
* the applicable End User Licensing Agreement found at
|
|
10
|
+
* https://www.contrastsecurity.com/enduser-terms-0317a or as otherwise agreed
|
|
11
|
+
* between Contrast Security and the End User. The Software may not be reverse
|
|
12
|
+
* engineered, modified, repackaged, sold, redistributed or otherwise used in a
|
|
13
|
+
* way not consistent with the End User License Agreement.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
'use strict';
|
|
17
|
+
|
|
18
|
+
const { patchType } = require('../constants');
|
|
19
|
+
const { isSecurityException } = require('../../security-exception');
|
|
20
|
+
|
|
21
|
+
module.exports = (core) => {
|
|
22
|
+
const {
|
|
23
|
+
depHooks,
|
|
24
|
+
patcher,
|
|
25
|
+
logger,
|
|
26
|
+
scopes: { wrap },
|
|
27
|
+
protect,
|
|
28
|
+
protect: { inputAnalysis },
|
|
29
|
+
} = core;
|
|
30
|
+
|
|
31
|
+
// Patch `multer`
|
|
32
|
+
function install() {
|
|
33
|
+
depHooks.resolve({ name: 'multer', file: 'lib/make-middleware.js' }, (multerMakeMiddleware) => patcher.patch(multerMakeMiddleware, {
|
|
34
|
+
name: 'multer.make-middleware',
|
|
35
|
+
patchType,
|
|
36
|
+
post(data) {
|
|
37
|
+
data.result = patcher.patch(data.result, {
|
|
38
|
+
name: 'multerMiddleware',
|
|
39
|
+
patchType,
|
|
40
|
+
pre(data) {
|
|
41
|
+
const [req, , origNext] = data.args;
|
|
42
|
+
|
|
43
|
+
// We are getting the sourceContext here because in the time of calling
|
|
44
|
+
// the contrastNext() method the context is lost
|
|
45
|
+
const sourceContext = protect.getSourceContext('multer');
|
|
46
|
+
|
|
47
|
+
if (!sourceContext) return;
|
|
48
|
+
|
|
49
|
+
function contrastNext(origErr) {
|
|
50
|
+
let securityException;
|
|
51
|
+
|
|
52
|
+
if (req.body) {
|
|
53
|
+
sourceContext.parsedBody = req.body;
|
|
54
|
+
|
|
55
|
+
try {
|
|
56
|
+
inputAnalysis.handleParsedBody(sourceContext, req.body);
|
|
57
|
+
} catch (err) {
|
|
58
|
+
if (isSecurityException(err)) {
|
|
59
|
+
securityException = err;
|
|
60
|
+
} else {
|
|
61
|
+
logger.error({ err }, 'Unexpected error during input analysis');
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if (req.file || req.files) {
|
|
67
|
+
logger.debug('Check for vulnerable filename upload nyi');
|
|
68
|
+
// TODO: NODE-2601
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const error = securityException || origErr;
|
|
72
|
+
|
|
73
|
+
origNext(error);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
data.args[2] = wrap(contrastNext);
|
|
77
|
+
}
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
}));
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const multer1Instrumentation = inputAnalysis.multer1Instrumentation = {
|
|
84
|
+
install
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
return multer1Instrumentation;
|
|
88
|
+
};
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright: 2022 Contrast Security, Inc
|
|
3
|
+
* Contact: support@contrastsecurity.com
|
|
4
|
+
* License: Commercial
|
|
5
|
+
|
|
6
|
+
* NOTICE: This Software and the patented inventions embodied within may only be
|
|
7
|
+
* used as part of Contrast Security’s commercial offerings. Even though it is
|
|
8
|
+
* made available through public repositories, use of this Software is subject to
|
|
9
|
+
* the applicable End User Licensing Agreement found at
|
|
10
|
+
* https://www.contrastsecurity.com/enduser-terms-0317a or as otherwise agreed
|
|
11
|
+
* between Contrast Security and the End User. The Software may not be reverse
|
|
12
|
+
* engineered, modified, repackaged, sold, redistributed or otherwise used in a
|
|
13
|
+
* way not consistent with the End User License Agreement.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
'use strict';
|
|
17
|
+
|
|
18
|
+
const { patchType } = require('../constants');
|
|
19
|
+
|
|
20
|
+
module.exports = (core) => {
|
|
21
|
+
const {
|
|
22
|
+
depHooks,
|
|
23
|
+
patcher,
|
|
24
|
+
protect,
|
|
25
|
+
protect: { inputAnalysis },
|
|
26
|
+
} = core;
|
|
27
|
+
|
|
28
|
+
// Patch `qs`
|
|
29
|
+
function install() {
|
|
30
|
+
depHooks.resolve({ name: 'qs' },
|
|
31
|
+
(qs) => patcher.patch(qs, 'parse', {
|
|
32
|
+
name: 'qs',
|
|
33
|
+
patchType,
|
|
34
|
+
post({ args, result }) {
|
|
35
|
+
if (result && Object.keys(result).length) {
|
|
36
|
+
const sourceContext = protect.getSourceContext('qs');
|
|
37
|
+
|
|
38
|
+
// We need to run analysis for the `qs` result only when it's used as a query parser.
|
|
39
|
+
// `qs` is used also for parsing bodies, but these cases we handle individually with
|
|
40
|
+
// the respective library that's using it (e.g. `formidable`, `co-body`) because in
|
|
41
|
+
// some cases its use is optional and we cannot rely on it.
|
|
42
|
+
if (sourceContext && sourceContext.reqData?.queries === args[0]) {
|
|
43
|
+
sourceContext.parsedQuery = result;
|
|
44
|
+
inputAnalysis.handleQueryParams(sourceContext, result);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
})
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const qs6Instrumentation = inputAnalysis.qs6Instrumentation = {
|
|
53
|
+
install
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
return qs6Instrumentation;
|
|
57
|
+
};
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright: 2022 Contrast Security, Inc
|
|
3
|
+
* Contact: support@contrastsecurity.com
|
|
4
|
+
* License: Commercial
|
|
5
|
+
|
|
6
|
+
* NOTICE: This Software and the patented inventions embodied within may only be
|
|
7
|
+
* used as part of Contrast Security’s commercial offerings. Even though it is
|
|
8
|
+
* made available through public repositories, use of this Software is subject to
|
|
9
|
+
* the applicable End User Licensing Agreement found at
|
|
10
|
+
* https://www.contrastsecurity.com/enduser-terms-0317a or as otherwise agreed
|
|
11
|
+
* between Contrast Security and the End User. The Software may not be reverse
|
|
12
|
+
* engineered, modified, repackaged, sold, redistributed or otherwise used in a
|
|
13
|
+
* way not consistent with the End User License Agreement.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
'use strict';
|
|
17
|
+
|
|
18
|
+
const { patchType } = require('../constants');
|
|
19
|
+
|
|
20
|
+
module.exports = (core) => {
|
|
21
|
+
const {
|
|
22
|
+
depHooks,
|
|
23
|
+
patcher,
|
|
24
|
+
protect,
|
|
25
|
+
protect: { inputAnalysis },
|
|
26
|
+
} = core;
|
|
27
|
+
|
|
28
|
+
// Patch `universal-cookie` package
|
|
29
|
+
function install() {
|
|
30
|
+
depHooks.resolve({ name: 'universal-cookie', file: 'cjs/utils.js' }, (uCookieUtils) => patcher.patch(uCookieUtils, 'parseCookies', {
|
|
31
|
+
name: 'universal-cookie.utils',
|
|
32
|
+
patchType,
|
|
33
|
+
post({ result }) {
|
|
34
|
+
if (result && Object.keys(result).length) {
|
|
35
|
+
const sourceContext = protect.getSourceContext('universal-cookie');
|
|
36
|
+
|
|
37
|
+
if (sourceContext) {
|
|
38
|
+
sourceContext.parsedCookies = result;
|
|
39
|
+
inputAnalysis.handleCookies(sourceContext, result);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
})
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const universalCookie4Instrumentation = inputAnalysis.universalCookie4Instrumentation = {
|
|
48
|
+
install
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
return universalCookie4Instrumentation;
|
|
52
|
+
};
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright: 2022 Contrast Security, Inc
|
|
3
|
+
* Contact: support@contrastsecurity.com
|
|
4
|
+
* License: Commercial
|
|
5
|
+
|
|
6
|
+
* NOTICE: This Software and the patented inventions embodied within may only be
|
|
7
|
+
* used as part of Contrast Security’s commercial offerings. Even though it is
|
|
8
|
+
* made available through public repositories, use of this Software is subject to
|
|
9
|
+
* the applicable End User Licensing Agreement found at
|
|
10
|
+
* https://www.contrastsecurity.com/enduser-terms-0317a or as otherwise agreed
|
|
11
|
+
* between Contrast Security and the End User. The Software may not be reverse
|
|
12
|
+
* engineered, modified, repackaged, sold, redistributed or otherwise used in a
|
|
13
|
+
* way not consistent with the End User License Agreement.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
'use strict';
|
|
17
|
+
|
|
18
|
+
const { Event } = require('@contrast/common');
|
|
19
|
+
const address = require('ipaddr.js');
|
|
20
|
+
|
|
21
|
+
module.exports = (core) => {
|
|
22
|
+
const {
|
|
23
|
+
messages,
|
|
24
|
+
protect: { inputAnalysis },
|
|
25
|
+
} = core;
|
|
26
|
+
|
|
27
|
+
const ipAllowlist = inputAnalysis.ipAllowlist = [];
|
|
28
|
+
const ipDenylist = inputAnalysis.ipDenylist = [];
|
|
29
|
+
|
|
30
|
+
messages.on(Event.SERVER_SETTINGS_UPDATE, (serverUpdate) => {
|
|
31
|
+
const now = new Date().getTime();
|
|
32
|
+
const updatedIpAllowList = serverUpdate.features?.defend?.ipAllowlist.map((ipEntry) => ipEntryMap(ipEntry, now));
|
|
33
|
+
const updatedIpDenyList = serverUpdate.features?.defend?.ipDenylist.map((ipEntry) => ipEntryMap(ipEntry, now));
|
|
34
|
+
|
|
35
|
+
if (updatedIpAllowList) {
|
|
36
|
+
ipAllowlist.length = 0;
|
|
37
|
+
ipAllowlist.push(...updatedIpAllowList);
|
|
38
|
+
}
|
|
39
|
+
if (updatedIpDenyList) {
|
|
40
|
+
ipDenylist.length = 0;
|
|
41
|
+
ipDenylist.push(...updatedIpDenyList);
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
function ipEntryMap(ipEntry, startTime) {
|
|
47
|
+
const { ip, expires } = ipEntry;
|
|
48
|
+
let doesExpire, expiresAt, cidr;
|
|
49
|
+
|
|
50
|
+
if (expires) {
|
|
51
|
+
doesExpire = true;
|
|
52
|
+
expiresAt = startTime + expires;
|
|
53
|
+
} else {
|
|
54
|
+
doesExpire = false;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const slashIdx = ip.indexOf('/');
|
|
58
|
+
const isCIDR = slashIdx >= 0;
|
|
59
|
+
const ipInstance = isCIDR
|
|
60
|
+
? address.process(ip.substr(0, slashIdx))
|
|
61
|
+
: address.process(ip);
|
|
62
|
+
|
|
63
|
+
const normalizedValue = ipInstance.toNormalizedString();
|
|
64
|
+
|
|
65
|
+
if (isCIDR) {
|
|
66
|
+
cidr = { range: address.parseCIDR(ip), kind: ipInstance.kind() };
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
return {
|
|
70
|
+
...ipEntry,
|
|
71
|
+
doesExpire,
|
|
72
|
+
expiresAt,
|
|
73
|
+
normalizedValue,
|
|
74
|
+
cidr
|
|
75
|
+
};
|
|
76
|
+
}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright: 2022 Contrast Security, Inc
|
|
3
|
+
* Contact: support@contrastsecurity.com
|
|
4
|
+
* License: Commercial
|
|
5
|
+
|
|
6
|
+
* NOTICE: This Software and the patented inventions embodied within may only be
|
|
7
|
+
* used as part of Contrast Security’s commercial offerings. Even though it is
|
|
8
|
+
* made available through public repositories, use of this Software is subject to
|
|
9
|
+
* the applicable End User Licensing Agreement found at
|
|
10
|
+
* https://www.contrastsecurity.com/enduser-terms-0317a or as otherwise agreed
|
|
11
|
+
* between Contrast Security and the End User. The Software may not be reverse
|
|
12
|
+
* engineered, modified, repackaged, sold, redistributed or otherwise used in a
|
|
13
|
+
* way not consistent with the End User License Agreement.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
'use strict';
|
|
17
|
+
|
|
18
|
+
const { Event } = require('@contrast/common');
|
|
19
|
+
|
|
20
|
+
module.exports = (core) => {
|
|
21
|
+
const {
|
|
22
|
+
messages,
|
|
23
|
+
protect: { inputAnalysis },
|
|
24
|
+
} = core;
|
|
25
|
+
|
|
26
|
+
const virtualPatchesEvaluators = inputAnalysis.virtualPatchesEvaluators = [];
|
|
27
|
+
|
|
28
|
+
messages.on(Event.SERVER_SETTINGS_UPDATE, (serverUpdate) => {
|
|
29
|
+
const virtualPatches = serverUpdate.settings?.defend.virtualPatches;
|
|
30
|
+
if (virtualPatches) {
|
|
31
|
+
buildVPEvaluators(virtualPatches, virtualPatchesEvaluators);
|
|
32
|
+
}
|
|
33
|
+
});
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
function buildVPEvaluators(virtualPatches, evaluatorsArray) {
|
|
37
|
+
evaluatorsArray.length = 0;
|
|
38
|
+
for (const { headers, parameters, urls, uuid, name } of virtualPatches) {
|
|
39
|
+
const evaluators = new Map();
|
|
40
|
+
|
|
41
|
+
if (headers?.length) {
|
|
42
|
+
evaluators.set('HEADERS', (reqHeaders) => {
|
|
43
|
+
let result;
|
|
44
|
+
for (const { evaluation, name, value } of headers) {
|
|
45
|
+
const evalCheck = buildEvaluationCheck(evaluation);
|
|
46
|
+
const keyIndex = reqHeaders.indexOf(name.toLowerCase());
|
|
47
|
+
|
|
48
|
+
result = keyIndex !== -1 && evalCheck(reqHeaders[keyIndex + 1], value);
|
|
49
|
+
if (!result) break;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return result;
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (parameters?.length) {
|
|
57
|
+
evaluators.set('PARAMETERS', (reqParameters) => {
|
|
58
|
+
let result;
|
|
59
|
+
for (const { evaluation, name, value } of parameters) {
|
|
60
|
+
const evalCheck = buildEvaluationCheck(evaluation);
|
|
61
|
+
|
|
62
|
+
result = evalCheck(reqParameters[name], value);
|
|
63
|
+
if (!result) break;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return result;
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
if (urls?.length) {
|
|
71
|
+
evaluators.set('URLS', (reqUrl) => {
|
|
72
|
+
let result;
|
|
73
|
+
for (const { evaluation, value } of urls) {
|
|
74
|
+
const evalCheck = buildEvaluationCheck(evaluation);
|
|
75
|
+
|
|
76
|
+
result = evalCheck(reqUrl, value);
|
|
77
|
+
if (!result) break;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
return result;
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
if (evaluators.size) {
|
|
85
|
+
evaluators.set('metadata', { name, uuid });
|
|
86
|
+
evaluatorsArray.push(evaluators);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function buildEvaluationCheck(evaluation) {
|
|
92
|
+
switch (evaluation) {
|
|
93
|
+
case 'MATCHES':
|
|
94
|
+
return (reqValue, matchedValue) => new RegExp(matchedValue, 'i').test(reqValue);
|
|
95
|
+
case 'EQUALS':
|
|
96
|
+
return (reqValue, matchedValue) => reqValue.toString() === matchedValue.toString();
|
|
97
|
+
case 'CONTAINS':
|
|
98
|
+
return (reqValue, matchedValue) => reqValue.includes(matchedValue);
|
|
99
|
+
case 'DOESNT_MATCH':
|
|
100
|
+
return (reqValue, matchedValue) => !new RegExp(matchedValue, 'i').test(reqValue);
|
|
101
|
+
// This is a typo but it is how it's passed from ContrastUI
|
|
102
|
+
case 'DOESNT_EQUALS':
|
|
103
|
+
return (reqValue, matchedValue) => reqValue.toString() !== matchedValue.toString();
|
|
104
|
+
case 'DOESNT_CONTAIN':
|
|
105
|
+
return (reqValue, matchedValue) => !reqValue.includes(matchedValue);
|
|
106
|
+
default:
|
|
107
|
+
return () => false;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
@@ -1,3 +1,18 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright: 2022 Contrast Security, Inc
|
|
3
|
+
* Contact: support@contrastsecurity.com
|
|
4
|
+
* License: Commercial
|
|
5
|
+
|
|
6
|
+
* NOTICE: This Software and the patented inventions embodied within may only be
|
|
7
|
+
* used as part of Contrast Security’s commercial offerings. Even though it is
|
|
8
|
+
* made available through public repositories, use of this Software is subject to
|
|
9
|
+
* the applicable End User Licensing Agreement found at
|
|
10
|
+
* https://www.contrastsecurity.com/enduser-terms-0317a or as otherwise agreed
|
|
11
|
+
* between Contrast Security and the End User. The Software may not be reverse
|
|
12
|
+
* engineered, modified, repackaged, sold, redistributed or otherwise used in a
|
|
13
|
+
* way not consistent with the End User License Agreement.
|
|
14
|
+
*/
|
|
15
|
+
|
|
1
16
|
'use strict';
|
|
2
17
|
|
|
3
18
|
module.exports = {
|