@contrast/assess 1.9.0 → 1.10.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.
Files changed (30) hide show
  1. package/lib/dataflow/propagation/index.js +1 -0
  2. package/lib/dataflow/propagation/install/path/basename.js +124 -0
  3. package/lib/dataflow/propagation/install/path/common.js +176 -0
  4. package/lib/dataflow/propagation/install/path/index.js +32 -0
  5. package/lib/dataflow/propagation/install/path/join-and-resolve.js +141 -0
  6. package/lib/dataflow/propagation/install/path/normalize.js +123 -0
  7. package/lib/dataflow/propagation/install/querystring/parse.js +1 -1
  8. package/lib/dataflow/propagation/install/string/match.js +2 -2
  9. package/lib/dataflow/propagation/install/string/replace.js +1 -1
  10. package/lib/dataflow/propagation/install/string/slice.js +1 -1
  11. package/lib/dataflow/propagation/install/string/split.js +1 -1
  12. package/lib/dataflow/propagation/install/string/substring.js +2 -2
  13. package/lib/dataflow/propagation/install/string/trim.js +1 -1
  14. package/lib/dataflow/propagation/install/url/index.js +1 -0
  15. package/lib/dataflow/propagation/install/url/url.js +228 -0
  16. package/lib/dataflow/sinks/index.js +8 -4
  17. package/lib/dataflow/sinks/install/eval.js +138 -0
  18. package/lib/dataflow/sinks/install/express/unvalidated-redirect.js +1 -1
  19. package/lib/dataflow/sinks/install/fastify/unvalidated-redirect.js +2 -1
  20. package/lib/dataflow/sinks/install/fs.js +3 -3
  21. package/lib/dataflow/sinks/install/function.js +160 -0
  22. package/lib/dataflow/sinks/install/http/index.js +31 -0
  23. package/lib/dataflow/sinks/install/http/request.js +152 -0
  24. package/lib/dataflow/sinks/install/{http.js → http/server-response.js} +2 -2
  25. package/lib/dataflow/sinks/install/koa/unvalidated-redirect.js +1 -1
  26. package/lib/dataflow/sinks/install/mongodb.js +4 -23
  27. package/lib/dataflow/sinks/install/mssql.js +44 -31
  28. package/lib/dataflow/sinks/install/vm.js +276 -0
  29. package/lib/dataflow/tag-utils.js +70 -1
  30. package/package.json +2 -2
@@ -0,0 +1,152 @@
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
+ inspect,
20
+ isString,
21
+ DataflowTag: {
22
+ UNTRUSTED,
23
+ CUSTOM_ENCODED_SSRF,
24
+ CUSTOM_ENCODED,
25
+ CUSTOM_VALIDATED_SSRF,
26
+ CUSTOM_VALIDATED,
27
+ LIMITED_CHARS
28
+ }
29
+ } = require('@contrast/common');
30
+ const Url = require('url');
31
+ const trustedLibs = [/^(?!.*(newrelic)).*http.*$/];
32
+ const { patchType } = require('../../common');
33
+ const { createAppendTags } = require('../../../tag-utils');
34
+
35
+ module.exports = function(core) {
36
+ const {
37
+ depHooks,
38
+ patcher,
39
+ scopes: { sources },
40
+ assess: {
41
+ dataflow: {
42
+ tracker,
43
+ sinks: {
44
+ isVulnerable,
45
+ reportFindings
46
+ },
47
+ eventFactory: { createSinkEvent },
48
+ },
49
+ },
50
+ } = core;
51
+ const http = core.assess.dataflow.sinks.http.request = {};
52
+
53
+ const safeTags = [
54
+ CUSTOM_ENCODED_SSRF,
55
+ CUSTOM_ENCODED,
56
+ CUSTOM_VALIDATED_SSRF,
57
+ CUSTOM_VALIDATED,
58
+ LIMITED_CHARS
59
+ ];
60
+
61
+ function getValueFromReq(value, key) {
62
+ if (isString(value)) {
63
+ const url = new Url.URL(value);
64
+ if (isString(url[key])) {
65
+ return url[key];
66
+ }
67
+ }
68
+
69
+ if (isString(value[key])) {
70
+ return value[key];
71
+ }
72
+ }
73
+
74
+ function containsTrustedLib(stack) {
75
+ for (const { file } of stack) {
76
+ for (const trusted of trustedLibs) {
77
+ if (trusted.exec(file)) {
78
+ return true;
79
+ }
80
+ }
81
+ }
82
+
83
+ return false;
84
+ }
85
+
86
+ http.install = function() {
87
+ ['http', 'https'].forEach((moduleName) => {
88
+ depHooks.resolve({ name: moduleName }, (module) => {
89
+ const name = `${moduleName}.request`;
90
+ const methodName = 'request';
91
+ patcher.patch(module, methodName, {
92
+ name,
93
+ patchType,
94
+ pre(data) {
95
+ const sourceContext = sources.getStore()?.assess;
96
+ if (!sourceContext) return;
97
+
98
+ const [req] = data.args;
99
+ if (!req) return;
100
+
101
+ ['host', 'hostname', 'localAddress', 'protocol'].forEach((key) => {
102
+ const value = getValueFromReq(req, key);
103
+ if (!value) return;
104
+
105
+ const strInfo = tracker.getData(value);
106
+ if (!strInfo) return;
107
+
108
+ if (containsTrustedLib(strInfo.stack)) return;
109
+
110
+ const arg0 = isString(req) ? req : inspect(req);
111
+ const idx = arg0.indexOf(value);
112
+ const urlTags = createAppendTags({}, strInfo.tags, idx);
113
+
114
+ if (urlTags && isVulnerable(UNTRUSTED, safeTags, urlTags)) {
115
+ const event = createSinkEvent({
116
+ name,
117
+ moduleName,
118
+ methodName: 'request',
119
+ context: `${moduleName}.request(${arg0})`,
120
+ history: [strInfo],
121
+ object: {
122
+ tracked: false,
123
+ value: moduleName
124
+ },
125
+ args: [{
126
+ value: arg0,
127
+ tracked: true
128
+ }],
129
+ source: 'P0',
130
+ stacktraceOpts: {
131
+ constructorOpt: data.hooked,
132
+ prependFrames: [data.orig]
133
+ },
134
+ tags: urlTags,
135
+ });
136
+
137
+ if (event) {
138
+ reportFindings({
139
+ ruleId: 'ssrf',
140
+ sinkEvent: event
141
+ });
142
+ }
143
+ }
144
+ });
145
+ }
146
+ });
147
+ });
148
+ });
149
+ };
150
+
151
+ return http;
152
+ };
@@ -31,7 +31,7 @@ const {
31
31
  },
32
32
  Rule: { REFLECTED_XSS: ruleId },
33
33
  } = require('@contrast/common');
34
- const { patchType, filterSafeTags } = require('../common');
34
+ const { patchType, filterSafeTags } = require('../../common');
35
35
 
36
36
  module.exports = function(core) {
37
37
  const {
@@ -52,7 +52,7 @@ module.exports = function(core) {
52
52
  },
53
53
  },
54
54
  } = core;
55
- const http = core.assess.dataflow.sinks.http = {};
55
+ const http = core.assess.dataflow.sinks.http.serverResponse = {};
56
56
 
57
57
  const safeTags = [
58
58
  ALPHANUM_SPACE_HYPHEN,
@@ -84,7 +84,7 @@ module.exports = function(core) {
84
84
  let urlPathTags = strInfo.tags;
85
85
 
86
86
  if (url.indexOf('?') > -1) {
87
- urlPathTags = createSubsetTags(strInfo.tags, 0, url.indexOf('?'));
87
+ urlPathTags = createSubsetTags(strInfo.tags, 0, url.indexOf('?') + 1);
88
88
  }
89
89
 
90
90
  if (urlPathTags && isVulnerable(UNTRUSTED, safeTags, urlPathTags)) {
@@ -15,7 +15,6 @@
15
15
 
16
16
  'use strict';
17
17
 
18
- const util = require('util');
19
18
  const {
20
19
  DataflowTag: {
21
20
  UNTRUSTED,
@@ -28,7 +27,8 @@ const {
28
27
  Rule: { NOSQL_INJECTION_MONGO },
29
28
  isNonEmptyObject,
30
29
  traverseValues,
31
- isString
30
+ isString,
31
+ inspect
32
32
  } = require('@contrast/common');
33
33
  const utils = require('../../tag-utils');
34
34
  const { patchType, filterSafeTags } = require('../common');
@@ -82,7 +82,6 @@ module.exports = function(core) {
82
82
  }
83
83
  } = core;
84
84
 
85
- const inspect = patcher.unwrap(util.inspect);
86
85
  const instr = core.assess.dataflow.sinks.mongodb = {};
87
86
 
88
87
  instr.getQueryVulnerabilityInfo = function getQueryVulnerabilityInfo(query) {
@@ -256,7 +255,7 @@ module.exports = function(core) {
256
255
  if (safeReports.length && config.assess.safe_positives.enable) {
257
256
  const safeTags = safeReports.map((report) => filterSafeTags(querySafeTags, report.strInfo));
258
257
  const strInfo = safeReports.map((report) => {
259
- const tags = report.path ? getAdjustedQueryTags(report.path, report.strInfo, inspect(origArgs[report.argIdx], { depth: 4 })) : report.strInfo?.tags;
258
+ const tags = report.path ? utils.createAdjustedQueryTags(report.path, report.strInfo.tags, report.strInfo.value, inspect(origArgs[report.argIdx], { depth: 4 })) : report.strInfo?.tags;
260
259
 
261
260
  return {
262
261
  value: inspect(origArgs[report.argIdx], { depth: 4 }),
@@ -282,7 +281,7 @@ module.exports = function(core) {
282
281
  tracked: idx === vulnArgIdx,
283
282
  }));
284
283
 
285
- const tags = path ? getAdjustedQueryTags(path, strInfo, args[vulnArgIdx].value) : strInfo?.tags;
284
+ const tags = path ? utils.createAdjustedQueryTags(path, strInfo.tags, strInfo.value, args[vulnArgIdx].value) : strInfo?.tags;
286
285
  const resultVal = args[args.length - 1].value.startsWith('[Function') ? '' : 'Promise';
287
286
  const sinkEvent = createSinkEvent({
288
287
  args,
@@ -401,22 +400,4 @@ module.exports = function(core) {
401
400
 
402
401
  return name;
403
402
  }
404
-
405
- function getAdjustedQueryTags(path, strInfo, argString) {
406
- const { tags } = strInfo;
407
- let idx = -1;
408
- for (const str of [...path, strInfo.value]) {
409
- // This is the case with the `.aggregate` method
410
- // where the argument is an array
411
- if (str === 0) continue;
412
-
413
- idx = argString.indexOf(str, idx);
414
- if (idx == -1) {
415
- idx = -1;
416
- break;
417
- }
418
- }
419
-
420
- return idx > 0 ? utils.createAppendTags([], tags, idx) : strInfo.tags;
421
- }
422
403
  };
@@ -21,7 +21,7 @@ const {
21
21
  isString
22
22
  } = require('@contrast/common');
23
23
  const { createModuleLabel } = require('../../propagation/common');
24
- const { patchType } = require('../common');
24
+ const { patchType, filterSafeTags } = require('../common');
25
25
 
26
26
  const safeTags = [
27
27
  SQL_ENCODED,
@@ -30,15 +30,18 @@ const safeTags = [
30
30
  CUSTOM_ENCODED,
31
31
  ];
32
32
 
33
+ const ruleId = SQL_INJECTION;
34
+
33
35
  module.exports = function(core) {
34
36
  const {
35
37
  depHooks,
36
38
  patcher,
39
+ config,
37
40
  scopes: { sources },
38
41
  assess: {
39
42
  dataflow: {
40
43
  tracker,
41
- sinks: { isVulnerable, isLocked, reportFindings },
44
+ sinks: { isVulnerable, isLocked, reportFindings, reportSafePositive },
42
45
  eventFactory: { createSinkEvent },
43
46
  },
44
47
  },
@@ -54,38 +57,48 @@ module.exports = function(core) {
54
57
  ) return;
55
58
 
56
59
  const strInfo = tracker.getData(data.args[0]);
57
- if (!strInfo || !isVulnerable(UNTRUSTED, safeTags, strInfo.tags)) {
58
- return;
59
- }
60
+ if (!strInfo) return;
60
61
 
61
- const event = createSinkEvent({
62
- name,
63
- moduleName: 'mssql',
64
- methodName: `PreparedStatement.prototype.${method}`,
65
- context: `mssql.PreparedStatement.${method}('${strInfo.value}')`,
66
- history: [strInfo],
67
- object: {
68
- value: `[${createModuleLabel('mssql', version)}].${obj}`,
69
- tracked: false,
70
- },
71
- args: [
72
- {
73
- value: strInfo.value,
74
- tracked: true,
62
+ if (isVulnerable(UNTRUSTED, safeTags, strInfo.tags)) {
63
+ const event = createSinkEvent({
64
+ name,
65
+ moduleName: 'mssql',
66
+ methodName: `PreparedStatement.prototype.${method}`,
67
+ context: `mssql.PreparedStatement.${method}('${strInfo.value}')`,
68
+ history: [strInfo],
69
+ object: {
70
+ value: `[${createModuleLabel('mssql', version)}].${obj}`,
71
+ tracked: false,
75
72
  },
76
- ],
77
- tags: strInfo.tags,
78
- source: 'P0',
79
- stacktraceOpts: {
80
- contructorOpt: data.hooked,
81
- prependFrames: [data.orig]
82
- },
83
- });
73
+ args: [
74
+ {
75
+ value: strInfo.value,
76
+ tracked: true,
77
+ },
78
+ ],
79
+ tags: strInfo.tags,
80
+ source: 'P0',
81
+ stacktraceOpts: {
82
+ contructorOpt: data.hooked,
83
+ prependFrames: [data.orig]
84
+ },
85
+ });
84
86
 
85
- if (event) {
86
- reportFindings({
87
- ruleId: SQL_INJECTION,
88
- sinkEvent: event,
87
+ if (event) {
88
+ reportFindings({
89
+ ruleId: SQL_INJECTION,
90
+ sinkEvent: event,
91
+ });
92
+ }
93
+ } else if (config.assess.safe_positives.enable) {
94
+ reportSafePositive({
95
+ name,
96
+ ruleId,
97
+ safeTags: filterSafeTags(safeTags, strInfo),
98
+ strInfo: {
99
+ tags: strInfo.tags,
100
+ value: strInfo.value,
101
+ }
89
102
  });
90
103
  }
91
104
  };
@@ -0,0 +1,276 @@
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
+ const { patchType, filterSafeTags } = require('../common');
18
+ const {
19
+ isString,
20
+ DataflowTag: {
21
+ UNTRUSTED,
22
+ CUSTOM_ENCODED_TRUST_BOUNDARY_VIOLATION,
23
+ CUSTOM_ENCODED,
24
+ CUSTOM_VALIDATED_TRUST_BOUNDARY_VIOLATION,
25
+ CUSTOM_VALIDATED,
26
+ LIMITED_CHARS,
27
+ },
28
+ isNonEmptyObject,
29
+ inspect,
30
+ traverseValues,
31
+ join,
32
+ split,
33
+ } = require('@contrast/common');
34
+ const { createAdjustedQueryTags } = require('../../tag-utils');
35
+ const ruleId = 'unsafe-code-execution';
36
+ const safeTags = [
37
+ CUSTOM_ENCODED_TRUST_BOUNDARY_VIOLATION,
38
+ CUSTOM_ENCODED,
39
+ CUSTOM_VALIDATED_TRUST_BOUNDARY_VIOLATION,
40
+ CUSTOM_VALIDATED,
41
+ LIMITED_CHARS,
42
+ ];
43
+
44
+ module.exports = function(core) {
45
+ const {
46
+ config,
47
+ depHooks,
48
+ patcher,
49
+ scopes: { sources, instrumentation },
50
+ assess: {
51
+ dataflow: {
52
+ tracker,
53
+ sinks: {
54
+ isVulnerable,
55
+ runInActiveSink,
56
+ isLocked,
57
+ reportFindings,
58
+ reportSafePositive,
59
+ },
60
+ eventFactory: { createSinkEvent },
61
+ },
62
+ },
63
+ } = core;
64
+
65
+ function accumulateArgsInfo(origArgs, idxsToCheck) {
66
+ const argsInfo = [];
67
+ for (let i = 0; i < origArgs.length; i++) {
68
+ const arg = origArgs[i];
69
+ const infoObj = {
70
+ isStringArg: isString(arg),
71
+ idx: i,
72
+ sanitizedStrInfos: [],
73
+ sanitizedStrPaths: []
74
+ };
75
+
76
+ if (
77
+ !arg ||
78
+ !idxsToCheck.includes(i) ||
79
+ (!infoObj.isStringArg && !isNonEmptyObject(arg))
80
+ ) {
81
+ const inspectedArg = inspect(origArgs[i]);
82
+ infoObj.argsValue = { value: inspectedArg, tracked: false };
83
+ infoObj.ctxValue = inspectedArg;
84
+ argsInfo.push(infoObj);
85
+
86
+ continue;
87
+ }
88
+
89
+ if (infoObj.isStringArg) {
90
+ const strInfo = tracker.getData(arg);
91
+ infoObj.strInfo = strInfo;
92
+ infoObj.argsValue = {
93
+ value: strInfo ? strInfo.value : arg,
94
+ tracked: !!strInfo,
95
+ };
96
+ infoObj.ctxValue = `"${strInfo?.value || arg}"`;
97
+ infoObj.isVulnerableArg =
98
+ strInfo && isVulnerable(UNTRUSTED, safeTags, strInfo.tags);
99
+ if (strInfo && !infoObj.isVulnerableArg) {
100
+ infoObj.sanitizedStrInfos.push(strInfo);
101
+ infoObj.isSanitizedArg = true;
102
+ }
103
+ } else {
104
+ traverseValues(
105
+ arg,
106
+ (path, _type, value) => {
107
+ const strInfo = tracker.getData(value);
108
+ infoObj.strInfo = strInfo;
109
+ infoObj.isVulnerableArg =
110
+ strInfo && isVulnerable(UNTRUSTED, safeTags, strInfo.tags);
111
+ if (infoObj.isVulnerableArg) {
112
+ infoObj.vulnerableArgPath = [...path];
113
+ return true;
114
+ } else if (strInfo) {
115
+ infoObj.sanitizedStrInfos.push(strInfo);
116
+ infoObj.sanitizedStrPaths.push([...path]);
117
+ infoObj.isSanitizedArg = true;
118
+ }
119
+ },
120
+ 100
121
+ );
122
+
123
+ const inspectedArg = inspect(arg);
124
+
125
+ infoObj.argsValue = { value: inspectedArg, tracked: false };
126
+ infoObj.ctxValue = inspectedArg;
127
+ }
128
+
129
+ argsInfo.push(infoObj);
130
+ }
131
+
132
+ return argsInfo;
133
+ }
134
+
135
+ function around(next, { args: origArgs, hooked, orig, name }) {
136
+ const store = sources.getStore()?.assess;
137
+ if (
138
+ !store ||
139
+ instrumentation.isLocked() ||
140
+ isLocked('unsafe-code-execution')
141
+ ) {
142
+ return next();
143
+ }
144
+
145
+ const methodPath = split(name, '.');
146
+ const method = methodPath[methodPath.length - 1];
147
+ const idxsToCheck = method === 'runInNewContext' ? [0, 1] : [0];
148
+ const argsInfo = accumulateArgsInfo(origArgs, idxsToCheck);
149
+ const vulnerableArg = argsInfo.find((arg) => arg.isVulnerableArg);
150
+ const sanitizedArgs = argsInfo.filter((arg) => arg.isSanitizedArg);
151
+
152
+ if (!vulnerableArg && sanitizedArgs.length && config.assess.safe_positives.enable) {
153
+ const foundSafeTags = sanitizedArgs.reduce((a, c) => {
154
+ const tags = [];
155
+
156
+ c.sanitizedStrInfos.forEach((i) => {
157
+ tags.push(filterSafeTags(safeTags, i));
158
+ });
159
+
160
+ tags.forEach((tag) => a.add(...tag));
161
+ return a;
162
+ }, new Set());
163
+ const strInfo = sanitizedArgs.map((arg) => {
164
+ const value = inspect(origArgs[arg.idx], { depth: 4 });
165
+ const tags = [];
166
+ const strValues = [];
167
+ arg.sanitizedStrInfos.forEach((info, idx) => {
168
+ strValues.push(info.value);
169
+ if (arg.isStringArg) {
170
+ tags.push(info.tags);
171
+ } else {
172
+ tags.push(
173
+ createAdjustedQueryTags(
174
+ arg.sanitizedStrPaths[idx] || [],
175
+ info.tags,
176
+ info.value,
177
+ value
178
+ )
179
+ );
180
+ }
181
+ });
182
+
183
+ return {
184
+ value,
185
+ strValues,
186
+ tags,
187
+ };
188
+ });
189
+
190
+ reportSafePositive({
191
+ name,
192
+ ruleId,
193
+ safeTags: Array.from(foundSafeTags),
194
+ strInfo,
195
+ });
196
+
197
+ return name === 'vm.runInNewContext'
198
+ ? runInActiveSink('unsafe-code-execution', () => next())
199
+ : next();
200
+ }
201
+
202
+ if (vulnerableArg) {
203
+ const event = createSinkEvent({
204
+ name,
205
+ context: `${name}(${join(
206
+ argsInfo.map((a) => a.ctxValue),
207
+ ', '
208
+ )})`,
209
+ history: [vulnerableArg.strInfo],
210
+ object: {
211
+ value: methodPath.includes('prototype')
212
+ ? 'vm.Script.prototype'
213
+ : 'vm',
214
+ tracked: false,
215
+ },
216
+ moduleName: 'vm',
217
+ methodName: method,
218
+ args: argsInfo.map((a) => a.argsValue),
219
+ tags: vulnerableArg.vulnerableArgPath?.length
220
+ ? createAdjustedQueryTags(
221
+ vulnerableArg.vulnerableArgPath,
222
+ vulnerableArg.strInfo.tags,
223
+ vulnerableArg.strInfo.value,
224
+ vulnerableArg.argsValue.value
225
+ )
226
+ : vulnerableArg.strInfo.tags,
227
+ source: `P${vulnerableArg.idx}`,
228
+ stacktraceOpts: {
229
+ contructorOpt: hooked,
230
+ prependFrames: [orig],
231
+ },
232
+ });
233
+
234
+ if (event) {
235
+ reportFindings({
236
+ ruleId,
237
+ sinkEvent: event,
238
+ });
239
+ }
240
+ }
241
+
242
+ return name === 'vm.runInNewContext'
243
+ ? runInActiveSink('unsafe-code-execution', () => next())
244
+ : next();
245
+ }
246
+
247
+ core.assess.dataflow.sinks.vm = {
248
+ install() {
249
+ depHooks.resolve({ name: 'vm' }, (vm) => {
250
+ [
251
+ 'Script',
252
+ 'createScript',
253
+ 'runInContext',
254
+ 'runInThisContext',
255
+ 'createContext',
256
+ 'runInNewContext',
257
+ ].forEach((method) => {
258
+ const name = `vm.${method}`;
259
+ patcher.patch(vm, method, {
260
+ name,
261
+ patchType,
262
+ around,
263
+ });
264
+ });
265
+
266
+ patcher.patch(vm.Script.prototype, 'runInNewContext', {
267
+ name: 'vm.Script.prototype.runInNewContext',
268
+ patchType,
269
+ around,
270
+ });
271
+ });
272
+ },
273
+ };
274
+
275
+ return core.assess.dataflow.sinks.vm;
276
+ };