@contrast/protect 1.28.1 → 1.30.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/LICENSE +1 -1
- package/lib/error-handlers/common-handler.js +1 -1
- package/lib/error-handlers/constants.js +1 -1
- package/lib/error-handlers/index.js +1 -1
- package/lib/error-handlers/init-domain.js +1 -1
- package/lib/error-handlers/install/express4.js +4 -4
- package/lib/error-handlers/install/fastify.js +1 -1
- package/lib/error-handlers/install/hapi.js +3 -3
- package/lib/error-handlers/install/koa2.js +3 -3
- package/lib/get-source-context.js +1 -1
- package/lib/hardening/constants.js +1 -1
- package/lib/hardening/handlers.js +1 -1
- package/lib/hardening/index.js +1 -1
- package/lib/hardening/install/node-serialize0.js +1 -1
- package/lib/index.d.ts +1 -1
- package/lib/index.js +61 -10
- package/lib/input-analysis/constants.js +1 -1
- package/lib/input-analysis/handlers.js +14 -14
- package/lib/input-analysis/index.js +1 -1
- package/lib/input-analysis/install/body-parser1.js +1 -1
- package/lib/input-analysis/install/busboy1.js +1 -1
- package/lib/input-analysis/install/cookie-parser1.js +5 -2
- package/lib/input-analysis/install/express4.js +6 -3
- package/lib/input-analysis/install/fastify.js +3 -3
- package/lib/input-analysis/install/formidable1.js +1 -1
- package/lib/input-analysis/install/hapi.js +12 -10
- package/lib/input-analysis/install/http.js +5 -5
- package/lib/input-analysis/install/koa-body5.js +1 -1
- package/lib/input-analysis/install/koa-bodyparser4.js +1 -1
- package/lib/input-analysis/install/koa2.js +1 -1
- package/lib/input-analysis/install/multer1.js +9 -3
- package/lib/input-analysis/install/qs6.js +1 -1
- package/lib/input-analysis/install/universal-cookie4.js +1 -1
- package/lib/input-analysis/ip-analysis.js +1 -1
- package/lib/input-analysis/virtual-patches.js +1 -1
- package/lib/input-tracing/constants.js +1 -1
- package/lib/input-tracing/handlers/index.js +6 -1
- package/lib/input-tracing/index.js +1 -1
- package/lib/input-tracing/install/child-process.js +1 -1
- package/lib/input-tracing/install/eval.js +1 -1
- package/lib/input-tracing/install/fs.js +1 -1
- package/lib/input-tracing/install/function.js +1 -1
- package/lib/input-tracing/install/http.js +1 -1
- package/lib/input-tracing/install/marsdb.js +1 -1
- package/lib/input-tracing/install/mongodb.js +1 -1
- package/lib/input-tracing/install/mssql.js +1 -1
- package/lib/input-tracing/install/mysql.js +1 -1
- package/lib/input-tracing/install/postgres.js +1 -1
- package/lib/input-tracing/install/sequelize.js +1 -1
- package/lib/input-tracing/install/sqlite3.js +1 -1
- package/lib/input-tracing/install/vm.js +36 -30
- package/lib/make-response-blocker.js +1 -1
- package/lib/make-source-context.js +1 -1
- package/lib/policy.js +44 -31
- package/lib/security-exception.js +1 -1
- package/lib/semantic-analysis/constants.js +1 -1
- package/lib/semantic-analysis/handlers.js +1 -1
- package/lib/semantic-analysis/index.js +1 -1
- package/lib/semantic-analysis/install/libxmljs.js +3 -3
- package/lib/semantic-analysis/utils/xml-analysis.js +1 -1
- package/lib/throw-security-exception.js +1 -1
- package/package.json +5 -4
- package/lib/esm-loader.mjs +0 -70
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*
|
|
2
|
-
* Copyright:
|
|
2
|
+
* Copyright: 2024 Contrast Security, Inc
|
|
3
3
|
* Contact: support@contrastsecurity.com
|
|
4
4
|
* License: Commercial
|
|
5
5
|
|
|
@@ -220,6 +220,11 @@ module.exports = function(core) {
|
|
|
220
220
|
if (stringFindings) {
|
|
221
221
|
const nosqlInjectionResult = { ...result, ruleId, mappedId: ruleId };
|
|
222
222
|
|
|
223
|
+
// don't modify ssjs-injection result items so use new exploit metadata array here
|
|
224
|
+
if (nosqlInjectionResult.idsList?.some?.((id) => id.startsWith('SSJS'))) {
|
|
225
|
+
nosqlInjectionResult.exploitMetadata = [];
|
|
226
|
+
}
|
|
227
|
+
|
|
223
228
|
const nosqlInjectionResults = sourceContext.resultsMap[ruleId];
|
|
224
229
|
const isAlreadyPresentInNosqlresults = result.idsList &&
|
|
225
230
|
result.idsList.some(
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*
|
|
2
|
-
* Copyright:
|
|
2
|
+
* Copyright: 2024 Contrast Security, Inc
|
|
3
3
|
* Contact: support@contrastsecurity.com
|
|
4
4
|
* License: Commercial
|
|
5
5
|
|
|
@@ -18,7 +18,21 @@
|
|
|
18
18
|
const { isString, isNonEmptyObject } = require('@contrast/common');
|
|
19
19
|
const { patchType } = require('../constants');
|
|
20
20
|
|
|
21
|
-
|
|
21
|
+
const VM_METHODS = [
|
|
22
|
+
['Script', 1],
|
|
23
|
+
['createContext', 1],
|
|
24
|
+
['compileFunction', 1],
|
|
25
|
+
['runInContext', 2],
|
|
26
|
+
['runInNewContext', 2],
|
|
27
|
+
['runInThisContext', 1],
|
|
28
|
+
];
|
|
29
|
+
|
|
30
|
+
const SCRIPT_METHODS = [
|
|
31
|
+
'runInContext',
|
|
32
|
+
'runInNewContext'
|
|
33
|
+
];
|
|
34
|
+
|
|
35
|
+
module.exports = function (core) {
|
|
22
36
|
const {
|
|
23
37
|
scopes: { instrumentation },
|
|
24
38
|
patcher,
|
|
@@ -27,15 +41,15 @@ module.exports = function(core) {
|
|
|
27
41
|
protect: { inputTracing }
|
|
28
42
|
} = core;
|
|
29
43
|
|
|
30
|
-
|
|
44
|
+
const createPre = (arity) => ({ args, hooked, orig, name }) => {
|
|
31
45
|
if (instrumentation.isLocked()) return;
|
|
32
46
|
|
|
33
47
|
const sourceContext = protect.getSourceContext(name);
|
|
34
48
|
if (!sourceContext) return;
|
|
35
49
|
|
|
36
|
-
for (let i = 0; i <
|
|
50
|
+
for (let i = 0; i < arity; i++) {
|
|
37
51
|
const arg = args[i];
|
|
38
|
-
if (!
|
|
52
|
+
if (!arg || !(isString(arg) || isNonEmptyObject(arg))) continue;
|
|
39
53
|
|
|
40
54
|
const sinkContext = {
|
|
41
55
|
name,
|
|
@@ -44,37 +58,29 @@ module.exports = function(core) {
|
|
|
44
58
|
};
|
|
45
59
|
inputTracing.ssjsInjection(sourceContext, sinkContext);
|
|
46
60
|
}
|
|
47
|
-
}
|
|
61
|
+
};
|
|
48
62
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
'createScript',
|
|
54
|
-
'runInContext',
|
|
55
|
-
'runInThisContext',
|
|
56
|
-
'createContext',
|
|
57
|
-
'runInNewContext'
|
|
58
|
-
].forEach(
|
|
59
|
-
(method) => {
|
|
60
|
-
const name = `vm.${method}`;
|
|
63
|
+
const vmInstrumentation = inputTracing.vmInstrumentation = {
|
|
64
|
+
install() {
|
|
65
|
+
depHooks.resolve({ name: 'vm' }, (vm) => {
|
|
66
|
+
VM_METHODS.forEach(([method, arity]) => {
|
|
61
67
|
patcher.patch(vm, method, {
|
|
62
|
-
name
|
|
68
|
+
name: `vm.${method}`,
|
|
63
69
|
patchType,
|
|
64
|
-
pre
|
|
70
|
+
pre: createPre(arity)
|
|
65
71
|
});
|
|
66
|
-
}
|
|
67
|
-
);
|
|
72
|
+
});
|
|
68
73
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
74
|
+
SCRIPT_METHODS.forEach((method) => {
|
|
75
|
+
patcher.patch(vm.Script.prototype, method, {
|
|
76
|
+
name: `vm.Script.prototype.${method}`,
|
|
77
|
+
patchType,
|
|
78
|
+
pre: createPre(1)
|
|
79
|
+
});
|
|
80
|
+
});
|
|
73
81
|
});
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
const vmInstrumentation = inputTracing.vmInstrumentation = { install };
|
|
82
|
+
}
|
|
83
|
+
};
|
|
78
84
|
|
|
79
85
|
return vmInstrumentation;
|
|
80
86
|
};
|
package/lib/policy.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*
|
|
2
|
-
* Copyright:
|
|
2
|
+
* Copyright: 2024 Contrast Security, Inc
|
|
3
3
|
* Contact: support@contrastsecurity.com
|
|
4
4
|
* License: Commercial
|
|
5
5
|
|
|
@@ -17,33 +17,34 @@
|
|
|
17
17
|
|
|
18
18
|
const {
|
|
19
19
|
Rule,
|
|
20
|
-
ProtectRuleMode
|
|
21
|
-
|
|
22
|
-
OFF,
|
|
23
|
-
},
|
|
24
|
-
Rule: {
|
|
25
|
-
BOT_BLOCKER,
|
|
26
|
-
CMD_INJECTION,
|
|
27
|
-
CMD_INJECTION_COMMAND_BACKDOORS,
|
|
28
|
-
CMD_INJECTION_SEMANTIC_CHAINED_COMMANDS,
|
|
29
|
-
CMD_INJECTION_SEMANTIC_DANGEROUS_PATHS,
|
|
30
|
-
METHOD_TAMPERING,
|
|
31
|
-
NOSQL_INJECTION,
|
|
32
|
-
NOSQL_INJECTION_MONGO,
|
|
33
|
-
PATH_TRAVERSAL,
|
|
34
|
-
PATH_TRAVERSAL_SEMANTIC_FILE_SECURITY_BYPASS,
|
|
35
|
-
REFLECTED_XSS,
|
|
36
|
-
SQL_INJECTION,
|
|
37
|
-
SSJS_INJECTION,
|
|
38
|
-
UNSAFE_FILE_UPLOAD,
|
|
39
|
-
UNTRUSTED_DESERIALIZATION,
|
|
40
|
-
XXE,
|
|
41
|
-
},
|
|
42
|
-
Event: { SERVER_SETTINGS_UPDATE },
|
|
20
|
+
ProtectRuleMode,
|
|
21
|
+
Event,
|
|
43
22
|
toLowerCase,
|
|
44
23
|
split,
|
|
45
24
|
join
|
|
46
25
|
} = require('@contrast/common');
|
|
26
|
+
const { ConfigSource } = require('@contrast/config');
|
|
27
|
+
|
|
28
|
+
const { BLOCK_AT_PERIMETER, OFF } = ProtectRuleMode;
|
|
29
|
+
const {
|
|
30
|
+
BOT_BLOCKER,
|
|
31
|
+
CMD_INJECTION,
|
|
32
|
+
CMD_INJECTION_COMMAND_BACKDOORS,
|
|
33
|
+
CMD_INJECTION_SEMANTIC_CHAINED_COMMANDS,
|
|
34
|
+
CMD_INJECTION_SEMANTIC_DANGEROUS_PATHS,
|
|
35
|
+
METHOD_TAMPERING,
|
|
36
|
+
NOSQL_INJECTION,
|
|
37
|
+
NOSQL_INJECTION_MONGO,
|
|
38
|
+
PATH_TRAVERSAL,
|
|
39
|
+
PATH_TRAVERSAL_SEMANTIC_FILE_SECURITY_BYPASS,
|
|
40
|
+
REFLECTED_XSS,
|
|
41
|
+
SQL_INJECTION,
|
|
42
|
+
SSJS_INJECTION,
|
|
43
|
+
UNSAFE_FILE_UPLOAD,
|
|
44
|
+
UNTRUSTED_DESERIALIZATION,
|
|
45
|
+
XXE,
|
|
46
|
+
} = Rule;
|
|
47
|
+
const { DEFAULT_VALUE } = ConfigSource;
|
|
47
48
|
|
|
48
49
|
module.exports = function (core) {
|
|
49
50
|
const {
|
|
@@ -116,8 +117,14 @@ module.exports = function (core) {
|
|
|
116
117
|
if (config.protect.rules.disabled_rules.includes(ruleId)) {
|
|
117
118
|
return OFF;
|
|
118
119
|
}
|
|
119
|
-
|
|
120
|
-
|
|
120
|
+
// If nosql-injection is set in config but nosql-injection-mongo isn't,
|
|
121
|
+
// then have nosql-injection-mongo inherit config value from umbrella rule.
|
|
122
|
+
if (
|
|
123
|
+
ruleId === 'nosql-injection-mongo' &&
|
|
124
|
+
config.getEffectiveSource(`protect.rules.${ruleId}.mode`) === DEFAULT_VALUE &&
|
|
125
|
+
config.getEffectiveSource(`protect.rules.${NOSQL_INJECTION}.mode`) !== DEFAULT_VALUE
|
|
126
|
+
) {
|
|
127
|
+
return config.protect.rules?.[NOSQL_INJECTION]?.mode;
|
|
121
128
|
}
|
|
122
129
|
return config.protect.rules?.[ruleId]?.mode;
|
|
123
130
|
}
|
|
@@ -235,8 +242,6 @@ module.exports = function (core) {
|
|
|
235
242
|
}
|
|
236
243
|
|
|
237
244
|
function updateGlobalPolicy(remoteSettings) {
|
|
238
|
-
let update;
|
|
239
|
-
|
|
240
245
|
const protectionRules = remoteSettings?.protect?.rules;
|
|
241
246
|
if (protectionRules) {
|
|
242
247
|
[
|
|
@@ -246,7 +251,6 @@ module.exports = function (core) {
|
|
|
246
251
|
CMD_INJECTION_SEMANTIC_DANGEROUS_PATHS,
|
|
247
252
|
METHOD_TAMPERING,
|
|
248
253
|
NOSQL_INJECTION,
|
|
249
|
-
NOSQL_INJECTION_MONGO,
|
|
250
254
|
PATH_TRAVERSAL,
|
|
251
255
|
PATH_TRAVERSAL_SEMANTIC_FILE_SECURITY_BYPASS,
|
|
252
256
|
REFLECTED_XSS,
|
|
@@ -260,6 +264,15 @@ module.exports = function (core) {
|
|
|
260
264
|
policy[ruleId] = OFF;
|
|
261
265
|
} else {
|
|
262
266
|
policy[ruleId] = config.getEffectiveValue(`protect.rules.${ruleId}.mode`);
|
|
267
|
+
|
|
268
|
+
// nosql-injection-mongo should inherit nosql-injection mode if unset in config
|
|
269
|
+
if (ruleId === NOSQL_INJECTION) {
|
|
270
|
+
if (config.getEffectiveSource(`protect.rules.${NOSQL_INJECTION_MONGO}.mode`) === DEFAULT_VALUE) {
|
|
271
|
+
policy[NOSQL_INJECTION_MONGO] = policy[ruleId];
|
|
272
|
+
} else {
|
|
273
|
+
policy[NOSQL_INJECTION_MONGO] = config.getEffectiveValue(`protect.rules.${NOSQL_INJECTION_MONGO}.mode`);
|
|
274
|
+
}
|
|
275
|
+
}
|
|
263
276
|
}
|
|
264
277
|
});
|
|
265
278
|
|
|
@@ -275,7 +288,7 @@ module.exports = function (core) {
|
|
|
275
288
|
|
|
276
289
|
updateRulesMask();
|
|
277
290
|
protect.policy.exclusions = compiled;
|
|
278
|
-
logger.info({ policy: protect.policy },
|
|
291
|
+
logger.info({ policy: protect.policy }, 'Protect policy updated');
|
|
279
292
|
}
|
|
280
293
|
}
|
|
281
294
|
|
|
@@ -345,7 +358,7 @@ module.exports = function (core) {
|
|
|
345
358
|
}
|
|
346
359
|
}
|
|
347
360
|
|
|
348
|
-
messages.on(SERVER_SETTINGS_UPDATE, (msg) => {
|
|
361
|
+
messages.on(Event.SERVER_SETTINGS_UPDATE, (msg) => {
|
|
349
362
|
if (!config.getEffectiveValue('protect.enable')) return;
|
|
350
363
|
|
|
351
364
|
updateExclusions(msg);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*
|
|
2
|
-
* Copyright:
|
|
2
|
+
* Copyright: 2024 Contrast Security, Inc
|
|
3
3
|
* Contact: support@contrastsecurity.com
|
|
4
4
|
* License: Commercial
|
|
5
5
|
|
|
@@ -44,7 +44,7 @@ module.exports = function (core) {
|
|
|
44
44
|
patcher.patch(mod, method, {
|
|
45
45
|
name,
|
|
46
46
|
patchType,
|
|
47
|
-
pre({ args, hooked, orig }) {
|
|
47
|
+
pre({ args, hooked, orig, funcKey }) {
|
|
48
48
|
const sourceContext = protect.getSourceContext(name);
|
|
49
49
|
const [value, options] = args;
|
|
50
50
|
|
|
@@ -68,7 +68,7 @@ module.exports = function (core) {
|
|
|
68
68
|
throw err;
|
|
69
69
|
}
|
|
70
70
|
|
|
71
|
-
logger.error({ err }, 'Unexpected error during semantic analysis');
|
|
71
|
+
logger.error({ err, funcKey }, 'Unexpected error during semantic analysis');
|
|
72
72
|
}
|
|
73
73
|
}
|
|
74
74
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@contrast/protect",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.30.0",
|
|
4
4
|
"description": "Contrast service providing framework-agnostic Protect support",
|
|
5
5
|
"license": "SEE LICENSE IN LICENSE",
|
|
6
6
|
"author": "Contrast Security <nodejs@contrastsecurity.com> (https://www.contrastsecurity.com)",
|
|
@@ -18,9 +18,10 @@
|
|
|
18
18
|
},
|
|
19
19
|
"dependencies": {
|
|
20
20
|
"@contrast/agent-lib": "^7.0.1",
|
|
21
|
-
"@contrast/common": "1.
|
|
22
|
-
"@contrast/
|
|
23
|
-
"@contrast/
|
|
21
|
+
"@contrast/common": "1.16.0",
|
|
22
|
+
"@contrast/config": "1.23.0",
|
|
23
|
+
"@contrast/core": "1.27.1",
|
|
24
|
+
"@contrast/esm-hooks": "2.0.1",
|
|
24
25
|
"@contrast/scopes": "1.4.0",
|
|
25
26
|
"ipaddr.js": "^2.0.1",
|
|
26
27
|
"semver": "^7.3.7"
|
package/lib/esm-loader.mjs
DELETED
|
@@ -1,70 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
* Copyright: 2023 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
|
-
import esmHooks from '@contrast/esm-hooks';
|
|
17
|
-
import { createRequire } from 'node:module';
|
|
18
|
-
|
|
19
|
-
const ERROR_MESSAGE = 'A fatal agent installation error has occurred. The application will be run without instrumentation.';
|
|
20
|
-
|
|
21
|
-
let getSource = async function getSource(url, context, defaultGetSource) {
|
|
22
|
-
return defaultGetSource(url, context, defaultGetSource);
|
|
23
|
-
};
|
|
24
|
-
|
|
25
|
-
let transformSource = async function transformSource(url, context, defaultTransformSource) {
|
|
26
|
-
return defaultTransformSource(url, context, defaultTransformSource);
|
|
27
|
-
};
|
|
28
|
-
|
|
29
|
-
let load = async function load(url, context, defaultLoad) {
|
|
30
|
-
return defaultLoad(url, context, defaultLoad);
|
|
31
|
-
};
|
|
32
|
-
|
|
33
|
-
const require = createRequire(import.meta.url);
|
|
34
|
-
const { name: agentName, version: agentVersion } = require('../package.json');
|
|
35
|
-
const core = { agentName, agentVersion };
|
|
36
|
-
try {
|
|
37
|
-
require('@contrast/core/lib/events')(core);
|
|
38
|
-
require('@contrast/config')(core);
|
|
39
|
-
require('@contrast/logger').default(core);
|
|
40
|
-
|
|
41
|
-
// @contrast/info ?
|
|
42
|
-
require('@contrast/core/lib/agent-info')(core);
|
|
43
|
-
require('@contrast/core/lib/system-info')(core);
|
|
44
|
-
require('@contrast/core/lib/app-info')(core);
|
|
45
|
-
require('@contrast/core/lib/sensitive-data-masking')(core);
|
|
46
|
-
require('@contrast/core/lib/is-agent-path')(core);
|
|
47
|
-
require('@contrast/core/lib/capture-stacktrace')(core);
|
|
48
|
-
|
|
49
|
-
require('@contrast/patcher')(core);
|
|
50
|
-
require('@contrast/rewriter')(core); // merge contrast-methods?
|
|
51
|
-
require('@contrast/core/lib/contrast-methods')(core); // can we remove dependency on patcher?
|
|
52
|
-
|
|
53
|
-
require('@contrast/dep-hooks')(core);
|
|
54
|
-
require('@contrast/scopes')(core);
|
|
55
|
-
require('@contrast/deadzones')(core);
|
|
56
|
-
require('@contrast/reporter').default(core);
|
|
57
|
-
require('@contrast/instrumentation')(core);
|
|
58
|
-
|
|
59
|
-
// TODO: i think we still need to INSTALL components.
|
|
60
|
-
({ getSource, transformSource, load } = esmHooks(core));
|
|
61
|
-
} catch (err) {
|
|
62
|
-
// TODO: Consider proper UNINSTALLATION and normal startup w/o agent
|
|
63
|
-
if (core.logger) {
|
|
64
|
-
core.logger.error({ err }, ERROR_MESSAGE);
|
|
65
|
-
} else {
|
|
66
|
-
console.error(new Error(ERROR_MESSAGE, { cause: err }));
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
export { getSource, transformSource, load };
|