@contrast/protect 1.38.0 → 1.40.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 (58) hide show
  1. package/lib/error-handlers/common-handler.test.js +52 -0
  2. package/lib/error-handlers/index.test.js +32 -0
  3. package/lib/error-handlers/init-domain.test.js +34 -0
  4. package/lib/error-handlers/install/express4.test.js +238 -0
  5. package/lib/error-handlers/install/fastify.test.js +130 -0
  6. package/lib/error-handlers/install/hapi.test.js +102 -0
  7. package/lib/error-handlers/install/koa2.test.js +83 -0
  8. package/lib/error-handlers/install/restify.test.js +57 -0
  9. package/lib/get-source-context.test.js +35 -0
  10. package/lib/hardening/handlers.test.js +89 -0
  11. package/lib/hardening/index.test.js +31 -0
  12. package/lib/hardening/install/node-serialize0.test.js +61 -0
  13. package/lib/index.test.js +53 -0
  14. package/lib/input-analysis/handlers.test.js +1604 -0
  15. package/lib/input-analysis/index.test.js +45 -0
  16. package/lib/input-analysis/install/body-parser1.test.js +134 -0
  17. package/lib/input-analysis/install/busboy1.test.js +81 -0
  18. package/lib/input-analysis/install/cookie-parser1.test.js +144 -0
  19. package/lib/input-analysis/install/express4.test.js +208 -0
  20. package/lib/input-analysis/install/fastify.test.js +96 -0
  21. package/lib/input-analysis/install/formidable1.test.js +114 -0
  22. package/lib/input-analysis/install/hapi.test.js +300 -0
  23. package/lib/input-analysis/install/http.test.js +264 -0
  24. package/lib/input-analysis/install/koa-body5.test.js +92 -0
  25. package/lib/input-analysis/install/koa-bodyparser4.test.js +92 -0
  26. package/lib/input-analysis/install/koa2.test.js +259 -0
  27. package/lib/input-analysis/install/multer1.test.js +209 -0
  28. package/lib/input-analysis/install/qs6.test.js +79 -0
  29. package/lib/input-analysis/install/restify.test.js +98 -0
  30. package/lib/input-analysis/install/universal-cookie4.test.js +70 -0
  31. package/lib/input-analysis/ip-analysis.test.js +71 -0
  32. package/lib/input-analysis/virtual-patches.test.js +106 -0
  33. package/lib/input-tracing/handlers/index.test.js +1236 -0
  34. package/lib/input-tracing/index.test.js +62 -0
  35. package/lib/input-tracing/install/child-process.test.js +133 -0
  36. package/lib/input-tracing/install/eval.test.js +78 -0
  37. package/lib/input-tracing/install/fs.test.js +108 -0
  38. package/lib/input-tracing/install/function.test.js +81 -0
  39. package/lib/input-tracing/install/http.test.js +85 -0
  40. package/lib/input-tracing/install/http2.test.js +83 -0
  41. package/lib/input-tracing/install/marsdb.test.js +126 -0
  42. package/lib/input-tracing/install/mongodb.test.js +282 -0
  43. package/lib/input-tracing/install/mssql.test.js +81 -0
  44. package/lib/input-tracing/install/mysql.test.js +108 -0
  45. package/lib/input-tracing/install/postgres.test.js +125 -0
  46. package/lib/input-tracing/install/sequelize.test.js +78 -0
  47. package/lib/input-tracing/install/spdy.test.js +76 -0
  48. package/lib/input-tracing/install/sqlite3.test.js +88 -0
  49. package/lib/input-tracing/install/vm.test.js +176 -0
  50. package/lib/make-response-blocker.test.js +99 -0
  51. package/lib/make-source-context.test.js +219 -0
  52. package/lib/policy.test.js +446 -0
  53. package/lib/semantic-analysis/handlers.test.js +379 -0
  54. package/lib/semantic-analysis/index.test.js +38 -0
  55. package/lib/semantic-analysis/install/libxmljs.test.js +156 -0
  56. package/lib/semantic-analysis/utils/xml-analysis.test.js +156 -0
  57. package/lib/throw-security-exception.test.js +37 -0
  58. package/package.json +5 -5
@@ -0,0 +1,96 @@
1
+ 'use strict';
2
+
3
+ const sinon = require('sinon');
4
+ const { expect } = require('chai');
5
+ const scopes = require('@contrast/scopes');
6
+ const patcher = require('@contrast/patcher');
7
+ const mocks = require('@contrast/test/mocks');
8
+ const SecurityException = require('../../security-exception');
9
+ const secEx = SecurityException.create();
10
+ const fastifyInstr = require('./fastify');
11
+
12
+ describe('protect input-analysis fastify v3.x', function () {
13
+ let core, inputAnalysis, fastify, serverMock, reqMock, resMock, doneMock;
14
+
15
+ beforeEach(function () {
16
+ core = mocks.core();
17
+ core.logger = mocks.logger();
18
+ core.scopes = scopes(core);
19
+ core.protect = mocks.protect();
20
+ require('../../get-source-context')(core);
21
+ core.depHooks = mocks.depHooks();
22
+ core.patcher = patcher(core);
23
+
24
+ reqMock = {
25
+ query: { foo: 'bar' },
26
+ cookies: { foo: 'bar' },
27
+ params: { foo: 'bar' },
28
+ body: { foo: 'bar' },
29
+ };
30
+ resMock = {};
31
+ doneMock = sinon.stub();
32
+ serverMock = {
33
+ addHook: sinon.stub().yields(reqMock, resMock, doneMock),
34
+ };
35
+
36
+ const fastifyOrig = () => serverMock;
37
+ core.depHooks.resolve.callsFake(function (desc, cb) {
38
+ fastify = cb(fastifyOrig);
39
+ });
40
+
41
+ inputAnalysis = core.protect.inputAnalysis;
42
+ fastifyInstr(core).install();
43
+ });
44
+
45
+ describe('preValidationHook', function () {
46
+ it('adds hook and calls appropriate analysis handlers', function () {
47
+ core.scopes.sources.run({ protect: {} }, () => {
48
+ fastify();
49
+ });
50
+
51
+ expect(serverMock.addHook).to.have.been.called;
52
+ expect(doneMock).to.have.been.called;
53
+ expect(core.logger.debug).not.to.have.been.called;
54
+
55
+ expect(inputAnalysis.handleQueryParams).to.have.been.called;
56
+ expect(inputAnalysis.handleUrlParams).to.have.been.called;
57
+ expect(inputAnalysis.handleCookies).to.have.been.called;
58
+ expect(inputAnalysis.handleParsedBody).to.have.been.called;
59
+ });
60
+
61
+ it('input analysis does not occur if there is no protect source context', function () {
62
+ fastify();
63
+ expect(doneMock).to.have.been.called;
64
+
65
+ expect(inputAnalysis.handleQueryParams).not.to.have.been.called;
66
+ expect(inputAnalysis.handleUrlParams).not.to.have.been.called;
67
+ expect(inputAnalysis.handleCookies).not.to.have.been.called;
68
+ expect(inputAnalysis.handleParsedBody).not.to.have.been.called;
69
+ });
70
+
71
+ it('handles a case with SecurityException from the input-analysis', function () {
72
+ inputAnalysis.handleQueryParams.throws(secEx);
73
+ core.scopes.sources.run({ protect: {} }, () => {
74
+ fastify();
75
+ });
76
+
77
+ expect(doneMock).to.have.been.calledOnceWith(secEx);
78
+ });
79
+
80
+ it('handles a case with a normal error in the input analysis', function () {
81
+ const err = new Error('Input Analysis Error');
82
+
83
+ inputAnalysis.handleQueryParams.throws(err);
84
+ core.scopes.sources.run({ protect: {} }, () => {
85
+ fastify();
86
+ });
87
+
88
+ expect(core.logger.error).to.have.been.calledWith(
89
+ { err, funcKey: 'protect-input-analysis:fastify.build' },
90
+ 'Unexpected error during input analysis'
91
+ );
92
+ expect(doneMock).to.have.been.calledOnce;
93
+ expect(doneMock).not.to.have.been.calledWith(err);
94
+ });
95
+ });
96
+ });
@@ -0,0 +1,114 @@
1
+ 'use strict';
2
+
3
+ const sinon = require('sinon');
4
+ const { expect } = require('chai');
5
+ const scopes = require('@contrast/scopes');
6
+ const patcher = require('@contrast/patcher');
7
+ const mocks = require('@contrast/test/mocks');
8
+ const formidableInstr = require('./formidable1');
9
+
10
+ describe('protect input-analysis formidable v1.x', function () {
11
+ let core, inputAnalysis, parseMock, patchedParse, req, cbStub;
12
+
13
+
14
+ beforeEach(function () {
15
+ core = mocks.core();
16
+ core.logger = mocks.logger();
17
+ core.scopes = scopes(core);
18
+ core.protect = mocks.protect();
19
+ require('../../get-source-context')(core);
20
+ core.depHooks = mocks.depHooks();
21
+ core.patcher = patcher(core);
22
+
23
+ inputAnalysis = core.protect.inputAnalysis;
24
+ req = { fields: { parameter: 'parsed' }, files: { fileField: { name: 'some-file' } } };
25
+ parseMock = (req, cb) => {
26
+ if (typeof cb === 'function') {
27
+ cb(null, req.fields, req.files);
28
+ }
29
+ };
30
+ cbStub = sinon.stub();
31
+ sinon.spy(core.patcher, 'patch');
32
+
33
+ formidableInstr(core).install();
34
+ patchedParse = core.depHooks.resolve.yield({ IncomingForm: { prototype: { parse: parseMock } } })[0].IncomingForm.prototype.parse;
35
+ core.patcher.patch.resetHistory();
36
+ });
37
+
38
+ it('calls patcher on the `.parse` method', function () {
39
+ core.depHooks.resolve.yield({ IncomingForm: { prototype: { parse: parseMock } } });
40
+
41
+ expect(core.patcher.patch).to.have.been.calledOnce;
42
+ expect(core.patcher.patch).to.have.been.calledWithMatch(sinon.match.func, {
43
+ name: 'Formidable.IncomingForm.prototype.parse',
44
+ patchType: 'protect-input-analysis',
45
+ pre: sinon.match.func
46
+ });
47
+ });
48
+
49
+ it('calls `.handleParsedBody` and `handleFileUploadName` when parsing is successful and in right context', function () {
50
+ core.scopes.sources.run({ protect: {} }, () => {
51
+ patchedParse(req, cbStub);
52
+ });
53
+
54
+ expect(cbStub).to.have.been.calledOnce;
55
+ expect(inputAnalysis.handleParsedBody).to.have.been.calledOnce;
56
+ expect(inputAnalysis.handleParsedBody).to.have.been.calledWithMatch({ parsedBody: req.fields }, req.fields);
57
+ expect(inputAnalysis.handleFileUploadName).to.have.been.calledOnce;
58
+ expect(inputAnalysis.handleFileUploadName).to.have.been.calledWithMatch({ parsedBody: req.fields }, ['some-file']);
59
+ });
60
+
61
+
62
+ it('calls `handleFileUploadName` when parsing is successful with multiple files and in right context', function () {
63
+ req.files = { filesField: [{ name: 'some-file-1' }, { name: 'some-file-2' }] };
64
+ core.scopes.sources.run({ protect: {} }, () => {
65
+ patchedParse(req, cbStub);
66
+ });
67
+
68
+ expect(inputAnalysis.handleFileUploadName).to.have.been.calledOnce;
69
+ expect(inputAnalysis.handleFileUploadName).to.have.been.calledWithMatch({ parsedBody: req.fields }, ['some-file-1', 'some-file-2']);
70
+ });
71
+
72
+ it('calls `.handleParsedBody` when parsing is successful and in right context, but there are no files', function () {
73
+ req.files = null;
74
+ core.scopes.sources.run({ protect: {} }, () => {
75
+ patchedParse(req, cbStub);
76
+ });
77
+
78
+ expect(cbStub).to.have.been.calledOnce;
79
+ expect(inputAnalysis.handleParsedBody).to.have.been.calledOnce;
80
+ expect(inputAnalysis.handleParsedBody).to.have.been.calledWithMatch({ parsedBody: req.fields }, req.fields);
81
+ expect(core.logger.debug).not.to.have.been.called;
82
+ });
83
+
84
+ it('does not call `.handleParsedBody` when not in context', function () {
85
+ core.scopes.sources.run({}, () => {
86
+ patchedParse(req, cbStub);
87
+ });
88
+
89
+ expect(inputAnalysis.handleParsedBody).not.to.have.been.called;
90
+ expect(cbStub).to.have.been.calledOnce;
91
+ });
92
+
93
+ it('does not call `.handleParsedBody` when `formidable` there is a result, but only files are uploaded', function () {
94
+ req.fields = null;
95
+ core.scopes.sources.run({ protect: {} }, () => {
96
+ patchedParse(req, cbStub);
97
+ });
98
+
99
+ expect(inputAnalysis.handleParsedBody).not.to.have.been.called;
100
+ expect(inputAnalysis.handleFileUploadName).to.have.been.calledOnce;
101
+ expect(inputAnalysis.handleFileUploadName).to.have.been.calledWithMatch({}, ['some-file']);
102
+ expect(cbStub).to.have.been.calledOnce;
103
+ });
104
+
105
+ it('does not call `.handleParsedBody` when `formidable` does not return a result', function () {
106
+ core.scopes.sources.run({ protect: {} }, () => {
107
+ patchedParse({}, cbStub);
108
+ });
109
+
110
+ expect(inputAnalysis.handleParsedBody).not.to.have.been.called;
111
+ expect(core.logger.debug).not.to.have.been.called;
112
+ expect(cbStub).to.have.been.calledOnce;
113
+ });
114
+ });
@@ -0,0 +1,300 @@
1
+ 'use strict';
2
+
3
+ const sinon = require('sinon');
4
+ const { expect } = require('chai');
5
+ const scopes = require('@contrast/scopes');
6
+ const patcher = require('@contrast/patcher');
7
+ const mocks = require('@contrast/test/mocks');
8
+ const { create, isSecurityException } = require('../../security-exception');
9
+
10
+ describe('protect input-analysis hapi', function () {
11
+ let core,
12
+ hapiSources,
13
+ store,
14
+ inputAnalysis;
15
+
16
+ const Hapi = {
17
+ name: 'hapi',
18
+ version: '>=18 <22'
19
+ };
20
+ const HapiHapi = {
21
+ name: '@hapi/hapi',
22
+ version: '>=18 <22'
23
+ };
24
+ const HapiPez = {
25
+ name: '@hapi/pez'
26
+ };
27
+
28
+ beforeEach(function () {
29
+ core = mocks.core();
30
+ core.config = mocks.config();
31
+ core.logger = mocks.logger();
32
+ core.scopes = scopes(core);
33
+ core.protect = mocks.protect();
34
+ require('../../get-source-context')(core);
35
+ core.depHooks = mocks.depHooks();
36
+ core.patcher = patcher(core);
37
+
38
+ inputAnalysis = core.protect.inputAnalysis;
39
+ sinon.spy(core.patcher, 'patch');
40
+ store = {
41
+ protect: {
42
+ block: sinon.stub(),
43
+ securityException: ['block', 'cmd-injection']
44
+ }
45
+ };
46
+
47
+ Hapi.server = function hapi(server) {
48
+ return server;
49
+ };
50
+ HapiHapi.server = function hapiHapi(server) {
51
+ return server;
52
+ };
53
+ HapiPez.Dispenser = function () { };
54
+ HapiPez.Dispenser.prototype.emit = function () { };
55
+
56
+ core.depHooks.resolve.withArgs({ name: Hapi.name, version: Hapi.version }).yields(Hapi);
57
+ core.depHooks.resolve.withArgs({ name: HapiHapi.name, version: HapiHapi.version }).yields(HapiHapi);
58
+ core.depHooks.resolve.withArgs({ name: HapiPez.name }).yields(HapiPez);
59
+
60
+ hapiSources = require('./hapi')(core);
61
+ hapiSources.install();
62
+ });
63
+
64
+ [Hapi, HapiHapi].forEach((module) => {
65
+ it(`skip instrumentation if server is missing - ${module.name}`, function () {
66
+ const { server } = module;
67
+
68
+ core.scopes.sources.run(store, () => {
69
+ server();
70
+ expect(core.logger.error).to.have.been.calledWith(
71
+ { funcKey: 'protect-input-analysis:hapi.server' },
72
+ 'Hapi Server is called but there is no server instance!'
73
+ );
74
+ });
75
+ });
76
+ });
77
+
78
+ [Hapi, HapiHapi].forEach((module) => {
79
+ it(`should log onPreStart hapi version - ${module.name}`, function () {
80
+ const { server } = module;
81
+ const serverInstance = {
82
+ ext: sinon.stub(),
83
+ };
84
+
85
+ serverInstance.ext.withArgs('onPreStart', sinon.match.func).callsFake((string, method) => {
86
+ method({ version: '4.0.0' });
87
+ });
88
+
89
+ core.scopes.sources.run(store, () => {
90
+ server(serverInstance);
91
+ expect(core.logger.debug).to.have.been.calledWith('hapi version %s', '4.0.0');
92
+ });
93
+ });
94
+ });
95
+
96
+ [Hapi, HapiHapi].forEach((module) => {
97
+ it(`should handle url params, state (cookies), payload/body, query - ${module.name}`, function () {
98
+ const { server } = module;
99
+ const serverInstance = {
100
+ ext: sinon.stub(),
101
+ };
102
+
103
+ const params = { userId: 1 };
104
+ const state = { auth: 'vasko jabata' };
105
+ const payload = { name: 'vasko jabata' };
106
+ const query = { name: 'vasko jabata' };
107
+
108
+ serverInstance.ext.withArgs('onPreHandler', sinon.match.func).callsFake((string, method) => {
109
+ method({
110
+ params,
111
+ state,
112
+ payload,
113
+ query,
114
+ }, {
115
+ continue: () => { }
116
+ });
117
+ });
118
+
119
+ core.scopes.sources.run(store, () => {
120
+ server(serverInstance);
121
+
122
+ expect(store.protect.parsedParams).to.eql(params);
123
+ expect(store.protect.parsedCookies).to.eql(state);
124
+ expect(store.protect.parsedBody).to.eql(payload);
125
+ expect(store.protect.parsedQuery).to.eql(query);
126
+ expect(core.protect.inputAnalysis.handleUrlParams).to.have.been.calledWith(sinon.match.object, params);
127
+ expect(core.protect.inputAnalysis.handleCookies).to.have.been.calledWith(sinon.match.object, state);
128
+ expect(core.protect.inputAnalysis.handleParsedBody).to.have.been.calledWith(sinon.match.object, payload);
129
+ expect(core.protect.inputAnalysis.handleQueryParams).to.have.been.calledWith(sinon.match.object, query);
130
+ });
131
+ });
132
+ });
133
+
134
+ [Hapi, HapiHapi].forEach((module) => {
135
+ it(`error handling - ${module.name}`, function () {
136
+ const err = new Error('error');
137
+
138
+ const { server } = module;
139
+ const serverInstance = {
140
+ ext: sinon.stub(),
141
+ };
142
+
143
+ const params = { userId: 1 };
144
+
145
+ serverInstance.ext.withArgs('onPreHandler', sinon.match.func).callsFake((string, method) => {
146
+ method({
147
+ params,
148
+ }, {
149
+ continue: () => { }
150
+ });
151
+ });
152
+
153
+ core.protect.inputAnalysis.handleUrlParams.callsFake(() => {
154
+ throw err;
155
+ });
156
+
157
+ core.scopes.sources.run(store, () => {
158
+ server(serverInstance);
159
+ expect(core.logger.error).to.have.been.calledWith(
160
+ { err, funcKey: 'protect-input-analysis:hapi.server' },
161
+ 'Unexpected error during input analysis',
162
+ );
163
+ });
164
+ });
165
+
166
+ it(`should throw SecurityException - ${module.name}`, function () {
167
+ const error = create();
168
+
169
+ const { server } = module;
170
+ const serverInstance = {
171
+ ext: sinon.stub(),
172
+ };
173
+
174
+ const params = { userId: 1 };
175
+
176
+ serverInstance.ext.withArgs('onPreHandler', sinon.match.func).callsFake((string, method) => {
177
+ method({
178
+ params,
179
+ }, {
180
+ continue: () => { }
181
+ });
182
+ });
183
+
184
+ core.protect.inputAnalysis.handleUrlParams.callsFake(() => {
185
+ throw error;
186
+ });
187
+
188
+ core.scopes.sources.run(store, () => {
189
+ try {
190
+ server(serverInstance);
191
+ } catch (error) {
192
+ expect(isSecurityException(error)).to.be.true;
193
+ }
194
+ });
195
+ });
196
+ });
197
+
198
+ [Hapi, HapiHapi].forEach((module) => {
199
+ it(`empty request - ${module.name}`, function () {
200
+ const { server } = module;
201
+ const serverInstance = {
202
+ ext: sinon.stub(),
203
+ };
204
+
205
+ const continueFunction = () => ({});
206
+ let result;
207
+ serverInstance.ext.withArgs('onPreHandler', sinon.match.func).callsFake((string, method) => {
208
+ result = method({}, {
209
+ continue: continueFunction
210
+ });
211
+ });
212
+
213
+ core.scopes.sources.run(store, () => {
214
+ server(serverInstance);
215
+ expect(result).to.eql(continueFunction);
216
+ });
217
+ });
218
+
219
+ it(`empty sourceContext - ${module.name}`, function () {
220
+ const { server } = module;
221
+ const serverInstance = {
222
+ ext: sinon.stub(),
223
+ };
224
+
225
+ const continueFunction = () => ({});
226
+ let result;
227
+ serverInstance.ext.withArgs('onPreHandler', sinon.match.func).callsFake((string, method) => {
228
+ result = method({}, {
229
+ continue: continueFunction
230
+ });
231
+ });
232
+
233
+ core.scopes.sources.run({}, () => {
234
+ server(serverInstance);
235
+ expect(result).to.eql(continueFunction);
236
+ });
237
+ });
238
+ });
239
+
240
+ describe('hapi/pez', function () {
241
+ it('works', function () {
242
+ HapiPez.Dispenser.prototype.emit();
243
+ });
244
+ it('calls patcher on the `.emit` method', function () {
245
+
246
+ expect(core.patcher.patch).to.have.callCount(3);
247
+ expect(core.patcher.patch).to.have.been.calledWithMatch(Hapi, 'server', {
248
+ name: 'hapi.server',
249
+ patchType: 'protect-input-analysis',
250
+ post: sinon.match.func
251
+ });
252
+ expect(core.patcher.patch).to.have.been.calledWithMatch(HapiHapi, 'server', {
253
+ name: 'hapi.server',
254
+ patchType: 'protect-input-analysis',
255
+ post: sinon.match.func
256
+ });
257
+ expect(core.patcher.patch).to.have.been.calledWithMatch(HapiPez.Dispenser.prototype, 'emit', {
258
+ name: 'Pez.Dispenser.prototype.emit',
259
+ patchType: 'protect-input-analysis',
260
+ pre: sinon.match.func
261
+ });
262
+ });
263
+
264
+ it('calls `.handleFileUploadName` when there is a multipart file and it is in right context', function () {
265
+ core.scopes.sources.run({ protect: {} }, () => {
266
+ HapiPez.Dispenser.prototype.emit('part', { filename: 'filename' });
267
+ });
268
+
269
+ expect(inputAnalysis.handleFileUploadName).to.have.been.calledOnce;
270
+ expect(inputAnalysis.handleFileUploadName).to.have.been.calledWithMatch({}, ['filename']);
271
+ });
272
+
273
+
274
+ it('does not call `.handleFileUploadName` when not in context', function () {
275
+ core.scopes.sources.run({}, () => {
276
+ HapiPez.Dispenser.prototype.emit('part', { filename: 'filename' });
277
+ });
278
+
279
+ expect(inputAnalysis.handleFileUploadName).not.to.have.been.called;
280
+ });
281
+
282
+ it('does not call `.handleFileUploadName` when `.emit` is not called with `part` event', function () {
283
+ core.scopes.sources.run({ protect: {} }, () => {
284
+ HapiPez.Dispenser.prototype.emit('not-part', { filename: 'filename' });
285
+ });
286
+
287
+ expect(inputAnalysis.handleParsedBody).not.to.have.been.called;
288
+ expect(core.logger.debug).not.to.have.been.called;
289
+ });
290
+
291
+ it('does not call `.handleFileUploadName` when `.emit` call does not contain a filename', function () {
292
+ core.scopes.sources.run({ protect: {} }, () => {
293
+ HapiPez.Dispenser.prototype.emit('part', { filename: null });
294
+ });
295
+
296
+ expect(inputAnalysis.handleParsedBody).not.to.have.been.called;
297
+ expect(core.logger.debug).not.to.have.been.called;
298
+ });
299
+ });
300
+ });