@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
|
@@ -16,13 +16,12 @@
|
|
|
16
16
|
'use strict';
|
|
17
17
|
|
|
18
18
|
const onFinished = require('on-finished');
|
|
19
|
-
const {
|
|
19
|
+
const { primordials: { StringPrototypeToLowerCase, ArrayPrototypeSlice } } = require('@contrast/common');
|
|
20
20
|
const { patchType } = require('../constants');
|
|
21
21
|
|
|
22
22
|
module.exports = function (core) {
|
|
23
23
|
const {
|
|
24
24
|
logger,
|
|
25
|
-
messages,
|
|
26
25
|
scopes: { sources },
|
|
27
26
|
instrumentation: { instrument },
|
|
28
27
|
protect: {
|
|
@@ -75,8 +74,8 @@ module.exports = function (core) {
|
|
|
75
74
|
|
|
76
75
|
onFinished(res, (/* err, req */) => {
|
|
77
76
|
resData.statusCode = res.statusCode;
|
|
77
|
+
// check for probes and method-tampering outcome
|
|
78
78
|
inputAnalysis.handleRequestEnd(store.protect);
|
|
79
|
-
messages.emit(Event.PROTECT, store);
|
|
80
79
|
});
|
|
81
80
|
|
|
82
81
|
const connectInputs = {
|
|
@@ -29,6 +29,7 @@ const {
|
|
|
29
29
|
|
|
30
30
|
module.exports = function(core) {
|
|
31
31
|
const {
|
|
32
|
+
protect,
|
|
32
33
|
protect: {
|
|
33
34
|
agentLib,
|
|
34
35
|
inputTracing,
|
|
@@ -40,16 +41,21 @@ module.exports = function(core) {
|
|
|
40
41
|
function handleFindings(sourceContext, sinkContext, ruleId, result, findings) {
|
|
41
42
|
const { stacktraceOpts } = sinkContext;
|
|
42
43
|
captureStacktrace(sinkContext, stacktraceOpts);
|
|
43
|
-
result.
|
|
44
|
+
result.exploited = true;
|
|
44
45
|
|
|
45
|
-
const mode = sourceContext.policy
|
|
46
|
+
const mode = sourceContext.policy.getRuleMode(ruleId);
|
|
47
|
+
const eventArg = { findings, result, sinkContext };
|
|
46
48
|
|
|
49
|
+
let blockInfo;
|
|
47
50
|
if (BLOCKING_MODES.includes(mode)) {
|
|
48
51
|
result.blocked = true;
|
|
49
|
-
|
|
52
|
+
blockInfo = [mode, ruleId, eventArg];
|
|
50
53
|
sourceContext.securityException = blockInfo;
|
|
51
|
-
throwSecurityException(sourceContext);
|
|
52
54
|
}
|
|
55
|
+
|
|
56
|
+
protect.reportFinding(eventArg);
|
|
57
|
+
|
|
58
|
+
if (blockInfo) throwSecurityException(sourceContext);
|
|
53
59
|
}
|
|
54
60
|
|
|
55
61
|
inputTracing.handlePathTraversal = function(sourceContext, sinkContext) {
|
|
@@ -61,7 +67,6 @@ module.exports = function(core) {
|
|
|
61
67
|
for (const result of results) {
|
|
62
68
|
const idx = sinkContext.value.indexOf(result.value);
|
|
63
69
|
const findings = idx !== -1 ? { path: sinkContext.value } : null;
|
|
64
|
-
|
|
65
70
|
if (findings) {
|
|
66
71
|
handleFindings(sourceContext, sinkContext, ruleId, result, findings);
|
|
67
72
|
}
|
|
@@ -218,13 +223,7 @@ module.exports = function(core) {
|
|
|
218
223
|
}
|
|
219
224
|
|
|
220
225
|
if (stringFindings) {
|
|
221
|
-
const nosqlInjectionResult = { ...result, ruleId, mappedId: ruleId };
|
|
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
|
-
|
|
226
|
+
const nosqlInjectionResult = { ...result, ruleId, mappedId: ruleId, exploited: false };
|
|
228
227
|
const nosqlInjectionResults = sourceContext.resultsMap[ruleId];
|
|
229
228
|
const isAlreadyPresentInNosqlresults = result.idsList &&
|
|
230
229
|
result.idsList.some(
|
|
@@ -312,12 +311,13 @@ module.exports = function(core) {
|
|
|
312
311
|
const findings = idx !== -1 ? { value: sinkContext.value } : null;
|
|
313
312
|
|
|
314
313
|
if (findings) {
|
|
315
|
-
result.
|
|
314
|
+
result.exploited = true;
|
|
315
|
+
handleFindings(sourceContext, sinkContext, ruleId, result, findings);
|
|
316
|
+
break;
|
|
316
317
|
}
|
|
317
318
|
}
|
|
318
319
|
};
|
|
319
320
|
|
|
320
|
-
|
|
321
321
|
return inputTracing;
|
|
322
322
|
};
|
|
323
323
|
|
|
@@ -328,7 +328,7 @@ module.exports = function(core) {
|
|
|
328
328
|
* @returns {AnalysisResult[]}
|
|
329
329
|
*/
|
|
330
330
|
function getResultsByRuleId(ruleId, context) {
|
|
331
|
-
if (!context.policy || context.policy
|
|
331
|
+
if (!context.policy || context.policy.getRuleMode(ruleId) === OFF) {
|
|
332
332
|
return;
|
|
333
333
|
}
|
|
334
334
|
// because agent-lib stores all nosql-injection results under nosql-injection-mongo
|
|
@@ -18,8 +18,6 @@
|
|
|
18
18
|
module.exports = function(core) {
|
|
19
19
|
const { protect } = core;
|
|
20
20
|
|
|
21
|
-
const DISABLED_POLICY = { allowed: true };
|
|
22
|
-
|
|
23
21
|
/**
|
|
24
22
|
* @param {object} param
|
|
25
23
|
* @param {object} param.store
|
|
@@ -33,12 +31,7 @@ module.exports = function(core) {
|
|
|
33
31
|
// incomingMessage,
|
|
34
32
|
serverResponse,
|
|
35
33
|
}) {
|
|
36
|
-
if (!core.config.getEffectiveValue('protect.enable')) return DISABLED_POLICY;
|
|
37
|
-
|
|
38
34
|
const policy = protect.getPolicy({ uriPath: sourceInfo.uriPath });
|
|
39
|
-
// URL exclusions can disable all rules
|
|
40
|
-
if (!policy || policy.rulesMask === 0) return DISABLED_POLICY;
|
|
41
|
-
|
|
42
35
|
const protectStore = {
|
|
43
36
|
resData: {
|
|
44
37
|
statusCode: null,
|
|
@@ -56,6 +49,11 @@ module.exports = function(core) {
|
|
|
56
49
|
resultsMap: Object.create(null),
|
|
57
50
|
};
|
|
58
51
|
|
|
52
|
+
if (policy.allowed) {
|
|
53
|
+
protectStore.allowed = true;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
|
|
59
57
|
return protectStore;
|
|
60
58
|
}
|
|
61
59
|
|
package/lib/policy.js
CHANGED
|
@@ -24,10 +24,10 @@ const {
|
|
|
24
24
|
StringPrototypeToLowerCase,
|
|
25
25
|
StringPrototypeSplit,
|
|
26
26
|
RegExpPrototypeTest
|
|
27
|
-
}
|
|
27
|
+
},
|
|
28
|
+
set,
|
|
28
29
|
} = require('@contrast/common');
|
|
29
30
|
const { ConfigSource } = require('@contrast/config');
|
|
30
|
-
|
|
31
31
|
const { BLOCK_AT_PERIMETER, OFF } = ProtectRuleMode;
|
|
32
32
|
const {
|
|
33
33
|
BOT_BLOCKER,
|
|
@@ -58,6 +58,121 @@ module.exports = function (core) {
|
|
|
58
58
|
protect: { agentLib }
|
|
59
59
|
} = core;
|
|
60
60
|
|
|
61
|
+
// todo: can we not init this and just set what's needed
|
|
62
|
+
let processedExclusions = initCompiled();
|
|
63
|
+
|
|
64
|
+
const policy = protect.policy = {
|
|
65
|
+
version: Date.now(),
|
|
66
|
+
exclusions: processedExclusions
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
class RequestPolicy {
|
|
71
|
+
constructor(core, sourceInfo) {
|
|
72
|
+
Object.defineProperty(this, 'core', { value: core });
|
|
73
|
+
Object.defineProperty(this, 'sourceInfo', { value: sourceInfo });
|
|
74
|
+
|
|
75
|
+
this.init();
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
init() {
|
|
79
|
+
const { uriPath } = this.sourceInfo;
|
|
80
|
+
this.version = core.protect.policy.version;
|
|
81
|
+
|
|
82
|
+
if (!this.core.config.getEffectiveValue('protect.enable')) {
|
|
83
|
+
this.allowed = true;
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// todo build exclusions
|
|
88
|
+
for (const [inputType, exclusions] of Object.entries(processedExclusions)) {
|
|
89
|
+
for (const e of exclusions) {
|
|
90
|
+
if (!e.matchesUriPath(uriPath)) continue;
|
|
91
|
+
|
|
92
|
+
// url exclusions
|
|
93
|
+
if (inputType === 'url') {
|
|
94
|
+
// if applies to all rules, there is no policy for the request i.e. disable protect
|
|
95
|
+
if (!e.policy) {
|
|
96
|
+
this.allowed = true;
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// merge exclusion's policy into the request's policy
|
|
101
|
+
for (const key of Object.keys(e.policy)) {
|
|
102
|
+
const value = e.policy[key];
|
|
103
|
+
if (key === 'rulesMask') {
|
|
104
|
+
if (this.exclusions?.rulesMask == null)
|
|
105
|
+
set(this, 'exclusions.rulesMask', this.core.protect.policy.rulesMask);
|
|
106
|
+
// this is how to disable rules bitwise
|
|
107
|
+
this.exclusions.rulesMask = this.exclusions.rulesMask & ~value;
|
|
108
|
+
} else {
|
|
109
|
+
set(this, `exclusions.${key}`, value);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
} else if (inputType === 'querystring') {
|
|
113
|
+
if (!e.policy) {
|
|
114
|
+
set(this, 'exclusions.ignoreQuerystring', true);
|
|
115
|
+
} else {
|
|
116
|
+
// merge exclusion's policy into the querystring's policy
|
|
117
|
+
// this.exclusions.querystringPolicy = this.exclusions.querystringPolicy || {};
|
|
118
|
+
for (const key of Object.keys(e.policy)) {
|
|
119
|
+
const value = e.policy[key];
|
|
120
|
+
if (key !== 'rulesMask') {
|
|
121
|
+
set(this, `exclusions.querystringPolicy.${key}`, value);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
} else if (inputType === 'body') {
|
|
126
|
+
if (!e.policy) {
|
|
127
|
+
set(this, 'exclusions.ignoreBody', true);
|
|
128
|
+
} else {
|
|
129
|
+
// merge exclusion's policy into the querystring's policy
|
|
130
|
+
// set(this, `exclusions.bodyPolicy = this.exclusions.bodyPolicy || {};
|
|
131
|
+
for (const key of Object.keys(e.policy)) {
|
|
132
|
+
const value = e.policy[key];
|
|
133
|
+
if (key !== 'rulesMask') {
|
|
134
|
+
set(this, `exclusions.bodyPolicy.${key}`, value);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
} else {
|
|
139
|
+
// copy matching input exclusions into request policy
|
|
140
|
+
if (!this.exclusions?.[inputType]) set(this, `exclusions.${inputType}`, []);
|
|
141
|
+
this.exclusions[inputType].push(e);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
checkInit() {
|
|
148
|
+
if (!this.version == core.protect.policy.version) {
|
|
149
|
+
this.init();
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
isDisabled() {
|
|
154
|
+
this.checkInit();
|
|
155
|
+
return this.allowed === true;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
getRulesMask(inputType) {
|
|
159
|
+
this.checkInit();
|
|
160
|
+
if (this.allowed) return 0;
|
|
161
|
+
return this.exclusions?.rulesMask ?? this.core.protect.policy.rulesMask;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
getRuleMode(ruleId) {
|
|
165
|
+
this.checkInit();
|
|
166
|
+
if (this.allowed) return OFF;
|
|
167
|
+
return this.exclusions?.[ruleId] ?? this.core.protect.policy[ruleId];
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
getExclusionInfo(key, inputType) {
|
|
171
|
+
this.checkInit();
|
|
172
|
+
return key ? this.exclusions?.[key] : this.exclusions;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
61
176
|
function initCompiled() {
|
|
62
177
|
return {
|
|
63
178
|
url: [],
|
|
@@ -69,12 +184,6 @@ module.exports = function (core) {
|
|
|
69
184
|
};
|
|
70
185
|
}
|
|
71
186
|
|
|
72
|
-
let compiled = initCompiled();
|
|
73
|
-
|
|
74
|
-
const policy = protect.policy = {
|
|
75
|
-
exclusions: compiled
|
|
76
|
-
};
|
|
77
|
-
|
|
78
187
|
function regExpCheck(str) {
|
|
79
188
|
return str.indexOf('*') > 0 ||
|
|
80
189
|
str.indexOf('.') > 0 ||
|
|
@@ -156,96 +265,19 @@ module.exports = function (core) {
|
|
|
156
265
|
ruleId = 'nosql-injection-mongo';
|
|
157
266
|
}
|
|
158
267
|
|
|
159
|
-
if (
|
|
160
|
-
rulesMask = rulesMask |
|
|
268
|
+
if (agentLib.RuleType[ruleId] && mode !== OFF) {
|
|
269
|
+
rulesMask = rulesMask | agentLib.RuleType[ruleId];
|
|
161
270
|
}
|
|
162
271
|
}
|
|
163
272
|
|
|
164
273
|
policy.rulesMask = rulesMask;
|
|
165
274
|
}
|
|
166
275
|
|
|
167
|
-
/**
|
|
168
|
-
* This gets called by protect.makeSourceContext(). We return copy of policy to avoid
|
|
169
|
-
* inconsistent behavior if policy is updated during request handling.
|
|
170
|
-
*/
|
|
171
|
-
function getPolicy({ uriPath } = {}) {
|
|
172
|
-
const requestPolicy = {
|
|
173
|
-
exclusions: {
|
|
174
|
-
ignoreQuerystring: false,
|
|
175
|
-
querystringPolicy: null,
|
|
176
|
-
ignoreBody: false,
|
|
177
|
-
bodyPolicy: null,
|
|
178
|
-
header: [],
|
|
179
|
-
cookie: [],
|
|
180
|
-
parameter: [],
|
|
181
|
-
},
|
|
182
|
-
rulesMask: policy.rulesMask,
|
|
183
|
-
};
|
|
184
|
-
|
|
185
|
-
for (const ruleId of Object.values(Rule)) {
|
|
186
|
-
requestPolicy[ruleId] = policy[ruleId];
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
// handle exclusions
|
|
190
|
-
for (const [inputType, exclusions] of Object.entries(compiled)) {
|
|
191
|
-
for (const e of exclusions) {
|
|
192
|
-
if (!e.matchesUriPath(uriPath)) continue;
|
|
193
|
-
|
|
194
|
-
// url exclusions
|
|
195
|
-
if (inputType === 'url') {
|
|
196
|
-
// if applies to all rules, there is no policy for the request i.e. disable protect
|
|
197
|
-
if (!e.policy) {
|
|
198
|
-
return null;
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
// merge exclusion's policy into the request's policy
|
|
202
|
-
for (const key of Object.keys(e.policy)) {
|
|
203
|
-
const value = e.policy[key];
|
|
204
|
-
if (key === 'rulesMask') {
|
|
205
|
-
// this is how to disable rules bitwise
|
|
206
|
-
requestPolicy.rulesMask = requestPolicy.rulesMask & ~value;
|
|
207
|
-
} else {
|
|
208
|
-
requestPolicy[key] = value;
|
|
209
|
-
}
|
|
210
|
-
}
|
|
211
|
-
} else if (inputType === 'querystring') {
|
|
212
|
-
if (!e.policy) {
|
|
213
|
-
requestPolicy.exclusions.ignoreQuerystring = true;
|
|
214
|
-
} else {
|
|
215
|
-
// merge exclusion's policy into the querystring's policy
|
|
216
|
-
requestPolicy.exclusions.querystringPolicy = requestPolicy.exclusions.querystringPolicy || {};
|
|
217
|
-
for (const key of Object.keys(e.policy)) {
|
|
218
|
-
const value = e.policy[key];
|
|
219
|
-
if (key !== 'rulesMask') {
|
|
220
|
-
requestPolicy.exclusions.querystringPolicy[key] = value;
|
|
221
|
-
}
|
|
222
|
-
}
|
|
223
|
-
}
|
|
224
|
-
} else if (inputType === 'body') {
|
|
225
|
-
if (!e.policy) {
|
|
226
|
-
requestPolicy.exclusions.ignoreBody = true;
|
|
227
|
-
} else {
|
|
228
|
-
// merge exclusion's policy into the querystring's policy
|
|
229
|
-
requestPolicy.exclusions.bodyPolicy = requestPolicy.exclusions.bodyPolicy || {};
|
|
230
|
-
for (const key of Object.keys(e.policy)) {
|
|
231
|
-
const value = e.policy[key];
|
|
232
|
-
if (key !== 'rulesMask') {
|
|
233
|
-
requestPolicy.exclusions.bodyPolicy[key] = value;
|
|
234
|
-
}
|
|
235
|
-
}
|
|
236
|
-
}
|
|
237
|
-
} else {
|
|
238
|
-
// copy matching input exclusions into request policy
|
|
239
|
-
requestPolicy.exclusions[inputType].push(e);
|
|
240
|
-
}
|
|
241
|
-
}
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
return requestPolicy;
|
|
245
|
-
}
|
|
246
|
-
|
|
247
276
|
function updateGlobalPolicy(remoteSettings) {
|
|
248
277
|
const protectionRules = remoteSettings?.protect?.rules;
|
|
278
|
+
// last updated
|
|
279
|
+
protect.policy.version = Date.now();
|
|
280
|
+
|
|
249
281
|
if (protectionRules) {
|
|
250
282
|
[
|
|
251
283
|
CMD_INJECTION,
|
|
@@ -290,7 +322,8 @@ module.exports = function (core) {
|
|
|
290
322
|
}
|
|
291
323
|
|
|
292
324
|
updateRulesMask();
|
|
293
|
-
protect.policy.exclusions =
|
|
325
|
+
protect.policy.exclusions = processedExclusions;
|
|
326
|
+
|
|
294
327
|
logger.info({ policy: protect.policy }, 'Protect policy updated');
|
|
295
328
|
}
|
|
296
329
|
}
|
|
@@ -302,7 +335,7 @@ module.exports = function (core) {
|
|
|
302
335
|
].filter((exclusion) => exclusion.modes.includes('defend'));
|
|
303
336
|
|
|
304
337
|
if (!exclusions.length) return;
|
|
305
|
-
|
|
338
|
+
processedExclusions = initCompiled();
|
|
306
339
|
|
|
307
340
|
for (const exclusionDtm of exclusions) {
|
|
308
341
|
exclusionDtm.type = exclusionDtm.type || 'URL';
|
|
@@ -310,7 +343,7 @@ module.exports = function (core) {
|
|
|
310
343
|
const { name, protect_rules, urls, type } = exclusionDtm;
|
|
311
344
|
const key = StringPrototypeToLowerCase.call(type);
|
|
312
345
|
|
|
313
|
-
if (!
|
|
346
|
+
if (!processedExclusions[key]) continue;
|
|
314
347
|
|
|
315
348
|
try {
|
|
316
349
|
const e = { name };
|
|
@@ -354,7 +387,7 @@ module.exports = function (core) {
|
|
|
354
387
|
};
|
|
355
388
|
}
|
|
356
389
|
|
|
357
|
-
|
|
390
|
+
processedExclusions[key].push(e);
|
|
358
391
|
} catch (err) {
|
|
359
392
|
logger.error({ err, exclusionDtm }, 'failed to process exclusion');
|
|
360
393
|
}
|
|
@@ -370,5 +403,7 @@ module.exports = function (core) {
|
|
|
370
403
|
|
|
371
404
|
initPolicy();
|
|
372
405
|
|
|
373
|
-
return protect.getPolicy = getPolicy
|
|
406
|
+
return protect.getPolicy = function getPolicy(sourceInfo) {
|
|
407
|
+
return new RequestPolicy(core, sourceInfo);
|
|
408
|
+
};
|
|
374
409
|
};
|
|
@@ -44,6 +44,7 @@ const getRuleResults = function(obj, prop) {
|
|
|
44
44
|
|
|
45
45
|
module.exports = function(core) {
|
|
46
46
|
const {
|
|
47
|
+
protect,
|
|
47
48
|
protect: {
|
|
48
49
|
agentLib,
|
|
49
50
|
semanticAnalysis,
|
|
@@ -52,27 +53,32 @@ module.exports = function(core) {
|
|
|
52
53
|
captureStacktrace,
|
|
53
54
|
} = core;
|
|
54
55
|
|
|
55
|
-
function handleResult(sourceContext, sinkContext, ruleId, mode,
|
|
56
|
+
function handleResult(sourceContext, sinkContext, ruleId, mode, findings) {
|
|
56
57
|
const { value, stacktraceOpts } = sinkContext;
|
|
57
58
|
captureStacktrace(sinkContext, stacktraceOpts);
|
|
58
59
|
|
|
59
60
|
// shoehorn findings into agent-lib result data model
|
|
60
61
|
const result = {
|
|
61
62
|
blocked: false,
|
|
63
|
+
inputType: InputType.UNKNOWN,
|
|
62
64
|
ruleId,
|
|
63
65
|
value,
|
|
64
66
|
mappedId: ruleId,
|
|
65
|
-
|
|
66
|
-
...finding
|
|
67
|
+
exploited: true,
|
|
67
68
|
};
|
|
69
|
+
|
|
68
70
|
getRuleResults(sourceContext.resultsMap, ruleId).push(result);
|
|
69
71
|
|
|
72
|
+
let blockInfo;
|
|
70
73
|
if (BLOCKING_MODES.includes(mode)) {
|
|
71
74
|
result.blocked = true;
|
|
72
|
-
|
|
75
|
+
blockInfo = [mode, ruleId];
|
|
73
76
|
sourceContext.securityException = blockInfo;
|
|
74
|
-
throwSecurityException(sourceContext);
|
|
75
77
|
}
|
|
78
|
+
|
|
79
|
+
protect.reportFinding({ findings, result, sinkContext });
|
|
80
|
+
|
|
81
|
+
if (blockInfo) throwSecurityException(sourceContext);
|
|
76
82
|
}
|
|
77
83
|
|
|
78
84
|
/**
|
|
@@ -149,7 +155,7 @@ module.exports = function(core) {
|
|
|
149
155
|
}
|
|
150
156
|
|
|
151
157
|
semanticAnalysis.handleCmdInjectionSemanticDangerous = function(sourceContext, sinkContext) {
|
|
152
|
-
const mode = sourceContext.policy
|
|
158
|
+
const mode = sourceContext.policy.getRuleMode(Rule.CMD_INJECTION_SEMANTIC_DANGEROUS_PATHS);
|
|
153
159
|
|
|
154
160
|
if (mode == OFF) return;
|
|
155
161
|
|
|
@@ -161,7 +167,7 @@ module.exports = function(core) {
|
|
|
161
167
|
};
|
|
162
168
|
|
|
163
169
|
semanticAnalysis.handleCmdInjectionSemanticChainedCommands = function(sourceContext, sinkContext) {
|
|
164
|
-
const mode = sourceContext.policy
|
|
170
|
+
const mode = sourceContext.policy.getRuleMode(Rule.CMD_INJECTION_SEMANTIC_CHAINED_COMMANDS);
|
|
165
171
|
|
|
166
172
|
if (mode == OFF) return;
|
|
167
173
|
|
|
@@ -173,38 +179,33 @@ module.exports = function(core) {
|
|
|
173
179
|
};
|
|
174
180
|
|
|
175
181
|
semanticAnalysis.handleCommandInjectionCommandBackdoors = function(sourceContext, sinkContext) {
|
|
176
|
-
const mode = sourceContext.policy
|
|
182
|
+
const mode = sourceContext.policy.getRuleMode(Rule.CMD_INJECTION_COMMAND_BACKDOORS);
|
|
177
183
|
|
|
178
184
|
if (mode == OFF) return;
|
|
179
|
-
|
|
180
185
|
const finding = findBackdoorInjection(sourceContext, sinkContext.value);
|
|
181
186
|
|
|
182
187
|
if (finding) {
|
|
183
|
-
handleResult(sourceContext, sinkContext, Rule.CMD_INJECTION_COMMAND_BACKDOORS, mode
|
|
188
|
+
handleResult(sourceContext, sinkContext, Rule.CMD_INJECTION_COMMAND_BACKDOORS, mode);
|
|
184
189
|
}
|
|
185
190
|
};
|
|
186
191
|
|
|
187
192
|
semanticAnalysis.handlePathTraversalFileSecurityBypass = function(sourceContext, sinkContext) {
|
|
188
|
-
const mode = sourceContext.policy
|
|
193
|
+
const mode = sourceContext.policy.getRuleMode(Rule.PATH_TRAVERSAL_SEMANTIC_FILE_SECURITY_BYPASS);
|
|
189
194
|
|
|
190
195
|
if (mode == OFF) return;
|
|
191
196
|
|
|
192
197
|
if (agentLib.isDangerousPath(sinkContext.value, true)) {
|
|
193
|
-
handleResult(sourceContext, sinkContext, Rule.PATH_TRAVERSAL_SEMANTIC_FILE_SECURITY_BYPASS, mode
|
|
194
|
-
exploitMetadata: [{ sinkContext, path: sinkContext.value }]
|
|
195
|
-
});
|
|
198
|
+
handleResult(sourceContext, sinkContext, Rule.PATH_TRAVERSAL_SEMANTIC_FILE_SECURITY_BYPASS, mode);
|
|
196
199
|
}
|
|
197
200
|
};
|
|
198
201
|
|
|
199
202
|
semanticAnalysis.handleXXE = function (sourceContext, sinkContext) {
|
|
200
|
-
const mode = sourceContext.policy
|
|
203
|
+
const mode = sourceContext.policy.getRuleMode(Rule.XXE);
|
|
201
204
|
if (mode == OFF) return;
|
|
202
205
|
|
|
203
206
|
const findings = findExternalEntities(sinkContext.value);
|
|
204
207
|
if (findings.entities.length) {
|
|
205
|
-
handleResult(sourceContext, sinkContext, Rule.XXE, mode,
|
|
206
|
-
exploitMetadata: [{ sinkContext, ...findings }],
|
|
207
|
-
});
|
|
208
|
+
handleResult(sourceContext, sinkContext, Rule.XXE, mode, findings);
|
|
208
209
|
}
|
|
209
210
|
};
|
|
210
211
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@contrast/protect",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.68.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)",
|
|
@@ -21,16 +21,16 @@
|
|
|
21
21
|
},
|
|
22
22
|
"dependencies": {
|
|
23
23
|
"@contrast/agent-lib": "^9.1.0",
|
|
24
|
-
"@contrast/common": "1.
|
|
25
|
-
"@contrast/config": "1.
|
|
26
|
-
"@contrast/core": "1.
|
|
27
|
-
"@contrast/dep-hooks": "1.
|
|
28
|
-
"@contrast/esm-hooks": "2.
|
|
29
|
-
"@contrast/instrumentation": "1.
|
|
30
|
-
"@contrast/logger": "1.
|
|
31
|
-
"@contrast/patcher": "1.
|
|
32
|
-
"@contrast/rewriter": "1.
|
|
33
|
-
"@contrast/scopes": "1.
|
|
24
|
+
"@contrast/common": "1.37.0",
|
|
25
|
+
"@contrast/config": "1.52.1",
|
|
26
|
+
"@contrast/core": "1.57.1",
|
|
27
|
+
"@contrast/dep-hooks": "1.26.1",
|
|
28
|
+
"@contrast/esm-hooks": "2.32.0",
|
|
29
|
+
"@contrast/instrumentation": "1.36.1",
|
|
30
|
+
"@contrast/logger": "1.30.1",
|
|
31
|
+
"@contrast/patcher": "1.29.1",
|
|
32
|
+
"@contrast/rewriter": "1.34.0",
|
|
33
|
+
"@contrast/scopes": "1.27.1",
|
|
34
34
|
"async-hook-domain": "^4.0.1",
|
|
35
35
|
"ipaddr.js": "^2.0.1",
|
|
36
36
|
"on-finished": "^2.4.1",
|