@contrast/protect 1.39.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.
- package/lib/error-handlers/common-handler.test.js +52 -0
- package/lib/error-handlers/index.test.js +32 -0
- package/lib/error-handlers/init-domain.test.js +34 -0
- package/lib/error-handlers/install/express4.test.js +238 -0
- package/lib/error-handlers/install/fastify.test.js +130 -0
- package/lib/error-handlers/install/hapi.test.js +102 -0
- package/lib/error-handlers/install/koa2.test.js +83 -0
- package/lib/error-handlers/install/restify.test.js +57 -0
- package/lib/get-source-context.test.js +35 -0
- package/lib/hardening/handlers.test.js +89 -0
- package/lib/hardening/index.test.js +31 -0
- package/lib/hardening/install/node-serialize0.test.js +61 -0
- package/lib/index.test.js +53 -0
- package/lib/input-analysis/handlers.test.js +1604 -0
- package/lib/input-analysis/index.test.js +45 -0
- package/lib/input-analysis/install/body-parser1.test.js +134 -0
- package/lib/input-analysis/install/busboy1.test.js +81 -0
- package/lib/input-analysis/install/cookie-parser1.test.js +144 -0
- package/lib/input-analysis/install/express4.test.js +208 -0
- package/lib/input-analysis/install/fastify.test.js +96 -0
- package/lib/input-analysis/install/formidable1.test.js +114 -0
- package/lib/input-analysis/install/hapi.test.js +300 -0
- package/lib/input-analysis/install/http.test.js +264 -0
- package/lib/input-analysis/install/koa-body5.test.js +92 -0
- package/lib/input-analysis/install/koa-bodyparser4.test.js +92 -0
- package/lib/input-analysis/install/koa2.test.js +259 -0
- package/lib/input-analysis/install/multer1.test.js +209 -0
- package/lib/input-analysis/install/qs6.test.js +79 -0
- package/lib/input-analysis/install/restify.test.js +98 -0
- package/lib/input-analysis/install/universal-cookie4.test.js +70 -0
- package/lib/input-analysis/ip-analysis.test.js +71 -0
- package/lib/input-analysis/virtual-patches.test.js +106 -0
- package/lib/input-tracing/handlers/index.test.js +1236 -0
- package/lib/input-tracing/index.test.js +62 -0
- package/lib/input-tracing/install/child-process.test.js +133 -0
- package/lib/input-tracing/install/eval.test.js +78 -0
- package/lib/input-tracing/install/fs.test.js +108 -0
- package/lib/input-tracing/install/function.test.js +81 -0
- package/lib/input-tracing/install/http.test.js +85 -0
- package/lib/input-tracing/install/http2.test.js +83 -0
- package/lib/input-tracing/install/marsdb.test.js +126 -0
- package/lib/input-tracing/install/mongodb.test.js +282 -0
- package/lib/input-tracing/install/mssql.test.js +81 -0
- package/lib/input-tracing/install/mysql.test.js +108 -0
- package/lib/input-tracing/install/postgres.test.js +125 -0
- package/lib/input-tracing/install/sequelize.test.js +78 -0
- package/lib/input-tracing/install/spdy.test.js +76 -0
- package/lib/input-tracing/install/sqlite3.test.js +88 -0
- package/lib/input-tracing/install/vm.test.js +176 -0
- package/lib/make-response-blocker.test.js +99 -0
- package/lib/make-source-context.test.js +219 -0
- package/lib/policy.test.js +446 -0
- package/lib/semantic-analysis/handlers.test.js +379 -0
- package/lib/semantic-analysis/index.test.js +38 -0
- package/lib/semantic-analysis/install/libxmljs.test.js +156 -0
- package/lib/semantic-analysis/utils/xml-analysis.test.js +156 -0
- package/lib/throw-security-exception.test.js +37 -0
- 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
|
+
});
|