@contrast/assess 1.30.0 → 1.32.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 (156) hide show
  1. package/lib/crypto-analysis/install/crypto.test.js +146 -0
  2. package/lib/crypto-analysis/install/math.test.js +65 -0
  3. package/lib/dataflow/index.test.js +36 -0
  4. package/lib/dataflow/propagation/index.test.js +103 -0
  5. package/lib/dataflow/propagation/install/JSON/index.test.js +50 -0
  6. package/lib/dataflow/propagation/install/JSON/parse-fn.test.js +232 -0
  7. package/lib/dataflow/propagation/install/JSON/parse.test.js +968 -0
  8. package/lib/dataflow/propagation/install/JSON/stringify.test.js +265 -0
  9. package/lib/dataflow/propagation/install/array-prototype-join.test.js +106 -0
  10. package/lib/dataflow/propagation/install/buffer.test.js +109 -0
  11. package/lib/dataflow/propagation/install/contrast-methods/add.test.js +94 -0
  12. package/lib/dataflow/propagation/install/contrast-methods/index.test.js +49 -0
  13. package/lib/dataflow/propagation/install/contrast-methods/number.test.js +50 -0
  14. package/lib/dataflow/propagation/install/contrast-methods/string.test.js +148 -0
  15. package/lib/dataflow/propagation/install/contrast-methods/tag.test.js +145 -0
  16. package/lib/dataflow/propagation/install/decode-uri-component.test.js +78 -0
  17. package/lib/dataflow/propagation/install/ejs/escape-xml.test.js +69 -0
  18. package/lib/dataflow/propagation/install/ejs/template.test.js +62 -0
  19. package/lib/dataflow/propagation/install/encode-uri.test.js +83 -0
  20. package/lib/dataflow/propagation/install/escape-html.test.js +71 -0
  21. package/lib/dataflow/propagation/install/escape.test.js +73 -0
  22. package/lib/dataflow/propagation/install/handlebars-utils-escape-expression.test.js +71 -0
  23. package/lib/dataflow/propagation/install/isnumeric-0.test.js +58 -0
  24. package/lib/dataflow/propagation/install/joi/any.test.js +270 -0
  25. package/lib/dataflow/propagation/install/joi/array.test.js +912 -0
  26. package/lib/dataflow/propagation/install/joi/boolean.test.js +103 -0
  27. package/lib/dataflow/propagation/install/joi/expression.test.js +76 -0
  28. package/lib/dataflow/propagation/install/joi/index.test.js +39 -0
  29. package/lib/dataflow/propagation/install/joi/number.test.js +103 -0
  30. package/lib/dataflow/propagation/install/joi/object.test.js +119 -0
  31. package/lib/dataflow/propagation/install/joi/ref.test.js +607 -0
  32. package/lib/dataflow/propagation/install/joi/string-schema.test.js +513 -0
  33. package/lib/dataflow/propagation/install/mongoose/index.test.js +42 -0
  34. package/lib/dataflow/propagation/install/mongoose/schema-map.test.js +348 -0
  35. package/lib/dataflow/propagation/install/mongoose/schema-mixed.test.js +512 -0
  36. package/lib/dataflow/propagation/install/mongoose/schema-string.test.js +160 -0
  37. package/lib/dataflow/propagation/install/mustache-escape.test.js +62 -0
  38. package/lib/dataflow/propagation/install/mysql-connection-escape.test.js +74 -0
  39. package/lib/dataflow/propagation/install/parse-int.test.js +48 -0
  40. package/lib/dataflow/propagation/install/path/basename.test.js +143 -0
  41. package/lib/dataflow/propagation/install/path/dirname.test.js +167 -0
  42. package/lib/dataflow/propagation/install/path/extname.test.js +141 -0
  43. package/lib/dataflow/propagation/install/path/format.test.js +250 -0
  44. package/lib/dataflow/propagation/install/path/index.test.js +45 -0
  45. package/lib/dataflow/propagation/install/path/join-and-resolve.test.js +485 -0
  46. package/lib/dataflow/propagation/install/path/normalize.test.js +176 -0
  47. package/lib/dataflow/propagation/install/path/parse.test.js +238 -0
  48. package/lib/dataflow/propagation/install/path/relative.test.js +239 -0
  49. package/lib/dataflow/propagation/install/path/toNamespacedPath.test.js +158 -0
  50. package/lib/dataflow/propagation/install/pug/index.test.js +55 -0
  51. package/lib/dataflow/propagation/install/pug-runtime-escape.test.js +69 -0
  52. package/lib/dataflow/propagation/install/querystring/escape.test.js +63 -0
  53. package/lib/dataflow/propagation/install/querystring/index.test.js +40 -0
  54. package/lib/dataflow/propagation/install/querystring/parse.test.js +272 -0
  55. package/lib/dataflow/propagation/install/querystring/stringify.test.js +301 -0
  56. package/lib/dataflow/propagation/install/reg-exp-prototype-exec.test.js +281 -0
  57. package/lib/dataflow/propagation/install/send.test.js +63 -0
  58. package/lib/dataflow/propagation/install/sequelize/query-generator.test.js +73 -0
  59. package/lib/dataflow/propagation/install/sequelize/sql-string.test.js +130 -0
  60. package/lib/dataflow/propagation/install/sql-template-strings.test.js +100 -0
  61. package/lib/dataflow/propagation/install/string/concat.test.js +132 -0
  62. package/lib/dataflow/propagation/install/string/format-methods.test.js +61 -0
  63. package/lib/dataflow/propagation/install/string/html-methods.test.js +164 -0
  64. package/lib/dataflow/propagation/install/string/index.test.js +103 -0
  65. package/lib/dataflow/propagation/install/string/match-all.test.js +399 -0
  66. package/lib/dataflow/propagation/install/string/match.test.js +361 -0
  67. package/lib/dataflow/propagation/install/string/replace.test.js +588 -0
  68. package/lib/dataflow/propagation/install/string/slice.test.js +265 -0
  69. package/lib/dataflow/propagation/install/string/split.test.js +500 -0
  70. package/lib/dataflow/propagation/install/string/substring.test.js +238 -0
  71. package/lib/dataflow/propagation/install/string/trim.test.js +122 -0
  72. package/lib/dataflow/propagation/install/unescape.test.js +78 -0
  73. package/lib/dataflow/propagation/install/url/domain-parsers.test.js +63 -0
  74. package/lib/dataflow/propagation/install/url/parse.test.js +391 -0
  75. package/lib/dataflow/propagation/install/url/searchParams.test.js +538 -0
  76. package/lib/dataflow/propagation/install/url/url.test.js +466 -0
  77. package/lib/dataflow/propagation/install/util-format.test.js +336 -0
  78. package/lib/dataflow/propagation/install/validator/hooks.test.js +211 -0
  79. package/lib/dataflow/sinks/index.test.js +78 -0
  80. package/lib/dataflow/sinks/install/child-process.test.js +338 -0
  81. package/lib/dataflow/sinks/install/eval.test.js +95 -0
  82. package/lib/dataflow/sinks/install/express/index.test.js +33 -0
  83. package/lib/dataflow/sinks/install/express/reflected-xss.js +55 -57
  84. package/lib/dataflow/sinks/install/express/reflected-xss.test.js +109 -0
  85. package/lib/dataflow/sinks/install/express/unvalidated-redirect.test.js +144 -0
  86. package/lib/dataflow/sinks/install/fastify/index.test.js +32 -0
  87. package/lib/dataflow/sinks/install/fastify/unvalidated-redirect.test.js +130 -0
  88. package/lib/dataflow/sinks/install/fs.test.js +138 -0
  89. package/lib/dataflow/sinks/install/function.test.js +103 -0
  90. package/lib/dataflow/sinks/install/hapi/index.test.js +32 -0
  91. package/lib/dataflow/sinks/install/hapi/unvalidated-redirect.test.js +130 -0
  92. package/lib/dataflow/sinks/install/http/index.test.js +33 -0
  93. package/lib/dataflow/sinks/install/http/request.test.js +184 -0
  94. package/lib/dataflow/sinks/install/http/server-response.test.js +162 -0
  95. package/lib/dataflow/sinks/install/koa/index.test.js +32 -0
  96. package/lib/dataflow/sinks/install/koa/unvalidated-redirect.test.js +200 -0
  97. package/lib/dataflow/sinks/install/libxmljs.test.js +158 -0
  98. package/lib/dataflow/sinks/install/marsdb.test.js +166 -0
  99. package/lib/dataflow/sinks/install/mongodb.test.js +621 -0
  100. package/lib/dataflow/sinks/install/mssql.test.js +136 -0
  101. package/lib/dataflow/sinks/install/mysql.test.js +233 -0
  102. package/lib/dataflow/sinks/install/node-serialize.test.js +85 -0
  103. package/lib/dataflow/sinks/install/postgres.test.js +158 -0
  104. package/lib/dataflow/sinks/install/restify.test.js +142 -0
  105. package/lib/dataflow/sinks/install/sequelize.test.js +100 -0
  106. package/lib/dataflow/sinks/install/sqlite3.test.js +118 -0
  107. package/lib/dataflow/sinks/install/vm.test.js +326 -0
  108. package/lib/dataflow/sources/handler.test.js +463 -0
  109. package/lib/dataflow/sources/index.test.js +58 -0
  110. package/lib/dataflow/sources/install/body-parser1.test.js +248 -0
  111. package/lib/dataflow/sources/install/busboy.test.js +152 -0
  112. package/lib/dataflow/sources/install/cookie-parser1.test.js +143 -0
  113. package/lib/dataflow/sources/install/express/params.test.js +105 -0
  114. package/lib/dataflow/sources/install/express/parsedUrl.test.js +65 -0
  115. package/lib/dataflow/sources/install/fastify/fastify.js +1 -1
  116. package/lib/dataflow/sources/install/fastify/fastify.test.js +210 -0
  117. package/lib/dataflow/sources/install/fastify/index.test.js +33 -0
  118. package/lib/dataflow/sources/install/formidable1.test.js +119 -0
  119. package/lib/dataflow/sources/install/hapi/hapi.test.js +172 -0
  120. package/lib/dataflow/sources/install/hapi/index.test.js +33 -0
  121. package/lib/dataflow/sources/install/http.test.js +155 -0
  122. package/lib/dataflow/sources/install/koa/index.test.js +40 -0
  123. package/lib/dataflow/sources/install/koa/koa-bodyparsers.test.js +161 -0
  124. package/lib/dataflow/sources/install/koa/koa-multer.test.js +197 -0
  125. package/lib/dataflow/sources/install/koa/koa-routers.test.js +146 -0
  126. package/lib/dataflow/sources/install/koa/koa2.test.js +145 -0
  127. package/lib/dataflow/sources/install/multer1.test.js +145 -0
  128. package/lib/dataflow/sources/install/qs6.test.js +131 -0
  129. package/lib/dataflow/sources/install/querystring.test.js +82 -0
  130. package/lib/dataflow/sources/install/restify/fieldedTextBodyParser.test.js +88 -0
  131. package/lib/dataflow/sources/install/restify/index.test.js +38 -0
  132. package/lib/dataflow/sources/install/restify/jsonBodyParser.test.js +144 -0
  133. package/lib/dataflow/sources/install/restify/router.test.js +83 -0
  134. package/lib/dataflow/tag-utils-complete.test.js +27 -0
  135. package/lib/dataflow/tag-utils.test.js +192 -0
  136. package/lib/dataflow/tracker.test.js +216 -0
  137. package/lib/dataflow/utils/is-safe-content-type.test.js +16 -0
  138. package/lib/dataflow/utils/is-vulnerable.test.js +115 -0
  139. package/lib/event-factory.test.js +321 -0
  140. package/lib/get-policy.test.js +194 -0
  141. package/lib/get-source-context.test.js +108 -0
  142. package/lib/index.test.js +41 -0
  143. package/lib/make-source-context.test.js +50 -0
  144. package/lib/response-scanning/handlers/index.test.js +425 -0
  145. package/lib/response-scanning/handlers/utils.test.js +391 -0
  146. package/lib/response-scanning/index.test.js +41 -0
  147. package/lib/response-scanning/install/http.test.js +175 -0
  148. package/lib/rule-scopes.test.js +27 -0
  149. package/lib/session-configuration/handlers.test.js +84 -0
  150. package/lib/session-configuration/index.test.js +36 -0
  151. package/lib/session-configuration/install/express-session.test.js +220 -0
  152. package/lib/session-configuration/install/fastify-cookie.test.js +65 -0
  153. package/lib/session-configuration/install/hapi.test.js +269 -0
  154. package/lib/session-configuration/install/koa.js +50 -44
  155. package/lib/session-configuration/install/koa.test.js +92 -0
  156. package/package.json +2 -2
@@ -0,0 +1,301 @@
1
+ 'use strict';
2
+
3
+ const { expect } = require('chai');
4
+ const {
5
+ DataflowTag: { URL_ENCODED, UNTRUSTED }
6
+ } = require('@contrast/common');
7
+ const { initAssessFixture } = require('@contrast/test/fixtures');
8
+
9
+ describe('assess dataflow propagation querystring stringify', function () {
10
+ let core, simulateRequestScope, tracker, trackString, querystring;
11
+
12
+ before(function () {
13
+ ({ core, trackString, simulateRequestScope } = initAssessFixture());
14
+ tracker = core.assess.dataflow.tracker;
15
+
16
+ querystring = require('querystring');
17
+ core.depHooks.resolve.withArgs({ name: 'querystring' }).yields(querystring);
18
+
19
+ core.assess.dataflow.propagation.stringInstrumentation.concat.install();
20
+ core.assess.dataflow.propagation.querystringInstrumentation.escape.install();
21
+ core.assess.dataflow.propagation.querystringInstrumentation.stringify.install();
22
+ });
23
+
24
+ after(function () {
25
+ core.assess.dataflow.propagation.stringInstrumentation.concat.uninstall();
26
+ core.assess.dataflow.propagation.querystringInstrumentation.escape.uninstall();
27
+ core.assess.dataflow.propagation.querystringInstrumentation.stringify.uninstall();
28
+ });
29
+
30
+ [
31
+ 'stringify',
32
+ 'encode',
33
+ ].forEach((methodName) => {
34
+ const baseEvent = {
35
+ moduleName: 'querystring',
36
+ methodName: 'stringify',
37
+ object: { tracked: false, value: 'querystring' },
38
+ source: 'P',
39
+ target: 'R',
40
+ };
41
+
42
+ describe(`querystring.${methodName}`, function () {
43
+
44
+ it('no action is taken when result is not a string', function () {
45
+ simulateRequestScope(() => {
46
+ [null, undefined, 'foo', {}, []].forEach((value) => {
47
+ const result = querystring.stringify(value);
48
+ expect(tracker.getData(result)).to.be.null;
49
+ });
50
+ });
51
+ });
52
+
53
+ it('result should not be tracked if no object values are tracked', function () {
54
+ simulateRequestScope(() => {
55
+ const obj = {
56
+ x: 'abcd',
57
+ y: 'bcde',
58
+ z: '',
59
+ w: 'cdef'
60
+ };
61
+ const result = querystring.stringify(obj);
62
+ expect(tracker.getData(result)).to.be.null;
63
+ });
64
+ });
65
+
66
+ it('should propagate tags correctly for flat object with tracked values (values unchanged)', function () {
67
+ simulateRequestScope(() => {
68
+ const obj = {
69
+ x: trackString('abcd'),
70
+ y: trackString('bcde'),
71
+ z: '',
72
+ w: 'cdef',
73
+ aa: ['a', trackString('b')],
74
+ bb: { c: 'd' },
75
+ };
76
+ const result = querystring.stringify(obj);
77
+ const strInfo = tracker.getData(result);
78
+
79
+ expect(strInfo).to.deep.include({
80
+ ...baseEvent,
81
+ context: 'querystring.stringify({\n x: \'abcd\',\n y: \'bcde\',\n z: \'\',\n w: \'cdef\',\n aa: [ \'a\', \'b\' ],\n bb: { c: \'d\' }\n})',
82
+ result: { tracked: true, value: 'x=abcd&y=bcde&z=&w=cdef&aa=a&aa=b&bb=' },
83
+ tags: {
84
+ [UNTRUSTED]: [2, 5, 9, 12, 32, 32],
85
+ [URL_ENCODED]: [2, 5, 9, 12, 32, 32],
86
+ },
87
+ value: 'x=abcd&y=bcde&z=&w=cdef&aa=a&aa=b&bb=',
88
+ });
89
+ expect(strInfo.history).to.have.lengthOf(3);
90
+ });
91
+ });
92
+
93
+ it('should propagate tags correctly for flat object with tracked values (values percent encoded)', function () {
94
+ simulateRequestScope(() => {
95
+ const obj = {
96
+ x: trackString('✓😐🙉'),
97
+ y: trackString('/?#'),
98
+ z: '',
99
+ w: 'cdef'
100
+ };
101
+ const result = querystring.stringify(obj);
102
+ const strInfo = tracker.getData(result);
103
+
104
+ expect(strInfo).to.deep.include({
105
+ ...baseEvent,
106
+ context: 'querystring.stringify({ x: \'✓😐🙉\', y: \'/?#\', z: \'\', w: \'cdef\' })',
107
+ result: {
108
+ tracked: true,
109
+ value: 'x=%E2%9C%93%F0%9F%98%90%F0%9F%99%89&y=%2F%3F%23&z=&w=cdef'
110
+ },
111
+ tags: {
112
+ [UNTRUSTED]: [2, 34, 38, 46],
113
+ [URL_ENCODED]: [2, 34, 38, 46],
114
+ },
115
+ // ✓ 😐 🙉 / ? #
116
+ value: 'x=%E2%9C%93%F0%9F%98%90%F0%9F%99%89&y=%2F%3F%23&z=&w=cdef'
117
+ });
118
+ expect(strInfo.history).to.have.lengthOf(2);
119
+ });
120
+ });
121
+
122
+ it('will propagate tags correctly through custom encoding function (custom validators disabled)', function () {
123
+ simulateRequestScope(() => {
124
+ const obj = {
125
+ foo: ['a', trackString('b'), trackString('c')],
126
+ bar: 'baz'
127
+ };
128
+ const options = {
129
+ encodeURIComponent(value) {
130
+ return value.concat('_');
131
+ }
132
+ };
133
+ const result = querystring.stringify(obj, false, false, options);
134
+ const strInfo = tracker.getData(result);
135
+
136
+ expect(strInfo).to.deep.include({
137
+ ...baseEvent,
138
+ context: 'querystring.stringify({ foo: [ \'a\', \'b\', \'c\' ], bar: \'baz\' }, false, false, { encodeURIComponent: [Function: encodeURIComponent] })',
139
+ result: { tracked: true, value: 'foo_=a_&foo_=b_&foo_=c_&bar_=baz_' },
140
+ tags: {
141
+ [UNTRUSTED]: [13, 14, 21, 22],
142
+ },
143
+ value: 'foo_=a_&foo_=b_&foo_=c_&bar_=baz_',
144
+ });
145
+ expect(strInfo.tags).not.to.have.property(URL_ENCODED);
146
+ expect(strInfo.history).to.have.lengthOf(2);
147
+ });
148
+ });
149
+
150
+ // NODE-2835
151
+ it.skip('will propagate tags correctly through custom encoding function (custom validators enabled)', function () {
152
+ core.config.assess.trust_custom_validators = true;
153
+ simulateRequestScope(() => {
154
+ const obj = {
155
+ foo: ['a', trackString('b'), trackString('c')],
156
+ bar: 'baz'
157
+ };
158
+ const options = {
159
+ encodeURIComponent(value) {
160
+ return value.concat('_');
161
+ }
162
+ };
163
+ const result = querystring.stringify(obj, false, false, options);
164
+ const strInfo = tracker.getData(result);
165
+
166
+ expect(strInfo).to.deep.include({
167
+ ...baseEvent,
168
+ context: 'querystring.stringify({ foo: [ \'a\', \'b\', \'c\' ], bar: \'baz\' }, false, false, { encodeURIComponent: [Function: encodeURIComponent] })',
169
+ result: { tracked: true, value: 'foo_=a_&foo_=b_&foo_=c_&bar_=baz_' },
170
+ tags: {
171
+ [UNTRUSTED]: [13, 14, 21, 22],
172
+ [URL_ENCODED]: [13, 14, 21, 22],
173
+ },
174
+ value: 'foo_=a_&foo_=b_&foo_=c_&bar_=baz_',
175
+ });
176
+ expect(strInfo.history).to.have.lengthOf(2);
177
+ });
178
+ });
179
+
180
+ it('options with encodeURIComponent (add\'l tagged string in escape)', function () {
181
+ simulateRequestScope(() => {
182
+ const obj = {
183
+ foo: trackString('a'),
184
+ bar: ['b', 'c']
185
+ };
186
+ const options = {
187
+ encodeURIComponent(value) {
188
+ return value.concat(trackString('_'));
189
+ }
190
+ };
191
+ const result = querystring.stringify(obj, false, false, options);
192
+ const strInfo = tracker.getData(result);
193
+
194
+ expect(strInfo).to.deep.include({
195
+ ...baseEvent,
196
+ context: 'querystring.stringify({ foo: \'a\', bar: [ \'b\', \'c\' ] }, false, false, { encodeURIComponent: [Function: encodeURIComponent] })',
197
+ result: { tracked: true, value: 'foo_=a_&bar_=b_&bar_=c_' },
198
+ tags: {
199
+ [UNTRUSTED]: [3, 3, 5, 6, 11, 11, 13, 14, 19, 19, 21, 22],
200
+ },
201
+ value: 'foo_=a_&bar_=b_&bar_=c_',
202
+ });
203
+ expect(strInfo.history).to.have.lengthOf(5);
204
+ });
205
+ });
206
+
207
+ it('should handle multi-char separators and eq', function () {
208
+ simulateRequestScope(() => {
209
+ const obj = {
210
+ x: trackString('abcd'),
211
+ y: trackString('bcde'),
212
+ z: '',
213
+ w: 'cdef'
214
+ };
215
+ const result = querystring.stringify(obj, '&&&', '====');
216
+ const strInfo = tracker.getData(result);
217
+
218
+ expect(strInfo).to.deep.include({
219
+ ...baseEvent,
220
+ context: 'querystring.stringify({ x: \'abcd\', y: \'bcde\', z: \'\', w: \'cdef\' }, \'&&&\', \'====\')',
221
+ result: { tracked: true, value: 'x====abcd&&&y====bcde&&&z====&&&w====cdef' },
222
+ tags: {
223
+ [UNTRUSTED]: [5, 8, 17, 20],
224
+ [URL_ENCODED]: [5, 8, 17, 20],
225
+ },
226
+ value: 'x====abcd&&&y====bcde&&&z====&&&w====cdef',
227
+ });
228
+ expect(strInfo.history).to.have.lengthOf(2);
229
+ });
230
+ });
231
+
232
+ it('should propagate if `separator` or `equals` char is also contained in key or value', function () {
233
+ simulateRequestScope(() => {
234
+ const obj = {
235
+ x: trackString('abcd'),
236
+ y: trackString('bcde'),
237
+ z: '',
238
+ w: 'cdef'
239
+ };
240
+ const result = querystring.stringify(obj, 'a', 'b');
241
+ const strInfo = tracker.getData(result);
242
+
243
+ expect(strInfo).to.deep.include({
244
+ ...baseEvent,
245
+ context: 'querystring.stringify({ x: \'abcd\', y: \'bcde\', z: \'\', w: \'cdef\' }, \'a\', \'b\')',
246
+ result: { tracked: true, value: 'xbabcdaybbcdeazbawbcdef' },
247
+ tags: {
248
+ [UNTRUSTED]: [2, 5, 9, 12],
249
+ [URL_ENCODED]: [2, 5, 9, 12],
250
+ },
251
+ value: 'xbabcdaybbcdeazbawbcdef',
252
+ });
253
+ expect(strInfo.history).to.have.lengthOf(2);
254
+ });
255
+ });
256
+
257
+ it('should handle multiple tag ranges being shifted', function () {
258
+ simulateRequestScope(() => {
259
+ const obj = {
260
+ x: trackString('one😐two🙉three', {
261
+ tags: {
262
+ tag1: [0, 2],
263
+ tag2: [5, 7],
264
+ tag3: [10, 14],
265
+ }
266
+ }),
267
+ y: trackString('one#two?three', {
268
+ tags: {
269
+ tag1: [0, 2],
270
+ tag2: [4, 6],
271
+ tag3: [8, 12],
272
+ }
273
+ }),
274
+ };
275
+ const result = querystring.stringify(obj);
276
+ const strInfo = tracker.getData(result);
277
+
278
+ expect(strInfo).to.deep.include({
279
+ ...baseEvent,
280
+ result: { tracked: true, value: 'x=one%F0%9F%98%90two%F0%9F%99%89three&y=one%23two%3Fthree' },
281
+ tags: {
282
+ tag1: [2, 36, 40, 56],
283
+ tag2: [2, 36, 40, 56],
284
+ tag3: [2, 36, 40, 56],
285
+ [URL_ENCODED]: [2, 36, 40, 56],
286
+ },
287
+ value: 'x=one%F0%9F%98%90two%F0%9F%99%89three&y=one%23two%3Fthree',
288
+ });
289
+ expect(strInfo.history).to.have.lengthOf(2);
290
+ });
291
+ });
292
+
293
+ it.skip('should propagate tags from basic keys', function () {
294
+ // this is also skipped in v4 b/c tracking object keys causes issues
295
+ // const obj = {
296
+ // [trackString('x')] = 'abcd',
297
+ // };
298
+ });
299
+ });
300
+ });
301
+ });
@@ -0,0 +1,281 @@
1
+ 'use strict';
2
+
3
+ const { expect } = require('chai');
4
+ const { inspect } = require('util');
5
+ const { DataflowTag: { UNTRUSTED, CUSTOM_ENCODED, LIMITED_CHARS } } = require('@contrast/common');
6
+ const { initAssessFixture } = require('@contrast/test/fixtures');
7
+
8
+ describe('assess dataflow propagation RegExp exec', function () {
9
+ let core, simulateRequestScope, trackString, tracker, instr;
10
+
11
+ beforeEach(function () {
12
+ ({
13
+ core,
14
+ trackString,
15
+ simulateRequestScope
16
+ } = initAssessFixture());
17
+
18
+ tracker = core.assess.dataflow.tracker;
19
+ instr = require('./reg-exp-prototype-exec')(core);
20
+ instr.install();
21
+ });
22
+
23
+ afterEach(function () {
24
+ instr.uninstall();
25
+ });
26
+
27
+ describe('base cases', function () {
28
+ [
29
+ [],
30
+ [undefined],
31
+ ['']
32
+ ].forEach((val) => {
33
+ it(`returns null when called with ${inspect(val[0])}`, function () {
34
+ simulateRequestScope(() => {
35
+ const re = /test/;
36
+ const ret = re.exec(val[0]);
37
+
38
+ expect(ret).to.be.null;
39
+ });
40
+ });
41
+ });
42
+
43
+ it('returns null when no match is found', function () {
44
+ simulateRequestScope(() => {
45
+ const re = /test/;
46
+ const ret = re.exec('foo');
47
+
48
+ expect(ret).to.be.null;
49
+ });
50
+ });
51
+
52
+ it('returns only the first match string', function () {
53
+ simulateRequestScope(() => {
54
+ const re = /o/;
55
+ const ret = re.exec('foo');
56
+
57
+ expect(ret).to.deep.equal(['o']);
58
+ expect(tracker.getData(ret[0])).to.be.null;
59
+ });
60
+ });
61
+
62
+ it('returns first match string using capture groups', function () {
63
+ simulateRequestScope(() => {
64
+ const re = /(f)|(o)/;
65
+ const ret = re.exec('foo');
66
+ expect(ret).to.deep.equal(['f', 'f', undefined]);
67
+ expect(ret.groups).to.be.undefined;
68
+ ret.forEach((val) => {
69
+ expect(tracker.getData(val)).to.be.null;
70
+ });
71
+ });
72
+ });
73
+
74
+ it('returns first match string using named capture groups', function () {
75
+ simulateRequestScope(() => {
76
+ const re = /(?<foo>f)|(?<bar>o)/;
77
+ const ret = re.exec('foo');
78
+ expect(ret).to.deep.equal(['f', 'f', undefined]);
79
+ expect(ret.groups.foo).to.equal('f');
80
+ ret.forEach((val) => {
81
+ expect(tracker.getData(val)).to.be.null;
82
+ });
83
+ });
84
+ });
85
+ });
86
+
87
+ describe('propagation', function () {
88
+ it('propagates a fully tracked string', function () {
89
+ simulateRequestScope(() => {
90
+ const re = /f/;
91
+ const extern = trackString('foo');
92
+
93
+ const ret = re.exec(extern);
94
+ const matchInfo = tracker.getData(ret[0]);
95
+
96
+ expect(ret).to.deep.equal(['f']);
97
+ expect(matchInfo.tags).to.deep.equal({
98
+ [UNTRUSTED]: [0, 0]
99
+ });
100
+ expect(matchInfo.name).to.equal('RegExp.prototype.exec');
101
+ });
102
+ });
103
+
104
+ it.skip('propagates strings when iteratively called', function() {
105
+ simulateRequestScope(() => {
106
+ const re = /^\/?$/i;
107
+ // 0123456789*1234567
108
+ const extern = trackString('');
109
+
110
+ let ret;
111
+ while ((ret = re.exec(''))) {
112
+ const matchInfo = tracker.getData(ret[0]);
113
+ // console.log(re.lastIndex, ret.index, ret.indices);
114
+ re.lastIndex += 1;
115
+ //console.log(matchInfo);
116
+ // check match info...
117
+ }
118
+ });
119
+ });
120
+
121
+ it('propagates a partialy tracked string', function () {
122
+ simulateRequestScope(() => {
123
+ const re = /bar/;
124
+ const extern = trackString('foobar', {
125
+ tags: {
126
+ [UNTRUSTED]: [0, 4]
127
+ }
128
+ });
129
+
130
+ const ret = re.exec(extern);
131
+ const matchInfo = tracker.getData(ret[0]);
132
+
133
+ expect(ret).to.deep.equal(['bar']);
134
+ expect(matchInfo.tags).to.deep.equal({
135
+ [UNTRUSTED]: [0, 1]
136
+ });
137
+ expect(matchInfo.name).to.equal('RegExp.prototype.exec');
138
+ });
139
+ });
140
+
141
+ it('does not propagate if we exeeded the maximum propagation count for a match', function () {
142
+ simulateRequestScope(() => {
143
+ const re = /foo/;
144
+ const extern = trackString('foobar');
145
+
146
+ const ret = re.exec(extern);
147
+ const matchInfo = tracker.getData(ret[0]);
148
+
149
+ expect(ret).to.deep.equal(['foo']);
150
+ expect(matchInfo).to.be.null;
151
+ }, { assess: { propagationEventsCount: 500 } });
152
+ });
153
+
154
+ it('does not propagate if we exeeded the maximum propagation count for a group', function () {
155
+ simulateRequestScope(() => {
156
+ const re = /foo(?<bar>bar)/;
157
+ const extern = trackString('foobar');
158
+
159
+ const ret = re.exec(extern);
160
+ const foobarInfo = tracker.getData(ret[0]);
161
+ const barInfo = tracker.getData(ret[1]);
162
+ const barGroupInfo = tracker.getData(ret.groups.b);
163
+
164
+ expect(ret).to.deep.equal([
165
+ 'foobar',
166
+ 'bar'
167
+ ]);
168
+ expect(ret.groups.bar).to.equal('bar');
169
+ expect(foobarInfo.tags).to.deep.equal({
170
+ [UNTRUSTED]: [0, 5]
171
+ });
172
+ expect(barInfo.tags).to.deep.equal({
173
+ [UNTRUSTED]: [0, 2]
174
+ });
175
+ expect(barGroupInfo).to.be.null;
176
+ }, { assess: { propagationEventsCount: 498 } });
177
+ });
178
+
179
+
180
+ it('handles RegExp that matches an empty string', function () {
181
+ simulateRequestScope(() => {
182
+ const re = /foo(?<empty>)bar/;
183
+ const extern = trackString('foobar');
184
+
185
+ const ret = re.exec(extern);
186
+ const matchInfo = tracker.getData(ret[0]);
187
+ const emptyMatchInfo = tracker.getData(ret[1]);
188
+ const emptyGroupInfo = tracker.getData(ret.groups.empty);
189
+
190
+ expect(ret).to.deep.equal([
191
+ 'foobar',
192
+ '',
193
+ ]);
194
+ expect(matchInfo.tags).to.deep.equal({
195
+ [UNTRUSTED]: [0, 5]
196
+ });
197
+ expect(matchInfo.name).to.equal('RegExp.prototype.exec');
198
+ expect(emptyMatchInfo).to.be.null;
199
+ expect(ret.groups.empty).to.equal('');
200
+ expect(emptyGroupInfo).to.be.null;
201
+ });
202
+ });
203
+
204
+ it('handles RegExp that matches the full string', function () {
205
+ simulateRequestScope(() => {
206
+ const re = /(?<full>foobar)/;
207
+ const extern = trackString('foobar');
208
+
209
+ const ret = re.exec(extern);
210
+ const matchInfo = tracker.getData(ret[0]);
211
+ const captrueMatchInfo = tracker.getData(ret[1]);
212
+ const fullGroupInfo = tracker.getData(ret.groups.full);
213
+
214
+ expect(ret).to.deep.equal([
215
+ 'foobar',
216
+ 'foobar',
217
+ ]);
218
+ expect(matchInfo.tags).to.deep.equal({
219
+ [UNTRUSTED]: [0, 5]
220
+ });
221
+ expect(matchInfo.name).to.equal('RegExp.prototype.exec');
222
+ expect(captrueMatchInfo.tags).to.deep.equal({
223
+ [UNTRUSTED]: [0, 5]
224
+ });
225
+ expect(captrueMatchInfo.name).to.equal('RegExp.prototype.exec');
226
+ expect(ret.groups.full).to.equal('foobar');
227
+ expect(fullGroupInfo.tags).to.deep.equal({
228
+ [UNTRUSTED]: [0, 5]
229
+ });
230
+ expect(fullGroupInfo.name).to.equal('RegExp.prototype.exec');
231
+ });
232
+ });
233
+
234
+ it('handles complex cases', function () {
235
+ simulateRequestScope(() => {
236
+ const re = /quick\s(?<color>brown|black).+?(jumps).+(Black)/gi;
237
+ // 01234567890123456789012345678901234567890123456789012345678
238
+ const extern = trackString('The Quick Brown Fox Jumps Over The Lazy Black And Brown Dog', {
239
+ // 1 2 3 4 5
240
+ tags: {
241
+ [UNTRUSTED]: [0, 6, 13, 15, 40, 44],
242
+ [CUSTOM_ENCODED]: [16, 19],
243
+ [LIMITED_CHARS]: [20, 29, 40, 58]
244
+ }
245
+ });
246
+
247
+ const ret = re.exec(extern);
248
+ const fullMatchInfo = tracker.getData(ret[0]);
249
+ const brownInfo = tracker.getData(ret[1]);
250
+ const jumpsInfo = tracker.getData(ret[2]);
251
+ const blackInfo = tracker.getData(ret[3]);
252
+ const colorGroupInfo = tracker.getData(ret.groups.color);
253
+
254
+
255
+ expect(ret).to.deep.equal([
256
+ 'Quick Brown Fox Jumps Over The Lazy Black',
257
+ 'Brown',
258
+ 'Jumps',
259
+ 'Black'
260
+ ]);
261
+ expect(ret.groups.color).to.equal('Brown');
262
+ expect(fullMatchInfo.tags).to.deep.equal({
263
+ [UNTRUSTED]: [0, 2, 9, 11, 36, 40],
264
+ [CUSTOM_ENCODED]: [12, 15],
265
+ [LIMITED_CHARS]: [16, 25, 36, 40]
266
+ });
267
+ expect(brownInfo.tags).to.deep.equal({
268
+ [UNTRUSTED]: [3, 4],
269
+ });
270
+ expect(jumpsInfo.tags).to.deep.equal({
271
+ [LIMITED_CHARS]: [0, 4]
272
+ });
273
+ expect(blackInfo.tags).to.deep.equal({
274
+ [UNTRUSTED]: [0, 4],
275
+ [LIMITED_CHARS]: [0, 4]
276
+ });
277
+ expect(colorGroupInfo.tags).to.deep.equal(brownInfo.tags);
278
+ });
279
+ });
280
+ });
281
+ });
@@ -0,0 +1,63 @@
1
+ 'use strict';
2
+
3
+ const { expect } = require('chai');
4
+ const sinon = require('sinon');
5
+ const { initAssessFixture } = require('../../../../../test/fixtures');
6
+ const {
7
+ DataflowTag: { UNTRUSTED }
8
+ } = require('@contrast/common');
9
+
10
+ describe('assess send', function() {
11
+ let core, simulateRequestScope, mockSend, sendInstanceMock, trackString, tracker;
12
+
13
+ beforeEach(function() {
14
+ ({
15
+ core,
16
+ simulateRequestScope,
17
+ trackString
18
+ } = initAssessFixture());
19
+
20
+ tracker = core.assess.dataflow.tracker;
21
+
22
+ sendInstanceMock = {
23
+ sendFile: sinon.stub()
24
+ };
25
+
26
+ require('./send')(core).install();
27
+
28
+ const result = core.depHooks.resolve.yield(() => sendInstanceMock);
29
+ mockSend = result[0];
30
+ });
31
+
32
+ afterEach(function() {
33
+ sinon.resetHistory();
34
+ });
35
+
36
+ it('should skip instrumentation if it is out of scope', function() {
37
+ const path = trackString('foo');
38
+ sendInstanceMock.sendFile = (path) => {
39
+ expect(tracker.getData(path)).to.be.null;
40
+ };
41
+
42
+ const { sendFile } = mockSend();
43
+ sendFile(path);
44
+ });
45
+
46
+ it('should create an untagged copy of the string', function() {
47
+ simulateRequestScope(() => {
48
+ const path = trackString('foo');
49
+
50
+ sendInstanceMock.sendFile = (path) => {
51
+ expect(tracker.getData(path)).to.be.null;
52
+ };
53
+
54
+ const { sendFile } = mockSend();
55
+ sendFile(path);
56
+
57
+ const stringInfo = tracker.getData(path);
58
+ expect(stringInfo.tags).to.be.eql({
59
+ [UNTRUSTED]: [0, 2]
60
+ });
61
+ });
62
+ });
63
+ });