@contrast/assess 1.31.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 (154) 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.test.js +210 -0
  116. package/lib/dataflow/sources/install/fastify/index.test.js +33 -0
  117. package/lib/dataflow/sources/install/formidable1.test.js +119 -0
  118. package/lib/dataflow/sources/install/hapi/hapi.test.js +172 -0
  119. package/lib/dataflow/sources/install/hapi/index.test.js +33 -0
  120. package/lib/dataflow/sources/install/http.test.js +155 -0
  121. package/lib/dataflow/sources/install/koa/index.test.js +40 -0
  122. package/lib/dataflow/sources/install/koa/koa-bodyparsers.test.js +161 -0
  123. package/lib/dataflow/sources/install/koa/koa-multer.test.js +197 -0
  124. package/lib/dataflow/sources/install/koa/koa-routers.test.js +146 -0
  125. package/lib/dataflow/sources/install/koa/koa2.test.js +145 -0
  126. package/lib/dataflow/sources/install/multer1.test.js +145 -0
  127. package/lib/dataflow/sources/install/qs6.test.js +131 -0
  128. package/lib/dataflow/sources/install/querystring.test.js +82 -0
  129. package/lib/dataflow/sources/install/restify/fieldedTextBodyParser.test.js +88 -0
  130. package/lib/dataflow/sources/install/restify/index.test.js +38 -0
  131. package/lib/dataflow/sources/install/restify/jsonBodyParser.test.js +144 -0
  132. package/lib/dataflow/sources/install/restify/router.test.js +83 -0
  133. package/lib/dataflow/tag-utils-complete.test.js +27 -0
  134. package/lib/dataflow/tag-utils.test.js +192 -0
  135. package/lib/dataflow/tracker.test.js +216 -0
  136. package/lib/dataflow/utils/is-safe-content-type.test.js +16 -0
  137. package/lib/dataflow/utils/is-vulnerable.test.js +115 -0
  138. package/lib/event-factory.test.js +321 -0
  139. package/lib/get-policy.test.js +194 -0
  140. package/lib/get-source-context.test.js +108 -0
  141. package/lib/index.test.js +41 -0
  142. package/lib/make-source-context.test.js +50 -0
  143. package/lib/response-scanning/handlers/index.test.js +425 -0
  144. package/lib/response-scanning/handlers/utils.test.js +391 -0
  145. package/lib/response-scanning/index.test.js +41 -0
  146. package/lib/response-scanning/install/http.test.js +175 -0
  147. package/lib/rule-scopes.test.js +27 -0
  148. package/lib/session-configuration/handlers.test.js +84 -0
  149. package/lib/session-configuration/index.test.js +36 -0
  150. package/lib/session-configuration/install/express-session.test.js +220 -0
  151. package/lib/session-configuration/install/fastify-cookie.test.js +65 -0
  152. package/lib/session-configuration/install/hapi.test.js +269 -0
  153. package/lib/session-configuration/install/koa.test.js +92 -0
  154. package/package.json +2 -2
@@ -0,0 +1,968 @@
1
+ 'use strict';
2
+
3
+ const { expect } = require('chai');
4
+ const { initAssessFixture } = require('@contrast/test/fixtures');
5
+ const {
6
+ DataflowTag: { UNTRUSTED, LIMITED_CHARS, HEADER, WEAK_URL_ENCODED },
7
+ traverseValues,
8
+ } = require('@contrast/common');
9
+
10
+ describe('assess dataflow propagation JSON parse', function () {
11
+ let core,
12
+ tracker,
13
+ trackString,
14
+ simulateRequestScope,
15
+ objWithTrackedStr,
16
+ arrayWithTrackedStr,
17
+ standardEvent;
18
+
19
+ beforeEach(function () {
20
+ ({ core, trackString, simulateRequestScope } = initAssessFixture());
21
+
22
+ tracker = core.assess.dataflow.tracker;
23
+
24
+ core.assess.dataflow.propagation.jsonInstrumentation.stringify.install();
25
+ core.assess.dataflow.propagation.stringInstrumentation.concat.install();
26
+ core.assess.dataflow.propagation.jsonInstrumentation.parse.install();
27
+
28
+ simulateRequestScope(() => {
29
+ objWithTrackedStr = {
30
+ prop1: trackString('test'),
31
+ prop2: trackString('test2'),
32
+ test2: "':\"",
33
+ arr: [
34
+ {
35
+ name: {
36
+ prop3: trackString('test3', {
37
+ tags: {
38
+ [UNTRUSTED]: [0, 0, 2, 4],
39
+ },
40
+ }),
41
+ },
42
+ },
43
+ 2,
44
+ trackString('test4'),
45
+ ],
46
+ nested: {
47
+ prop4: trackString('prop'),
48
+ },
49
+ prop5: trackString('test5', {
50
+ tags: {
51
+ [UNTRUSTED]: [0, 0, 2, 3],
52
+ [LIMITED_CHARS]: [0, 1],
53
+ [HEADER]: [1, 2],
54
+ [WEAK_URL_ENCODED]: [0, 1, 3, 4],
55
+ },
56
+ }),
57
+ };
58
+ });
59
+
60
+ simulateRequestScope(() => {
61
+ arrayWithTrackedStr = [
62
+ [1, trackString('test')],
63
+ [
64
+ 2,
65
+ trackString('test2', {
66
+ tags: {
67
+ [UNTRUSTED]: [0, 0, 2, 3],
68
+ [LIMITED_CHARS]: [0, 1],
69
+ [HEADER]: [1, 2],
70
+ [WEAK_URL_ENCODED]: [0, 1, 3, 4],
71
+ },
72
+ }),
73
+ ],
74
+ [
75
+ 3,
76
+ trackString('test3', {
77
+ tags: {
78
+ [UNTRUSTED]: [0, 0, 2, 3],
79
+ },
80
+ }),
81
+ ],
82
+ [
83
+ 4,
84
+ {
85
+ name: trackString('test4', {
86
+ tags: {
87
+ [UNTRUSTED]: [0, 0, 2, 3],
88
+ [LIMITED_CHARS]: [0, 1],
89
+ },
90
+ }),
91
+ },
92
+ ],
93
+ [trackString('test5'), 5],
94
+ ];
95
+ });
96
+
97
+ standardEvent = {
98
+ object: {
99
+ tracked: false,
100
+ value: 'JSON',
101
+ },
102
+ name: 'JSON.parse',
103
+ source: 'P0',
104
+ target: 'R',
105
+ tags: {},
106
+ };
107
+ });
108
+
109
+ afterEach(function () {
110
+ core.assess.dataflow.propagation.jsonInstrumentation.parse.uninstall();
111
+ });
112
+
113
+ it('will not propagate if the instrumentation is locked', function () {
114
+ simulateRequestScope(function () {
115
+ core.scopes.instrumentation.run({ lock: true }, function () {
116
+ const trackedString = trackString('foo');
117
+ const string = JSON.stringify({ prop: trackedString });
118
+ const result = JSON.parse(string);
119
+
120
+ expect(result.prop).to.be.equal('foo');
121
+ expect(tracker.getData(result.prop)).to.be.null;
122
+ });
123
+ });
124
+ });
125
+
126
+ it('will not propagate if there is no assess context', function () {
127
+ simulateRequestScope(function () {
128
+ const trackedString = trackString('foo');
129
+ const string = JSON.stringify({ prop: trackedString });
130
+ const result = JSON.parse(string);
131
+
132
+ expect(result.prop).to.be.equal('foo');
133
+ expect(tracker.getData(result.prop)).to.be.null;
134
+ }, {});
135
+ });
136
+
137
+ it('should ignore empty string', function () {
138
+ simulateRequestScope(function () {
139
+ const trackedString = trackString('');
140
+ const string = JSON.stringify({ prop: trackedString });
141
+ const result = JSON.parse(string);
142
+
143
+ expect(result.prop).to.be.equal('');
144
+ expect(tracker.getData(result.prop)).to.be.null;
145
+ });
146
+ });
147
+
148
+ it('will not propagate if the argument is not tracked', function () {
149
+ simulateRequestScope(function () {
150
+ const string = JSON.stringify({ prop: 'foo' });
151
+ const result = JSON.parse(string);
152
+
153
+ expect(result.prop).to.be.equal('foo');
154
+ expect(tracker.getData(result.prop)).to.be.null;
155
+ });
156
+ });
157
+
158
+ it('parse objWithTrackedStr', function () {
159
+ simulateRequestScope(() => {
160
+ const string = JSON.stringify(objWithTrackedStr);
161
+ const result = JSON.parse(string);
162
+ const prop1 = tracker.getData(result.prop1);
163
+
164
+ const expected1 = Object.assign({}, standardEvent, {
165
+ args: [
166
+ {
167
+ value: string,
168
+ tracked: true,
169
+ },
170
+ ],
171
+ value: result.prop1,
172
+ result: {
173
+ tracked: true,
174
+ value: result.prop1,
175
+ },
176
+ tags: {
177
+ [UNTRUSTED]: [0, 3],
178
+ },
179
+ });
180
+ for (const key in expected1) {
181
+ expect(prop1[key]).deep.equal(expected1[key], `prop1.${key} is not as expected`);
182
+ }
183
+
184
+ const prop2 = tracker.getData(result.prop2);
185
+ const expected2 = Object.assign({}, standardEvent, {
186
+ args: [
187
+ {
188
+ value: string,
189
+ tracked: true,
190
+ },
191
+ ],
192
+ value: result.prop2,
193
+ result: {
194
+ tracked: true,
195
+ value: result.prop2,
196
+ },
197
+ tags: {
198
+ [UNTRUSTED]: [0, 4],
199
+ },
200
+ });
201
+ for (const key in expected2) {
202
+ expect(prop2[key]).deep.equal(expected2[key], `prop2.${key} is not as expected`);
203
+ }
204
+
205
+ const prop3 = tracker.getData(result.arr[0].name.prop3);
206
+ const expected3 = Object.assign({}, standardEvent, {
207
+ args: [
208
+ {
209
+ value: string,
210
+ tracked: true,
211
+ },
212
+ ],
213
+ value: prop3.value,
214
+ result: {
215
+ tracked: true,
216
+ value: prop3.value,
217
+ },
218
+ tags: {
219
+ [UNTRUSTED]: [0, 0, 2, 4],
220
+ },
221
+ });
222
+ for (const key in expected3) {
223
+ expect(prop3[key]).deep.equal(expected3[key], `prop3.${key} is not as expected`);
224
+ }
225
+
226
+ const test4 = tracker.getData(result.arr[2]);
227
+ const expected4 = Object.assign({}, standardEvent, {
228
+ args: [
229
+ {
230
+ value: string,
231
+ tracked: true,
232
+ },
233
+ ],
234
+ value: test4.value,
235
+ result: {
236
+ tracked: true,
237
+ value: test4.value,
238
+ },
239
+ tags: {
240
+ [UNTRUSTED]: [0, 4],
241
+ },
242
+ });
243
+ for (const key in expected4) {
244
+ expect(test4[key]).deep.equal(expected4[key], `test4.${key} is not as expected`);
245
+ }
246
+
247
+ const prop4 = tracker.getData(result.nested.prop4);
248
+ const expected5 = Object.assign({}, standardEvent, {
249
+ args: [
250
+ {
251
+ value: string,
252
+ tracked: true,
253
+ },
254
+ ],
255
+ value: prop4.value,
256
+ result: {
257
+ tracked: true,
258
+ value: prop4.value,
259
+ },
260
+ tags: {
261
+ [UNTRUSTED]: [0, 3],
262
+ },
263
+ });
264
+ for (const key in expected5) {
265
+ expect(prop4[key]).deep.equal(expected5[key], `prop4.${key} is not as expected`);
266
+ }
267
+
268
+ const prop5 = tracker.getData(result.prop5);
269
+ const expected6 = Object.assign({}, standardEvent, {
270
+ args: [
271
+ {
272
+ value: string,
273
+ tracked: true,
274
+ },
275
+ ],
276
+ value: prop5.value,
277
+ result: {
278
+ tracked: true,
279
+ value: prop5.value,
280
+ },
281
+ tags: {
282
+ [UNTRUSTED]: [0, 0, 2, 3],
283
+ [LIMITED_CHARS]: [0, 1],
284
+ [HEADER]: [1, 2],
285
+ [WEAK_URL_ENCODED]: [0, 1, 3, 4],
286
+ },
287
+ });
288
+ for (const key in expected6) {
289
+ expect(prop5[key]).deep.equal(expected6[key], `prop5.${key} is not as expected`);
290
+ }
291
+
292
+ });
293
+ });
294
+
295
+ it('parse objWithTrackedStr with reviver', function () {
296
+ simulateRequestScope(() => {
297
+ const string = JSON.stringify(objWithTrackedStr);
298
+ const reviver = (key, value) => value;
299
+ const result = JSON.parse(string, reviver);
300
+
301
+ const prop1 = tracker.getData(result.prop1);
302
+ const expectedProp1 = Object.assign({}, standardEvent, {
303
+ args: [
304
+ {
305
+ value: string,
306
+ tracked: true,
307
+ },
308
+ {
309
+ tracked: false,
310
+ value: reviver.toString(),
311
+ },
312
+ ],
313
+ value: result.prop1,
314
+ result: {
315
+ tracked: true,
316
+ value: result.prop1,
317
+ },
318
+ tags: {
319
+ [UNTRUSTED]: [0, 3],
320
+ },
321
+ });
322
+ for (const key in expectedProp1) {
323
+ expect(prop1[key]).deep.equal(expectedProp1[key], `prop1.${key} is not as expected`);
324
+ }
325
+
326
+ const prop2 = tracker.getData(result.prop2);
327
+ const expectedProp2 = Object.assign({}, standardEvent, {
328
+ args: [
329
+ {
330
+ value: string,
331
+ tracked: true,
332
+ },
333
+ {
334
+ tracked: false,
335
+ value: reviver.toString(),
336
+ },
337
+ ],
338
+ value: result.prop2,
339
+ result: {
340
+ tracked: true,
341
+ value: result.prop2,
342
+ },
343
+ tags: {
344
+ [UNTRUSTED]: [0, 4],
345
+ },
346
+ });
347
+ for (const key in expectedProp2) {
348
+ expect(prop2[key]).deep.equal(expectedProp2[key], `prop2.${key} is not as expected`);
349
+ }
350
+
351
+ const prop3 = tracker.getData(result.arr[0].name.prop3);
352
+ const expectedProp3 = Object.assign({}, standardEvent, {
353
+ args: [
354
+ {
355
+ value: string,
356
+ tracked: true,
357
+ },
358
+ {
359
+ tracked: false,
360
+ value: reviver.toString(),
361
+ },
362
+ ],
363
+ value: prop3.value,
364
+ result: {
365
+ tracked: true,
366
+ value: prop3.value,
367
+ },
368
+ tags: {
369
+ [UNTRUSTED]: [0, 0, 2, 4],
370
+ },
371
+ });
372
+ for (const key in expectedProp3) {
373
+ expect(prop3[key]).deep.equal(expectedProp3[key], `prop3.${key} is not as expected`);
374
+ }
375
+
376
+ const test4 = tracker.getData(result.arr[2]);
377
+ const expectedTest4 = Object.assign({}, standardEvent, {
378
+ args: [
379
+ {
380
+ value: string,
381
+ tracked: true,
382
+ },
383
+ {
384
+ tracked: false,
385
+ value: reviver.toString(),
386
+ },
387
+ ],
388
+ value: test4.value,
389
+ result: {
390
+ tracked: true,
391
+ value: test4.value,
392
+ },
393
+ tags: {
394
+ [UNTRUSTED]: [0, 4],
395
+ },
396
+ });
397
+ for (const key in expectedTest4) {
398
+ expect(test4[key]).deep.equal(expectedTest4[key], `test4.${key} is not as expected`);
399
+ }
400
+
401
+ const prop4 = tracker.getData(result.nested.prop4);
402
+ const expectedProp4 = Object.assign({}, standardEvent, {
403
+ args: [
404
+ {
405
+ value: string,
406
+ tracked: true,
407
+ },
408
+ {
409
+ tracked: false,
410
+ value: reviver.toString(),
411
+ },
412
+ ],
413
+ value: prop4.value,
414
+ result: {
415
+ tracked: true,
416
+ value: prop4.value,
417
+ },
418
+ tags: {
419
+ [UNTRUSTED]: [0, 3],
420
+ },
421
+ });
422
+ for (const key in expectedProp4) {
423
+ expect(prop4[key]).deep.equal(expectedProp4[key], `prop4.${key} is not as expected`);
424
+ }
425
+
426
+ const prop5 = tracker.getData(result.prop5);
427
+ const expectedProp5 = Object.assign({}, standardEvent, {
428
+ args: [
429
+ {
430
+ value: string,
431
+ tracked: true,
432
+ },
433
+ {
434
+ tracked: false,
435
+ value: reviver.toString(),
436
+ },
437
+ ],
438
+ value: prop5.value,
439
+ result: {
440
+ tracked: true,
441
+ value: prop5.value,
442
+ },
443
+ tags: {
444
+ [UNTRUSTED]: [0, 0, 2, 3],
445
+ [LIMITED_CHARS]: [0, 1],
446
+ [HEADER]: [1, 2],
447
+ [WEAK_URL_ENCODED]: [0, 1, 3, 4],
448
+ },
449
+ });
450
+ for (const key in expectedProp5) {
451
+ expect(prop5[key]).deep.equal(expectedProp5[key], `prop5.${key} is not as expected`);
452
+ }
453
+ });
454
+ });
455
+
456
+ it('parse arrayWithTrackedStr', function () {
457
+ simulateRequestScope(() => {
458
+ const string = JSON.stringify(arrayWithTrackedStr);
459
+ const result = JSON.parse(string);
460
+
461
+ const test = tracker.getData(result[0][1]);
462
+ expect(test).to.be.like({
463
+ ...standardEvent,
464
+ args: [
465
+ {
466
+ value: string,
467
+ tracked: true,
468
+ },
469
+ ],
470
+ value: test.value,
471
+ result: {
472
+ tracked: true,
473
+ value: test.value,
474
+ },
475
+ tags: {
476
+ [UNTRUSTED]: [0, 3],
477
+ },
478
+ });
479
+
480
+ const test2 = tracker.getData(result[1][1]);
481
+ expect(test2).to.be.like({
482
+ ...standardEvent,
483
+ args: [
484
+ {
485
+ value: string,
486
+ tracked: true,
487
+ },
488
+ ],
489
+ value: test2.value,
490
+ result: {
491
+ tracked: true,
492
+ value: test2.value,
493
+ },
494
+ tags: {
495
+ [UNTRUSTED]: [0, 0, 2, 3],
496
+ [LIMITED_CHARS]: [0, 1],
497
+ [HEADER]: [1, 2],
498
+ [WEAK_URL_ENCODED]: [0, 1, 3, 4],
499
+ },
500
+ });
501
+
502
+ const test3 = tracker.getData(result[2][1]);
503
+ expect(test3).to.be.like({
504
+ ...standardEvent,
505
+ args: [
506
+ {
507
+ value: string,
508
+ tracked: true,
509
+ },
510
+ ],
511
+ value: test3.value,
512
+ result: {
513
+ tracked: true,
514
+ value: test3.value,
515
+ },
516
+ tags: {
517
+ [UNTRUSTED]: [0, 0, 2, 3],
518
+ },
519
+ });
520
+
521
+ const test4 = tracker.getData(result[3][1].name);
522
+ expect(test4).to.be.like({
523
+ ...standardEvent,
524
+ args: [
525
+ {
526
+ value: string,
527
+ tracked: true,
528
+ },
529
+ ],
530
+ value: test4.value,
531
+ result: {
532
+ tracked: true,
533
+ value: test4.value,
534
+ },
535
+ tags: {
536
+ [UNTRUSTED]: [0, 0, 2, 3],
537
+ [LIMITED_CHARS]: [0, 1],
538
+ },
539
+ });
540
+
541
+ const test5 = tracker.getData(result[4][0]);
542
+ expect(test5).to.be.like({
543
+ ...standardEvent,
544
+ args: [
545
+ {
546
+ value: string,
547
+ tracked: true,
548
+ },
549
+ ],
550
+ value: test5.value,
551
+ result: {
552
+ tracked: true,
553
+ value: test5.value,
554
+ },
555
+ tags: {
556
+ [UNTRUSTED]: [0, 4],
557
+ },
558
+ });
559
+ });
560
+ });
561
+
562
+ it('parse arrayWithTrackedStr with reviver', function () {
563
+ simulateRequestScope(() => {
564
+ const string = JSON.stringify(arrayWithTrackedStr);
565
+ const reviver = (key, value) => (key === '' ? new Map(value) : value);
566
+ const result = JSON.parse(string, reviver);
567
+
568
+ const test = tracker.getData(result.get(1));
569
+ expect(test).to.be.like({
570
+ ...standardEvent,
571
+ args: [
572
+ {
573
+ value: string,
574
+ tracked: true,
575
+ },
576
+ {
577
+ tracked: false,
578
+ value: reviver.toString(),
579
+ },
580
+ ],
581
+ value: test.value,
582
+ result: {
583
+ tracked: true,
584
+ value: test.value,
585
+ },
586
+ tags: {
587
+ [UNTRUSTED]: [0, 3],
588
+ },
589
+ });
590
+
591
+ const test2 = tracker.getData(result.get(2));
592
+ expect(test2).to.be.like({
593
+ ...standardEvent,
594
+ args: [
595
+ {
596
+ value: string,
597
+ tracked: true,
598
+ },
599
+ {
600
+ tracked: false,
601
+ value: reviver.toString(),
602
+ },
603
+ ],
604
+ value: test2.value,
605
+ result: {
606
+ tracked: true,
607
+ value: test2.value,
608
+ },
609
+ tags: {
610
+ [UNTRUSTED]: [0, 0, 2, 3],
611
+ [LIMITED_CHARS]: [0, 1],
612
+ [HEADER]: [1, 2],
613
+ [WEAK_URL_ENCODED]: [0, 1, 3, 4],
614
+ },
615
+ });
616
+
617
+ const test3 = tracker.getData(result.get(3));
618
+ expect(test3).to.be.like({
619
+ ...standardEvent,
620
+ args: [
621
+ {
622
+ value: string,
623
+ tracked: true,
624
+ },
625
+ {
626
+ tracked: false,
627
+ value: reviver.toString(),
628
+ },
629
+ ],
630
+ value: test3.value,
631
+ result: {
632
+ tracked: true,
633
+ value: test3.value,
634
+ },
635
+ tags: {
636
+ [UNTRUSTED]: [0, 0, 2, 3],
637
+ },
638
+ });
639
+
640
+ const test4 = tracker.getData(result.get(4).name);
641
+ expect(test4).to.be.like({
642
+ ...standardEvent,
643
+ args: [
644
+ {
645
+ value: string,
646
+ tracked: true,
647
+ },
648
+ {
649
+ tracked: false,
650
+ value: reviver.toString(),
651
+ },
652
+ ],
653
+ value: test4.value,
654
+ result: {
655
+ tracked: true,
656
+ value: test4.value,
657
+ },
658
+ tags: {
659
+ [UNTRUSTED]: [0, 0, 2, 3],
660
+ [LIMITED_CHARS]: [0, 1],
661
+ },
662
+ });
663
+
664
+ const test5 = tracker.getData(
665
+ Array.from(result.keys()).find((key) => key === 'test5')
666
+ );
667
+ expect(test5).to.be.like({
668
+ ...standardEvent,
669
+ args: [
670
+ {
671
+ value: string,
672
+ tracked: true,
673
+ },
674
+ {
675
+ tracked: false,
676
+ value: reviver.toString(),
677
+ },
678
+ ],
679
+ value: test5.value,
680
+ result: {
681
+ tracked: true,
682
+ value: test5.value,
683
+ },
684
+ tags: {
685
+ [UNTRUSTED]: [0, 4],
686
+ },
687
+ });
688
+ });
689
+ });
690
+
691
+ it('parse with spaces', function () {
692
+ simulateRequestScope(() => {
693
+ const input = {
694
+ prop1: 'test',
695
+ prop2: trackString('test2'),
696
+ arr: [
697
+ {
698
+ name: {
699
+ prop3: trackString('test3'),
700
+ },
701
+ },
702
+ 2,
703
+ trackString('test4'),
704
+ ],
705
+ nested: {
706
+ prop4: trackString('prop'),
707
+ },
708
+ prop5: trackString('test5'),
709
+ };
710
+
711
+ const string = JSON.stringify(input);
712
+ const result = JSON.parse(string, null, ' ');
713
+
714
+ const prop1 = tracker.getData(result.prop1);
715
+ expect(prop1).to.be.null;
716
+
717
+ const prop2 = tracker.getData(result.prop2);
718
+ expect(prop2).to.be.like({
719
+ ...standardEvent,
720
+ args: [
721
+ {
722
+ value: string,
723
+ tracked: true,
724
+ },
725
+ ],
726
+ value: result.prop2,
727
+ result: {
728
+ tracked: true,
729
+ value: result.prop2,
730
+ },
731
+ tags: {
732
+ [UNTRUSTED]: [0, 4],
733
+ },
734
+ });
735
+
736
+ const prop3 = tracker.getData(result.arr[0].name.prop3);
737
+ expect(prop3).to.be.like({
738
+ ...standardEvent,
739
+ args: [
740
+ {
741
+ value: string,
742
+ tracked: true,
743
+ },
744
+ ],
745
+ value: prop3.value,
746
+ result: {
747
+ tracked: true,
748
+ value: prop3.value,
749
+ },
750
+ tags: {
751
+ [UNTRUSTED]: [0, 4],
752
+ },
753
+ });
754
+
755
+ const test4 = tracker.getData(result.arr[2]);
756
+ expect(test4).to.be.like({
757
+ ...standardEvent,
758
+ args: [
759
+ {
760
+ value: string,
761
+ tracked: true,
762
+ },
763
+ ],
764
+ value: test4.value,
765
+ result: {
766
+ tracked: true,
767
+ value: test4.value,
768
+ },
769
+ tags: {
770
+ [UNTRUSTED]: [0, 4],
771
+ },
772
+ });
773
+
774
+ const prop4 = tracker.getData(result.nested.prop4);
775
+ expect(prop4).to.be.like({
776
+ ...standardEvent,
777
+ args: [
778
+ {
779
+ value: string,
780
+ tracked: true,
781
+ },
782
+ ],
783
+ value: prop4.value,
784
+ result: {
785
+ tracked: true,
786
+ value: prop4.value,
787
+ },
788
+ tags: {
789
+ [UNTRUSTED]: [0, 3],
790
+ },
791
+ });
792
+
793
+ const prop5 = tracker.getData(result.prop5);
794
+ expect(prop5).to.be.like({
795
+ ...standardEvent,
796
+ args: [
797
+ {
798
+ value: string,
799
+ tracked: true,
800
+ },
801
+ ],
802
+ value: prop5.value,
803
+ result: {
804
+ tracked: true,
805
+ value: prop5.value,
806
+ },
807
+ tags: {
808
+ [UNTRUSTED]: [0, 4],
809
+ },
810
+ });
811
+ });
812
+ });
813
+
814
+ it('parse with spaces and key values as values', function () {
815
+ simulateRequestScope(() => {
816
+ const input = {
817
+ test: true,
818
+ test2: trackString('test2'),
819
+ arr: [
820
+ {
821
+ name: {
822
+ test3: trackString('test3'),
823
+ },
824
+ },
825
+ 2,
826
+ trackString('test4'),
827
+ ],
828
+ nested: {
829
+ prop: trackString('prop'),
830
+ },
831
+ test5: trackString('test5'),
832
+ };
833
+
834
+ const string = JSON.stringify(input, null);
835
+ const result = JSON.parse(string);
836
+
837
+ const prop1 = tracker.getData(result.test);
838
+ expect(prop1).to.be.null;
839
+
840
+ const prop2 = tracker.getData(result.test2);
841
+ expect(prop2).to.be.like({
842
+ ...standardEvent,
843
+ args: [
844
+ {
845
+ value: string,
846
+ tracked: true,
847
+ },
848
+ ],
849
+ value: prop2.value,
850
+ result: {
851
+ tracked: true,
852
+ value: prop2.value,
853
+ },
854
+ tags: {
855
+ [UNTRUSTED]: [0, 4],
856
+ },
857
+ });
858
+
859
+ const prop3 = tracker.getData(result.arr[0].name.test3);
860
+ expect(prop3).to.be.like({
861
+ ...standardEvent,
862
+ args: [
863
+ {
864
+ value: string,
865
+ tracked: true,
866
+ },
867
+ ],
868
+ value: prop3.value,
869
+ result: {
870
+ tracked: true,
871
+ value: prop3.value,
872
+ },
873
+ tags: {
874
+ [UNTRUSTED]: [0, 4],
875
+ },
876
+ });
877
+
878
+ const test4 = tracker.getData(result.arr[2]);
879
+ expect(test4).to.be.like({
880
+ ...standardEvent,
881
+ args: [
882
+ {
883
+ value: string,
884
+ tracked: true,
885
+ },
886
+ ],
887
+ value: test4.value,
888
+ result: {
889
+ tracked: true,
890
+ value: test4.value,
891
+ },
892
+ tags: {
893
+ [UNTRUSTED]: [0, 4],
894
+ },
895
+ });
896
+
897
+ const prop4 = tracker.getData(result.nested.prop);
898
+ expect(prop4).to.be.like({
899
+ ...standardEvent,
900
+ args: [
901
+ {
902
+ value: string,
903
+ tracked: true,
904
+ },
905
+ ],
906
+ value: prop4.value,
907
+ result: {
908
+ tracked: true,
909
+ value: prop4.value,
910
+ },
911
+ tags: {
912
+ [UNTRUSTED]: [0, 3],
913
+ },
914
+ });
915
+
916
+ const prop5 = tracker.getData(result.test5);
917
+ expect(prop5).to.be.like({
918
+ ...standardEvent,
919
+ args: [
920
+ {
921
+ value: string,
922
+ tracked: true,
923
+ },
924
+ ],
925
+ value: prop5.value,
926
+ result: {
927
+ tracked: true,
928
+ value: prop5.value,
929
+ },
930
+ tags: {
931
+ [UNTRUSTED]: [0, 4],
932
+ },
933
+ });
934
+ });
935
+ });
936
+
937
+ it('propagates correctly when JSON string has leading and trailing \\n characters', function () {
938
+ const jsonStr = '\n{"hello":"world","foo":["bar"]}\n';
939
+ // happens outside of request scope so no propagation occurs
940
+ const expected = JSON.parse(jsonStr);
941
+
942
+ simulateRequestScope(() => {
943
+ const tracked = trackString(jsonStr);
944
+ const result = JSON.parse(tracked);
945
+
946
+ expect(result).to.deep.equal(expected);
947
+ traverseValues(result, (path, type, value) => {
948
+ expect(tracker.getData(value)?.tags?.[UNTRUSTED]).to.deep.equal([0, value.length - 1]);
949
+ });
950
+ });
951
+ });
952
+
953
+ it('propagates correctly when JSON string is partially tracked', function () {
954
+ const jsonStr = '\n{"hello":"world","foo":["bar"]}\n';
955
+ // xxx
956
+ const expected = JSON.parse(jsonStr);
957
+
958
+ simulateRequestScope(() => {
959
+ const tracked = trackString(jsonStr, { tags: { UNTRUSTED: [11, 13] } });
960
+ const result = JSON.parse(tracked);
961
+
962
+ expect(result).to.deep.equal(expected);
963
+ expect(tracker.getData(result.hello).tags).to.deep.equal({
964
+ UNTRUSTED: [0, 2],
965
+ });
966
+ });
967
+ });
968
+ });