@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,336 @@
1
+
2
+ 'use strict';
3
+
4
+ const {
5
+ DataflowTag: { UNTRUSTED }
6
+ } = require('@contrast/common');
7
+ const util = require('util');
8
+ const { expect } = require('chai');
9
+ const { initAssessFixture } = require('@contrast/test/fixtures');
10
+
11
+ describe('assess dataflow propagation util.format', function () {
12
+ let core, trackString, simulateRequestScope, tracker;
13
+
14
+ beforeEach(function () {
15
+ ({
16
+ core,
17
+ simulateRequestScope,
18
+ trackString
19
+ } = initAssessFixture());
20
+
21
+ tracker = core.assess.dataflow.tracker;
22
+ core.assess.dataflow.propagation.utilFormat.install();
23
+ core.assess.dataflow.propagation.jsonInstrumentation.parse.install();
24
+ core.assess.dataflow.propagation.jsonInstrumentation.stringify.install();
25
+ core.depHooks.resolve.yield(util);
26
+ });
27
+
28
+ it('will not propagate if there is no assess context', function () {
29
+ simulateRequestScope(function () {
30
+ const value = trackString('foo');
31
+ const result = util.format('%s:%s', value, 'bar');
32
+ expect(tracker.getData(result)).to.be.null;
33
+ }, {});
34
+ });
35
+
36
+ it('will not propagate if there instrumentation is locked', function () {
37
+ simulateRequestScope(function () {
38
+ core.scopes.instrumentation.run({ lock: true }, function () {
39
+ const value = trackString('foo');
40
+ const result = util.format('%s:%s', value, 'bar');
41
+ expect(tracker.getData(result)).to.be.null;
42
+ });
43
+ });
44
+ });
45
+
46
+ it('will not propagate numbers', function () {
47
+ simulateRequestScope(function () {
48
+ const result = util.format(1, 2, 3);
49
+ expect(tracker.getData(result)).to.be.null;
50
+ });
51
+ });
52
+
53
+ ['%d', '%i', '%f'].forEach((frmt) => {
54
+ it(`will not propagate numbers using ${frmt} format string`, function () {
55
+ simulateRequestScope(function () {
56
+ const result = util.format(frmt, 1, 2, 3);
57
+ expect(tracker.getData(result)).to.be.null;
58
+ });
59
+ });
60
+ });
61
+
62
+ it('will not propagate untracked strings', function () {
63
+ simulateRequestScope(function () {
64
+ const result = util.format('%s', 'foo');
65
+ expect(tracker.getData(result)).to.be.null;
66
+ });
67
+ });
68
+
69
+ it('will not propagate tracked strings not in result', function () {
70
+ simulateRequestScope(function () {
71
+ const value = trackString('%s');
72
+ const result = util.format(value, 'foo');
73
+ expect(tracker.getData(result)).to.be.null;
74
+ });
75
+ });
76
+
77
+ it('will not propagate css', function () {
78
+ simulateRequestScope(function () {
79
+ const value = trackString('<!DOCTYPE html>');
80
+ const result = util.format('%c', value);
81
+ expect(tracker.getData(result)).to.be.null;
82
+ });
83
+ });
84
+
85
+ describe('%s', function() {
86
+ it('will propagate single tracked strings', function () {
87
+ simulateRequestScope(function () {
88
+ const value = trackString('foo');
89
+ const result = util.format(value);
90
+ expect(result).to.be.equal('foo');
91
+ expect(tracker.getData(result).tags).to.be.deep.equal({
92
+ [UNTRUSTED]: [0, 2]
93
+ });
94
+ });
95
+ });
96
+
97
+ it('will propagate basic tracked strings', function () {
98
+ simulateRequestScope(function () {
99
+ const value = trackString('foo');
100
+ const result = util.format('%s', value);
101
+ expect(result).to.be.equal('foo');
102
+ expect(tracker.getData(result).tags).to.be.deep.equal({
103
+ [UNTRUSTED]: [0, 2]
104
+ });
105
+ });
106
+ });
107
+
108
+ it('will propagate multiple tracked strings', function () {
109
+ simulateRequestScope(function () {
110
+ const foo = trackString('foo');
111
+ const bar = trackString('bar');
112
+ const buzz = trackString('buzz');
113
+
114
+ const result = util.format('%s:%s %s-%s', foo, bar, 'fizz', buzz);
115
+ expect(result).to.be.equal('foo:bar fizz-buzz');
116
+ expect(tracker.getData(result).tags).to.be.deep.equal({
117
+ [UNTRUSTED]: [0, 2, 4, 6, 13, 16]
118
+ });
119
+ });
120
+ });
121
+
122
+ it('will propagate multiple identical tracked strings', function () {
123
+ simulateRequestScope(function () {
124
+ const foo1 = trackString('foo');
125
+ const foo2 = trackString('foo');
126
+ const buzz = trackString('buzz');
127
+
128
+ const result = util.format('%s:%s %s-%s', foo1, foo2, 'fizz', buzz);
129
+ expect(result).to.be.equal('foo:foo fizz-buzz');
130
+ expect(tracker.getData(result).tags).to.be.deep.equal({
131
+ [UNTRUSTED]: [0, 2, 4, 6, 13, 16]
132
+ });
133
+ });
134
+ });
135
+
136
+ it('will propagate partially tracked strings', function () {
137
+ simulateRequestScope(function () {
138
+ const foobar = trackString('foobar', { tags: { UNTRUSTED: [0, 2] } });
139
+ const fizzbuzz = trackString('fizzbuzz', { tags: { UNTRUSTED: [4, 7] } });
140
+
141
+ const result = util.format('%s===%s', foobar, fizzbuzz);
142
+ expect(result).to.be.equal('foobar===fizzbuzz');
143
+ expect(tracker.getData(result).tags).to.be.deep.equal({
144
+ [UNTRUSTED]: [0, 2, 13, 16]
145
+ });
146
+ });
147
+ });
148
+
149
+ it('will propagate multiple tag ranges', function () {
150
+ simulateRequestScope(function () {
151
+ const foo = trackString('foo', { tags: { 'FOO': [0, 2] } });
152
+ const bar = trackString('bar', { tags: { 'BAR': [0, 2] } });
153
+ const fizz = trackString('fizz', { tags: { 'FIZZ': [0, 3] } });
154
+
155
+ const result = util.format('%s,%s,%s', foo, bar, fizz);
156
+ expect(result).to.be.equal('foo,bar,fizz');
157
+ expect(tracker.getData(result).tags).to.be.deep.equal({
158
+ ['FOO']: [0, 2],
159
+ ['BAR']: [4, 6],
160
+ ['FIZZ']: [8, 11]
161
+ });
162
+ });
163
+ });
164
+ });
165
+
166
+ describe('%j', function() {
167
+ it('will propagate JSON parsed string', function () {
168
+ simulateRequestScope(function () {
169
+ const foo = trackString('{"val":"foo"}', { tags: { UNTRUSTED: [8, 10] } });
170
+
171
+ const fooJSON = JSON.parse(foo);
172
+ const result = util.format('%j', fooJSON);
173
+ expect(result).to.be.equal('{"val":"foo"}');
174
+ expect(tracker.getData(result).tags).to.be.deep.equal({
175
+ [UNTRUSTED]: [8, 10]
176
+ });
177
+ });
178
+ });
179
+
180
+ it('will propagate JSON parseable object', function () {
181
+ simulateRequestScope(function () {
182
+ const foo = trackString('foo');
183
+ const bar = trackString('bar');
184
+
185
+ const result = util.format('%j', { val1: foo, val2: bar });
186
+ expect(result).to.be.equal('{"val1":"foo","val2":"bar"}');
187
+ expect(tracker.getData(result).tags).to.be.deep.equal({
188
+ [UNTRUSTED]: [9, 11, 22, 24]
189
+ });
190
+ });
191
+ });
192
+
193
+ it('will propagate multiple objects', function () {
194
+ simulateRequestScope(function () {
195
+ const foo = trackString('foo');
196
+ const bar = trackString('bar');
197
+
198
+ const result = util.format('%j===%j', { val1: foo }, { val2: bar });
199
+ expect(result).to.be.equal('{"val1":"foo"}==={"val2":"bar"}');
200
+ expect(tracker.getData(result).tags).to.be.deep.equal({
201
+ [UNTRUSTED]: [9, 11, 26, 28]
202
+ });
203
+ });
204
+ });
205
+
206
+ it('will propagate identical objects', function () {
207
+ simulateRequestScope(function () {
208
+ const foo = trackString('foo');
209
+
210
+ const result = util.format('%j===%j', { val1: foo }, { val2: foo });
211
+ expect(result).to.be.equal('{"val1":"foo"}==={"val2":"foo"}');
212
+ expect(tracker.getData(result).tags).to.be.deep.equal({
213
+ [UNTRUSTED]: [9, 11, 26, 28]
214
+ });
215
+ });
216
+ });
217
+
218
+ it('will propagate multiple tag ranges', function () {
219
+ simulateRequestScope(function () {
220
+ const foo = trackString('foo', { tags: { 'FOO': [0, 2] } });
221
+ const bar = trackString('bar', { tags: { 'BAR': [0, 2] } });
222
+ const fizz = trackString('fizz', { tags: { 'FIZZ': [0, 3] } });
223
+
224
+ const result = util.format('%j,%j,%j', { val: foo }, { val: bar }, { val: fizz });
225
+ expect(result).to.be.equal('{"val":"foo"},{"val":"bar"},{"val":"fizz"}');
226
+ expect(tracker.getData(result).tags).to.be.deep.equal({
227
+ ['FOO']: [8, 10],
228
+ ['BAR']: [22, 24],
229
+ ['FIZZ']: [36, 39]
230
+ });
231
+ });
232
+ });
233
+ });
234
+
235
+ describe('%o', function() {
236
+ //TO DO: NODE-3235
237
+ ['%o', '%O'].forEach((frmt) => {
238
+ it(`${frmt} works but does not propagate`, function() {
239
+ simulateRequestScope(function () {
240
+ const foo = trackString('foo');
241
+ const result = util.format(frmt, { val: foo });
242
+ expect(result).to.be.equal("{ val: 'foo' }");
243
+ expect(tracker.getData(result)).to.be.null;
244
+ });
245
+ });
246
+ });
247
+ });
248
+
249
+ it('will propagate special character % correctly', function() {
250
+ simulateRequestScope(function () {
251
+ const foo = trackString('foo');
252
+
253
+ const result = util.format('%%%s%%', foo);
254
+ expect(result).to.be.equal('%foo%');
255
+ expect(tracker.getData(result).tags).to.be.deep.equal({
256
+ [UNTRUSTED]: [1, 3]
257
+ });
258
+ });
259
+ });
260
+
261
+ it('will propagate strings without format strings', function() {
262
+ simulateRequestScope(function () {
263
+ const foo = trackString('foo');
264
+ const bar = trackString('bar');
265
+ const buzz = trackString('buzz');
266
+
267
+ const result = util.format(foo, bar, 'fizz', buzz);
268
+ expect(result).to.be.equal('foo bar fizz buzz');
269
+ expect(tracker.getData(result).tags).to.be.deep.equal({
270
+ [UNTRUSTED]: [0, 2, 4, 6, 13, 16]
271
+ });
272
+ });
273
+ });
274
+
275
+ it('will propagate mix of string and number format strings', function() {
276
+ simulateRequestScope(function () {
277
+ const foo = trackString('foo');
278
+ const bar = trackString('bar');
279
+
280
+ const result = util.format('%s===%d===%s===%i', foo, 42, bar, 1.3);
281
+ expect(result).to.be.equal('foo===42===bar===1');
282
+ expect(tracker.getData(result).tags).to.be.deep.equal({
283
+ [UNTRUSTED]: [0, 2, 11, 13]
284
+ });
285
+ });
286
+ });
287
+
288
+ it('will propagate mix of string and JSON format strings', function() {
289
+ simulateRequestScope(function () {
290
+ const foo = trackString('foo');
291
+ const bar = trackString('bar');
292
+ const fizz = trackString('fizz');
293
+
294
+ const result = util.format('%s===%j===%s===%j', foo, { value: bar }, fizz, { value: 'buzz' });
295
+ expect(result).to.be.equal('foo==={"value":"bar"}===fizz==={"value":"buzz"}');
296
+ expect(tracker.getData(result).tags).to.be.deep.equal({
297
+ [UNTRUSTED]: [0, 2, 16, 18, 24, 27]
298
+ });
299
+ });
300
+ });
301
+
302
+ it('will propagate when number of format strings > number of arguments', function() {
303
+ simulateRequestScope(function () {
304
+ const foo = trackString('foo');
305
+
306
+ const result = util.format('%s===%s', foo);
307
+ expect(result).to.be.equal('foo===%s');
308
+ expect(tracker.getData(result).tags).to.be.deep.equal({
309
+ [UNTRUSTED]: [0, 2]
310
+ });
311
+ });
312
+ });
313
+
314
+ it('will propagate when number of format strings < number of arguments', function() {
315
+ simulateRequestScope(function () {
316
+ const foo = trackString('foo');
317
+ const bar = trackString('bar');
318
+
319
+ const result = util.format('==%s==', foo, bar);
320
+ expect(result).to.be.equal('==foo== bar');
321
+ expect(tracker.getData(result).tags).to.be.deep.equal({
322
+ [UNTRUSTED]: [2, 4, 8, 10]
323
+ });
324
+ });
325
+ });
326
+
327
+ it('will not error when number of format strings < number of arguments (obj)', function() {
328
+ simulateRequestScope(function () {
329
+ const foo = trackString('foo');
330
+ const bar = trackString('bar');
331
+
332
+ const result = util.format('==%o==', { foo }, { bar });
333
+ expect(result).to.be.equal("=={ foo: 'foo' }== { bar: 'bar' }");
334
+ });
335
+ });
336
+ });
@@ -0,0 +1,211 @@
1
+ 'use strict';
2
+
3
+ const sinon = require('sinon');
4
+ const { expect } = require('chai');
5
+ const {
6
+ DataflowTag: {
7
+ HTML_ENCODED,
8
+ LIMITED_CHARS,
9
+ UNTRUSTED,
10
+ }
11
+ } = require('@contrast/common');
12
+ const { validators, untrackers } = require('./methods');
13
+ const { initAssessFixture } = require('@contrast/test/fixtures');
14
+
15
+ describe('assess dataflow propagation validator', function () {
16
+ let core, simulateRequestScope, trackString, tracker, validatorFiles, validator;
17
+
18
+ // This setup is very expensive, so we only do it once.
19
+ before(function () {
20
+ ({ core, simulateRequestScope, trackString } = initAssessFixture());
21
+ core.config.assess.trust_custom_validators = true;
22
+ sinon.spy(core.patcher, 'patch');
23
+ tracker = core.assess.dataflow.tracker;
24
+
25
+ validatorFiles = {};
26
+ validator = {};
27
+
28
+ // most validator methods export a function with a circular default prop:
29
+ // <ref *1> [Function: escape] { default: [Circular *1] }
30
+ core.depHooks.resolve.callsFake((details, callback) => {
31
+ const [, method] = details.file.split('/');
32
+ validatorFiles[method] = sinon.stub().returns(true);
33
+ validatorFiles[method].default = validatorFiles[method];
34
+ validator[method] = callback(validatorFiles[method]);
35
+ });
36
+
37
+ // some, like isAlpha, export an object:
38
+ // { default: [Function: isAlpha], locales: [...] }
39
+ core.depHooks.resolve
40
+ .withArgs({ name: 'validator', file: 'lib/isAlpha' })
41
+ .callsFake((_, callback) => {
42
+ validatorFiles.isAlpha = { default: sinon.stub().returns(true) };
43
+ validator.isAlpha = callback(validatorFiles.isAlpha).default;
44
+ });
45
+
46
+
47
+ core.assess.dataflow.propagation.validatorInstrumentation.install();
48
+ });
49
+
50
+ it('patches every validator.js method', function () {
51
+ Object.keys(validators).forEach(method => {
52
+ expect(core.patcher.patch).to.have.been.calledWith(validatorFiles[method]);
53
+ });
54
+
55
+ untrackers.forEach(method => {
56
+ expect(core.patcher.patch).to.have.been.calledWith(validatorFiles[method]);
57
+ });
58
+
59
+ // sanitizers
60
+ expect(core.patcher.patch).to.have.been.calledWith(validatorFiles.escape);
61
+ // custom
62
+ expect(core.patcher.patch).to.have.been.calledWith(validatorFiles.isEmail);
63
+ });
64
+
65
+ describe('validators', function () {
66
+ Object.entries(validators).forEach(([method, tag]) => {
67
+ it(`${method}() tags with the correct tag`, function () {
68
+ simulateRequestScope(() => {
69
+ const extern = trackString('foo', {
70
+ tags: {
71
+ [UNTRUSTED]: [0, 2]
72
+ },
73
+ history: [{ mock: 'sourceEvent' }]
74
+ });
75
+
76
+ validator[method](extern);
77
+ expect(tracker.getData(extern).tags).to.deep.equal({
78
+ [UNTRUSTED]: [0, 2],
79
+ [tag]: [0, 2]
80
+ });
81
+ });
82
+ });
83
+
84
+ it(`${method}() does not tag if validator returns false`, function () {
85
+ // .default covers both function and object export files
86
+ validatorFiles[method].default.returns(false);
87
+ simulateRequestScope(() => {
88
+ const extern = trackString('foo', {
89
+ tags: {
90
+ [UNTRUSTED]: [0, 2]
91
+ },
92
+ history: [{ mock: 'sourceEvent' }]
93
+ });
94
+
95
+ validator[method](extern);
96
+ expect(tracker.getData(extern).tags).to.deep.equal({
97
+ [UNTRUSTED]: [0, 2],
98
+ });
99
+ });
100
+ });
101
+
102
+ });
103
+ });
104
+
105
+ describe('untrackers', function () {
106
+ it('equals() untracks tracked strings', function () {
107
+ simulateRequestScope(() => {
108
+ const extern = trackString('foo', {
109
+ tags: {
110
+ [UNTRUSTED]: [0, 2]
111
+ },
112
+ history: [{ mock: 'sourceEvent' }]
113
+ });
114
+
115
+ validator.equals(extern);
116
+ expect(tracker.getData(extern)).to.be.null;
117
+ });
118
+ });
119
+
120
+ it('equals() does not untrack if validator returns false', function () {
121
+ validatorFiles.equals.returns(false);
122
+ simulateRequestScope(() => {
123
+ const extern = trackString('foo', {
124
+ tags: {
125
+ [UNTRUSTED]: [0, 2]
126
+ },
127
+ history: [{ mock: 'sourceEvent' }]
128
+ });
129
+
130
+ validator.equals(extern);
131
+ expect(tracker.getData(extern).tags).to.deep.equal({
132
+ [UNTRUSTED]: [0, 2]
133
+ });
134
+ });
135
+ });
136
+
137
+ });
138
+
139
+ describe('sanitizers', function () {
140
+ it('escape() adds correct tags to results if tracked ', function () {
141
+ simulateRequestScope(() => {
142
+ const extern = trackString('foo', {
143
+ tags: {
144
+ [UNTRUSTED]: [0, 2]
145
+ },
146
+ history: [{ mock: 'sourceEvent' }]
147
+ });
148
+ validatorFiles.escape.returns(extern);
149
+
150
+ const ret = validator.escape(extern);
151
+ expect(tracker.getData(ret).tags).to.deep.equal({
152
+ [UNTRUSTED]: [0, 2],
153
+ [HTML_ENCODED]: [0, 2]
154
+ });
155
+ });
156
+ });
157
+
158
+ it('escape() tracks result if untracked', function () {
159
+ simulateRequestScope(() => {
160
+ const extern = trackString('<script>', {
161
+ tags: {
162
+ [UNTRUSTED]: [0, 7]
163
+ },
164
+ history: [{ mock: 'sourceEvent' }]
165
+ });
166
+ validatorFiles.escape.returns('&lt;script&rt;');
167
+
168
+ const ret = validator.escape(extern);
169
+ expect(tracker.getData(ret).tags).to.deep.equal({
170
+ [UNTRUSTED]: [0, 7],
171
+ [HTML_ENCODED]: [0, 13]
172
+ });
173
+ });
174
+ });
175
+ });
176
+
177
+ describe('custom', function () {
178
+ it('isEmail() tags with the correct tag', function () {
179
+ simulateRequestScope(() => {
180
+ const extern = trackString('foo@bar.com', {
181
+ tags: {
182
+ [UNTRUSTED]: [0, 10]
183
+ },
184
+ history: [{ mock: 'sourceEvent' }]
185
+ });
186
+
187
+ validator.isEmail(extern);
188
+ expect(tracker.getData(extern).tags).to.deep.equal({
189
+ [UNTRUSTED]: [0, 10],
190
+ [LIMITED_CHARS]: [0, 10]
191
+ });
192
+ });
193
+ });
194
+
195
+ it('isEmail() does not tag when require_display_name is true', function () {
196
+ simulateRequestScope(() => {
197
+ const extern = trackString('FOO <foo@bar.com>', {
198
+ tags: {
199
+ [UNTRUSTED]: [0, 10]
200
+ },
201
+ history: [{ mock: 'sourceEvent' }]
202
+ });
203
+
204
+ validator.isEmail(extern, { require_display_name: true });
205
+ expect(tracker.getData(extern).tags).to.deep.equal({
206
+ [UNTRUSTED]: [0, 10]
207
+ });
208
+ });
209
+ });
210
+ });
211
+ });
@@ -0,0 +1,78 @@
1
+ 'use strict';
2
+
3
+ const { Event } = require('@contrast/common');
4
+ const { expect } = require('chai');
5
+ const proxyquire = require('proxyquire');
6
+ const sinon = require('sinon');
7
+ const { initAssessFixture } = require('@contrast/test/fixtures');
8
+
9
+ const MODULES = ['fastify', 'koa', 'postgres', 'http', 'mssql', 'marsdb', 'express', 'hapi'];
10
+
11
+ describe('assess dataflow sinks', function () {
12
+ let core, sinks, simulateRequestScope;
13
+
14
+ beforeEach(function () {
15
+ ({ core, simulateRequestScope } = initAssessFixture());
16
+ sinon.spy(core.messages, 'emit');
17
+
18
+ const moduleMock = (name) => (deps) => {
19
+ deps.assess.dataflow.sinks[name] = { install: sinon.stub() };
20
+ };
21
+
22
+ sinks = proxyquire('.', {
23
+ './install/fastify': moduleMock('fastify'),
24
+ './install/hapi': moduleMock('hapi'),
25
+ './install/koa': moduleMock('koa'),
26
+ './install/child-process': moduleMock('child-process'),
27
+ './install/fs': moduleMock('fs'),
28
+ './install/postgres': moduleMock('postgres'),
29
+ './install/http': moduleMock('http'),
30
+ './install/mssql': moduleMock('mssql'),
31
+ './install/mysql': moduleMock('mysql'),
32
+ './install/mongodb': moduleMock('mongodb'),
33
+ './install/sqlite3': moduleMock('sqlite3'),
34
+ './install/sequelize': moduleMock('sequelize'),
35
+ './install/marsdb': moduleMock('marsdb'),
36
+ './install/express': moduleMock('express'),
37
+ './install/vm': moduleMock('vm'),
38
+ './install/function': moduleMock('function'),
39
+ './install/eval': moduleMock('eval'),
40
+ './install/libxmljs': moduleMock('libxmljs'),
41
+ './install/node-serialize': moduleMock('nodeSerialize')
42
+ })(core);
43
+ });
44
+
45
+ it('installs its components', function () {
46
+ expect(sinks).to.respondTo('isVulnerable');
47
+ expect(sinks).to.respondTo('isSafeContentType');
48
+
49
+ sinks.install();
50
+ MODULES.forEach((module) => {
51
+ expect(sinks[module].install).to.have.been.called;
52
+ });
53
+ });
54
+
55
+ it('reportFindings()', function () {
56
+ const data = { name: 'ivan' };
57
+ sinks.reportFindings(data);
58
+
59
+ expect(core.messages.emit).to.have.been.calledWith(
60
+ Event.ASSESS_DATAFLOW_FINDING,
61
+ sinon.match(data),
62
+ );
63
+
64
+ const anotherData = { name: 'petar' };
65
+ simulateRequestScope(function () {
66
+ const store = core.scopes.sources.getStore();
67
+
68
+ sinks.reportFindings(anotherData);
69
+ expect(core.messages.emit).to.have.been.calledWith(
70
+ Event.ASSESS_DATAFLOW_FINDING,
71
+ sinon.match({
72
+ sourceInfo: store.sourceInfo,
73
+ ...anotherData
74
+ })
75
+ );
76
+ });
77
+ });
78
+ });