@contrast/assess 1.1.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/coverage/lcov-report/base.css +224 -0
- package/coverage/lcov-report/block-navigation.js +87 -0
- package/coverage/lcov-report/favicon.png +0 -0
- package/coverage/lcov-report/index.html +266 -0
- package/coverage/lcov-report/lib/dataflow/common.js.html +154 -0
- package/coverage/lcov-report/lib/dataflow/event-factory.js.html +598 -0
- package/coverage/lcov-report/lib/dataflow/index.html +191 -0
- package/coverage/lcov-report/lib/dataflow/index.js.html +190 -0
- package/coverage/lcov-report/lib/dataflow/propagation/common.js.html +145 -0
- package/coverage/lcov-report/lib/dataflow/propagation/index.html +131 -0
- package/coverage/lcov-report/lib/dataflow/propagation/index.js.html +190 -0
- package/coverage/lcov-report/lib/dataflow/propagation/install/contrast-methods/index.html +131 -0
- package/coverage/lcov-report/lib/dataflow/propagation/install/contrast-methods/index.js.html +184 -0
- package/coverage/lcov-report/lib/dataflow/propagation/install/contrast-methods/plus.js.html +397 -0
- package/coverage/lcov-report/lib/dataflow/propagation/install/string/concat.js.html +478 -0
- package/coverage/lcov-report/lib/dataflow/propagation/install/string/index.html +176 -0
- package/coverage/lcov-report/lib/dataflow/propagation/install/string/index.js.html +202 -0
- package/coverage/lcov-report/lib/dataflow/propagation/install/string/replace.js.html +496 -0
- package/coverage/lcov-report/lib/dataflow/propagation/install/string/substring.js.html +415 -0
- package/coverage/lcov-report/lib/dataflow/propagation/install/string/trim.js.html +403 -0
- package/coverage/lcov-report/lib/dataflow/signatures.js.html +5923 -0
- package/coverage/lcov-report/lib/dataflow/sinks/common.js.html +145 -0
- package/coverage/lcov-report/lib/dataflow/sinks/index.html +131 -0
- package/coverage/lcov-report/lib/dataflow/sinks/index.js.html +211 -0
- package/coverage/lcov-report/lib/dataflow/sinks/install/fastify/index.html +146 -0
- package/coverage/lcov-report/lib/dataflow/sinks/install/fastify/index.js.html +172 -0
- package/coverage/lcov-report/lib/dataflow/sinks/install/fastify/unvalidated-redirect.js.html +319 -0
- package/coverage/lcov-report/lib/dataflow/sinks/install/fastify/xss.js.html +721 -0
- package/coverage/lcov-report/lib/dataflow/sinks/install/http.js.html +340 -0
- package/coverage/lcov-report/lib/dataflow/sinks/install/index.html +116 -0
- package/coverage/lcov-report/lib/dataflow/sources/common.js.html +145 -0
- package/coverage/lcov-report/lib/dataflow/sources/index.html +131 -0
- package/coverage/lcov-report/lib/dataflow/sources/index.js.html +226 -0
- package/coverage/lcov-report/lib/dataflow/sources/install/fastify.js.html +379 -0
- package/coverage/lcov-report/lib/dataflow/sources/install/http.js.html +502 -0
- package/coverage/lcov-report/lib/dataflow/sources/install/index.html +146 -0
- package/coverage/lcov-report/lib/dataflow/sources/install/qs.js.html +322 -0
- package/coverage/lcov-report/lib/dataflow/tag-utils.js.html +418 -0
- package/coverage/lcov-report/lib/dataflow/tracker.js.html +466 -0
- package/coverage/lcov-report/lib/dataflow/utils/index.html +116 -0
- package/coverage/lcov-report/lib/dataflow/utils/is-vulnerable.js.html +424 -0
- package/coverage/lcov-report/lib/index.html +116 -0
- package/coverage/lcov-report/lib/index.js.html +193 -0
- package/coverage/lcov-report/prettify.css +1 -0
- package/coverage/lcov-report/prettify.js +2 -0
- package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
- package/coverage/lcov-report/sorter.js +196 -0
- package/coverage/lcov.info +4856 -0
- package/coverage/tmp/coverage-9548-1675168551025-0.json +1 -0
- package/coverage/tmp/coverage-9551-1675168550963-0.json +1 -0
- package/coverage/tmp/coverage-9552-1675168550969-0.json +1 -0
- package/coverage/tmp/coverage-9553-1675168550970-0.json +1 -0
- package/coverage/tmp/coverage-9554-1675168550962-0.json +1 -0
- package/coverage/tmp/coverage-9555-1675168550965-0.json +1 -0
- package/coverage/tmp/coverage-9556-1675168550964-0.json +1 -0
- package/coverage/tmp/coverage-9557-1675168550969-0.json +1 -0
- package/coverage/tmp/coverage-9558-1675168550964-0.json +1 -0
- package/coverage/tmp/coverage-9559-1675168550971-0.json +1 -0
- package/lib/dataflow/event-factory.js +256 -0
- package/lib/dataflow/index.js +35 -0
- package/lib/dataflow/propagation/common.js +26 -0
- package/lib/dataflow/propagation/index.js +50 -0
- package/lib/dataflow/propagation/install/array-prototype-join.js +125 -0
- package/lib/dataflow/propagation/install/contrast-methods/add.js +104 -0
- package/lib/dataflow/propagation/install/contrast-methods/index.js +34 -0
- package/lib/dataflow/propagation/install/contrast-methods/tag.js +102 -0
- package/lib/dataflow/propagation/install/decode-uri-component.js +88 -0
- package/lib/dataflow/propagation/install/ejs/escape-xml.js +89 -0
- package/lib/dataflow/propagation/install/ejs/index.js +30 -0
- package/lib/dataflow/propagation/install/encode-uri-component.js +87 -0
- package/lib/dataflow/propagation/install/escape-html.js +89 -0
- package/lib/dataflow/propagation/install/escape.js +89 -0
- package/lib/dataflow/propagation/install/handlebars-utils-escape-expression.js +89 -0
- package/lib/dataflow/propagation/install/mysql-connection-escape.js +111 -0
- package/lib/dataflow/propagation/install/pug/index.js +64 -0
- package/lib/dataflow/propagation/install/pug-runtime-escape.js +89 -0
- package/lib/dataflow/propagation/install/sql-template-strings.js +103 -0
- package/lib/dataflow/propagation/install/string/concat.js +108 -0
- package/lib/dataflow/propagation/install/string/format-methods.js +83 -0
- package/lib/dataflow/propagation/install/string/html-methods.js +163 -0
- package/lib/dataflow/propagation/install/string/index.js +38 -0
- package/lib/dataflow/propagation/install/string/replace.js +197 -0
- package/lib/dataflow/propagation/install/string/substring.js +117 -0
- package/lib/dataflow/propagation/install/string/trim.js +115 -0
- package/lib/dataflow/propagation/install/unescape.js +90 -0
- package/lib/dataflow/propagation/install/url/domain-parsers.js +89 -0
- package/lib/dataflow/propagation/install/url/index.js +33 -0
- package/lib/dataflow/propagation/install/validator/hooks.js +172 -0
- package/lib/dataflow/propagation/install/validator/index.js +28 -0
- package/lib/dataflow/propagation/install/validator/methods.js +82 -0
- package/lib/dataflow/signatures.js +2022 -0
- package/lib/dataflow/sinks/common.js +20 -0
- package/lib/dataflow/sinks/index.js +44 -0
- package/lib/dataflow/sinks/install/fastify/index.js +30 -0
- package/lib/dataflow/sinks/install/fastify/unvalidated-redirect.js +107 -0
- package/lib/dataflow/sinks/install/http.js +119 -0
- package/lib/dataflow/sinks/install/postgres/index.js +129 -0
- package/lib/dataflow/sources/common.js +20 -0
- package/lib/dataflow/sources/handler.js +114 -0
- package/lib/dataflow/sources/index.js +35 -0
- package/lib/dataflow/sources/install/fastify/cookie.js +61 -0
- package/lib/dataflow/sources/install/fastify/fastify.js +89 -0
- package/lib/dataflow/sources/install/fastify/index.js +31 -0
- package/lib/dataflow/sources/install/http.js +181 -0
- package/lib/dataflow/sources/install/qs.js +88 -0
- package/lib/dataflow/tag-utils.js +122 -0
- package/lib/dataflow/tracker.js +127 -0
- package/lib/dataflow/utils/is-safe-content-type.js +30 -0
- package/lib/dataflow/utils/is-vulnerable.js +113 -0
- package/lib/index.js +36 -0
- package/package.json +21 -0
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright: 2022 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
|
+
'use strict';
|
|
17
|
+
|
|
18
|
+
const {
|
|
19
|
+
createAppendTags
|
|
20
|
+
} = require('../../../tag-utils');
|
|
21
|
+
const { patchType } = require('../../common');
|
|
22
|
+
const htmlTagsLengths = {
|
|
23
|
+
anchor: 11,
|
|
24
|
+
big: 5,
|
|
25
|
+
blink: 7,
|
|
26
|
+
italics: 3,
|
|
27
|
+
small: 7,
|
|
28
|
+
strike: 8,
|
|
29
|
+
sub: 5,
|
|
30
|
+
fixed: 4
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
module.exports = function(core) {
|
|
34
|
+
const {
|
|
35
|
+
scopes: { sources, instrumentation },
|
|
36
|
+
patcher,
|
|
37
|
+
assess: {
|
|
38
|
+
dataflow: { tracker, eventFactory: { createPropagationEvent } }
|
|
39
|
+
}
|
|
40
|
+
} = core;
|
|
41
|
+
function adjustTags(method, objTags, argLength, argTags = null) {
|
|
42
|
+
let offset;
|
|
43
|
+
if (method === 'anchor') {
|
|
44
|
+
let newArgTags = {};
|
|
45
|
+
offset = htmlTagsLengths[method] + argLength;
|
|
46
|
+
if (argTags) {
|
|
47
|
+
newArgTags = createAppendTags({}, argTags, 9);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return createAppendTags(newArgTags, objTags, offset);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
offset = htmlTagsLengths[method];
|
|
54
|
+
|
|
55
|
+
return createAppendTags({}, objTags, offset);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return core.assess.dataflow.propagation.stringInstrumentation.htmlMethods = {
|
|
59
|
+
install() {
|
|
60
|
+
patcher.patch(String.prototype, 'anchor', {
|
|
61
|
+
name: 'String.prototype.anchor',
|
|
62
|
+
patchType,
|
|
63
|
+
post(data) {
|
|
64
|
+
const { args, obj, result, hooked, orig } = data;
|
|
65
|
+
if (!result || !sources.getStore()?.assess || instrumentation.isLocked()) return;
|
|
66
|
+
|
|
67
|
+
const objInfo = tracker.getData(obj);
|
|
68
|
+
const history = objInfo ? new Set([objInfo]) : new Set();
|
|
69
|
+
|
|
70
|
+
const arg = args[0]?.toString();
|
|
71
|
+
const argInfo = arg && tracker.getData(arg);
|
|
72
|
+
if (argInfo) {
|
|
73
|
+
history.add(argInfo);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
if (history.size) {
|
|
77
|
+
const event = createPropagationEvent({
|
|
78
|
+
name: 'String.prototype.anchor',
|
|
79
|
+
object: {
|
|
80
|
+
value: objInfo?.value || String(obj),
|
|
81
|
+
isTracked: !!objInfo
|
|
82
|
+
},
|
|
83
|
+
result: {
|
|
84
|
+
value: result,
|
|
85
|
+
isTracked: true
|
|
86
|
+
},
|
|
87
|
+
args: [
|
|
88
|
+
{ value: arg, isTracked: !!argInfo }
|
|
89
|
+
],
|
|
90
|
+
tags: adjustTags('anchor', objInfo?.tags, `${arg}`.length, argInfo?.tags),
|
|
91
|
+
history: Array.from(history),
|
|
92
|
+
source: objInfo ? (history.size > 1 ? 'A' : 'O') : 'P',
|
|
93
|
+
target: 'R',
|
|
94
|
+
stacktraceOpts: {
|
|
95
|
+
constructorOpt: hooked,
|
|
96
|
+
prependFrames: [orig]
|
|
97
|
+
},
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
if (!event) return;
|
|
101
|
+
|
|
102
|
+
const { extern } = tracker.track(result, event);
|
|
103
|
+
|
|
104
|
+
if (extern) {
|
|
105
|
+
data.result = extern;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
},
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
['big', 'blink', 'italics', 'small', 'strike', 'sub', 'fixed'].forEach((method) => {
|
|
112
|
+
patcher.patch(String.prototype, method, {
|
|
113
|
+
name: `String.prototype.${method}`,
|
|
114
|
+
patchType,
|
|
115
|
+
post(data) {
|
|
116
|
+
const { obj, result, hooked, orig } = data;
|
|
117
|
+
if (!result || !sources.getStore()?.assess || instrumentation.isLocked()) return;
|
|
118
|
+
|
|
119
|
+
const objInfo = tracker.getData(obj);
|
|
120
|
+
|
|
121
|
+
if (!objInfo) return;
|
|
122
|
+
|
|
123
|
+
const history = [objInfo];
|
|
124
|
+
|
|
125
|
+
const event = createPropagationEvent({
|
|
126
|
+
name: `String.prototype.${method}`,
|
|
127
|
+
object: {
|
|
128
|
+
value: objInfo.value,
|
|
129
|
+
isTracked: true
|
|
130
|
+
},
|
|
131
|
+
result: {
|
|
132
|
+
value: result,
|
|
133
|
+
isTracked: true
|
|
134
|
+
},
|
|
135
|
+
args: [],
|
|
136
|
+
tags: adjustTags(method, objInfo.tags),
|
|
137
|
+
history,
|
|
138
|
+
source: 'O',
|
|
139
|
+
target: 'R',
|
|
140
|
+
stacktraceOpts: {
|
|
141
|
+
constructorOpt: hooked,
|
|
142
|
+
prependFrames: [orig]
|
|
143
|
+
},
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
if (!event) return;
|
|
147
|
+
|
|
148
|
+
const { extern } = tracker.track(result, event);
|
|
149
|
+
|
|
150
|
+
if (extern) {
|
|
151
|
+
data.result = extern;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
});
|
|
155
|
+
});
|
|
156
|
+
},
|
|
157
|
+
uninstall() {
|
|
158
|
+
['anchor', 'big', 'blink', 'italics', 'small', 'strike', 'sub', 'fixed'].forEach((method) => {
|
|
159
|
+
String.prototype[method] = patcher.unwrap(String.prototype[method]);
|
|
160
|
+
});
|
|
161
|
+
},
|
|
162
|
+
};
|
|
163
|
+
};
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright: 2022 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
|
+
'use strict';
|
|
17
|
+
|
|
18
|
+
const { callChildComponentMethodsSync } = require('@contrast/common');
|
|
19
|
+
|
|
20
|
+
module.exports = function(core) {
|
|
21
|
+
const stringInstrumentation = core.assess.dataflow.propagation.stringInstrumentation = {
|
|
22
|
+
install() {
|
|
23
|
+
callChildComponentMethodsSync(stringInstrumentation, 'install');
|
|
24
|
+
},
|
|
25
|
+
uninstall() {
|
|
26
|
+
callChildComponentMethodsSync(stringInstrumentation, 'uninstall');
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
require('./concat')(core);
|
|
31
|
+
require('./replace')(core);
|
|
32
|
+
require('./substring')(core);
|
|
33
|
+
require('./trim')(core);
|
|
34
|
+
require('./html-methods')(core);
|
|
35
|
+
require('./format-methods')(core);
|
|
36
|
+
|
|
37
|
+
return stringInstrumentation;
|
|
38
|
+
};
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright: 2022 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
|
+
'use strict';
|
|
17
|
+
|
|
18
|
+
const { patchType } = require('../../common');
|
|
19
|
+
const { createSubsetTags, createAppendTags } = require('../../../tag-utils');
|
|
20
|
+
|
|
21
|
+
module.exports = function(core) {
|
|
22
|
+
const {
|
|
23
|
+
patcher,
|
|
24
|
+
assess: {
|
|
25
|
+
dataflow: { tracker, eventFactory: { createPropagationEvent } }
|
|
26
|
+
},
|
|
27
|
+
scopes: { sources, instrumentation }
|
|
28
|
+
} = core;
|
|
29
|
+
|
|
30
|
+
function parseArgs(args) {
|
|
31
|
+
// [match, p1, p2, ..., matchIdx, str, ?groups]
|
|
32
|
+
const match = args[0];
|
|
33
|
+
const len = args.length;
|
|
34
|
+
const lastEl = args[len - 1];
|
|
35
|
+
const hasNamedGroup = typeof lastEl === 'object';
|
|
36
|
+
const captureGroups = args.slice(1, hasNamedGroup ? -3 : -2);
|
|
37
|
+
const matchIdx = args[hasNamedGroup ? len - 3 : len - 2];
|
|
38
|
+
const str = hasNamedGroup ? args[len - 2] : lastEl;
|
|
39
|
+
const namedGroups = hasNamedGroup ? lastEl : null;
|
|
40
|
+
return { match, captureGroups, matchIdx, str, namedGroups };
|
|
41
|
+
}
|
|
42
|
+
function replaceSpecialCharacters(replacement, { captureGroups, match, str, namedGroups }) {
|
|
43
|
+
const origReplace = patcher.unwrap(String.prototype.replace);
|
|
44
|
+
let ret = replacement;
|
|
45
|
+
[
|
|
46
|
+
{
|
|
47
|
+
regex: /\$\$/g,
|
|
48
|
+
replace: '$'
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
regex: /\$&/g,
|
|
52
|
+
replace: match
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
regex: /\$`/g,
|
|
56
|
+
replace: str.substring(0, str.indexOf(match))
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
regex: /\$'/g,
|
|
60
|
+
replace: str.substring(str.indexOf(match) + match.length, str.length)
|
|
61
|
+
}
|
|
62
|
+
].forEach(({ regex, replace }) => {
|
|
63
|
+
if (ret && ret.match(regex)) {
|
|
64
|
+
// If the match string is tracked, we can actually use the patched replace
|
|
65
|
+
// to keep track of its tag ranges
|
|
66
|
+
if (tracker.getData(replace)) {
|
|
67
|
+
ret = ret.replace(regex, replace);
|
|
68
|
+
} else {
|
|
69
|
+
ret = origReplace.call(ret, regex, replace);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
const numberedGroupMatches = replacement.match(/\$[1-9][0-9]|\$[1-9]/g);
|
|
75
|
+
if (numberedGroupMatches) {
|
|
76
|
+
numberedGroupMatches.forEach((numberedGroup) => {
|
|
77
|
+
const group = Number(numberedGroup.substring(1));
|
|
78
|
+
ret = origReplace.call(ret, numberedGroup, captureGroups[group - 1]);
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if (namedGroups) {
|
|
83
|
+
for (const name in namedGroups) {
|
|
84
|
+
ret = origReplace.call(ret, `$${name}`, namedGroups[name]);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
return ret;
|
|
89
|
+
}
|
|
90
|
+
function getReplacementInfo(data, replacerArgs, parsedArgs) {
|
|
91
|
+
let replacement = data._replacementType === 'function' ?
|
|
92
|
+
data._replacement.call(global, ...replacerArgs) :
|
|
93
|
+
data._replacement;
|
|
94
|
+
|
|
95
|
+
replacement = replaceSpecialCharacters(String(replacement), parsedArgs);
|
|
96
|
+
|
|
97
|
+
data._replacementInfo = tracker.getData(replacement);
|
|
98
|
+
if (data._replacement) {
|
|
99
|
+
data._history.add(data._replacementInfo);
|
|
100
|
+
}
|
|
101
|
+
return { replacement, replacementInfo: data._replacementInfo };
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function getReplacer(data) {
|
|
105
|
+
return function replacer(...args) {
|
|
106
|
+
const parsedArgs = parseArgs(args);
|
|
107
|
+
const { match, matchIdx, str } = parsedArgs;
|
|
108
|
+
|
|
109
|
+
const { _accumOffset, _accumTags } = data;
|
|
110
|
+
const { replacement, replacementInfo } = getReplacementInfo(data, args, parsedArgs);
|
|
111
|
+
|
|
112
|
+
const preTags = createSubsetTags(_accumTags, 0, _accumOffset + matchIdx - 1);
|
|
113
|
+
const postTags = createSubsetTags(_accumTags, _accumOffset + matchIdx + match.length, str.length - matchIdx - match.length);
|
|
114
|
+
data._accumOffset += (replacement.length - match.length);
|
|
115
|
+
if (preTags || postTags || replacementInfo) {
|
|
116
|
+
data._accumTags = createAppendTags(
|
|
117
|
+
createAppendTags(preTags, replacementInfo?.tags, _accumOffset + matchIdx),
|
|
118
|
+
postTags,
|
|
119
|
+
data._accumOffset + matchIdx + match.length
|
|
120
|
+
);
|
|
121
|
+
} else {
|
|
122
|
+
data._accumTags = {};
|
|
123
|
+
}
|
|
124
|
+
return replacement;
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
return core.assess.dataflow.propagation.stringInstrumentation.replace = {
|
|
129
|
+
install() {
|
|
130
|
+
patcher.patch(String.prototype, 'replace', {
|
|
131
|
+
name: 'String.prototype.replace',
|
|
132
|
+
patchType,
|
|
133
|
+
pre(data) {
|
|
134
|
+
if (!sources.getStore()?.assess || instrumentation.isLocked()) return;
|
|
135
|
+
|
|
136
|
+
// setup state
|
|
137
|
+
data._objInfo = tracker.getData(data.obj);
|
|
138
|
+
data._replacement = data.args[1];
|
|
139
|
+
data._replacementType = typeof data._replacement;
|
|
140
|
+
data._history = data._objInfo ? new Set([data._objInfo]) : new Set();
|
|
141
|
+
data._accumTags = data._objInfo?.tags || {};
|
|
142
|
+
data._accumOffset = 0;
|
|
143
|
+
|
|
144
|
+
data.args[1] = getReplacer(data);
|
|
145
|
+
},
|
|
146
|
+
post(data) {
|
|
147
|
+
if (
|
|
148
|
+
!sources.getStore()?.assess ||
|
|
149
|
+
instrumentation.isLocked() ||
|
|
150
|
+
!data.result ||
|
|
151
|
+
!data._accumTags?.untrusted
|
|
152
|
+
) return;
|
|
153
|
+
|
|
154
|
+
const { _replacementInfo, obj, args, result, hooked, orig } = data;
|
|
155
|
+
|
|
156
|
+
const event = createPropagationEvent({
|
|
157
|
+
name: 'String.prototype.replace',
|
|
158
|
+
history: Array.from(data._history),
|
|
159
|
+
object: {
|
|
160
|
+
value: obj,
|
|
161
|
+
isTracked: !!data._objInfo
|
|
162
|
+
},
|
|
163
|
+
args: [{
|
|
164
|
+
value: args[0].toString(),
|
|
165
|
+
isTracked: !!tracker.getData(args[0])
|
|
166
|
+
},
|
|
167
|
+
{
|
|
168
|
+
value: data._replacement,
|
|
169
|
+
isTracked: !!_replacementInfo
|
|
170
|
+
}],
|
|
171
|
+
result: {
|
|
172
|
+
value: result,
|
|
173
|
+
isTracked: true
|
|
174
|
+
},
|
|
175
|
+
tags: data._accumTags,
|
|
176
|
+
stacktraceOpts: {
|
|
177
|
+
constructorOpt: hooked,
|
|
178
|
+
prependFrames: [orig]
|
|
179
|
+
},
|
|
180
|
+
source: data._objInfo ? (data._history.size > 1 ? 'A' : 'O') : 'P',
|
|
181
|
+
target: 'R',
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
if (event) {
|
|
185
|
+
const { extern } = tracker.track(result, event);
|
|
186
|
+
if (extern) {
|
|
187
|
+
data.result = extern;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
});
|
|
192
|
+
},
|
|
193
|
+
uninstall() {
|
|
194
|
+
String.prototype.replace = patcher.unwrap(String.prototype.replace);
|
|
195
|
+
}
|
|
196
|
+
};
|
|
197
|
+
};
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright: 2022 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
|
+
'use strict';
|
|
17
|
+
|
|
18
|
+
const { createSubsetTags } = require('../../../tag-utils');
|
|
19
|
+
const { patchType } = require('../../common');
|
|
20
|
+
|
|
21
|
+
module.exports = function(core) {
|
|
22
|
+
const {
|
|
23
|
+
scopes: { sources, instrumentation },
|
|
24
|
+
patcher,
|
|
25
|
+
assess: {
|
|
26
|
+
dataflow: { tracker, eventFactory: { createPropagationEvent } }
|
|
27
|
+
}
|
|
28
|
+
} = core;
|
|
29
|
+
|
|
30
|
+
function calculateSubsetRangeForSubstr({ args, result }) {
|
|
31
|
+
const startIdx = args[0] || 0;
|
|
32
|
+
const subsetLen = args[1] === undefined ? result.length - startIdx - 1 : args[1] - 1;
|
|
33
|
+
|
|
34
|
+
return {
|
|
35
|
+
startIdx,
|
|
36
|
+
subsetLen
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function calculateSubsetRangeForSubstring({ obj, args }) {
|
|
41
|
+
const hasSingleArg = args[1] === undefined;
|
|
42
|
+
const startIdx = hasSingleArg ? args[0] : Math.min(...args);
|
|
43
|
+
const subsetLen = (hasSingleArg ? obj.length - args[0] : Math.abs(args[1] - args[0])) - 1;
|
|
44
|
+
|
|
45
|
+
return {
|
|
46
|
+
startIdx,
|
|
47
|
+
subsetLen
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const calculateSubsetRange = {
|
|
52
|
+
substr: calculateSubsetRangeForSubstr,
|
|
53
|
+
substring: calculateSubsetRangeForSubstring
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
return core.assess.dataflow.propagation.stringInstrumentation.substring = {
|
|
57
|
+
install() {
|
|
58
|
+
['substr', 'substring'].forEach((method) => {
|
|
59
|
+
patcher.patch(String.prototype, method, {
|
|
60
|
+
name: `String.prototype.${method}`,
|
|
61
|
+
patchType,
|
|
62
|
+
post(data) {
|
|
63
|
+
const { obj, args, result, name, hooked, orig } = data;
|
|
64
|
+
if (!result || !sources.getStore() || instrumentation.isLocked()) return;
|
|
65
|
+
|
|
66
|
+
const objInfo = tracker.getData(obj);
|
|
67
|
+
if (!objInfo) return;
|
|
68
|
+
|
|
69
|
+
const rInfo = tracker.getData(result);
|
|
70
|
+
|
|
71
|
+
if (rInfo) {
|
|
72
|
+
// this may happen w/ trackedStr.substring(0) => trackedStr
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const { startIdx, subsetLen } = calculateSubsetRange[method](data);
|
|
77
|
+
const tags = createSubsetTags(objInfo.tags, startIdx, subsetLen);
|
|
78
|
+
|
|
79
|
+
if (!tags) return;
|
|
80
|
+
|
|
81
|
+
const event = createPropagationEvent({
|
|
82
|
+
name,
|
|
83
|
+
history: [objInfo],
|
|
84
|
+
object: {
|
|
85
|
+
value: obj,
|
|
86
|
+
isTracked: true,
|
|
87
|
+
},
|
|
88
|
+
args: args.map((arg) => ({
|
|
89
|
+
value: arg.toString(),
|
|
90
|
+
isTracked: false
|
|
91
|
+
})),
|
|
92
|
+
result: {
|
|
93
|
+
value: result,
|
|
94
|
+
isTracked: true
|
|
95
|
+
},
|
|
96
|
+
tags,
|
|
97
|
+
stacktraceOpts: {
|
|
98
|
+
constructorOpt: hooked,
|
|
99
|
+
prependFrames: [orig]
|
|
100
|
+
}
|
|
101
|
+
});
|
|
102
|
+
const { extern } = tracker.track(result, event);
|
|
103
|
+
|
|
104
|
+
if (extern) {
|
|
105
|
+
data.result = extern;
|
|
106
|
+
}
|
|
107
|
+
},
|
|
108
|
+
});
|
|
109
|
+
});
|
|
110
|
+
},
|
|
111
|
+
uninstall() {
|
|
112
|
+
String.prototype.substr = patcher.unwrap(String.prototype.substr);
|
|
113
|
+
String.prototype.substring = patcher.unwrap(String.prototype.substring);
|
|
114
|
+
},
|
|
115
|
+
};
|
|
116
|
+
};
|
|
117
|
+
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright: 2022 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
|
+
'use strict';
|
|
17
|
+
|
|
18
|
+
const {
|
|
19
|
+
createSubsetTags,
|
|
20
|
+
} = require('../../../tag-utils');
|
|
21
|
+
const { patchType } = require('../../common');
|
|
22
|
+
|
|
23
|
+
module.exports = function(core) {
|
|
24
|
+
const {
|
|
25
|
+
scopes: { sources, instrumentation },
|
|
26
|
+
patcher,
|
|
27
|
+
assess: {
|
|
28
|
+
dataflow: { tracker, eventFactory: { createPropagationEvent } }
|
|
29
|
+
}
|
|
30
|
+
} = core;
|
|
31
|
+
|
|
32
|
+
function createPostHook(methodName, presetStart) {
|
|
33
|
+
return function(data) {
|
|
34
|
+
const { obj, result, hooked, orig } = data;
|
|
35
|
+
|
|
36
|
+
if (!result?.length || !sources.getStore()?.assess || instrumentation.isLocked()) {
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
const rInfo = tracker.getData(result);
|
|
40
|
+
if (rInfo) {
|
|
41
|
+
// this may happen w/ 'trackedStr'.trim() => 'trackedStr'
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const objInfo = tracker.getData(obj);
|
|
46
|
+
|
|
47
|
+
if (!objInfo) {
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const history = [objInfo];
|
|
52
|
+
const start = presetStart || obj.indexOf(result);
|
|
53
|
+
const newTags = {};
|
|
54
|
+
const objTags = objInfo.tags || {};
|
|
55
|
+
Object.assign(newTags, createSubsetTags(objTags, start, result.length - 1));
|
|
56
|
+
|
|
57
|
+
if (!newTags.untrusted) {
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const event = createPropagationEvent({
|
|
62
|
+
name: `String.prototype.${methodName}`,
|
|
63
|
+
history,
|
|
64
|
+
object: {
|
|
65
|
+
value: obj,
|
|
66
|
+
isTracked: true
|
|
67
|
+
},
|
|
68
|
+
result: {
|
|
69
|
+
value: result,
|
|
70
|
+
isTracked: true
|
|
71
|
+
},
|
|
72
|
+
tags: newTags,
|
|
73
|
+
stacktraceOpts: {
|
|
74
|
+
constructorOpt: hooked,
|
|
75
|
+
prependFrames: [orig]
|
|
76
|
+
},
|
|
77
|
+
source: 'O',
|
|
78
|
+
target: 'R'
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
const { extern } = tracker.track(result, event);
|
|
82
|
+
|
|
83
|
+
if (extern) {
|
|
84
|
+
data.result = extern;
|
|
85
|
+
}
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
return core.assess.dataflow.propagation.stringInstrumentation.trim = {
|
|
90
|
+
install() {
|
|
91
|
+
patcher.patch(String.prototype, 'trim', {
|
|
92
|
+
name: 'String.prototype.trim',
|
|
93
|
+
patchType,
|
|
94
|
+
post: createPostHook('trim'),
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
patcher.patch(String.prototype, 'trimStart', {
|
|
98
|
+
name: 'String.prototype.trimStart',
|
|
99
|
+
patchType,
|
|
100
|
+
post: createPostHook('trimStart')
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
patcher.patch(String.prototype, 'trimEnd', {
|
|
104
|
+
name: 'String.prototype.trimEnd',
|
|
105
|
+
patchType,
|
|
106
|
+
post: createPostHook('trimEnd', 0),
|
|
107
|
+
});
|
|
108
|
+
},
|
|
109
|
+
uninstall() {
|
|
110
|
+
String.prototype.trim = patcher.unwrap(String.prototype.trim);
|
|
111
|
+
String.prototype.trimStart = patcher.unwrap(String.prototype.trimStart);
|
|
112
|
+
String.prototype.trimEnd = patcher.unwrap(String.prototype.trimEnd);
|
|
113
|
+
},
|
|
114
|
+
};
|
|
115
|
+
};
|