@contrast/protect 1.49.0 → 1.50.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/index.js +1 -1
- package/lib/error-handlers/index.test.js +1 -1
- package/lib/error-handlers/install/express.js +162 -0
- package/lib/error-handlers/install/express.test.js +290 -0
- package/lib/error-handlers/install/hapi.js +2 -2
- package/lib/error-handlers/install/hapi.test.js +2 -2
- package/lib/error-handlers/install/koa2.js +1 -1
- package/lib/error-handlers/install/restify.js +1 -1
- package/lib/error-handlers/install/restify.test.js +1 -1
- package/lib/hardening/install/node-serialize0.js +2 -2
- package/lib/hardening/install/node-serialize0.test.js +1 -4
- package/lib/index.d.ts +1 -1
- package/lib/input-analysis/index.js +1 -1
- package/lib/input-analysis/index.test.js +1 -1
- package/lib/input-analysis/install/body-parser1.js +2 -2
- package/lib/input-analysis/install/busboy1.js +1 -1
- package/lib/input-analysis/install/cookie-parser1.js +1 -1
- package/lib/input-analysis/install/{express4.js → express.js} +61 -20
- package/lib/input-analysis/install/{express4.test.js → express.test.js} +92 -59
- package/lib/input-analysis/install/formidable1.js +1 -1
- package/lib/input-analysis/install/hapi.js +1 -1
- package/lib/input-analysis/install/hapi.test.js +6 -14
- package/lib/input-analysis/install/koa-body5.js +1 -1
- package/lib/input-analysis/install/koa-bodyparser4.js +1 -1
- package/lib/input-analysis/install/koa2.js +5 -5
- package/lib/input-analysis/install/multer1.js +1 -1
- package/lib/input-analysis/install/qs6.js +1 -1
- package/lib/input-analysis/install/restify.js +1 -1
- package/lib/input-analysis/install/restify.test.js +1 -1
- package/lib/input-analysis/install/universal-cookie4.js +1 -1
- package/lib/input-tracing/install/child-process.js +1 -1
- package/lib/input-tracing/install/fs.js +2 -2
- package/lib/input-tracing/install/fs.test.js +2 -2
- package/lib/input-tracing/install/http.js +2 -2
- package/lib/input-tracing/install/http2.js +2 -2
- package/lib/input-tracing/install/marsdb.js +2 -2
- package/lib/input-tracing/install/marsdb.test.js +1 -1
- package/lib/input-tracing/install/mongodb.js +2 -2
- package/lib/input-tracing/install/mongodb.test.js +2 -4
- package/lib/input-tracing/install/mssql.js +3 -3
- package/lib/input-tracing/install/mssql.test.js +2 -2
- package/lib/input-tracing/install/mysql.js +7 -9
- package/lib/input-tracing/install/postgres.js +3 -3
- package/lib/input-tracing/install/postgres.test.js +2 -10
- package/lib/input-tracing/install/sequelize.js +2 -2
- package/lib/input-tracing/install/spdy.js +2 -2
- package/lib/input-tracing/install/sqlite3.js +2 -2
- package/lib/input-tracing/install/vm.js +2 -2
- package/lib/semantic-analysis/install/libxmljs.js +3 -3
- package/lib/semantic-analysis/install/libxmljs.test.js +2 -2
- package/package.json +11 -11
- package/lib/error-handlers/install/express4.js +0 -138
- package/lib/error-handlers/install/express4.test.js +0 -238
|
@@ -25,7 +25,7 @@ module.exports = function(core) {
|
|
|
25
25
|
require('./init-domain')(core);
|
|
26
26
|
|
|
27
27
|
// installers
|
|
28
|
-
require('./install/
|
|
28
|
+
require('./install/express')(core);
|
|
29
29
|
require('./install/fastify')(core);
|
|
30
30
|
require('./install/hapi')(core);
|
|
31
31
|
require('./install/koa2')(core);
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright: 2024 Contrast Security, Inc
|
|
3
|
+
* Contact: support@contrastsecurity.com
|
|
4
|
+
* License: Commercial
|
|
5
|
+
|
|
6
|
+
* NOTICE: This Software and the patented inventions embodied within may only be
|
|
7
|
+
* used as part of Contrast Security’s commercial offerings. Even though it is
|
|
8
|
+
* made available through public repositories, use of this Software is subject to
|
|
9
|
+
* the applicable End User Licensing Agreement found at
|
|
10
|
+
* https://www.contrastsecurity.com/enduser-terms-0317a or as otherwise agreed
|
|
11
|
+
* between Contrast Security and the End User. The Software may not be reverse
|
|
12
|
+
* engineered, modified, repackaged, sold, redistributed or otherwise used in a
|
|
13
|
+
* way not consistent with the End User License Agreement.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
'use strict';
|
|
17
|
+
|
|
18
|
+
const SecurityException = require('../../security-exception');
|
|
19
|
+
const { patchType } = require('../constants');
|
|
20
|
+
|
|
21
|
+
module.exports = function (core) {
|
|
22
|
+
const {
|
|
23
|
+
logger,
|
|
24
|
+
depHooks,
|
|
25
|
+
patcher,
|
|
26
|
+
protect,
|
|
27
|
+
} = core;
|
|
28
|
+
|
|
29
|
+
const expressErrorHandler = protect.errorHandlers.expressErrorHandler = {};
|
|
30
|
+
|
|
31
|
+
function checkSecurityException(err, funcKey, orig, throwErr = false) {
|
|
32
|
+
const sourceContext = protect.getSourceContext();
|
|
33
|
+
const isSecurityException = SecurityException.isSecurityException(err);
|
|
34
|
+
|
|
35
|
+
if (isSecurityException && sourceContext) {
|
|
36
|
+
const blockInfo = sourceContext.securityException;
|
|
37
|
+
|
|
38
|
+
sourceContext.block(...blockInfo);
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
if (!sourceContext && isSecurityException) {
|
|
43
|
+
logger.info({ funcKey }, 'source context not found; unable to handle response');
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
if (throwErr) {
|
|
48
|
+
throw err;
|
|
49
|
+
} else {
|
|
50
|
+
return orig();
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
expressErrorHandler.install = function () {
|
|
55
|
+
|
|
56
|
+
// Express 4 and 5
|
|
57
|
+
depHooks.resolve({ name: 'finalhandler', version: '<3' }, (finalhandler) =>
|
|
58
|
+
patcher.patch(finalhandler, {
|
|
59
|
+
name: 'finalHandler',
|
|
60
|
+
patchType,
|
|
61
|
+
post(data) {
|
|
62
|
+
data.result = patcher.patch(data.result, {
|
|
63
|
+
name: 'finalHandler.returnedFunction',
|
|
64
|
+
patchType,
|
|
65
|
+
around(orig, data) {
|
|
66
|
+
const [err] = data.args;
|
|
67
|
+
checkSecurityException(err, data.funcKey, orig);
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
},
|
|
71
|
+
})
|
|
72
|
+
);
|
|
73
|
+
|
|
74
|
+
function postHook() {
|
|
75
|
+
return function(data) {
|
|
76
|
+
patcher.patch(data.result, 'handle', {
|
|
77
|
+
name: 'handle',
|
|
78
|
+
patchType,
|
|
79
|
+
post(data) {
|
|
80
|
+
data.result = data.result?.catch?.((err) => {
|
|
81
|
+
checkSecurityException(err, data.funcKey, data.orig, true);
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
});
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Express 4
|
|
89
|
+
depHooks.resolve({ name: 'express', version: '4', file: 'lib/router/layer.js' }, (Layer) => {
|
|
90
|
+
|
|
91
|
+
patcher.patch(Layer.prototype, 'handle_error', {
|
|
92
|
+
name: 'Layer.prototype.handle_error',
|
|
93
|
+
patchType,
|
|
94
|
+
around(orig, data) {
|
|
95
|
+
const [err] = data.args;
|
|
96
|
+
checkSecurityException(err, data.funcKey, orig);
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
return patcher.patch(Layer, {
|
|
101
|
+
name: 'Layer',
|
|
102
|
+
patchType,
|
|
103
|
+
post: postHook()
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
// Express 5
|
|
109
|
+
depHooks.resolve({ name: 'router', version: '2', file: 'lib/layer.js' }, (Layer) => {
|
|
110
|
+
|
|
111
|
+
patcher.patch(Layer.prototype, 'handleError', {
|
|
112
|
+
name: 'Layer.prototype.handleError',
|
|
113
|
+
patchType,
|
|
114
|
+
around(orig, data) {
|
|
115
|
+
const [err] = data.args;
|
|
116
|
+
checkSecurityException(err, data.funcKey, orig);
|
|
117
|
+
}
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
return patcher.patch(Layer, {
|
|
121
|
+
name: 'router.Layer',
|
|
122
|
+
patchType,
|
|
123
|
+
post: postHook()
|
|
124
|
+
});
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
function preHook() {
|
|
128
|
+
return function(data) {
|
|
129
|
+
if (typeof data.args[1] === 'function') {
|
|
130
|
+
patcher.patch(data.args, '1', {
|
|
131
|
+
name: 'express.route-handler',
|
|
132
|
+
patchType,
|
|
133
|
+
post(data) {
|
|
134
|
+
data.result = data.result?.catch?.((err) => {
|
|
135
|
+
checkSecurityException(err, data.funcKey, data.orig, true);
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// Express 4
|
|
144
|
+
depHooks.resolve({ name: 'express', version: '4', file: 'lib/router/index.js' }, (Router) => {
|
|
145
|
+
patcher.patch(Router.prototype.constructor, 'param', {
|
|
146
|
+
name: 'Router.prototype.constructor.param',
|
|
147
|
+
patchType,
|
|
148
|
+
pre: preHook()
|
|
149
|
+
});
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
// Express 5
|
|
153
|
+
depHooks.resolve({ name: 'router', version: '2' }, (Router) => {
|
|
154
|
+
patcher.patch(Router.prototype, 'param', {
|
|
155
|
+
name: 'Router.prototype.param',
|
|
156
|
+
patchType,
|
|
157
|
+
pre: preHook()
|
|
158
|
+
});
|
|
159
|
+
});
|
|
160
|
+
};
|
|
161
|
+
return expressErrorHandler;
|
|
162
|
+
};
|
|
@@ -0,0 +1,290 @@
|
|
|
1
|
+
/* eslint-disable */
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
const sinon = require('sinon');
|
|
5
|
+
const { expect } = require('chai');
|
|
6
|
+
const scopes = require('@contrast/scopes');
|
|
7
|
+
const patcher = require('@contrast/patcher');
|
|
8
|
+
const mocks = require('@contrast/test/mocks');
|
|
9
|
+
const SecurityException = require('../../security-exception');
|
|
10
|
+
|
|
11
|
+
describe('protect error-handlers express', function () {
|
|
12
|
+
let core, store, errorHandlerInstr;
|
|
13
|
+
|
|
14
|
+
const throwingHandler = (error) => async function () {
|
|
15
|
+
await Promise.reject(error);
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
beforeEach(function () {
|
|
19
|
+
core = mocks.core();
|
|
20
|
+
core.config = mocks.config();
|
|
21
|
+
core.logger = mocks.logger();
|
|
22
|
+
core.scopes = scopes(core);
|
|
23
|
+
core.protect = mocks.protect();
|
|
24
|
+
require('../../get-source-context')(core);
|
|
25
|
+
core.depHooks = mocks.depHooks();
|
|
26
|
+
core.patcher = patcher(core);
|
|
27
|
+
|
|
28
|
+
store = {
|
|
29
|
+
protect: {
|
|
30
|
+
block: sinon.stub(),
|
|
31
|
+
securityException: ['block', 'cmd-injection']
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
sinon.spy(core.patcher, 'patch');
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
describe('finalhandler', function () {
|
|
39
|
+
let finalhandler, returnedFn;
|
|
40
|
+
|
|
41
|
+
beforeEach(function () {
|
|
42
|
+
finalhandler = function () {
|
|
43
|
+
return function () { };
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
core.depHooks.resolve.withArgs({ name: 'finalhandler', version: '<3' }).yields(finalhandler);
|
|
47
|
+
|
|
48
|
+
errorHandlerInstr = require('./express')(core);
|
|
49
|
+
errorHandlerInstr.install();
|
|
50
|
+
|
|
51
|
+
const patchedFinalhandler = core.patcher.patch.getCall(0).returnValue;
|
|
52
|
+
returnedFn = patchedFinalhandler();
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
it('should block the request when there is SecurityException', function () {
|
|
56
|
+
const error = SecurityException.create();
|
|
57
|
+
|
|
58
|
+
core.scopes.sources.run(store, () => {
|
|
59
|
+
returnedFn(error);
|
|
60
|
+
expect(core.logger.info).not.to.have.been.called;
|
|
61
|
+
expect(store.protect.block).to.have.been.calledWith('block', 'cmd-injection');
|
|
62
|
+
});
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
it('should not block the request when there is SecurityException but sourceContext is missing', function () {
|
|
66
|
+
const error = SecurityException.create();
|
|
67
|
+
|
|
68
|
+
core.scopes.sources.run({}, () => {
|
|
69
|
+
returnedFn(error);
|
|
70
|
+
expect(core.logger.info).to.have.been.calledWith(
|
|
71
|
+
{ funcKey: 'protect-error-handling:finalHandler.returnedFunction' },
|
|
72
|
+
'source context not found; unable to handle response',
|
|
73
|
+
);
|
|
74
|
+
});
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it('should skip the instrumentation when there is no SecurityException', function () {
|
|
78
|
+
const error = new Error('Error');
|
|
79
|
+
|
|
80
|
+
core.scopes.sources.run({}, () => {
|
|
81
|
+
returnedFn(error);
|
|
82
|
+
expect(core.logger.info).not.to.have.been.called;
|
|
83
|
+
expect(store.protect.block).not.to.have.been.called;
|
|
84
|
+
});
|
|
85
|
+
});
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
[
|
|
89
|
+
{ name: 'express', version: '4', file: 'lib/router/layer.js' },
|
|
90
|
+
{ name: 'router', version: '2', file: 'lib/layer.js' }
|
|
91
|
+
].forEach((args) => {
|
|
92
|
+
const fn = args.name === 'express' ? 'handle_error' : 'handleError';
|
|
93
|
+
describe(`${args.name} Layer.prototype.${fn}`, function () {
|
|
94
|
+
let Layer;
|
|
95
|
+
|
|
96
|
+
beforeEach(function () {
|
|
97
|
+
Layer = function () { };
|
|
98
|
+
|
|
99
|
+
Layer.prototype[fn] = function () { };
|
|
100
|
+
|
|
101
|
+
core.depHooks.resolve.withArgs(args).yields(Layer);
|
|
102
|
+
|
|
103
|
+
errorHandlerInstr = require('./express')(core);
|
|
104
|
+
errorHandlerInstr.install();
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
it('should block the request when there is SecurityException', function () {
|
|
108
|
+
const error = SecurityException.create();
|
|
109
|
+
|
|
110
|
+
core.scopes.sources.run(store, () => {
|
|
111
|
+
Layer.prototype[fn](error);
|
|
112
|
+
expect(core.logger.info).not.to.have.been.called;
|
|
113
|
+
expect(store.protect.block).to.have.been.calledWith('block', 'cmd-injection');
|
|
114
|
+
});
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
it('should not block the request when there is SecurityException but sourceContext is missing', function () {
|
|
118
|
+
const error = SecurityException.create();
|
|
119
|
+
|
|
120
|
+
core.scopes.sources.run({}, () => {
|
|
121
|
+
Layer.prototype[fn](error);
|
|
122
|
+
expect(core.logger.info).to.have.been.calledWith(
|
|
123
|
+
{ funcKey: `protect-error-handling:Layer.prototype.${fn}` },
|
|
124
|
+
'source context not found; unable to handle response'
|
|
125
|
+
);
|
|
126
|
+
});
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
it('should skip the instrumentation when there is no SecurityException', function () {
|
|
130
|
+
const error = new Error('Error');
|
|
131
|
+
|
|
132
|
+
core.scopes.sources.run({}, () => {
|
|
133
|
+
Layer.prototype[fn](error);
|
|
134
|
+
expect(core.logger.info).not.to.have.been.called;
|
|
135
|
+
expect(store.protect.block).not.to.have.been.called;
|
|
136
|
+
});
|
|
137
|
+
});
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
describe(`${args.name} Layer handle`, function () {
|
|
141
|
+
let Layer, patchedLayer;
|
|
142
|
+
const error = SecurityException.create();
|
|
143
|
+
const fn = throwingHandler(error);
|
|
144
|
+
beforeEach(function () {
|
|
145
|
+
Layer = function () {
|
|
146
|
+
return {
|
|
147
|
+
handle: fn
|
|
148
|
+
}
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
errorHandlerInstr = require('./express')(core);
|
|
152
|
+
errorHandlerInstr.install();
|
|
153
|
+
[patchedLayer] = core.depHooks.resolve.withArgs(args).yield(Layer);
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
it('should block the request when there is SecurityException', async function () {
|
|
157
|
+
await core.scopes.sources.run(store, async () => {
|
|
158
|
+
await patchedLayer().handle();
|
|
159
|
+
});
|
|
160
|
+
expect(core.logger.info).not.to.have.been.called;
|
|
161
|
+
expect(store.protect.block).to.have.been.calledWith('block', 'cmd-injection');
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
it('should not block the request when there is SecurityException but sourceContext is missing', async function () {
|
|
165
|
+
await core.scopes.sources.run({}, async () => {
|
|
166
|
+
await patchedLayer().handle();
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
expect(core.logger.info).to.have.been.calledWith(
|
|
170
|
+
{ funcKey: 'protect-error-handling:handle' },
|
|
171
|
+
'source context not found; unable to handle response',
|
|
172
|
+
);
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
it('should skip the instrumentation when there is no SecurityException', async function () {
|
|
176
|
+
const error = new Error('Error');
|
|
177
|
+
const fn = throwingHandler(error);
|
|
178
|
+
Layer = function () {
|
|
179
|
+
return {
|
|
180
|
+
handle: fn
|
|
181
|
+
}
|
|
182
|
+
};
|
|
183
|
+
|
|
184
|
+
errorHandlerInstr = require('./express')(core);
|
|
185
|
+
errorHandlerInstr.install();
|
|
186
|
+
[patchedLayer] = core.depHooks.resolve.withArgs(args).yield(Layer);
|
|
187
|
+
|
|
188
|
+
try {
|
|
189
|
+
await core.scopes.sources.run(store, async () => {
|
|
190
|
+
await patchedLayer().handle();
|
|
191
|
+
});
|
|
192
|
+
} catch (err) {
|
|
193
|
+
expect(err).to.equal(error);
|
|
194
|
+
expect(core.logger.info).not.to.have.been.called;
|
|
195
|
+
expect(store.protect.block).not.to.have.been.called;
|
|
196
|
+
}
|
|
197
|
+
});
|
|
198
|
+
});
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
[
|
|
202
|
+
{ name: 'express', version: '4', file: 'lib/router/index.js' },
|
|
203
|
+
{ name: 'router', version: '2' }
|
|
204
|
+
].forEach((args) => {
|
|
205
|
+
describe(`${args.name} Router param`, function () {
|
|
206
|
+
let Router, param;
|
|
207
|
+
const sampleFn = function () { };
|
|
208
|
+
|
|
209
|
+
beforeEach(function () {
|
|
210
|
+
|
|
211
|
+
Router = function() { };
|
|
212
|
+
if (args.name === 'express') {
|
|
213
|
+
Router.prototype.constructor = {
|
|
214
|
+
param: function () { },
|
|
215
|
+
};
|
|
216
|
+
param = (...args) => Router.prototype.constructor.param(...args);
|
|
217
|
+
} else {
|
|
218
|
+
Router.prototype = {
|
|
219
|
+
param: function () { },
|
|
220
|
+
};
|
|
221
|
+
param = (...args) => Router.prototype.param(...args);
|
|
222
|
+
}
|
|
223
|
+
core.depHooks.resolve.withArgs(args).yields(Router);
|
|
224
|
+
|
|
225
|
+
core.patcher.patch.resetHistory();
|
|
226
|
+
|
|
227
|
+
errorHandlerInstr = require('./express')(core);
|
|
228
|
+
errorHandlerInstr.install();
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
it('should patch the function for the param property', function () {
|
|
232
|
+
param('sampleFn', sampleFn);
|
|
233
|
+
expect(core.patcher.patch).to.have.been.calledWith(sinon.match.array, '1', sinon.match.object);
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
it('should block the request when there is SecurityException', async function () {
|
|
237
|
+
const error = SecurityException.create();
|
|
238
|
+
const fn = throwingHandler(error);
|
|
239
|
+
|
|
240
|
+
param('fn', fn);
|
|
241
|
+
|
|
242
|
+
const patchedFn = core.patcher.patch.getCall(1).returnValue[1];
|
|
243
|
+
|
|
244
|
+
await core.scopes.sources.run(store, async () => {
|
|
245
|
+
await patchedFn();
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
expect(core.logger.info).not.to.have.been.called;
|
|
249
|
+
expect(store.protect.block).to.have.been.calledWith('block', 'cmd-injection');
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
it('should not block the request when there is SecurityException but sourceContext is missing', async function () {
|
|
253
|
+
const error = SecurityException.create();
|
|
254
|
+
const fn = throwingHandler(error);
|
|
255
|
+
|
|
256
|
+
param('fn', fn);
|
|
257
|
+
|
|
258
|
+
const patchedFn = core.patcher.patch.getCall(1).returnValue[1];
|
|
259
|
+
|
|
260
|
+
await core.scopes.sources.run({}, async () => {
|
|
261
|
+
await patchedFn();
|
|
262
|
+
});
|
|
263
|
+
|
|
264
|
+
expect(core.logger.info).to.have.been.calledWith(
|
|
265
|
+
{ funcKey: 'protect-error-handling:express.route-handler' },
|
|
266
|
+
'source context not found; unable to handle response',
|
|
267
|
+
);
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
it('should skip the instrumentation when there is no SecurityException', async function () {
|
|
271
|
+
const error = new Error('Error');
|
|
272
|
+
const fn = throwingHandler(error);
|
|
273
|
+
|
|
274
|
+
param('fn', fn);
|
|
275
|
+
|
|
276
|
+
const patchedFn = core.patcher.patch.getCall(1).returnValue[1];
|
|
277
|
+
|
|
278
|
+
try {
|
|
279
|
+
await core.scopes.sources.run(store, async () => {
|
|
280
|
+
await patchedFn();
|
|
281
|
+
});
|
|
282
|
+
} catch (err) {
|
|
283
|
+
expect(err).to.equal(error);
|
|
284
|
+
expect(core.logger.info).not.to.have.been.called;
|
|
285
|
+
expect(store.protect.block).not.to.have.been.called;
|
|
286
|
+
}
|
|
287
|
+
});
|
|
288
|
+
});
|
|
289
|
+
});
|
|
290
|
+
});
|
|
@@ -60,12 +60,12 @@ module.exports = function (core) {
|
|
|
60
60
|
|
|
61
61
|
hapiErrorHandler.install = function () {
|
|
62
62
|
depHooks.resolve(
|
|
63
|
-
{ name: 'boom' },
|
|
63
|
+
{ name: 'boom', version: '<8' },
|
|
64
64
|
(boom) => registerErrorHandler(boom, 'boom'),
|
|
65
65
|
);
|
|
66
66
|
|
|
67
67
|
depHooks.resolve(
|
|
68
|
-
{ name: '@hapi/boom' },
|
|
68
|
+
{ name: '@hapi/boom', version: '<11' },
|
|
69
69
|
(boom) => registerErrorHandler(boom, '@hapi/boom'),
|
|
70
70
|
);
|
|
71
71
|
};
|
|
@@ -39,8 +39,8 @@ describe('protect error-handlers hapi', function () {
|
|
|
39
39
|
Boom.boomify = function boom() { };
|
|
40
40
|
HapiBoom.boomify = function hapiBoom() { };
|
|
41
41
|
|
|
42
|
-
core.depHooks.resolve.withArgs({ name: Boom.name }).yields(Boom);
|
|
43
|
-
core.depHooks.resolve.withArgs({ name: HapiBoom.name }).yields(HapiBoom);
|
|
42
|
+
core.depHooks.resolve.withArgs(sinon.match({ name: Boom.name })).yields(Boom);
|
|
43
|
+
core.depHooks.resolve.withArgs(sinon.match({ name: HapiBoom.name })).yields(HapiBoom);
|
|
44
44
|
|
|
45
45
|
errorHandler = require('./hapi')(core);
|
|
46
46
|
errorHandler.install();
|
|
@@ -30,7 +30,7 @@ module.exports = function (core) {
|
|
|
30
30
|
const koa2ErrorHandler = protect.errorHandlers.koa2ErrorHandler = {};
|
|
31
31
|
|
|
32
32
|
koa2ErrorHandler.install = function () {
|
|
33
|
-
depHooks.resolve({ name: 'koa', version: '>=2.3.0' }, (Koa) => {
|
|
33
|
+
depHooks.resolve({ name: 'koa', version: '>=2.3.0 <3' }, (Koa) => {
|
|
34
34
|
patcher.patch(Koa.prototype, 'handleRequest', {
|
|
35
35
|
name: 'Koa.Application.handleRequest',
|
|
36
36
|
patchType,
|
|
@@ -24,7 +24,7 @@ module.exports = function init(core) {
|
|
|
24
24
|
return protect.errorHandlers.restifyErrorHandler = {
|
|
25
25
|
install() {
|
|
26
26
|
depHooks.resolve(
|
|
27
|
-
{ name: 'restify', file: 'lib/server.js', version: '>=8' },
|
|
27
|
+
{ name: 'restify', file: 'lib/server.js', version: '>=8 <12' },
|
|
28
28
|
(Server) => {
|
|
29
29
|
patcher.patch(Server.prototype, '_onHandlerError', {
|
|
30
30
|
name: 'restify.Server.prototype._onHandlerError',
|
|
@@ -15,7 +15,7 @@ describe('protect error-handlers restify v8+', function () {
|
|
|
15
15
|
store = { protect: { block: sinon.stub(), securityException: ['block'] } };
|
|
16
16
|
|
|
17
17
|
core.depHooks.resolve
|
|
18
|
-
.withArgs({ name: 'restify', file: 'lib/server.js', version: '>=8' })
|
|
18
|
+
.withArgs({ name: 'restify', file: 'lib/server.js', version: '>=8 <12' })
|
|
19
19
|
.yields(Server);
|
|
20
20
|
|
|
21
21
|
core.protect.errorHandlers.restifyErrorHandler.install();
|
|
@@ -32,13 +32,13 @@ module.exports = function(core) {
|
|
|
32
32
|
const method = 'unserialize';
|
|
33
33
|
|
|
34
34
|
depHooks.resolve(
|
|
35
|
-
{ name, version: '<1
|
|
35
|
+
{ name, version: '<1' },
|
|
36
36
|
(nodeSerialize) => {
|
|
37
37
|
patcher.patch(nodeSerialize, method, {
|
|
38
38
|
name,
|
|
39
39
|
patchType,
|
|
40
40
|
pre({ args: [value], hooked, orig }) {
|
|
41
|
-
const sourceContext = protect.getSourceContext(
|
|
41
|
+
const sourceContext = protect.getSourceContext();
|
|
42
42
|
|
|
43
43
|
if (!sourceContext || !value) return;
|
|
44
44
|
|
|
@@ -18,10 +18,7 @@ describe('protect hardening node-serialize0', function () {
|
|
|
18
18
|
core = mocks.core();
|
|
19
19
|
core.logger = mocks.logger();
|
|
20
20
|
core.depHooks = mocks.depHooks();
|
|
21
|
-
core.depHooks
|
|
22
|
-
.resolve
|
|
23
|
-
.withArgs({ name: 'node-serialize', version: '<1.0.0' })
|
|
24
|
-
.yields(mockLib);
|
|
21
|
+
core.depHooks.resolve.yields(mockLib);
|
|
25
22
|
core.patcher = patcher(core);
|
|
26
23
|
core.scopes = scopes(core);
|
|
27
24
|
core.protect = mocks.protect();
|
package/lib/index.d.ts
CHANGED
|
@@ -122,7 +122,7 @@ export interface Protect {
|
|
|
122
122
|
install: () => void
|
|
123
123
|
}
|
|
124
124
|
koa2ErrorHandler: { install: () => void },
|
|
125
|
-
|
|
125
|
+
expressErrorHandler: { install: () => void },
|
|
126
126
|
install: () => void,
|
|
127
127
|
},
|
|
128
128
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
@@ -39,7 +39,7 @@ module.exports = function(core) {
|
|
|
39
39
|
// framework specific instrumentation
|
|
40
40
|
require('./install/fastify')(core);
|
|
41
41
|
require('./install/koa2')(core);
|
|
42
|
-
require('./install/
|
|
42
|
+
require('./install/express')(core);
|
|
43
43
|
require('./install/hapi')(core);
|
|
44
44
|
require('./install/restify')(core);
|
|
45
45
|
|
|
@@ -25,7 +25,7 @@ describe('protect input-analysis', function () {
|
|
|
25
25
|
'./install/koa2': modulesMock('koa2Instrumentation'),
|
|
26
26
|
'./install/koa-body5': modulesMock('koaBody5Instrumentation'),
|
|
27
27
|
'./install/koa-bodyparser4': modulesMock('koaBodyparser4Instrumentation'),
|
|
28
|
-
'./install/
|
|
28
|
+
'./install/express': modulesMock('expressInstrumentation'),
|
|
29
29
|
'./install/body-parser1': modulesMock('bodyParser1Instrumentation'),
|
|
30
30
|
'./install/cookie-parser1': modulesMock('cookieParser1Instrumentation'),
|
|
31
31
|
'./install/formidable1': modulesMock('formidable1Instrumentation'),
|
|
@@ -28,7 +28,7 @@ module.exports = (core) => {
|
|
|
28
28
|
|
|
29
29
|
function contrastNext(req, origNext, fnName) {
|
|
30
30
|
return function next(origErr) {
|
|
31
|
-
const sourceContext = protect.getSourceContext(
|
|
31
|
+
const sourceContext = protect.getSourceContext();
|
|
32
32
|
let securityException;
|
|
33
33
|
|
|
34
34
|
if (sourceContext && req.body && Object.keys(req.body).length) {
|
|
@@ -62,7 +62,7 @@ module.exports = (core) => {
|
|
|
62
62
|
|
|
63
63
|
// Patch body parser - `body-parser` used by `express` framework
|
|
64
64
|
function install() {
|
|
65
|
-
depHooks.resolve({ name: 'body-parser' }, (bodyParser) => {
|
|
65
|
+
depHooks.resolve({ name: 'body-parser', version: '<2' }, (bodyParser) => {
|
|
66
66
|
const origBodyParser = bodyParser;
|
|
67
67
|
|
|
68
68
|
const { json: origJson, raw: origRaw, text: origText, urlencoded: origUrlencoded } = bodyParser;
|
|
@@ -27,7 +27,7 @@ module.exports = (core) => {
|
|
|
27
27
|
|
|
28
28
|
// Patch `busboy`
|
|
29
29
|
function install() {
|
|
30
|
-
depHooks.resolve({ name: 'busboy' }, (busboy) => {
|
|
30
|
+
depHooks.resolve({ name: 'busboy', version: '<2' }, (busboy) => {
|
|
31
31
|
patcher.patch(busboy.prototype, 'emit', {
|
|
32
32
|
name: 'busboy.prototype.emit',
|
|
33
33
|
patchType,
|
|
@@ -29,7 +29,7 @@ module.exports = (core) => {
|
|
|
29
29
|
|
|
30
30
|
// Patch `cookie-parser` package
|
|
31
31
|
function install() {
|
|
32
|
-
depHooks.resolve({ name: 'cookie-parser' }, (cookieParser) => patcher.patch(cookieParser, {
|
|
32
|
+
depHooks.resolve({ name: 'cookie-parser', version: '<2' }, (cookieParser) => patcher.patch(cookieParser, {
|
|
33
33
|
name: 'cookie-parser',
|
|
34
34
|
patchType,
|
|
35
35
|
post(data) {
|