@contrast/protect 1.66.0 → 1.68.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/hardening/handlers.js +37 -21
- package/lib/index.d.ts +3 -2
- package/lib/index.js +9 -2
- package/lib/input-analysis/handlers.js +275 -233
- package/lib/input-analysis/install/http.js +2 -3
- package/lib/input-tracing/{handlers/index.js → handlers.js} +15 -15
- package/lib/make-source-context.js +5 -7
- package/lib/policy.js +130 -95
- package/lib/semantic-analysis/handlers.js +19 -18
- package/package.json +11 -11
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
const {
|
|
19
19
|
BLOCKING_MODES,
|
|
20
20
|
isString,
|
|
21
|
+
InputType,
|
|
21
22
|
Rule: { UNTRUSTED_DESERIALIZATION }
|
|
22
23
|
} = require('@contrast/common');
|
|
23
24
|
|
|
@@ -25,6 +26,7 @@ const NODE_SERIALIZE_RCE_TOKEN = '_$$ND_FUNC$$_';
|
|
|
25
26
|
|
|
26
27
|
module.exports = function(core) {
|
|
27
28
|
const {
|
|
29
|
+
protect,
|
|
28
30
|
protect: {
|
|
29
31
|
hardening,
|
|
30
32
|
throwSecurityException,
|
|
@@ -40,32 +42,46 @@ module.exports = function(core) {
|
|
|
40
42
|
return results;
|
|
41
43
|
}
|
|
42
44
|
|
|
43
|
-
|
|
44
|
-
const
|
|
45
|
-
const mode = sourceContext.policy[ruleId];
|
|
46
|
-
const { name, value, stacktraceOpts } = sinkContext;
|
|
45
|
+
function handleFindings(sourceContext, sinkContext, ruleId, result, findings, mode) {
|
|
46
|
+
const { stacktraceOpts } = sinkContext;
|
|
47
47
|
|
|
48
|
-
|
|
48
|
+
captureStacktrace(sinkContext, stacktraceOpts);
|
|
49
|
+
getResults(sourceContext, ruleId).push(result);
|
|
49
50
|
|
|
50
|
-
|
|
51
|
-
|
|
51
|
+
let blockInfo;
|
|
52
|
+
if (BLOCKING_MODES.includes(mode)) {
|
|
53
|
+
result.blocked = true;
|
|
54
|
+
blockInfo = [mode, ruleId];
|
|
55
|
+
sourceContext.securityException = blockInfo;
|
|
56
|
+
}
|
|
52
57
|
|
|
53
|
-
|
|
54
|
-
const results = getResults(sourceContext, ruleId);
|
|
58
|
+
protect.reportFinding({ findings, result, sinkContext });
|
|
55
59
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
value: sinkContext.value,
|
|
59
|
-
blocked,
|
|
60
|
-
exploitMetadata: [{ deserializer: name, command: false }],
|
|
61
|
-
sinkContext,
|
|
62
|
-
});
|
|
60
|
+
if (blockInfo) throwSecurityException(sourceContext);
|
|
61
|
+
}
|
|
63
62
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
63
|
+
hardening.handleUntrustedDeserialization = function (sourceContext, sinkContext) {
|
|
64
|
+
const ruleId = UNTRUSTED_DESERIALIZATION;
|
|
65
|
+
const mode = sourceContext.policy.getRuleMode(ruleId);
|
|
66
|
+
const { name, value } = sinkContext;
|
|
67
|
+
|
|
68
|
+
if (
|
|
69
|
+
mode === 'off' ||
|
|
70
|
+
name !== 'node-serialize.unserialize' ||
|
|
71
|
+
!isString(value) ||
|
|
72
|
+
!value.indexOf(NODE_SERIALIZE_RCE_TOKEN)
|
|
73
|
+
) return;
|
|
74
|
+
|
|
75
|
+
const result = {
|
|
76
|
+
value: sinkContext.value,
|
|
77
|
+
ruleId: UNTRUSTED_DESERIALIZATION,
|
|
78
|
+
blocked: false,
|
|
79
|
+
exploited: true,
|
|
80
|
+
inputType: InputType.UNKNOWN,
|
|
81
|
+
};
|
|
82
|
+
const findings = { deserializer: name, command: false };
|
|
83
|
+
|
|
84
|
+
handleFindings(sourceContext, sinkContext, ruleId, result, findings, mode);
|
|
69
85
|
};
|
|
70
86
|
|
|
71
87
|
return hardening;
|
package/lib/index.d.ts
CHANGED
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
* way not consistent with the End User License Agreement.
|
|
14
14
|
*/
|
|
15
15
|
|
|
16
|
-
import { ReqData, ProtectMessage, ResultMap, ProtectRuleMode, Blocker } from '@contrast/common';
|
|
16
|
+
import { ReqData, ProtectMessage, ResultMap, ProtectRuleMode, Blocker, ProtectFindingEventArg } from '@contrast/common';
|
|
17
17
|
import { IncomingMessage, ServerResponse } from 'node:http';
|
|
18
18
|
import * as http from 'node:http';
|
|
19
19
|
import * as https from 'node:https';
|
|
@@ -60,7 +60,8 @@ export interface Protect {
|
|
|
60
60
|
makeSourceContext: (req: IncomingMessage, res: ServerResponse) => ProtectRequestStore,
|
|
61
61
|
throwSecurityException: (sourceContext: ProtectRequestStore) => void,
|
|
62
62
|
policy: ProtectPolicy,
|
|
63
|
-
getPolicy()
|
|
63
|
+
getPolicy: () => ProtectPolicy, // creates copy for request scope
|
|
64
|
+
reportFindings: (e: ProtectFindingEventArg) => void,
|
|
64
65
|
inputAnalysis: {
|
|
65
66
|
handleConnect: (sourceContext: ProtectRequestStore, connectInputs: ConnectInputs) => undefined | [string, string],
|
|
66
67
|
handleRequestEnd: (sourceContext: ProtectRequestStore) => void, //NYI
|
package/lib/index.js
CHANGED
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
'use strict';
|
|
17
17
|
|
|
18
18
|
const { isMainThread } = require('node:worker_threads');
|
|
19
|
-
const { callChildComponentMethodsSync } = require('@contrast/common');
|
|
19
|
+
const { callChildComponentMethodsSync, Event } = require('@contrast/common');
|
|
20
20
|
const { ConfigSource } = require('@contrast/config');
|
|
21
21
|
|
|
22
22
|
module.exports = function(core) {
|
|
@@ -37,7 +37,7 @@ module.exports = function(core) {
|
|
|
37
37
|
require('./hardening')(core);
|
|
38
38
|
require('./semantic-analysis')(core);
|
|
39
39
|
|
|
40
|
-
protect.install = function() {
|
|
40
|
+
protect.install = function install() {
|
|
41
41
|
// only force instrumentation if assess is explicitly enabled in local config
|
|
42
42
|
const forceInstrumentation =
|
|
43
43
|
config.preinstrument &&
|
|
@@ -60,6 +60,13 @@ module.exports = function(core) {
|
|
|
60
60
|
ctx.store.protect = protect.makeSourceContext(ctx);
|
|
61
61
|
});
|
|
62
62
|
|
|
63
|
+
protect.reportFinding = function reportFinding(data) {
|
|
64
|
+
core.messages.emit(Event.PROTECT_FINDING, {
|
|
65
|
+
store: core.scopes.sources.getStore(),
|
|
66
|
+
...data,
|
|
67
|
+
});
|
|
68
|
+
};
|
|
69
|
+
|
|
63
70
|
return protect;
|
|
64
71
|
};
|
|
65
72
|
|