@contrast/assess 1.27.2 → 1.28.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/constants.js +3 -3
- package/lib/dataflow/propagation/install/array-prototype-join.js +5 -6
- package/lib/dataflow/propagation/install/buffer.js +3 -1
- package/lib/dataflow/propagation/install/contrast-methods/add.js +72 -69
- package/lib/dataflow/propagation/install/contrast-methods/number.js +4 -5
- package/lib/dataflow/propagation/install/contrast-methods/string.js +3 -2
- package/lib/dataflow/propagation/install/contrast-methods/tag.js +3 -6
- package/lib/dataflow/propagation/install/string/concat.js +4 -5
- package/lib/dataflow/propagation/install/string/html-methods.js +5 -6
- package/lib/dataflow/propagation/install/string/match-all.js +6 -6
- package/lib/dataflow/propagation/install/string/replace.js +6 -6
- package/lib/dataflow/propagation/install/string/slice.js +4 -3
- package/lib/dataflow/propagation/install/string/split.js +5 -6
- package/lib/dataflow/propagation/install/string/substring.js +4 -4
- package/lib/dataflow/propagation/install/string/trim.js +4 -5
- package/lib/dataflow/sinks/install/mongodb.js +45 -8
- package/lib/dataflow/sources/install/body-parser1.js +12 -5
- package/lib/dataflow/sources/install/cookie-parser1.js +4 -3
- package/lib/dataflow/sources/install/qs6.js +7 -5
- package/lib/dataflow/sources/install/querystring.js +8 -2
- package/lib/dataflow/tag-utils.js +5 -4
- package/lib/get-source-context.js +2 -1
- package/package.json +2 -2
package/lib/constants.js
CHANGED
|
@@ -15,17 +15,16 @@
|
|
|
15
15
|
|
|
16
16
|
'use strict';
|
|
17
17
|
|
|
18
|
-
const {
|
|
19
|
-
createAppendTags
|
|
20
|
-
} = require('../../tag-utils');
|
|
21
|
-
const { patchType } = require('../common');
|
|
22
18
|
const { isString, join, inspect } = require('@contrast/common');
|
|
19
|
+
const { InstrumentationType: { PROPAGATOR } } = require('../../../constants');
|
|
20
|
+
const { createAppendTags } = require('../../tag-utils');
|
|
21
|
+
const { patchType } = require('../common');
|
|
23
22
|
|
|
24
23
|
module.exports = function(core) {
|
|
25
24
|
const {
|
|
26
|
-
scopes: { sources, instrumentation },
|
|
27
25
|
patcher,
|
|
28
26
|
assess: {
|
|
27
|
+
getSourceContext,
|
|
29
28
|
eventFactory: { createPropagationEvent },
|
|
30
29
|
dataflow: { tracker }
|
|
31
30
|
}
|
|
@@ -71,7 +70,7 @@ module.exports = function(core) {
|
|
|
71
70
|
patchType,
|
|
72
71
|
post(data) {
|
|
73
72
|
const { args: origArgs, obj, result, hooked, orig } = data;
|
|
74
|
-
if (!result || !
|
|
73
|
+
if (!result || !(getSourceContext(PROPAGATOR))) return;
|
|
75
74
|
|
|
76
75
|
const resultInfo = tracker.getData(result);
|
|
77
76
|
const delimiter = origArgs[0] === undefined ? ',' : origArgs[0];
|
|
@@ -14,11 +14,13 @@
|
|
|
14
14
|
*/
|
|
15
15
|
'use strict';
|
|
16
16
|
|
|
17
|
+
const { InstrumentationType: { PROPAGATOR } } = require('../../../constants');
|
|
17
18
|
const { patchType } = require('../common');
|
|
18
19
|
|
|
19
20
|
module.exports = function(core) {
|
|
20
21
|
const {
|
|
21
22
|
assess: {
|
|
23
|
+
getSourceContext,
|
|
22
24
|
eventFactory,
|
|
23
25
|
dataflow: { tracker }
|
|
24
26
|
},
|
|
@@ -35,7 +37,7 @@ module.exports = function(core) {
|
|
|
35
37
|
post(data) {
|
|
36
38
|
const { hooked, obj, orig, result } = data;
|
|
37
39
|
|
|
38
|
-
if (!result) return;
|
|
40
|
+
if (!result || !getSourceContext(PROPAGATOR)) return;
|
|
39
41
|
|
|
40
42
|
const bufferInfo = tracker.getData(obj);
|
|
41
43
|
if (!bufferInfo) {
|
|
@@ -16,102 +16,105 @@
|
|
|
16
16
|
'use strict';
|
|
17
17
|
|
|
18
18
|
const util = require('util');
|
|
19
|
-
const {
|
|
20
|
-
|
|
21
|
-
} = require('../../../tag-utils');
|
|
22
|
-
const { patchType } = require('../../common');
|
|
19
|
+
const { InstrumentationType: { PROPAGATOR } } = require('../../../../constants');
|
|
20
|
+
const { createAppendTags } = require('../../../tag-utils');
|
|
23
21
|
|
|
24
22
|
module.exports = function(core) {
|
|
25
23
|
const {
|
|
26
|
-
scopes: { instrumentation, sources },
|
|
27
24
|
patcher,
|
|
28
25
|
assess: {
|
|
26
|
+
getSourceContext,
|
|
29
27
|
eventFactory: { createPropagationEvent },
|
|
30
28
|
dataflow: { tracker }
|
|
31
29
|
}
|
|
32
30
|
} = core;
|
|
33
31
|
|
|
34
32
|
const inspect = patcher.unwrap(util.inspect);
|
|
33
|
+
const origSym = Symbol('ContrastMethods.add.orig');
|
|
35
34
|
|
|
36
35
|
return core.assess.dataflow.propagation.contrastMethodsInstrumentation.add = {
|
|
37
36
|
install() {
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
37
|
+
// + is fast and typically called often. therefore we patch ContrastMethods.add
|
|
38
|
+
// manually instead of using patcher. this propagator is the only
|
|
39
|
+
// patch for it, so we don't have to worry about managing patch execution order
|
|
40
|
+
// (which patcher would do).
|
|
41
|
+
const { add } = global.ContrastMethods;
|
|
42
|
+
global.ContrastMethods.add = function(...args) {
|
|
43
|
+
// first get result, then following logic acts as post-hook in patcher speak
|
|
44
|
+
const result = add(...args);
|
|
44
45
|
|
|
45
|
-
|
|
46
|
-
if (rInfo) {
|
|
47
|
-
// this may happen w/ '' + 'tracked' => 'tracked'
|
|
48
|
-
return;
|
|
49
|
-
}
|
|
46
|
+
if (!result || !getSourceContext(PROPAGATOR)) return result;
|
|
50
47
|
|
|
51
|
-
|
|
52
|
-
|
|
48
|
+
const rInfo = tracker.getData(result);
|
|
49
|
+
if (rInfo) {
|
|
50
|
+
// this may happen w/ '' + 'tracked' => 'tracked'
|
|
51
|
+
return result;
|
|
52
|
+
}
|
|
53
53
|
|
|
54
|
-
|
|
55
|
-
|
|
54
|
+
const leftStringInfo = tracker.getData(args[0]);
|
|
55
|
+
const rightStringInfo = tracker.getData(args[1]);
|
|
56
56
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
newTags = leftStringInfo.tags || {};
|
|
60
|
-
}
|
|
57
|
+
let newTags = {};
|
|
58
|
+
const history = [];
|
|
61
59
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
60
|
+
if (leftStringInfo) {
|
|
61
|
+
history.push(leftStringInfo);
|
|
62
|
+
newTags = leftStringInfo.tags || {};
|
|
63
|
+
}
|
|
66
64
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
args: [
|
|
72
|
-
{
|
|
73
|
-
tracked: !!leftStringInfo,
|
|
74
|
-
value: leftArg
|
|
75
|
-
},
|
|
76
|
-
{
|
|
77
|
-
tracked: !!rightStringInfo,
|
|
78
|
-
value: rightArg,
|
|
79
|
-
}
|
|
80
|
-
],
|
|
81
|
-
context: `${inspect(leftArg)} + ${inspect(rightArg)}`,
|
|
82
|
-
moduleName: 'global',
|
|
83
|
-
methodName: 'ContrastMethods.add',
|
|
84
|
-
history,
|
|
85
|
-
object: {
|
|
86
|
-
value: 'String Addition',
|
|
87
|
-
tracked: false
|
|
88
|
-
},
|
|
89
|
-
name: 'ContrastMethods.add',
|
|
90
|
-
result: {
|
|
91
|
-
value: result,
|
|
92
|
-
tracked: true
|
|
93
|
-
},
|
|
94
|
-
source: 'P',
|
|
95
|
-
stacktraceOpts: {
|
|
96
|
-
constructorOpt: hooked,
|
|
97
|
-
},
|
|
98
|
-
tags: newTags,
|
|
99
|
-
target: 'R',
|
|
100
|
-
});
|
|
65
|
+
if (rightStringInfo) {
|
|
66
|
+
history.push(rightStringInfo);
|
|
67
|
+
newTags = createAppendTags(newTags, rightStringInfo.tags, args[0].length);
|
|
68
|
+
}
|
|
101
69
|
|
|
102
|
-
|
|
70
|
+
if (history.length) {
|
|
71
|
+
const leftArg = leftStringInfo ? leftStringInfo.value : args[0];
|
|
72
|
+
const rightArg = rightStringInfo ? rightStringInfo.value : args[1];
|
|
73
|
+
const event = createPropagationEvent({
|
|
74
|
+
args: [
|
|
75
|
+
{
|
|
76
|
+
tracked: !!leftStringInfo,
|
|
77
|
+
value: leftArg
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
tracked: !!rightStringInfo,
|
|
81
|
+
value: rightArg,
|
|
82
|
+
}
|
|
83
|
+
],
|
|
84
|
+
context: `${inspect(leftArg)} + ${inspect(rightArg)}`,
|
|
85
|
+
moduleName: 'global',
|
|
86
|
+
methodName: 'ContrastMethods.add',
|
|
87
|
+
history,
|
|
88
|
+
object: {
|
|
89
|
+
value: 'String Addition',
|
|
90
|
+
tracked: false
|
|
91
|
+
},
|
|
92
|
+
name: 'ContrastMethods.add',
|
|
93
|
+
result: {
|
|
94
|
+
value: result,
|
|
95
|
+
tracked: true
|
|
96
|
+
},
|
|
97
|
+
source: 'P',
|
|
98
|
+
stacktraceOpts: {
|
|
99
|
+
constructorOpt: add,
|
|
100
|
+
},
|
|
101
|
+
tags: newTags,
|
|
102
|
+
target: 'R',
|
|
103
|
+
});
|
|
103
104
|
|
|
105
|
+
if (event) {
|
|
104
106
|
const { extern } = tracker.track(result, event);
|
|
105
|
-
|
|
106
|
-
if (extern) {
|
|
107
|
-
data.result = extern;
|
|
108
|
-
}
|
|
107
|
+
if (extern) return extern;
|
|
109
108
|
}
|
|
110
109
|
}
|
|
111
|
-
|
|
110
|
+
|
|
111
|
+
return result;
|
|
112
|
+
};
|
|
113
|
+
global.ContrastMethods.add[origSym] = add;
|
|
112
114
|
},
|
|
113
115
|
uninstall() {
|
|
114
|
-
|
|
116
|
+
const orig = global.ContrastMethods.add[origSym];
|
|
117
|
+
if (orig) global.ContrastMethods.add = orig;
|
|
115
118
|
},
|
|
116
119
|
};
|
|
117
120
|
};
|
|
@@ -16,14 +16,15 @@
|
|
|
16
16
|
'use strict';
|
|
17
17
|
|
|
18
18
|
const { isString } = require('@contrast/common');
|
|
19
|
+
const { InstrumentationType: { PROPAGATOR } } = require('../../../../constants');
|
|
19
20
|
const { patchType } = require('../../common');
|
|
20
21
|
|
|
21
22
|
module.exports = function (core) {
|
|
22
23
|
const {
|
|
23
24
|
logger,
|
|
24
|
-
scopes: { instrumentation, sources },
|
|
25
25
|
patcher,
|
|
26
26
|
assess: {
|
|
27
|
+
getSourceContext,
|
|
27
28
|
dataflow: { tracker }
|
|
28
29
|
}
|
|
29
30
|
} = core;
|
|
@@ -38,13 +39,11 @@ module.exports = function (core) {
|
|
|
38
39
|
post(data) {
|
|
39
40
|
const { args: [value], result } = data;
|
|
40
41
|
if (
|
|
42
|
+
!tracker.getData(value) ||
|
|
41
43
|
isNaN(result) ||
|
|
42
44
|
!value ||
|
|
43
45
|
!isString(value) ||
|
|
44
|
-
!
|
|
45
|
-
instrumentation.isLocked() ||
|
|
46
|
-
// why not just do this first? won't need check for NaN, !value, !isString, etc.
|
|
47
|
-
!tracker.getData(value)
|
|
46
|
+
!getSourceContext(PROPAGATOR)
|
|
48
47
|
) return;
|
|
49
48
|
|
|
50
49
|
tracker.untrack(value);
|
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
'use strict';
|
|
17
17
|
|
|
18
18
|
const { DataflowTag } = require('@contrast/common');
|
|
19
|
+
const { InstrumentationType: { PROPAGATOR } } = require('../../../../constants');
|
|
19
20
|
const { patchType } = require('../../common');
|
|
20
21
|
|
|
21
22
|
function metadataUpdate(strInfo, event) {
|
|
@@ -29,9 +30,9 @@ function metadataUpdate(strInfo, event) {
|
|
|
29
30
|
|
|
30
31
|
module.exports = function(core) {
|
|
31
32
|
const {
|
|
32
|
-
scopes: { sources, instrumentation },
|
|
33
33
|
patcher,
|
|
34
34
|
assess: {
|
|
35
|
+
getSourceContext,
|
|
35
36
|
eventFactory: { createPropagationEvent },
|
|
36
37
|
dataflow: { tracker },
|
|
37
38
|
}
|
|
@@ -44,7 +45,7 @@ module.exports = function(core) {
|
|
|
44
45
|
name,
|
|
45
46
|
patchType,
|
|
46
47
|
post(data) {
|
|
47
|
-
if (!data.result || !
|
|
48
|
+
if (!data.result || !getSourceContext(PROPAGATOR)) return;
|
|
48
49
|
|
|
49
50
|
const arg = data.args[0];
|
|
50
51
|
let argInfo = tracker.getData(arg);
|
|
@@ -15,16 +15,17 @@
|
|
|
15
15
|
|
|
16
16
|
'use strict';
|
|
17
17
|
|
|
18
|
+
const { InstrumentationType: { PROPAGATOR } } = require('../../../../constants');
|
|
18
19
|
const { patchType } = require('../../common');
|
|
19
20
|
|
|
20
21
|
module.exports = function(core) {
|
|
21
22
|
const {
|
|
22
23
|
assess: {
|
|
24
|
+
getSourceContext,
|
|
23
25
|
eventFactory: { createPropagationEvent },
|
|
24
26
|
dataflow: { tracker },
|
|
25
27
|
},
|
|
26
28
|
patcher,
|
|
27
|
-
scopes: { sources, instrumentation },
|
|
28
29
|
} = core;
|
|
29
30
|
|
|
30
31
|
const tag = {
|
|
@@ -33,11 +34,7 @@ module.exports = function(core) {
|
|
|
33
34
|
name: 'ContrastMethods.tag',
|
|
34
35
|
patchType,
|
|
35
36
|
post(data) {
|
|
36
|
-
if (
|
|
37
|
-
!data.result ||
|
|
38
|
-
!sources.getStore()?.assess ||
|
|
39
|
-
instrumentation.isLocked()
|
|
40
|
-
) {
|
|
37
|
+
if (!data.result || !getSourceContext(PROPAGATOR)) {
|
|
41
38
|
return;
|
|
42
39
|
}
|
|
43
40
|
|
|
@@ -15,17 +15,16 @@
|
|
|
15
15
|
|
|
16
16
|
'use strict';
|
|
17
17
|
|
|
18
|
-
const {
|
|
19
|
-
createAppendTags
|
|
20
|
-
} = require('../../../tag-utils');
|
|
21
18
|
const { join, inspect } = require('@contrast/common');
|
|
19
|
+
const { InstrumentationType: { PROPAGATOR } } = require('../../../../constants');
|
|
20
|
+
const { createAppendTags } = require('../../../tag-utils');
|
|
22
21
|
const { patchType } = require('../../common');
|
|
23
22
|
|
|
24
23
|
module.exports = function(core) {
|
|
25
24
|
const {
|
|
26
|
-
scopes: { sources, instrumentation },
|
|
27
25
|
patcher,
|
|
28
26
|
assess: {
|
|
27
|
+
getSourceContext,
|
|
29
28
|
eventFactory: { createPropagationEvent },
|
|
30
29
|
dataflow: { tracker }
|
|
31
30
|
}
|
|
@@ -40,7 +39,7 @@ module.exports = function(core) {
|
|
|
40
39
|
patchType,
|
|
41
40
|
post(data) {
|
|
42
41
|
const { args, obj, result, hooked, orig } = data;
|
|
43
|
-
if (!result || !
|
|
42
|
+
if (!result || !getSourceContext(PROPAGATOR)) return;
|
|
44
43
|
|
|
45
44
|
const rInfo = tracker.getData(result);
|
|
46
45
|
if (rInfo) {
|
|
@@ -15,10 +15,9 @@
|
|
|
15
15
|
|
|
16
16
|
'use strict';
|
|
17
17
|
|
|
18
|
-
const {
|
|
19
|
-
createAppendTags
|
|
20
|
-
} = require('../../../tag-utils');
|
|
21
18
|
const { inspect } = require('@contrast/common');
|
|
19
|
+
const { InstrumentationType: { PROPAGATOR } } = require('../../../../constants');
|
|
20
|
+
const { createAppendTags } = require('../../../tag-utils');
|
|
22
21
|
const { patchType } = require('../../common');
|
|
23
22
|
const htmlTagsLengths = {
|
|
24
23
|
anchor: 11,
|
|
@@ -33,9 +32,9 @@ const htmlTagsLengths = {
|
|
|
33
32
|
|
|
34
33
|
module.exports = function(core) {
|
|
35
34
|
const {
|
|
36
|
-
scopes: { sources, instrumentation },
|
|
37
35
|
patcher,
|
|
38
36
|
assess: {
|
|
37
|
+
getSourceContext,
|
|
39
38
|
eventFactory: { createPropagationEvent },
|
|
40
39
|
dataflow: { tracker }
|
|
41
40
|
}
|
|
@@ -65,7 +64,7 @@ module.exports = function(core) {
|
|
|
65
64
|
patchType,
|
|
66
65
|
post(data) {
|
|
67
66
|
const { args, obj, result, hooked, orig } = data;
|
|
68
|
-
if (!result || !
|
|
67
|
+
if (!result || !getSourceContext(PROPAGATOR)) return;
|
|
69
68
|
|
|
70
69
|
const objInfo = tracker.getData(obj);
|
|
71
70
|
const history = objInfo ? new Set([objInfo]) : new Set();
|
|
@@ -122,7 +121,7 @@ module.exports = function(core) {
|
|
|
122
121
|
patchType,
|
|
123
122
|
post(data) {
|
|
124
123
|
const { obj, result, hooked, orig } = data;
|
|
125
|
-
if (!result || !
|
|
124
|
+
if (!result || !getSourceContext(PROPAGATOR)) return;
|
|
126
125
|
|
|
127
126
|
const objInfo = tracker.getData(obj);
|
|
128
127
|
|
|
@@ -15,14 +15,15 @@
|
|
|
15
15
|
|
|
16
16
|
'use strict';
|
|
17
17
|
const { inspect } = require('@contrast/common');
|
|
18
|
-
const {
|
|
18
|
+
const { InstrumentationType: { PROPAGATOR } } = require('../../../../constants');
|
|
19
19
|
const { createSubsetTags } = require('../../../tag-utils');
|
|
20
|
+
const { patchType } = require('../../common');
|
|
20
21
|
|
|
21
22
|
module.exports = function(core) {
|
|
22
23
|
const {
|
|
23
|
-
scopes: { sources, instrumentation },
|
|
24
24
|
patcher,
|
|
25
25
|
assess: {
|
|
26
|
+
getSourceContext,
|
|
26
27
|
eventFactory: { createPropagationEvent },
|
|
27
28
|
dataflow: {
|
|
28
29
|
tracker,
|
|
@@ -75,7 +76,7 @@ module.exports = function(core) {
|
|
|
75
76
|
});
|
|
76
77
|
}
|
|
77
78
|
|
|
78
|
-
return
|
|
79
|
+
return stringInstrumentation.matchAll = {
|
|
79
80
|
install() {
|
|
80
81
|
patcher.patch(String.prototype, 'matchAll', {
|
|
81
82
|
name,
|
|
@@ -87,8 +88,7 @@ module.exports = function(core) {
|
|
|
87
88
|
!obj ||
|
|
88
89
|
!args[0] ||
|
|
89
90
|
typeof obj !== 'string' ||
|
|
90
|
-
!
|
|
91
|
-
instrumentation.isLocked()
|
|
91
|
+
!getSourceContext(PROPAGATOR)
|
|
92
92
|
)
|
|
93
93
|
return origFn();
|
|
94
94
|
|
|
@@ -233,5 +233,5 @@ module.exports = function(core) {
|
|
|
233
233
|
uninstall() {
|
|
234
234
|
String.prototype.matchAll = patcher.unwrap(String.prototype.matchAll);
|
|
235
235
|
},
|
|
236
|
-
}
|
|
236
|
+
};
|
|
237
237
|
};
|
|
@@ -18,20 +18,22 @@
|
|
|
18
18
|
const {
|
|
19
19
|
DataflowTag: { UNTRUSTED },
|
|
20
20
|
match: origMatch,
|
|
21
|
+
inspect,
|
|
22
|
+
join,
|
|
21
23
|
substring
|
|
22
24
|
} = require('@contrast/common');
|
|
23
|
-
const {
|
|
24
|
-
const { patchType } = require('../../common');
|
|
25
|
+
const { InstrumentationType: { PROPAGATOR } } = require('../../../../constants');
|
|
25
26
|
const { createSubsetTags, createAppendTags } = require('../../../tag-utils');
|
|
27
|
+
const { patchType } = require('../../common');
|
|
26
28
|
|
|
27
29
|
module.exports = function(core) {
|
|
28
30
|
const {
|
|
29
31
|
patcher,
|
|
30
32
|
assess: {
|
|
33
|
+
getSourceContext,
|
|
31
34
|
eventFactory: { createPropagationEvent },
|
|
32
35
|
dataflow: { tracker }
|
|
33
36
|
},
|
|
34
|
-
scopes: { sources, instrumentation }
|
|
35
37
|
} = core;
|
|
36
38
|
|
|
37
39
|
function parseArgs(args) {
|
|
@@ -141,7 +143,7 @@ module.exports = function(core) {
|
|
|
141
143
|
name,
|
|
142
144
|
patchType,
|
|
143
145
|
pre(data) {
|
|
144
|
-
if (!
|
|
146
|
+
if (!getSourceContext(PROPAGATOR)) return;
|
|
145
147
|
|
|
146
148
|
// setup state
|
|
147
149
|
data._objInfo = tracker.getData(data.obj);
|
|
@@ -155,8 +157,6 @@ module.exports = function(core) {
|
|
|
155
157
|
},
|
|
156
158
|
post(data) {
|
|
157
159
|
if (
|
|
158
|
-
!sources.getStore()?.assess ||
|
|
159
|
-
instrumentation.isLocked() ||
|
|
160
160
|
!data.result ||
|
|
161
161
|
// todo: can we reuse this optimization in other propagators? e.g those performing substring-like operations
|
|
162
162
|
!data._accumTags?.[UNTRUSTED] ||
|
|
@@ -13,15 +13,16 @@
|
|
|
13
13
|
* way not consistent with the End User License Agreement.
|
|
14
14
|
*/
|
|
15
15
|
'use strict';
|
|
16
|
-
const { patchType } = require('../../common');
|
|
17
16
|
const { inspect, join } = require('@contrast/common');
|
|
17
|
+
const { InstrumentationType: { PROPAGATOR } } = require('../../../../constants');
|
|
18
18
|
const { createSubsetTags } = require('../../../tag-utils');
|
|
19
|
+
const { patchType } = require('../../common');
|
|
19
20
|
|
|
20
21
|
module.exports = function(core) {
|
|
21
22
|
const {
|
|
22
|
-
scopes: { sources, instrumentation },
|
|
23
23
|
patcher,
|
|
24
24
|
assess: {
|
|
25
|
+
getSourceContext,
|
|
25
26
|
eventFactory: { createPropagationEvent },
|
|
26
27
|
dataflow: { tracker }
|
|
27
28
|
}
|
|
@@ -55,7 +56,7 @@ module.exports = function(core) {
|
|
|
55
56
|
patchType,
|
|
56
57
|
post(data) {
|
|
57
58
|
const { name, args: origArgs, obj, result, hooked, orig } = data;
|
|
58
|
-
if (!result || !
|
|
59
|
+
if (!result || !getSourceContext(PROPAGATOR)) return;
|
|
59
60
|
|
|
60
61
|
const objInfo = tracker.getData(obj);
|
|
61
62
|
if (!objInfo) return;
|
|
@@ -15,16 +15,16 @@
|
|
|
15
15
|
|
|
16
16
|
'use strict';
|
|
17
17
|
|
|
18
|
-
const { patchType } = require('../../common');
|
|
19
18
|
const { join, inspect } = require('@contrast/common');
|
|
19
|
+
const { InstrumentationType: { PROPAGATOR } } = require('../../../../constants');
|
|
20
20
|
const { createSubsetTags } = require('../../../tag-utils');
|
|
21
|
-
|
|
21
|
+
const { patchType } = require('../../common');
|
|
22
22
|
|
|
23
23
|
module.exports = function(core) {
|
|
24
24
|
const {
|
|
25
|
-
scopes: { sources, instrumentation },
|
|
26
25
|
patcher,
|
|
27
26
|
assess: {
|
|
27
|
+
getSourceContext,
|
|
28
28
|
eventFactory,
|
|
29
29
|
dataflow: { tracker }
|
|
30
30
|
}
|
|
@@ -44,10 +44,9 @@ module.exports = function(core) {
|
|
|
44
44
|
!result ||
|
|
45
45
|
origArgs.length === 0 ||
|
|
46
46
|
result.length === 0 ||
|
|
47
|
-
!sources.getStore() ||
|
|
48
47
|
typeof obj !== 'string' ||
|
|
49
|
-
|
|
50
|
-
(
|
|
48
|
+
(origArgs.length === 1 && origArgs[0] == null) ||
|
|
49
|
+
!getSourceContext(PROPAGATOR)
|
|
51
50
|
) return;
|
|
52
51
|
|
|
53
52
|
const objInfo = tracker.getData(obj);
|
|
@@ -15,15 +15,16 @@
|
|
|
15
15
|
|
|
16
16
|
'use strict';
|
|
17
17
|
|
|
18
|
-
const { createSubsetTags } = require('../../../tag-utils');
|
|
19
18
|
const { join, inspect } = require('@contrast/common');
|
|
19
|
+
const { InstrumentationType: { PROPAGATOR } } = require('../../../../constants');
|
|
20
|
+
const { createSubsetTags } = require('../../../tag-utils');
|
|
20
21
|
const { patchType } = require('../../common');
|
|
21
22
|
|
|
22
23
|
module.exports = function(core) {
|
|
23
24
|
const {
|
|
24
|
-
scopes: { sources, instrumentation },
|
|
25
25
|
patcher,
|
|
26
26
|
assess: {
|
|
27
|
+
getSourceContext,
|
|
27
28
|
eventFactory: { createPropagationEvent },
|
|
28
29
|
dataflow: { tracker }
|
|
29
30
|
}
|
|
@@ -63,7 +64,7 @@ module.exports = function(core) {
|
|
|
63
64
|
patchType,
|
|
64
65
|
post(data) {
|
|
65
66
|
const { obj, args: origArgs, result, name, hooked, orig } = data;
|
|
66
|
-
if (!result || !
|
|
67
|
+
if (!result || !getSourceContext(PROPAGATOR)) return;
|
|
67
68
|
|
|
68
69
|
const objInfo = tracker.getData(obj);
|
|
69
70
|
if (!objInfo) return;
|
|
@@ -125,4 +126,3 @@ module.exports = function(core) {
|
|
|
125
126
|
},
|
|
126
127
|
};
|
|
127
128
|
};
|
|
128
|
-
|
|
@@ -15,16 +15,15 @@
|
|
|
15
15
|
|
|
16
16
|
'use strict';
|
|
17
17
|
|
|
18
|
-
const {
|
|
19
|
-
|
|
20
|
-
} = require('../../../tag-utils');
|
|
18
|
+
const { InstrumentationType: { PROPAGATOR } } = require('../../../../constants');
|
|
19
|
+
const { createSubsetTags } = require('../../../tag-utils');
|
|
21
20
|
const { patchType } = require('../../common');
|
|
22
21
|
|
|
23
22
|
module.exports = function(core) {
|
|
24
23
|
const {
|
|
25
|
-
scopes: { sources, instrumentation },
|
|
26
24
|
patcher,
|
|
27
25
|
assess: {
|
|
26
|
+
getSourceContext,
|
|
28
27
|
eventFactory: { createPropagationEvent },
|
|
29
28
|
dataflow: { tracker }
|
|
30
29
|
}
|
|
@@ -34,7 +33,7 @@ module.exports = function(core) {
|
|
|
34
33
|
return function(data) {
|
|
35
34
|
const { obj, result, hooked, orig } = data;
|
|
36
35
|
|
|
37
|
-
if (!result?.length || !
|
|
36
|
+
if (!result?.length || !getSourceContext(PROPAGATOR)) {
|
|
38
37
|
return;
|
|
39
38
|
}
|
|
40
39
|
const rInfo = tracker.getData(result);
|
|
@@ -236,6 +236,49 @@ module.exports = function (core) {
|
|
|
236
236
|
return { vulnInfo, reportSafe };
|
|
237
237
|
};
|
|
238
238
|
|
|
239
|
+
/**
|
|
240
|
+
* Truncate `arg` values for reporting and get the adjusted `tags`.
|
|
241
|
+
* If an argument isn't directly relevant to the vulnerability, we report it by its type.
|
|
242
|
+
* If it is relevant, we truncate the value if it is an object or array, but not if it is a string.
|
|
243
|
+
* Ex1: Array truncations will look like `[...'<key>':'<value>'...]`
|
|
244
|
+
* Ex2: Object truncations will look like `{...'<key>':'<value>'...}`
|
|
245
|
+
* In the above examples, `key` will be the last value in the path array, and `value` should be the
|
|
246
|
+
* containing the injection. So whether the argument is a string or an object, the actual string value
|
|
247
|
+
* leading to the injection will not be truncated.
|
|
248
|
+
*/
|
|
249
|
+
function getAdjustedFields(origArgs, vulnInfo, vulnArgIdx) {
|
|
250
|
+
const { path, strInfo } = vulnInfo;
|
|
251
|
+
const coercedArgs = [];
|
|
252
|
+
let prefix = '';
|
|
253
|
+
let suffix = '';
|
|
254
|
+
|
|
255
|
+
if (path) {
|
|
256
|
+
let openChar, closeChar;
|
|
257
|
+
if (Array.isArray(origArgs[vulnArgIdx])) {
|
|
258
|
+
openChar = '[';
|
|
259
|
+
closeChar = ']';
|
|
260
|
+
} else {
|
|
261
|
+
openChar = '{';
|
|
262
|
+
closeChar = '}';
|
|
263
|
+
}
|
|
264
|
+
prefix = `${openChar}...'${path[path.length - 1]}':'`;
|
|
265
|
+
suffix = `'...${closeChar}`;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
for (let i = 0; i < origArgs.length; i++) {
|
|
269
|
+
if (i === vulnArgIdx) {
|
|
270
|
+
coercedArgs.push({ value: `${prefix}${strInfo.value}${suffix}`, tracked: true });
|
|
271
|
+
} else {
|
|
272
|
+
coercedArgs.push({ value: origArgs[i]?.constructor?.name ?? typeof origArgs[i], tracked: false });
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
return {
|
|
277
|
+
args: coercedArgs,
|
|
278
|
+
tags: prefix ? utils.createAppendTags(null, strInfo.tags, prefix.length) : strInfo.tags,
|
|
279
|
+
};
|
|
280
|
+
}
|
|
281
|
+
|
|
239
282
|
function createAroundHook(entity, name, method, getInfoMethod, vulnerableArgIdxs) {
|
|
240
283
|
const argsIdxsToCheck = vulnerableArgIdxs || [0];
|
|
241
284
|
return function (next, data) {
|
|
@@ -290,19 +333,13 @@ module.exports = function (core) {
|
|
|
290
333
|
: next();
|
|
291
334
|
}
|
|
292
335
|
|
|
293
|
-
const { path, strInfo } = vulnInfo;
|
|
294
336
|
const objName = getObjectName(obj, entity);
|
|
295
|
-
const args = origArgs
|
|
296
|
-
value: isString(arg) ? arg : inspect(arg, { depth: 4 }),
|
|
297
|
-
tracked: idx === vulnArgIdx,
|
|
298
|
-
}));
|
|
299
|
-
|
|
300
|
-
const tags = path ? utils.createAdjustedQueryTags(path, strInfo.tags, strInfo.value, args[vulnArgIdx].value) : strInfo?.tags;
|
|
337
|
+
const { args, tags } = getAdjustedFields(origArgs, vulnInfo, vulnArgIdx);
|
|
301
338
|
const resultVal = args[args.length - 1].value.startsWith('[Function') ? '' : 'Promise';
|
|
302
339
|
const sinkEvent = createSinkEvent({
|
|
303
340
|
args,
|
|
304
341
|
context: `${objName}.${method}(${args.map((a, idx) => isString(origArgs[idx]) ? `'${a.value}'` : a.value)})`,
|
|
305
|
-
history: [strInfo],
|
|
342
|
+
history: [vulnInfo.strInfo],
|
|
306
343
|
object: {
|
|
307
344
|
tracked: false,
|
|
308
345
|
value: `mongodb.${entity}`,
|
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
'use strict';
|
|
17
17
|
|
|
18
18
|
const { InputType } = require('@contrast/common');
|
|
19
|
+
const { InstrumentationType: { SOURCE } } = require('../../../constants');
|
|
19
20
|
const { patchType } = require('../common');
|
|
20
21
|
|
|
21
22
|
const METHODS = ['json', 'raw', 'text', 'urlencoded'];
|
|
@@ -27,12 +28,18 @@ const INPUT_TYPES = {
|
|
|
27
28
|
};
|
|
28
29
|
|
|
29
30
|
module.exports = function init(core) {
|
|
30
|
-
const {
|
|
31
|
+
const {
|
|
32
|
+
scopes,
|
|
33
|
+
assess: { dataflow, getSourceContext },
|
|
34
|
+
depHooks,
|
|
35
|
+
logger,
|
|
36
|
+
patcher,
|
|
37
|
+
} = core;
|
|
31
38
|
|
|
32
39
|
const preHook = (data) => {
|
|
33
40
|
const [req, , next] = data.args;
|
|
34
41
|
data.args[2] = scopes.wrap(function contrastNext(...args) {
|
|
35
|
-
const sourceContext =
|
|
42
|
+
const sourceContext = getSourceContext(SOURCE);
|
|
36
43
|
|
|
37
44
|
if (!sourceContext) {
|
|
38
45
|
logger.error({ funcKey: data.funcKey }, 'unable to handle source. Missing `sourceContext`');
|
|
@@ -68,7 +75,7 @@ module.exports = function init(core) {
|
|
|
68
75
|
}
|
|
69
76
|
|
|
70
77
|
try {
|
|
71
|
-
|
|
78
|
+
dataflow.sources.handle({
|
|
72
79
|
context,
|
|
73
80
|
data: _data,
|
|
74
81
|
name: data.name,
|
|
@@ -89,7 +96,7 @@ module.exports = function init(core) {
|
|
|
89
96
|
});
|
|
90
97
|
};
|
|
91
98
|
|
|
92
|
-
assess.dataflow.sources.bodyParser1Instrumentation = {
|
|
99
|
+
core.assess.dataflow.sources.bodyParser1Instrumentation = {
|
|
93
100
|
install() {
|
|
94
101
|
depHooks.resolve(
|
|
95
102
|
{ name: 'body-parser', version: '>=1.0.0' },
|
|
@@ -127,5 +134,5 @@ module.exports = function init(core) {
|
|
|
127
134
|
}
|
|
128
135
|
};
|
|
129
136
|
|
|
130
|
-
return assess.dataflow.sources.bodyParser1Instrumentation;
|
|
137
|
+
return core.assess.dataflow.sources.bodyParser1Instrumentation;
|
|
131
138
|
};
|
|
@@ -16,10 +16,11 @@
|
|
|
16
16
|
'use strict';
|
|
17
17
|
|
|
18
18
|
const { InputType } = require('@contrast/common');
|
|
19
|
+
const { InstrumentationType: { SOURCE } } = require('../../../constants');
|
|
19
20
|
const { patchType } = require('../common');
|
|
20
21
|
|
|
21
22
|
module.exports = function init(core) {
|
|
22
|
-
const { assess, depHooks, logger, patcher
|
|
23
|
+
const { assess, depHooks, logger, patcher } = core;
|
|
23
24
|
|
|
24
25
|
assess.dataflow.sources.cookieParser1Instrumentation = {
|
|
25
26
|
install() {
|
|
@@ -39,11 +40,11 @@ module.exports = function init(core) {
|
|
|
39
40
|
const { funcKey } = data;
|
|
40
41
|
const [req, , next] = data.args;
|
|
41
42
|
data.args[2] = function contrastNext(...args) {
|
|
42
|
-
const sourceContext =
|
|
43
|
+
const sourceContext = assess.getSourceContext(SOURCE);
|
|
43
44
|
|
|
44
45
|
if (!sourceContext) {
|
|
45
46
|
logger.error({ funcKey }, 'unable to handle source. Missing `sourceContext`');
|
|
46
|
-
return;
|
|
47
|
+
return next(...args);
|
|
47
48
|
}
|
|
48
49
|
|
|
49
50
|
if (sourceContext.parsedCookies) {
|
|
@@ -15,17 +15,19 @@
|
|
|
15
15
|
|
|
16
16
|
'use strict';
|
|
17
17
|
|
|
18
|
-
const { InputType } = require('@contrast/common');
|
|
18
|
+
const { InputType: { QUERYSTRING: inputType } } = require('@contrast/common');
|
|
19
19
|
const { patchType } = require('../common');
|
|
20
|
-
|
|
21
|
-
const inputType = InputType.QUERYSTRING;
|
|
20
|
+
const { InstrumentationType: { SOURCE } } = require('../../../constants');
|
|
22
21
|
|
|
23
22
|
module.exports = (core) => {
|
|
24
23
|
const {
|
|
25
24
|
depHooks,
|
|
26
25
|
patcher,
|
|
27
26
|
logger,
|
|
28
|
-
assess: {
|
|
27
|
+
assess: {
|
|
28
|
+
getSourceContext,
|
|
29
|
+
dataflow: { sources }
|
|
30
|
+
},
|
|
29
31
|
} = core;
|
|
30
32
|
|
|
31
33
|
// Patch `qs`
|
|
@@ -36,7 +38,7 @@ module.exports = (core) => {
|
|
|
36
38
|
name,
|
|
37
39
|
patchType,
|
|
38
40
|
post({ args, hooked, orig, result, funcKey }) {
|
|
39
|
-
const sourceContext =
|
|
41
|
+
const sourceContext = getSourceContext(SOURCE);
|
|
40
42
|
|
|
41
43
|
if (!sourceContext) {
|
|
42
44
|
logger.error({ inputType, funcKey }, 'unable to handle source. Missing `sourceContext`');
|
|
@@ -17,9 +17,15 @@
|
|
|
17
17
|
|
|
18
18
|
const { InputType } = require('@contrast/common');
|
|
19
19
|
const { patchType } = require('../common');
|
|
20
|
+
const { InstrumentationType: { SOURCE } } = require('../../../constants');
|
|
20
21
|
|
|
21
22
|
module.exports = (core) => {
|
|
22
|
-
const {
|
|
23
|
+
const {
|
|
24
|
+
assess: { getSourceContext },
|
|
25
|
+
depHooks,
|
|
26
|
+
patcher,
|
|
27
|
+
logger,
|
|
28
|
+
} = core;
|
|
23
29
|
|
|
24
30
|
core.assess.dataflow.sources.querystringInstrumentation = {
|
|
25
31
|
install() {
|
|
@@ -29,7 +35,7 @@ module.exports = (core) => {
|
|
|
29
35
|
name,
|
|
30
36
|
patchType,
|
|
31
37
|
post({ args, hooked, orig, result, funcKey }) {
|
|
32
|
-
const sourceContext =
|
|
38
|
+
const sourceContext = getSourceContext(SOURCE);
|
|
33
39
|
const inputType = InputType.QUERYSTRING;
|
|
34
40
|
|
|
35
41
|
if (!sourceContext) return;
|
|
@@ -14,6 +14,8 @@
|
|
|
14
14
|
*/
|
|
15
15
|
'use strict';
|
|
16
16
|
|
|
17
|
+
const { split } = require('@contrast/common');
|
|
18
|
+
|
|
17
19
|
//
|
|
18
20
|
// This module implements tag range manipulation functions. There are generally
|
|
19
21
|
// two types of functions:
|
|
@@ -461,8 +463,7 @@ function createAdjustedQueryTags(path, tags, value, argString) {
|
|
|
461
463
|
break;
|
|
462
464
|
}
|
|
463
465
|
}
|
|
464
|
-
|
|
465
|
-
return idx >= 0 ? createAppendTags([], tags, idx) : [...tags];
|
|
466
|
+
return idx >= 0 ? createAppendTags([], tags, idx) : { ...tags };
|
|
466
467
|
}
|
|
467
468
|
|
|
468
469
|
/**
|
|
@@ -484,8 +485,8 @@ function createAdjustedQueryTags(path, tags, value, argString) {
|
|
|
484
485
|
* - "?test=str","%3Ftest%3Dstr",{"UNTRUSTED":[0,8]} => {"UNTRUSTED":[3,6,10,12]}
|
|
485
486
|
*/
|
|
486
487
|
function createEscapeTagRanges(input, result, tags) {
|
|
487
|
-
const inputArr =
|
|
488
|
-
const escapedArr =
|
|
488
|
+
const inputArr = split(input, '');
|
|
489
|
+
const escapedArr = split(result, '');
|
|
489
490
|
const overlap = inputArr.filter((x) => {
|
|
490
491
|
if (escapedArr.includes(x)) {
|
|
491
492
|
return x;
|
|
@@ -39,7 +39,7 @@ module.exports = function(core) {
|
|
|
39
39
|
const ctx = sources.getStore()?.assess;
|
|
40
40
|
|
|
41
41
|
// policy will not exist if assess is altogether disabled for the active request e.g. url exclusion
|
|
42
|
-
if (!ctx
|
|
42
|
+
if (!ctx?.policy || instrumentation.isLocked()) return null;
|
|
43
43
|
|
|
44
44
|
switch (type) {
|
|
45
45
|
case InstrumentationType.PROPAGATOR: {
|
|
@@ -50,6 +50,7 @@ module.exports = function(core) {
|
|
|
50
50
|
if (ctx.sourceEventsCount > config.assess.max_context_source_events) return null;
|
|
51
51
|
break;
|
|
52
52
|
}
|
|
53
|
+
|
|
53
54
|
case InstrumentationType.RULE: {
|
|
54
55
|
const [ruleId] = rest;
|
|
55
56
|
if (!ruleId) break;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@contrast/assess",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.28.0",
|
|
4
4
|
"description": "Contrast service providing framework-agnostic Assess support",
|
|
5
5
|
"license": "SEE LICENSE IN LICENSE",
|
|
6
6
|
"author": "Contrast Security <nodejs@contrastsecurity.com> (https://www.contrastsecurity.com)",
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
"test": "../scripts/test.sh"
|
|
18
18
|
},
|
|
19
19
|
"dependencies": {
|
|
20
|
-
"@contrast/common": "1.
|
|
20
|
+
"@contrast/common": "1.21.0",
|
|
21
21
|
"@contrast/distringuish": "^4.4.0",
|
|
22
22
|
"@contrast/scopes": "1.4.1"
|
|
23
23
|
}
|