@contrast/assess 1.8.0 → 1.9.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 (62) hide show
  1. package/lib/dataflow/event-factory.js +17 -13
  2. package/lib/dataflow/propagation/index.js +3 -0
  3. package/lib/dataflow/propagation/install/JSON/index.js +1 -0
  4. package/lib/dataflow/propagation/install/JSON/parse-fn.js +248 -0
  5. package/lib/dataflow/propagation/install/JSON/parse.js +196 -0
  6. package/lib/dataflow/propagation/install/JSON/stringify.js +5 -3
  7. package/lib/dataflow/propagation/install/array-prototype-join.js +21 -14
  8. package/lib/dataflow/propagation/install/buffer.js +2 -0
  9. package/lib/dataflow/propagation/install/contrast-methods/add.js +2 -0
  10. package/lib/dataflow/propagation/install/contrast-methods/index.js +1 -0
  11. package/lib/dataflow/propagation/install/contrast-methods/number.js +58 -0
  12. package/lib/dataflow/propagation/install/contrast-methods/string.js +53 -6
  13. package/lib/dataflow/propagation/install/contrast-methods/tag.js +3 -1
  14. package/lib/dataflow/propagation/install/decode-uri-component.js +9 -2
  15. package/lib/dataflow/propagation/install/ejs/escape-xml.js +8 -2
  16. package/lib/dataflow/propagation/install/encode-uri-component.js +9 -2
  17. package/lib/dataflow/propagation/install/escape-html.js +13 -5
  18. package/lib/dataflow/propagation/install/escape.js +9 -2
  19. package/lib/dataflow/propagation/install/handlebars-utils-escape-expression.js +11 -4
  20. package/lib/dataflow/propagation/install/isnumeric-0.js +59 -0
  21. package/lib/dataflow/propagation/install/mongoose/index.js +36 -0
  22. package/lib/dataflow/propagation/install/mongoose/schema-string.js +156 -0
  23. package/lib/dataflow/propagation/install/mysql-connection-escape.js +5 -0
  24. package/lib/dataflow/propagation/install/parse-int.js +60 -0
  25. package/lib/dataflow/propagation/install/pug-runtime-escape.js +9 -2
  26. package/lib/dataflow/propagation/install/querystring/parse.js +11 -9
  27. package/lib/dataflow/propagation/install/sequelize.js +6 -3
  28. package/lib/dataflow/propagation/install/sql-template-strings.js +9 -2
  29. package/lib/dataflow/propagation/install/string/concat.js +8 -2
  30. package/lib/dataflow/propagation/install/string/format-methods.js +7 -2
  31. package/lib/dataflow/propagation/install/string/html-methods.js +15 -5
  32. package/lib/dataflow/propagation/install/string/match.js +14 -9
  33. package/lib/dataflow/propagation/install/string/replace.js +22 -14
  34. package/lib/dataflow/propagation/install/string/slice.js +13 -5
  35. package/lib/dataflow/propagation/install/string/split.js +15 -11
  36. package/lib/dataflow/propagation/install/string/substring.js +16 -6
  37. package/lib/dataflow/propagation/install/string/trim.js +3 -0
  38. package/lib/dataflow/propagation/install/unescape.js +9 -2
  39. package/lib/dataflow/propagation/install/url/domain-parsers.js +7 -2
  40. package/lib/dataflow/propagation/install/validator/hooks.js +6 -2
  41. package/lib/dataflow/sinks/install/child-process.js +116 -50
  42. package/lib/dataflow/sinks/install/express/unvalidated-redirect.js +6 -3
  43. package/lib/dataflow/sinks/install/fastify/unvalidated-redirect.js +7 -4
  44. package/lib/dataflow/sinks/install/fs.js +44 -12
  45. package/lib/dataflow/sinks/install/http.js +5 -2
  46. package/lib/dataflow/sinks/install/koa/unvalidated-redirect.js +7 -4
  47. package/lib/dataflow/sinks/install/marsdb.js +3 -0
  48. package/lib/dataflow/sinks/install/mongodb.js +3 -1
  49. package/lib/dataflow/sinks/install/mssql.js +9 -2
  50. package/lib/dataflow/sinks/install/mysql.js +9 -4
  51. package/lib/dataflow/sinks/install/postgres.js +6 -3
  52. package/lib/dataflow/sinks/install/sequelize.js +7 -5
  53. package/lib/dataflow/sinks/install/sqlite3.js +7 -3
  54. package/lib/dataflow/sources/handler.js +2 -1
  55. package/lib/dataflow/sources/install/http.js +1 -1
  56. package/lib/dataflow/tag-utils.js +25 -1
  57. package/lib/dataflow/tracker.js +6 -6
  58. package/lib/index.js +2 -0
  59. package/lib/response-scanning/handlers/utils.js +2 -2
  60. package/lib/session-configuration/index.js +34 -0
  61. package/lib/session-configuration/install/http.js +79 -0
  62. package/package.json +2 -2
@@ -0,0 +1,60 @@
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 { isString } = require('@contrast/common');
19
+ const { patchType } = require('../common');
20
+
21
+ module.exports = function(core) {
22
+ const {
23
+ logger,
24
+ scopes: { instrumentation, sources },
25
+ patcher,
26
+ assess: {
27
+ dataflow: { tracker }
28
+ }
29
+ } = core;
30
+
31
+ const name = 'global.parseInt';
32
+
33
+ return core.assess.dataflow.propagation.parseIntInstrumentation = {
34
+ install() {
35
+ patcher.patch(global, 'parseInt', {
36
+ name,
37
+ patchType,
38
+ post(data) {
39
+ const { args: [value], result } = data;
40
+ if (
41
+ isNaN(result) ||
42
+ !value ||
43
+ !isString(value) ||
44
+ !sources.getStore()?.assess ||
45
+ instrumentation.isLocked() ||
46
+ !tracker.getData(value)
47
+ ) return;
48
+
49
+ // todo NODE-3118 to handle when value has trailing non-integer values
50
+
51
+ tracker.untrack(value);
52
+ logger.trace({ sanitizer: name, value }, 'untracked a string value');
53
+ }
54
+ });
55
+ },
56
+ uninstall() {
57
+ global.parseInt = patcher.unwrap(global.parseInt);
58
+ },
59
+ };
60
+ };
@@ -36,8 +36,10 @@ module.exports = function(core) {
36
36
  return core.assess.dataflow.propagation.pugRuntimeEscape = {
37
37
  install() {
38
38
  depHooks.resolve({ name: 'pug-runtime' }, (pugRuntime, version) => {
39
+ const name = 'pug-runtime.escape';
40
+
39
41
  patcher.patch(pugRuntime, 'escape', {
40
- name: 'pug-runtime.escape',
42
+ name,
41
43
  patchType,
42
44
  post(data) {
43
45
  const { args, result, hooked, orig } = data;
@@ -54,7 +56,10 @@ module.exports = function(core) {
54
56
  newTags[WEAK_URL_ENCODED] = [0, result.length - 1];
55
57
 
56
58
  const event = createPropagationEvent({
57
- name: 'pug-runtime.escape',
59
+ name,
60
+ moduleName: 'pug-runtime',
61
+ methodName: 'escape',
62
+ context: `pugRuntime.escape('${argInfo.value}')`,
58
63
  object: {
59
64
  value: createModuleLabel('pug-runtime', version),
60
65
  tracked: false
@@ -67,6 +72,8 @@ module.exports = function(core) {
67
72
  tags: newTags,
68
73
  addedTags: [WEAK_URL_ENCODED],
69
74
  history,
75
+ source: 'P',
76
+ target: 'R',
70
77
  stacktraceOpts: {
71
78
  constructorOpt: hooked,
72
79
  prependFrames: [orig]
@@ -15,10 +15,11 @@
15
15
 
16
16
  'use strict';
17
17
 
18
- const util = require('util');
19
18
  const querystring = require('querystring');
20
19
  const {
21
- DataflowTag: { URL_ENCODED }
20
+ DataflowTag: { URL_ENCODED },
21
+ inspect,
22
+ join
22
23
  } = require('@contrast/common');
23
24
 
24
25
  const { patchType } = require('../../common');
@@ -46,20 +47,21 @@ module.exports = function(core) {
46
47
  if (!tagRanges) return result;
47
48
 
48
49
  const resultInfo = tracker.getData(result);
50
+ const [, ...restOfArgsValues] = data.origArgs.map(inspect);
49
51
  const event = createPropagationEvent({
50
52
  name: data.name,
53
+ context: `querystring.parse('${trackingData.value}', ${join(restOfArgsValues, ', ')})`,
54
+ moduleName: 'querystring',
55
+ methodName: 'parse',
51
56
  history: [trackingData],
52
57
  object: {
53
58
  value: part,
54
59
  tracked: true,
55
60
  },
56
- args: data.origArgs.map((arg) => {
57
- const argInfo = tracker.getData(arg);
58
- return {
59
- value: argInfo ? argInfo.value : util.inspect(arg),
60
- tracked: !!argInfo
61
- };
62
- }),
61
+ args: data.origArgs.map((_arg, idx) => ({
62
+ value: idx === 0 ? trackingData.value : restOfArgsValues[idx - 1],
63
+ tracked: !!idx === 0
64
+ })),
63
65
  result: {
64
66
  value: result,
65
67
  tracked: !!resultInfo
@@ -21,7 +21,7 @@ const {
21
21
  } = require('@contrast/common');
22
22
  const { patchType, createModuleLabel } = require('../common');
23
23
 
24
- module.exports = function (core) {
24
+ module.exports = function(core) {
25
25
  const {
26
26
  scopes: { sources, instrumentation },
27
27
  patcher,
@@ -58,9 +58,10 @@ module.exports = function (core) {
58
58
  { name: 'sequelize', file: 'lib/sql-string.js' },
59
59
  (sqlString, version) => {
60
60
  const origEscape = sqlString.escape;
61
+ const name = 'sequelize.escape';
61
62
 
62
63
  patcher.patch(sqlString, 'escape', {
63
- name: 'sequelize.escape',
64
+ name,
64
65
  patchType,
65
66
  post(data) {
66
67
  const { args, result, hooked, orig } = data;
@@ -85,8 +86,10 @@ module.exports = function (core) {
85
86
  newTags[SQL_ENCODED] = [0, result.length - 1];
86
87
 
87
88
  const event = createPropagationEvent({
88
- context: 'sequelize.escape',
89
+ context: `sequelize.escape('${argInfo.value}')`,
89
90
  name: 'sequelize/lib/sql-string.escape',
91
+ moduleName: 'sequelize',
92
+ methodName: 'escape',
90
93
  object: {
91
94
  value: createModuleLabel('sequelize/lib/sql-string.escape', version),
92
95
  tracked: false,
@@ -33,8 +33,10 @@ module.exports = function(core) {
33
33
  return core.assess.dataflow.propagation.sqlTemplateStrings = {
34
34
  install() {
35
35
  depHooks.resolve({ name: 'sql-template-strings' }, (sqlTemplateStrings, version) => {
36
+ const name = 'sql-template-strings.SQL';
37
+
36
38
  patcher.patch(sqlTemplateStrings, 'SQL', {
37
- name: 'sql-template-strings.SQL',
39
+ name,
38
40
  patchType,
39
41
  post(data) {
40
42
  const { args, result, hooked, orig } = data;
@@ -55,7 +57,10 @@ module.exports = function(core) {
55
57
  newTags[SQL_ENCODED] = [0, resultValue.length - 1];
56
58
 
57
59
  const event = createPropagationEvent({
58
- name: 'sql-template-strings.SQL',
60
+ name,
61
+ moduleName: 'sql-template-strings',
62
+ methodName: 'SQL',
63
+ context: `SQL\`${argInfo.value}\``,
59
64
  object: {
60
65
  value: createModuleLabel('sql-template-strings', version),
61
66
  tracked: false
@@ -68,6 +73,8 @@ module.exports = function(core) {
68
73
  tags: newTags,
69
74
  addedTags: [SQL_ENCODED],
70
75
  history,
76
+ source: 'P',
77
+ target: 'R',
71
78
  stacktraceOpts: {
72
79
  constructorOpt: hooked,
73
80
  prependFrames: [orig]
@@ -18,6 +18,7 @@
18
18
  const {
19
19
  createAppendTags
20
20
  } = require('../../../tag-utils');
21
+ const { join, inspect } = require('@contrast/common');
21
22
  const { patchType } = require('../../common');
22
23
 
23
24
  module.exports = function(core) {
@@ -31,8 +32,10 @@ module.exports = function(core) {
31
32
 
32
33
  return core.assess.dataflow.propagation.stringInstrumentation.concat = {
33
34
  install() {
35
+ const name = 'String.prototype.concat';
36
+
34
37
  patcher.patch(String.prototype, 'concat', {
35
- name: 'String.prototype.concat',
38
+ name,
36
39
  patchType,
37
40
  post(data) {
38
41
  const { args, obj, result, hooked, orig } = data;
@@ -70,7 +73,10 @@ module.exports = function(core) {
70
73
 
71
74
  if (history.size) {
72
75
  const event = createPropagationEvent({
73
- name: 'String.prototype.concat',
76
+ name,
77
+ moduleName: 'String',
78
+ methodName: 'prototype.concat',
79
+ context: `${inspect(objInfo?.value) || String(obj)}.concat(${join(argsData.map(d => d.value), ', ')})`,
74
80
  object: {
75
81
  value: objInfo?.value || String(obj),
76
82
  tracked: !!objInfo
@@ -29,8 +29,10 @@ module.exports = function(core) {
29
29
  return core.assess.dataflow.propagation.stringInstrumentation.formatMethods = {
30
30
  install() {
31
31
  ['toLowerCase', 'toUpperCase', 'toLocaleLowerCase', 'toLocaleUpperCase'].forEach((method) => {
32
+ const name = `String.prototype.${method}`;
33
+
32
34
  patcher.patch(String.prototype, method, {
33
- name: `String.prototype.${method}`,
35
+ name,
34
36
  patchType,
35
37
  post(data) {
36
38
  const { obj, result, hooked, orig } = data;
@@ -43,7 +45,10 @@ module.exports = function(core) {
43
45
  const history = [objInfo];
44
46
 
45
47
  const event = createPropagationEvent({
46
- name: `String.prototype.${method}`,
48
+ name,
49
+ moduleName: 'String',
50
+ methodName: `prototype.${method}`,
51
+ context: `'${objInfo.value}'.${method}()`,
47
52
  object: {
48
53
  value: objInfo.value,
49
54
  tracked: true
@@ -18,6 +18,7 @@
18
18
  const {
19
19
  createAppendTags
20
20
  } = require('../../../tag-utils');
21
+ const { inspect } = require('@contrast/common');
21
22
  const { patchType } = require('../../common');
22
23
  const htmlTagsLengths = {
23
24
  anchor: 11,
@@ -57,8 +58,9 @@ module.exports = function(core) {
57
58
 
58
59
  return core.assess.dataflow.propagation.stringInstrumentation.htmlMethods = {
59
60
  install() {
61
+ const name = 'String.prototype.anchor';
60
62
  patcher.patch(String.prototype, 'anchor', {
61
- name: 'String.prototype.anchor',
63
+ name,
62
64
  patchType,
63
65
  post(data) {
64
66
  const { args, obj, result, hooked, orig } = data;
@@ -75,7 +77,10 @@ module.exports = function(core) {
75
77
 
76
78
  if (history.size) {
77
79
  const event = createPropagationEvent({
78
- name: 'String.prototype.anchor',
80
+ name,
81
+ moduleName: 'String',
82
+ methodName: 'prototype.anchor',
83
+ context: `${inspect(objInfo?.value) || String(obj)}.anchor(${argInfo ? argInfo.value : arg})`,
79
84
  object: {
80
85
  value: objInfo?.value || String(obj),
81
86
  tracked: !!objInfo
@@ -85,7 +90,7 @@ module.exports = function(core) {
85
90
  tracked: true
86
91
  },
87
92
  args: [
88
- { value: arg, tracked: !!argInfo }
93
+ { value: argInfo ? argInfo.value : arg, tracked: !!argInfo }
89
94
  ],
90
95
  tags: adjustTags('anchor', objInfo?.tags, `${arg}`.length, argInfo?.tags),
91
96
  history: Array.from(history),
@@ -109,8 +114,10 @@ module.exports = function(core) {
109
114
  });
110
115
 
111
116
  ['big', 'blink', 'italics', 'small', 'strike', 'sub', 'fixed'].forEach((method) => {
117
+ const name = `String.prototype.${method}`;
118
+
112
119
  patcher.patch(String.prototype, method, {
113
- name: `String.prototype.${method}`,
120
+ name,
114
121
  patchType,
115
122
  post(data) {
116
123
  const { obj, result, hooked, orig } = data;
@@ -123,7 +130,10 @@ module.exports = function(core) {
123
130
  const history = [objInfo];
124
131
 
125
132
  const event = createPropagationEvent({
126
- name: `String.prototype.${method}`,
133
+ name,
134
+ moduleName: 'String',
135
+ methodName: `prototype.${method}`,
136
+ context: `${objInfo.value}.${method}()`,
127
137
  object: {
128
138
  value: objInfo.value,
129
139
  tracked: true
@@ -14,7 +14,7 @@
14
14
  */
15
15
 
16
16
  'use strict';
17
- const { join } = require('@contrast/common');
17
+ const { join, inspect } = require('@contrast/common');
18
18
  const { patchType } = require('../../common');
19
19
  const { createSubsetTags } = require('../../../tag-utils');
20
20
 
@@ -28,24 +28,29 @@ module.exports = function(core) {
28
28
  } = core;
29
29
 
30
30
  function getPropagationEvent(data, res, objInfo, start) {
31
- const { name, args, result, hooked, orig } = data;
31
+ const { name, args: origArgs, result, hooked, orig } = data;
32
32
  const tags = createSubsetTags(objInfo.tags, start, res.length - 1);
33
33
  if (!tags) return;
34
34
 
35
+ const args = origArgs.map((arg) => {
36
+ const argInfo = tracker.getData(arg);
37
+ return {
38
+ value: argInfo ? argInfo.value : inspect(arg),
39
+ tracked: !!argInfo
40
+ };
41
+ });
42
+
35
43
  return createPropagationEvent({
36
44
  name,
45
+ moduleName: 'String',
46
+ methodName: 'prototype.match',
47
+ context: `'${objInfo.value}'.match(${join(args.map(a => a.value), ', ')})`,
37
48
  history: [objInfo],
38
49
  object: {
39
50
  value: objInfo.value,
40
51
  tracked: true,
41
52
  },
42
- args: args.map((arg) => {
43
- const argInfo = tracker.getData(arg);
44
- return {
45
- value: argInfo ? argInfo.value : arg.toString(),
46
- tracked: !!argInfo
47
- };
48
- }),
53
+ args,
49
54
  tags,
50
55
  result: {
51
56
  value: join(result),
@@ -16,8 +16,11 @@
16
16
  'use strict';
17
17
 
18
18
  const {
19
- DataflowTag: { UNTRUSTED }
19
+ DataflowTag: { UNTRUSTED },
20
+ match: origMatch,
21
+ substring
20
22
  } = require('@contrast/common');
23
+ const { inspect, join } = require('@contrast/common');
21
24
  const { patchType } = require('../../common');
22
25
  const { createSubsetTags, createAppendTags } = require('../../../tag-utils');
23
26
 
@@ -63,7 +66,7 @@ module.exports = function(core) {
63
66
  replace: str.substring(str.indexOf(match) + match.length, str.length)
64
67
  }
65
68
  ].forEach(({ regex, replace }) => {
66
- if (ret && ret.match(regex)) {
69
+ if (ret && origMatch(ret, regex)) {
67
70
  // If the match string is tracked, we can actually use the patched replace
68
71
  // to keep track of its tag ranges
69
72
  if (tracker.getData(replace)) {
@@ -77,7 +80,7 @@ module.exports = function(core) {
77
80
  const numberedGroupMatches = replacementType !== 'function' && replacement.match(/\$[1-9][0-9]|\$[1-9]/g);
78
81
  if (numberedGroupMatches) {
79
82
  numberedGroupMatches.forEach((numberedGroup) => {
80
- const group = Number(numberedGroup.substring(1));
83
+ const group = Number(substring(numberedGroup, 1));
81
84
  ret = origReplace.call(ret, numberedGroup, captureGroups[group - 1]);
82
85
  });
83
86
  }
@@ -130,8 +133,9 @@ module.exports = function(core) {
130
133
 
131
134
  return core.assess.dataflow.propagation.stringInstrumentation.replace = {
132
135
  install() {
136
+ const name = 'String.prototype.replace';
133
137
  patcher.patch(String.prototype, 'replace', {
134
- name: 'String.prototype.replace',
138
+ name,
135
139
  patchType,
136
140
  pre(data) {
137
141
  if (!sources.getStore()?.assess || instrumentation.isLocked()) return;
@@ -160,23 +164,27 @@ module.exports = function(core) {
160
164
  return;
161
165
  }
162
166
 
163
- const { _replacementInfo, obj, args, result, hooked, orig } = data;
167
+ const { _replacementInfo, obj, args: origArgs, result, hooked, orig } = data;
168
+ const args = [{
169
+ value: inspect(origArgs[0]),
170
+ tracked: !!tracker.getData(origArgs[0])
171
+ },
172
+ {
173
+ value: data._replacement,
174
+ tracked: !!_replacementInfo
175
+ }];
164
176
 
165
177
  const event = createPropagationEvent({
166
- name: 'String.prototype.replace',
178
+ name,
179
+ moduleName: 'String',
180
+ methodName: 'prototype.replace',
181
+ context: `'${obj}'.replace(${join(args.map(a => a.value), ', ')})`,
167
182
  history: Array.from(data._history),
168
183
  object: {
169
184
  value: obj,
170
185
  tracked: !!data._objInfo
171
186
  },
172
- args: [{
173
- value: args[0].toString(),
174
- tracked: !!tracker.getData(args[0])
175
- },
176
- {
177
- value: data._replacement,
178
- tracked: !!_replacementInfo
179
- }],
187
+ args,
180
188
  result: {
181
189
  value: result,
182
190
  tracked: true
@@ -14,6 +14,7 @@
14
14
  */
15
15
  'use strict';
16
16
  const { patchType } = require('../../common');
17
+ const { inspect, join } = require('@contrast/common');
17
18
  const { createSubsetTags } = require('../../../tag-utils');
18
19
 
19
20
  module.exports = function(core) {
@@ -52,7 +53,7 @@ module.exports = function(core) {
52
53
  name,
53
54
  patchType,
54
55
  post(data) {
55
- const { name, args, obj, result, hooked, orig } = data;
56
+ const { name, args: origArgs, obj, result, hooked, orig } = data;
56
57
  if (!result || !sources.getStore() || instrumentation.isLocked()) return;
57
58
 
58
59
  const objInfo = tracker.getData(obj);
@@ -68,22 +69,29 @@ module.exports = function(core) {
68
69
  const tags = createSubsetTags(objInfo.tags, startIdx, subsetLen);
69
70
  if (!tags) return;
70
71
 
72
+ const args = origArgs.map((arg) => ({
73
+ value: inspect(arg),
74
+ tracked: false
75
+ }));
76
+
71
77
  const event = createPropagationEvent({
72
78
  name,
79
+ moduleName: 'String',
80
+ methodName: 'prototype.slice',
81
+ context: `'${objInfo.value}'.slice(${join(args.map(a => a.value), ', ')})`,
73
82
  history: [objInfo],
74
83
  object: {
75
84
  value: obj,
76
85
  tracked: true,
77
86
  },
78
- args: args.map((arg) => ({
79
- value: arg.toString(),
80
- tracked: false
81
- })),
87
+ args,
82
88
  result: {
83
89
  value: result,
84
90
  tracked: true
85
91
  },
86
92
  tags,
93
+ source: 'O',
94
+ target: 'R',
87
95
  stacktraceOpts: {
88
96
  constructorOpt: hooked,
89
97
  prependFrames: [orig]
@@ -16,7 +16,7 @@
16
16
  'use strict';
17
17
 
18
18
  const { patchType } = require('../../common');
19
- const { join } = require('@contrast/common');
19
+ const { join, inspect } = require('@contrast/common');
20
20
  const { createSubsetTags } = require('../../../tag-utils');
21
21
 
22
22
 
@@ -37,16 +37,16 @@ module.exports = function(core) {
37
37
  name,
38
38
  patchType,
39
39
  post(data) {
40
- const { name, args, obj, result, hooked, orig } = data;
40
+ const { name, args: origArgs, obj, result, hooked, orig } = data;
41
41
  if (
42
42
  !obj ||
43
43
  !result ||
44
- args.length === 0 ||
44
+ origArgs.length === 0 ||
45
45
  result.length === 0 ||
46
46
  !sources.getStore() ||
47
47
  typeof obj !== 'string' ||
48
48
  instrumentation.isLocked() ||
49
- (args.length === 1 && args[0] == null)
49
+ (origArgs.length === 1 && origArgs[0] == null)
50
50
  ) return;
51
51
 
52
52
  const objInfo = tracker.getData(obj);
@@ -63,20 +63,24 @@ module.exports = function(core) {
63
63
  const tags = createSubsetTags(objInfo.tags, start, res.length - 1);
64
64
  if (!tags) continue;
65
65
 
66
+ const args = origArgs.map((arg) => {
67
+ const argInfo = tracker.getData(arg);
68
+ return {
69
+ value: argInfo ? argInfo.value : inspect(arg),
70
+ tracked: !!argInfo
71
+ };
72
+ });
66
73
  const event = createPropagationEvent({
67
74
  name,
75
+ moduleName: 'String',
76
+ methodName: 'prototype.slice',
77
+ context: `'${objInfo.value}'.split(${join(args.map(a => a.value), ', ')})`,
68
78
  history: [objInfo],
69
79
  object: {
70
80
  value: obj,
71
81
  tracked: true,
72
82
  },
73
- args: args.map((arg) => {
74
- const argInfo = tracker.getData(arg);
75
- return {
76
- value: argInfo ? argInfo.value : arg.toString(),
77
- tracked: !!argInfo
78
- };
79
- }),
83
+ args,
80
84
  tags,
81
85
  result: {
82
86
  value: join(result),
@@ -16,6 +16,7 @@
16
16
  'use strict';
17
17
 
18
18
  const { createSubsetTags } = require('../../../tag-utils');
19
+ const { join, inspect } = require('@contrast/common');
19
20
  const { patchType } = require('../../common');
20
21
 
21
22
  module.exports = function(core) {
@@ -60,8 +61,8 @@ module.exports = function(core) {
60
61
  name: `String.prototype.${method}`,
61
62
  patchType,
62
63
  post(data) {
63
- const { obj, args, result, name, hooked, orig } = data;
64
- if (!result || !sources.getStore() || instrumentation.isLocked()) return;
64
+ const { obj, args: origArgs, result, name, hooked, orig } = data;
65
+ if (!result || !sources.getStore()?.assess || instrumentation.isLocked()) return;
65
66
 
66
67
  const objInfo = tracker.getData(obj);
67
68
  if (!objInfo) return;
@@ -78,17 +79,21 @@ module.exports = function(core) {
78
79
 
79
80
  if (!tags) return;
80
81
 
82
+ const args = origArgs.map((arg) => ({
83
+ value: inspect(arg),
84
+ tracked: false
85
+ }));
81
86
  const event = createPropagationEvent({
82
87
  name,
88
+ moduleName: 'String',
89
+ methodName: 'prototype.substring',
90
+ context: `'${objInfo.value}'.substring(${join(args.map(a => a.value), ', ')})`,
83
91
  history: [objInfo],
84
92
  object: {
85
93
  value: obj,
86
94
  tracked: true,
87
95
  },
88
- args: args.map((arg) => ({
89
- value: arg.toString(),
90
- tracked: false
91
- })),
96
+ args,
92
97
  result: {
93
98
  value: result,
94
99
  tracked: true
@@ -101,6 +106,11 @@ module.exports = function(core) {
101
106
  },
102
107
  target: 'R',
103
108
  });
109
+
110
+ if (!event) {
111
+ return;
112
+ }
113
+
104
114
  const { extern } = tracker.track(result, event);
105
115
 
106
116
  if (extern) {
@@ -60,6 +60,9 @@ module.exports = function(core) {
60
60
 
61
61
  const event = createPropagationEvent({
62
62
  name: `String.prototype.${methodName}`,
63
+ moduleName: 'String',
64
+ methodName: `prototype.${methodName}`,
65
+ context: `'${obj}'.${methodName}()`,
63
66
  history,
64
67
  object: {
65
68
  value: obj,
@@ -34,8 +34,10 @@ module.exports = function(core) {
34
34
 
35
35
  return core.assess.dataflow.propagation.unescape = {
36
36
  install() {
37
+ const name = 'global.unescape';
38
+
37
39
  patcher.patch(global, 'unescape', {
38
- name: 'global.unescape',
40
+ name,
39
41
  patchType,
40
42
  post(data) {
41
43
  const { args, result, hooked, orig } = data;
@@ -53,7 +55,10 @@ module.exports = function(core) {
53
55
  if (!Object.keys(newTags).length) return;
54
56
 
55
57
  const event = createPropagationEvent({
56
- name: 'global.unescape',
58
+ name,
59
+ moduleName: 'global',
60
+ methodName: 'unescape',
61
+ context: `unescape('${argInfo.value}')`,
57
62
  object: {
58
63
  value: createObjectLabel('global'),
59
64
  tracked: false
@@ -65,6 +70,8 @@ module.exports = function(core) {
65
70
  args: [{ value: argInfo.value, tracked: true }],
66
71
  tags: newTags,
67
72
  history,
73
+ source: 'P',
74
+ target: 'R',
68
75
  removedTags: [WEAK_URL_ENCODED],
69
76
  stacktraceOpts: {
70
77
  constructorOpt: hooked,