@contrast/protect 1.54.0 → 1.54.2

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/package.json +14 -11
  2. package/lib/error-handlers/common-handler.test.js +0 -52
  3. package/lib/error-handlers/index.test.js +0 -32
  4. package/lib/error-handlers/init-domain.test.js +0 -22
  5. package/lib/error-handlers/install/express.test.js +0 -290
  6. package/lib/error-handlers/install/fastify.test.js +0 -130
  7. package/lib/error-handlers/install/hapi.test.js +0 -102
  8. package/lib/error-handlers/install/koa2.test.js +0 -83
  9. package/lib/error-handlers/install/restify.test.js +0 -57
  10. package/lib/get-source-context.test.js +0 -35
  11. package/lib/hardening/handlers.test.js +0 -89
  12. package/lib/hardening/index.test.js +0 -31
  13. package/lib/hardening/install/node-serialize0.test.js +0 -58
  14. package/lib/index.test.js +0 -53
  15. package/lib/input-analysis/handlers.test.js +0 -1604
  16. package/lib/input-analysis/index.test.js +0 -45
  17. package/lib/input-analysis/install/body-parser1.test.js +0 -134
  18. package/lib/input-analysis/install/busboy1.test.js +0 -81
  19. package/lib/input-analysis/install/cookie-parser1.test.js +0 -144
  20. package/lib/input-analysis/install/express.test.js +0 -241
  21. package/lib/input-analysis/install/fastify.test.js +0 -96
  22. package/lib/input-analysis/install/formidable1.test.js +0 -114
  23. package/lib/input-analysis/install/hapi.test.js +0 -292
  24. package/lib/input-analysis/install/http.test.js +0 -270
  25. package/lib/input-analysis/install/koa-body5.test.js +0 -92
  26. package/lib/input-analysis/install/koa-bodyparser4.test.js +0 -92
  27. package/lib/input-analysis/install/koa2.test.js +0 -259
  28. package/lib/input-analysis/install/multer1.test.js +0 -209
  29. package/lib/input-analysis/install/qs6.test.js +0 -79
  30. package/lib/input-analysis/install/restify.test.js +0 -98
  31. package/lib/input-analysis/install/universal-cookie4.test.js +0 -70
  32. package/lib/input-analysis/ip-analysis.test.js +0 -71
  33. package/lib/input-analysis/virtual-patches.test.js +0 -106
  34. package/lib/input-tracing/handlers/index.test.js +0 -1237
  35. package/lib/input-tracing/index.test.js +0 -62
  36. package/lib/input-tracing/install/child-process.test.js +0 -133
  37. package/lib/input-tracing/install/eval.test.js +0 -78
  38. package/lib/input-tracing/install/fs.test.js +0 -108
  39. package/lib/input-tracing/install/function.test.js +0 -81
  40. package/lib/input-tracing/install/http.test.js +0 -85
  41. package/lib/input-tracing/install/http2.test.js +0 -83
  42. package/lib/input-tracing/install/marsdb.test.js +0 -126
  43. package/lib/input-tracing/install/mongodb.test.js +0 -280
  44. package/lib/input-tracing/install/mssql.test.js +0 -81
  45. package/lib/input-tracing/install/mysql.test.js +0 -108
  46. package/lib/input-tracing/install/postgres.test.js +0 -117
  47. package/lib/input-tracing/install/sequelize.test.js +0 -78
  48. package/lib/input-tracing/install/spdy.test.js +0 -76
  49. package/lib/input-tracing/install/sqlite3.test.js +0 -88
  50. package/lib/input-tracing/install/vm.test.js +0 -176
  51. package/lib/make-response-blocker.test.js +0 -99
  52. package/lib/make-source-context.test.js +0 -219
  53. package/lib/policy.test.js +0 -446
  54. package/lib/semantic-analysis/handlers.test.js +0 -379
  55. package/lib/semantic-analysis/index.test.js +0 -38
  56. package/lib/semantic-analysis/install/libxmljs.test.js +0 -156
  57. package/lib/semantic-analysis/utils/xml-analysis.test.js +0 -156
  58. package/lib/throw-security-exception.test.js +0 -37
@@ -1,270 +0,0 @@
1
- 'use strict';
2
-
3
- const sinon = require('sinon');
4
- const { expect } = require('chai');
5
- const EventEmitter = require('events');
6
- const Protect = require('../..');
7
- const { initProtectFixture } = require('@contrast/test/fixtures');
8
-
9
- describe('protect input-analysis http', function () {
10
-
11
- describe('initialization', function () {
12
- let core, httpInstr, mockServer;
13
- // this captures the sinon stub before it is wrapped by Perf so we can
14
- // determine if it was called/not called as expected. if we don't do this
15
- // the core.depHooks.install() will be replaced by the Perf wrapper and
16
- // sinon will complain that it's not a spy/call-to-spy.
17
-
18
- beforeEach(function () {
19
- ({ core } = initProtectFixture());
20
-
21
- core.protect = Protect(core);
22
- httpInstr = require('./http')(core);
23
- httpInstr.around = sinon.stub();
24
-
25
- sinon.stub(core.protect.errorHandlers, 'initDomain');
26
-
27
- mockServer = {
28
- Server: {
29
- prototype: {
30
- emit: () => { },
31
- _events: {
32
- request: 'REQUEST',
33
- }
34
- }
35
- },
36
- createServer() { },
37
- createSecureServer() { }
38
- };
39
-
40
- core.depHooks.resolve.yields(mockServer);
41
- });
42
-
43
- it('should not be initialized after being required', function () {
44
- expect(httpInstr.install).a('function');
45
- // the next two lines are effectively: expect(core.depHooks.install).not.to.have.been.called;
46
- const depHooksInstall = core.Perf.all.get('agentify').get('agentify:@contrast/dep-hooks:install');
47
- expect(depHooksInstall).equal(undefined);
48
- expect(core.depHooks.resolve).not.to.have.been.called;
49
- });
50
-
51
- it('should be initialized after being installed', function () {
52
- httpInstr.install();
53
- expect(core.depHooks.resolve).to.have.callCount(4);
54
- });
55
-
56
- describe('hook createServer', function () {
57
- let postHook;
58
-
59
- this.beforeEach(function () {
60
- sinon.spy(core.patcher, 'patch');
61
- httpInstr.install();
62
- postHook = core.patcher.patch.getCall(3).lastArg.post;
63
-
64
- });
65
-
66
- it('should patch the Server prototype for http2', function () {
67
- postHook({ result: Object.create(mockServer.Server.prototype) });
68
-
69
- expect(core.patcher.patch).to.have.been.calledWith(mockServer.Server.prototype, 'emit', {
70
- name: 'http2.Server.prototype.emit',
71
- patchType: 'protect-input-analysis',
72
- around: sinon.match.func,
73
- funcKey: 'protect-input-analysis:http2.Server.prototype.emit'
74
- });
75
- });
76
-
77
- it('should throw an error if we cannot patch the Server prototype for http2', function () {
78
- postHook({ result: Object.create(null), funcKey: 'a' });
79
- postHook({ result: null, funcKey: 'b' });
80
- expect(core.logger.error).to.have.been.calledWith(
81
- { funcKey: 'a' },
82
- 'Unable to patch %s, continuing without instrumentation',
83
- 'http2.Server.prototype',
84
- );
85
- expect(core.logger.error).to.have.been.calledWith(
86
- { funcKey: 'b' },
87
- 'Unable to patch %s, continuing without instrumentation',
88
- 'http2.Server.prototype',
89
- );
90
-
91
- });
92
- });
93
-
94
- describe('around()', function () {
95
- let core,
96
- httpInstr,
97
- server,
98
- serverEmit,
99
- req,
100
- res,
101
- reqEmitter,
102
- context,
103
- expectedReqData,
104
- connectInputs;
105
-
106
- beforeEach(function () {
107
- ({ core } = initProtectFixture());
108
-
109
- Object.assign(core.config.protect.rules, {
110
- 'cmd-injection': { mode: 'monitor' },
111
- 'reflected-xss': { mode: 'block' }
112
- });
113
- core.protect = Protect(core);
114
-
115
- sinon.spy(core.protect.inputAnalysis, 'handleConnect');
116
- sinon.spy(core.protect.inputAnalysis, 'handleIpDenylist');
117
- sinon.spy(core.protect.inputAnalysis, 'handleIpAllowlist');
118
-
119
- httpInstr = require('./http')(core);
120
-
121
- sinon.spy(core.protect, 'makeSourceContext');
122
-
123
- server = {};
124
- serverEmit = sinon.stub();
125
- server.prototype = { emit: serverEmit };
126
- reqEmitter = new EventEmitter();
127
- req = {
128
- url: '/',
129
- method: 'GET',
130
- rawHeaders: ['host', 'vogon.com', 'content-type', 'application/json'],
131
- // this needs to look sort of like a real incoming message
132
- emit: (...args) => reqEmitter.emit(...args),
133
- _events: {},
134
- socket: { _readableState: { autoDestroy: false } },
135
- resume: sinon.stub(),
136
- };
137
- res = {
138
- end: sinon.stub(),
139
- writeHead: sinon.stub(),
140
- headersSent: false,
141
- on: sinon.stub(),
142
- };
143
- context = {
144
- instance: server,
145
- method: serverEmit,
146
- args: ['request', req, res],
147
- funcKey: 'funcKey',
148
- };
149
-
150
- // setup a few expected results. these can be modified as needed by
151
- // different tests.
152
- expectedReqData = {
153
- method: req.method.toLowerCase(),
154
- headers: req.rawHeaders.map((h, i) => i & 1 ? h : h.toLowerCase()),
155
- uriPath: '/',
156
- url: '/',
157
- queries: '',
158
- contentType: '',
159
- };
160
- connectInputs = {
161
- headers: expectedReqData.headers,
162
- uriPath: expectedReqData.uriPath,
163
- method: expectedReqData.method
164
- };
165
- });
166
-
167
- it('does not handle non-requests', function () {
168
- context = {
169
- instance: server,
170
- method: serverEmit,
171
- args: ['foo', req, res]
172
- };
173
- const next = sinon.stub();
174
- core.scopes.sources.run({ protect: {} }, () => httpInstr.around(next, context));
175
- expect(core.protect.inputAnalysis.handleConnect).not.have.been.called;
176
- expect(next).to.have.been.called;
177
- });
178
-
179
- it('works as expected', function () {
180
- res.on = (eventName, fn) => {
181
- fn();
182
- };
183
- core.scopes.sources.run({ protect: {} }, () => httpInstr.around(() => { }, context));
184
- const sourceContext = core.protect.makeSourceContext.returnValues[0];
185
- expect(core.protect.inputAnalysis.handleConnect).to.have.been.calledOnceWith(sourceContext, connectInputs);
186
- });
187
-
188
- it('debug logs if no async context', function () {
189
- sinon.stub(core.scopes.sources, 'getStore').returns(undefined);
190
- core.logger.debug = sinon.stub();
191
- core.scopes.sources.run({ protect: {} }, () => httpInstr.around(() => { }, context));
192
-
193
- expect(core.logger.debug).to.have.been.calledOnceWith({ funcKey: 'funcKey' }, 'request store not available during http input-analysis');
194
- });
195
-
196
- it('error logs on exceptions', function () {
197
- const err = new Error('sometimes things do not work out');
198
- core.protect.inputAnalysis.handleConnect.restore();
199
- sinon.stub(core.protect.inputAnalysis, 'handleConnect').throws(err);
200
- core.scopes.sources.run({ protect: {} }, () => httpInstr.around(() => { }, context));
201
- expect(core.logger.error).to.have.been.calledWith({ err, funcKey: 'funcKey' }, 'Error during http input analysis');
202
- });
203
-
204
- it('connectInputs will contain not a cookies header', function () {
205
- const withoutCookies = req.rawHeaders.slice();
206
- req.rawHeaders.push('COOKIES', 'this;that;the other;thing');
207
- expectedReqData.headers.push('cookies', 'this;that;the other;thing');
208
- core.scopes.sources.run({ protect: {} }, () => httpInstr.around(() => { }, context));
209
- const sourceContext = core.protect.makeSourceContext.returnValues[0];
210
- connectInputs.headers = withoutCookies;
211
- expect(core.protect.inputAnalysis.handleConnect).to.have.been.calledOnceWith(sourceContext, connectInputs);
212
- });
213
-
214
- it('does no analysis and just executes the request if the sourceContext has the allowed property set to true', function () {
215
- core.protect.makeSourceContext = () => ({
216
- allowed: true
217
- });
218
- core.scopes.sources.run({ protect: {} }, () => httpInstr.around(() => { }, context));
219
-
220
- expect(core.protect.inputAnalysis.handleConnect).not.to.have.been.called;
221
- expect(core.logger.debug).not.to.have.been.called;
222
- });
223
-
224
- it('does not emit the original event when blocked', function () {
225
- req.url = '/need/<script>%20this&that';
226
- core.scopes.sources.run({ protect: {} }, () => httpInstr.around(() => { }, context));
227
-
228
- const block = ['block', 'reflected-xss'];
229
-
230
- expect(core.protect.inputAnalysis.handleConnect).to.have.been.calledOnce.returned(block);
231
- expect(core.logger.debug).to.have.been.calledWith({ block, funcKey: 'funcKey' }, 'request blocked by not emitting request event');
232
- });
233
-
234
- it('does not handle a request body when it is not JSON', function () {
235
- req.rawHeaders = ['Content-Type', 'multipart/form-data'];
236
- expectedReqData.headers = ['content-type', 'multipart/form-data'];
237
- expectedReqData.contentType = 'multipart/form-data';
238
- core.scopes.sources.run({ protect: {} }, () => httpInstr.around(() => { }, context));
239
- const sourceContext = core.protect.makeSourceContext.returnValues[0];
240
- connectInputs.headers = expectedReqData.headers;
241
- expect(core.protect.inputAnalysis.handleConnect).to.have.been.calledOnceWith(sourceContext, connectInputs);
242
- });
243
-
244
- it('stores the virtualPatchesEvaluators in the protect store from inputAnalysis if there are some', function () {
245
- core.protect.inputAnalysis.virtualPatchesEvaluators = [new Map([['a', 'b']]), new Map([['a', 'b'], ['c', 'd']])];
246
- core.scopes.sources.run({ protect: {} }, () => httpInstr.around(() => { }, context));
247
-
248
- const { virtualPatchesEvaluators } = core.protect.makeSourceContext.returnValues[0];
249
-
250
- expect(virtualPatchesEvaluators.length).to.equal(2);
251
- expect(virtualPatchesEvaluators[0].size).to.equal(1);
252
- expect(virtualPatchesEvaluators[1].size).to.equal(2);
253
- });
254
-
255
- it('calls handleIpDenylist() if there is a IP Denylist', function () {
256
- core.protect.inputAnalysis.ipDenylist = ['test-entry'];
257
- core.scopes.sources.run({ protect: {} }, () => httpInstr.around(() => { }, context));
258
-
259
- expect(core.protect.inputAnalysis.handleIpDenylist).to.have.been.called;
260
- });
261
-
262
- it('calls handleIpAllowlist() if there is a IP Denylist', function () {
263
- core.protect.inputAnalysis.ipAllowlist = ['test-entry'];
264
- core.scopes.sources.run({ protect: {} }, () => httpInstr.around(() => { }, context));
265
-
266
- expect(core.protect.inputAnalysis.handleIpAllowlist).to.have.been.called;
267
- });
268
- });
269
- });
270
- });
@@ -1,92 +0,0 @@
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 koaBodyInstr = require('./koa-body5');
9
-
10
- describe('protect input-analysis koa-body v5.x', function () {
11
- let core, inputAnalysis, koaBodyMock, patchedKoaBody, body, nextStub;
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
- body = { parameter: 'parsed' };
25
-
26
- koaBodyMock = (body) => (ctx, next) => {
27
- ctx.request.body = body;
28
- next();
29
- };
30
- nextStub = sinon.stub();
31
- sinon.spy(core.patcher, 'patch');
32
-
33
- koaBodyInstr(core).install();
34
- patchedKoaBody = core.depHooks.resolve.yield(koaBodyMock)[0];
35
- core.patcher.patch.resetHistory();
36
- });
37
-
38
- it('calls patcher on the `cookie-parser` module export', function () {
39
- core.depHooks.resolve.yield(koaBodyMock);
40
-
41
- expect(core.patcher.patch).to.have.been.calledOnce;
42
- expect(core.patcher.patch).to.have.been.calledWithMatch(koaBodyMock, {
43
- name: 'koa-body',
44
- patchType: 'protect-input-analysis',
45
- post: sinon.match.func
46
- });
47
- });
48
-
49
- it('calls `.handleParsedBody` when parsing body is successful and in right context', function () {
50
- core.scopes.sources.run({ protect: {} }, () => {
51
- const bParser = patchedKoaBody(body);
52
- bParser({ request: {} }, nextStub);
53
- });
54
-
55
- expect(core.patcher.patch).to.have.been.calledOnce;
56
- expect(core.patcher.patch).to.have.been.calledWithMatch(sinon.match.func, {
57
- name: 'koa-body',
58
- patchType: 'protect-input-analysis',
59
- pre: sinon.match.func
60
- });
61
-
62
- expect(nextStub).to.have.been.calledOnce;
63
- expect(inputAnalysis.handleParsedBody).to.have.been.calledOnce;
64
- expect(inputAnalysis.handleParsedBody).to.have.been.calledWithMatch({ parsedBody: body }, body);
65
- });
66
-
67
- it('does not call `.handleParsedBody` when not in context', function () {
68
- core.scopes.sources.run({}, () => {
69
- const cParser = patchedKoaBody(body);
70
- cParser({ request: {} }, nextStub);
71
- });
72
-
73
- expect(inputAnalysis.handleCookies).not.to.have.been.called;
74
- expect(nextStub).to.have.been.calledOnce;
75
- });
76
-
77
- it('does not call `.handleParsedBody` when `koa-body` does not return a result', function () {
78
- core.scopes.sources.run({ protect: {} }, () => {
79
- const cParser = patchedKoaBody();
80
- cParser({ request: {} }, nextStub);
81
- });
82
-
83
- core.scopes.sources.run({ protect: {} }, () => {
84
- const cParser = patchedKoaBody({});
85
- cParser({ request: {} }, nextStub);
86
- });
87
-
88
- expect(inputAnalysis.handleCookies).not.to.have.been.called;
89
- expect(core.logger.debug).not.to.have.been.called;
90
- expect(nextStub).to.have.been.calledTwice;
91
- });
92
- });
@@ -1,92 +0,0 @@
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 koaBodyparserInstr = require('./koa-bodyparser4');
9
-
10
- describe('protect input-analysis koa-bodyparser v4.x', function () {
11
- let core, inputAnalysis, koaBodyparserMock, patchedKoaBodyparser, body, nextStub;
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
- body = { parameter: 'parsed' };
25
-
26
- koaBodyparserMock = (body) => (ctx, next) => {
27
- ctx.request.body = body;
28
- next();
29
- };
30
- nextStub = sinon.stub();
31
- sinon.spy(core.patcher, 'patch');
32
-
33
- koaBodyparserInstr(core).install();
34
- patchedKoaBodyparser = core.depHooks.resolve.yield(koaBodyparserMock)[0];
35
- core.patcher.patch.resetHistory();
36
- });
37
-
38
- it('calls patcher on the `cookie-parser` module export', function () {
39
- core.depHooks.resolve.yield(koaBodyparserMock);
40
-
41
- expect(core.patcher.patch).to.have.been.calledOnce;
42
- expect(core.patcher.patch).to.have.been.calledWithMatch(koaBodyparserMock, {
43
- name: 'koa-bodyparser',
44
- patchType: 'protect-input-analysis',
45
- post: sinon.match.func
46
- });
47
- });
48
-
49
- it('calls `.handleParsedBody` when parsing body is successful and in right context', function () {
50
- core.scopes.sources.run({ protect: {} }, () => {
51
- const bParser = patchedKoaBodyparser(body);
52
- bParser({ request: {} }, nextStub);
53
- });
54
-
55
- expect(core.patcher.patch).to.have.been.calledOnce;
56
- expect(core.patcher.patch).to.have.been.calledWithMatch(sinon.match.func, {
57
- name: 'koa-bodyparser',
58
- patchType: 'protect-input-analysis',
59
- pre: sinon.match.func
60
- });
61
-
62
- expect(nextStub).to.have.been.calledOnce;
63
- expect(inputAnalysis.handleParsedBody).to.have.been.calledOnce;
64
- expect(inputAnalysis.handleParsedBody).to.have.been.calledWithMatch({ parsedBody: body }, body);
65
- });
66
-
67
- it('does not call `.handleParsedBody` when not in context', function () {
68
- core.scopes.sources.run({}, () => {
69
- const cParser = patchedKoaBodyparser(body);
70
- cParser({ request: {} }, nextStub);
71
- });
72
-
73
- expect(inputAnalysis.handleCookies).not.to.have.been.called;
74
- expect(nextStub).to.have.been.calledOnce;
75
- });
76
-
77
- it('does not call `.handleParsedBody` when `koa-bodyparser` does not return a result', function () {
78
- core.scopes.sources.run({ protect: {} }, () => {
79
- const cParser = patchedKoaBodyparser();
80
- cParser({ request: {} }, nextStub);
81
- });
82
-
83
- core.scopes.sources.run({ protect: {} }, () => {
84
- const cParser = patchedKoaBodyparser({});
85
- cParser({ request: {} }, nextStub);
86
- });
87
-
88
- expect(inputAnalysis.handleCookies).not.to.have.been.called;
89
- expect(core.logger.debug).not.to.have.been.called;
90
- expect(nextStub).to.have.been.calledTwice;
91
- });
92
- });