@contrast/protect 1.6.2 → 1.6.3
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.
|
@@ -38,7 +38,7 @@ module.exports = function(core) {
|
|
|
38
38
|
data.result = patcher.patch(data.result, {
|
|
39
39
|
name: 'finalHandler.returnedFunction',
|
|
40
40
|
patchType,
|
|
41
|
-
around(
|
|
41
|
+
around(orig, data) {
|
|
42
42
|
const [err] = data.args;
|
|
43
43
|
const sourceContext = protect.getSourceContext('finalHandler');
|
|
44
44
|
const isSecurityException = SecurityException.isSecurityException(err);
|
|
@@ -53,7 +53,7 @@ module.exports = function(core) {
|
|
|
53
53
|
if (!sourceContext && isSecurityException) {
|
|
54
54
|
logger.info('source context not found; unable to handle response');
|
|
55
55
|
}
|
|
56
|
-
|
|
56
|
+
return orig();
|
|
57
57
|
},
|
|
58
58
|
});
|
|
59
59
|
},
|
|
@@ -64,7 +64,7 @@ module.exports = function(core) {
|
|
|
64
64
|
patcher.patch(Layer.prototype, 'handle_error', {
|
|
65
65
|
name: 'Layer.prototype.handle_error',
|
|
66
66
|
patchType,
|
|
67
|
-
around(
|
|
67
|
+
around(orig, data) {
|
|
68
68
|
const [err] = data.args;
|
|
69
69
|
const sourceContext = protect.getSourceContext('express.Layer.handle_error');
|
|
70
70
|
const isSecurityException = SecurityException.isSecurityException(err);
|
|
@@ -79,10 +79,71 @@ module.exports = function(core) {
|
|
|
79
79
|
if (!sourceContext && isSecurityException) {
|
|
80
80
|
logger.info('source context not found; unable to handle response');
|
|
81
81
|
}
|
|
82
|
-
|
|
82
|
+
return orig();
|
|
83
|
+
}
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
// This should be revisited after the research ticket NODE-2556
|
|
87
|
+
Object.defineProperty(Layer.prototype, 'handle', {
|
|
88
|
+
enumerable: true,
|
|
89
|
+
configurable: true,
|
|
90
|
+
get() {
|
|
91
|
+
return this.__handle;
|
|
92
|
+
},
|
|
93
|
+
set(fn) {
|
|
94
|
+
fn = patchFn(fn);
|
|
95
|
+
this.__handle = fn;
|
|
96
|
+
},
|
|
97
|
+
});
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
// This should be revisited after the research ticket NODE-2556
|
|
101
|
+
depHooks.resolve({ name: 'express', version: '>=4.0.0', file: 'lib/router/index.js' }, (Router) => {
|
|
102
|
+
patcher.patch(Router.prototype.constructor, 'param', {
|
|
103
|
+
name: 'Router.prototype.constructor.param',
|
|
104
|
+
patchType,
|
|
105
|
+
pre({ args }) {
|
|
106
|
+
if (typeof args[1] === 'function') {
|
|
107
|
+
args[1] = patchFn(args[1]);
|
|
108
|
+
}
|
|
83
109
|
}
|
|
84
110
|
});
|
|
85
111
|
});
|
|
112
|
+
|
|
113
|
+
// This should be revisited after the research ticket NODE-2556
|
|
114
|
+
function patchFn(fn) {
|
|
115
|
+
return patcher.patch(fn, {
|
|
116
|
+
name: 'express.route-handler',
|
|
117
|
+
patchType,
|
|
118
|
+
around(orig) {
|
|
119
|
+
const ret = orig();
|
|
120
|
+
if (ret && ret.catch) {
|
|
121
|
+
return ret.catch((err) => {
|
|
122
|
+
const sourceContext = protect.getSourceContext('express.Layer.handle_error');
|
|
123
|
+
const isSecurityException = SecurityException.isSecurityException(err);
|
|
124
|
+
|
|
125
|
+
if (isSecurityException && sourceContext) {
|
|
126
|
+
const blockInfo = sourceContext.findings.securityException;
|
|
127
|
+
|
|
128
|
+
sourceContext.block(...blockInfo);
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
if (!sourceContext && isSecurityException) {
|
|
133
|
+
logger.info('source context not found; unable to handle response');
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
throw err;
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
return ret;
|
|
142
|
+
}
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
|
|
86
147
|
};
|
|
87
148
|
|
|
88
149
|
return express4ErrorHandler;
|
|
@@ -39,12 +39,12 @@ module.exports = function (core) {
|
|
|
39
39
|
const isSecurityException = SecurityException.isSecurityException(err);
|
|
40
40
|
|
|
41
41
|
if (isSecurityException && sourceContext && err.output.statusCode !== 403) {
|
|
42
|
-
const [mode, ruleId] = sourceContext.findings.securityException;
|
|
42
|
+
const [ mode, ruleId ] = sourceContext.findings.securityException;
|
|
43
43
|
|
|
44
44
|
err.output.statusCode = 403;
|
|
45
45
|
err.reformat();
|
|
46
46
|
err.output.payload = undefined;
|
|
47
|
-
data.result =
|
|
47
|
+
data.result = err;
|
|
48
48
|
|
|
49
49
|
logger.info({ mode, ruleId }, 'Request blocked');
|
|
50
50
|
|
|
@@ -39,7 +39,7 @@ module.exports = function(core) {
|
|
|
39
39
|
patcher.patch(ctx, 'onerror', {
|
|
40
40
|
name: 'koa.ctx.onerror',
|
|
41
41
|
patchType,
|
|
42
|
-
around(
|
|
42
|
+
around(orig, data) {
|
|
43
43
|
const [err] = data.args;
|
|
44
44
|
const sourceContext = protect.getSourceContext('Koa.Application.handleRequest');
|
|
45
45
|
const isSecurityException = SecurityException.isSecurityException(err);
|
|
@@ -55,7 +55,7 @@ module.exports = function(core) {
|
|
|
55
55
|
logger.info('source context not found; unable to handle response');
|
|
56
56
|
}
|
|
57
57
|
|
|
58
|
-
|
|
58
|
+
return orig();
|
|
59
59
|
}
|
|
60
60
|
});
|
|
61
61
|
}
|
package/lib/index.d.ts
CHANGED
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
* way not consistent with the End User License Agreement.
|
|
15
15
|
*/
|
|
16
16
|
|
|
17
|
-
import { Core } from '@contrast/core';
|
|
17
|
+
// import { Core } from '@contrast/core';
|
|
18
18
|
import { Logger } from '@contrast/logger';
|
|
19
19
|
import { Sources } from '@contrast/scopes';
|
|
20
20
|
import RequireHook from '@contrast/require-hook';
|
|
@@ -39,7 +39,7 @@ export class HttpInstrumentation {
|
|
|
39
39
|
maxBodySize: number;
|
|
40
40
|
installed: boolean;
|
|
41
41
|
|
|
42
|
-
constructor(core:
|
|
42
|
+
constructor(core: {});
|
|
43
43
|
|
|
44
44
|
install(): void;
|
|
45
45
|
uninstall(): void; //NYI
|
package/lib/index.js
CHANGED
|
@@ -34,9 +34,6 @@ module.exports = function(core) {
|
|
|
34
34
|
require('./semantic-analysis')(core);
|
|
35
35
|
require('./error-handlers')(core);
|
|
36
36
|
|
|
37
|
-
const pkj = require('../package.json');
|
|
38
|
-
protect.version = pkj.version;
|
|
39
|
-
|
|
40
37
|
protect.install = function() {
|
|
41
38
|
installChildComponentsSync(protect);
|
|
42
39
|
};
|
|
@@ -119,7 +119,7 @@ module.exports = function(core) {
|
|
|
119
119
|
inputTracing.nosqlInjectionMongo = function(sourceContext, sinkContext) {
|
|
120
120
|
const ruleId = 'nosql-injection-mongo';
|
|
121
121
|
const expansionResults = getResultsByRuleId(ruleId, sourceContext);
|
|
122
|
-
const
|
|
122
|
+
const stringInjectionResults = getResultsByRuleId('ssjs-injection', sourceContext);
|
|
123
123
|
|
|
124
124
|
if (expansionResults) {
|
|
125
125
|
let expansionFindings = null;
|
|
@@ -132,13 +132,31 @@ module.exports = function(core) {
|
|
|
132
132
|
}
|
|
133
133
|
}
|
|
134
134
|
|
|
135
|
-
if (
|
|
135
|
+
if (stringInjectionResults) {
|
|
136
136
|
let stringFindings = null;
|
|
137
|
-
|
|
138
|
-
|
|
137
|
+
|
|
138
|
+
for (const result of stringInjectionResults) {
|
|
139
|
+
if (typeof sinkContext.value === 'object') {
|
|
140
|
+
simpleTraverse(sinkContext.value, function(path, type, value) {
|
|
141
|
+
if (type !== 'Key' && !agentLib.isMongoQueryType(value)) return;
|
|
142
|
+
|
|
143
|
+
stringFindings = handleStringValue(result, sinkContext.value[value], agentLib);
|
|
144
|
+
});
|
|
145
|
+
} else if (typeof sinkContext.value === 'string') {
|
|
146
|
+
stringFindings = handleStringValue(result, sinkContext.value, agentLib);
|
|
147
|
+
}
|
|
139
148
|
|
|
140
149
|
if (stringFindings) {
|
|
141
|
-
|
|
150
|
+
const nosqlInjectionResult = { ...result, ruleId, mappedId: ruleId };
|
|
151
|
+
|
|
152
|
+
const nosqlInjectionResults = sourceContext.findings.resultsMap[ruleId];
|
|
153
|
+
if (Array.isArray(nosqlInjectionResults)) {
|
|
154
|
+
nosqlInjectionResults.push(nosqlInjectionResult);
|
|
155
|
+
} else {
|
|
156
|
+
sourceContext.findings.resultsMap[ruleId] = [nosqlInjectionResult];
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
handleFindings(sourceContext, sinkContext, ruleId, nosqlInjectionResult, stringFindings);
|
|
142
160
|
}
|
|
143
161
|
}
|
|
144
162
|
}
|
|
@@ -256,25 +274,35 @@ function handleObjectValue(result, object) {
|
|
|
256
274
|
return findings;
|
|
257
275
|
}
|
|
258
276
|
|
|
259
|
-
function handleStringValue(result,
|
|
260
|
-
if (typeof
|
|
277
|
+
function handleStringValue(result, cmd, agentLib) {
|
|
278
|
+
if (typeof cmd !== 'string') {
|
|
261
279
|
return null;
|
|
262
280
|
}
|
|
263
281
|
let findings = null;
|
|
264
282
|
|
|
265
|
-
const inputIndex =
|
|
283
|
+
const inputIndex = cmd.indexOf(result.value);
|
|
266
284
|
// if the user input is not in the sink input, there is nothing to do.
|
|
267
285
|
if (inputIndex === -1) {
|
|
268
286
|
return findings;
|
|
269
287
|
}
|
|
270
288
|
|
|
271
|
-
if (inputIndex === 0 &&
|
|
289
|
+
if (inputIndex === 0 && cmd === result.value) {
|
|
272
290
|
findings = {
|
|
273
291
|
start: 0,
|
|
274
292
|
end: result.value.length - 1,
|
|
275
293
|
boundaryOverrunIndex: 0,
|
|
276
294
|
inputBoundaryIndex: 0,
|
|
277
295
|
};
|
|
296
|
+
} else {
|
|
297
|
+
const isAttack = agentLib.checkSsjsInjectionSink(cmd, inputIndex, result.value.length);
|
|
298
|
+
if (!isAttack) return findings;
|
|
299
|
+
|
|
300
|
+
findings = {
|
|
301
|
+
start: inputIndex,
|
|
302
|
+
end: inputIndex + result.value.length - 1,
|
|
303
|
+
boundaryOverrunIndex: 0,
|
|
304
|
+
inputBoundaryIndex: 0,
|
|
305
|
+
};
|
|
278
306
|
}
|
|
279
307
|
|
|
280
308
|
return findings;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@contrast/protect",
|
|
3
|
-
"version": "1.6.
|
|
3
|
+
"version": "1.6.3",
|
|
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)",
|
|
@@ -20,9 +20,9 @@
|
|
|
20
20
|
"@babel/template": "^7.16.7",
|
|
21
21
|
"@babel/types": "^7.16.8",
|
|
22
22
|
"@contrast/agent-lib": "^5.1.0",
|
|
23
|
-
"@contrast/common": "1.1.
|
|
24
|
-
"@contrast/core": "1.
|
|
25
|
-
"@contrast/esm-hooks": "1.
|
|
23
|
+
"@contrast/common": "1.1.3",
|
|
24
|
+
"@contrast/core": "1.6.0",
|
|
25
|
+
"@contrast/esm-hooks": "1.2.0",
|
|
26
26
|
"@contrast/scopes": "1.1.2",
|
|
27
27
|
"builtin-modules": "^3.2.0",
|
|
28
28
|
"ipaddr.js": "^2.0.1",
|