@contrast/assess 1.7.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 (68) hide show
  1. package/lib/dataflow/event-factory.js +17 -13
  2. package/lib/dataflow/propagation/index.js +5 -0
  3. package/lib/dataflow/propagation/install/JSON/index.js +34 -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 +292 -0
  7. package/lib/dataflow/propagation/install/array-prototype-join.js +21 -14
  8. package/lib/dataflow/propagation/install/buffer.js +81 -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 +249 -149
  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 +141 -26
  55. package/lib/dataflow/sources/index.js +2 -7
  56. package/lib/dataflow/sources/install/body-parser1.js +19 -6
  57. package/lib/dataflow/sources/install/express/index.js +4 -1
  58. package/lib/dataflow/sources/install/express/params.js +81 -0
  59. package/lib/dataflow/sources/install/express/parsedUrl.js +87 -0
  60. package/lib/dataflow/sources/install/http.js +33 -19
  61. package/lib/dataflow/sources/install/querystring.js +75 -0
  62. package/lib/dataflow/tag-utils.js +92 -1
  63. package/lib/dataflow/tracker.js +6 -6
  64. package/lib/index.js +2 -0
  65. package/lib/response-scanning/handlers/utils.js +2 -2
  66. package/lib/session-configuration/index.js +34 -0
  67. package/lib/session-configuration/install/http.js +79 -0
  68. package/package.json +2 -2
@@ -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,
@@ -34,8 +34,10 @@ module.exports = function(core) {
34
34
  install() {
35
35
  depHooks.resolve({ name: 'url' }, (url, version) => {
36
36
  ['domainToASCII', 'domainToUnicode'].forEach((method) => {
37
+ const name = `url.${method}`;
38
+
37
39
  patcher.patch(url, method, {
38
- name: `url.${method}`,
40
+ name,
39
41
  patchType,
40
42
  post(data) {
41
43
  const { args, result, hooked, orig } = data;
@@ -49,7 +51,10 @@ module.exports = function(core) {
49
51
  const history = [argInfo];
50
52
 
51
53
  const event = createPropagationEvent({
52
- name: `url.${method}`,
54
+ name,
55
+ moduleName: 'url',
56
+ methodName: method,
57
+ context: `url.${method}('${argInfo.value}')`,
53
58
  object: {
54
59
  value: createModuleLabel('url', version),
55
60
  tracked: false
@@ -30,9 +30,12 @@ module.exports = function(core) {
30
30
  function createValidatorPropagationEvent(method, data, trackingData, newTags, target) {
31
31
  return createPropagationEvent({
32
32
  name: `validator.${method}`,
33
+ moduleName: 'validator',
34
+ methodName: method,
35
+ context: `validator.${method}('${trackingData.value}')`,
33
36
  history: [{ ...trackingData }],
34
37
  args: [{
35
- value: data.args[0],
38
+ value: trackingData.value,
36
39
  tracked: true
37
40
  }],
38
41
  result: {
@@ -59,8 +62,9 @@ module.exports = function(core) {
59
62
  { name: 'validator', file: `lib/${validator}` },
60
63
  (index) => {
61
64
  const patchFn = typeof index === 'object' ? index.default : index;
65
+ const name = `validator.${validator}`;
62
66
  return patcher.patch(patchFn, {
63
- name: `validator.${validator}`,
67
+ name,
64
68
  patchType,
65
69
  post(data) {
66
70
  const matches = validator === 'matches';