@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
|
@@ -27,7 +27,7 @@ module.exports = function (core) {
|
|
|
27
27
|
} = core;
|
|
28
28
|
|
|
29
29
|
const pre = ({ args, hooked, name, orig }) => {
|
|
30
|
-
const sourceContext = protect.getSourceContext(
|
|
30
|
+
const sourceContext = protect.getSourceContext();
|
|
31
31
|
const [value] = args;
|
|
32
32
|
if (!sourceContext || !value || !isString(value)) return;
|
|
33
33
|
|
|
@@ -46,7 +46,7 @@ module.exports = function (core) {
|
|
|
46
46
|
core.protect.inputTracing.mssqlInstrumentation = {
|
|
47
47
|
install() {
|
|
48
48
|
depHooks.resolve(
|
|
49
|
-
{ name: 'mssql', file: 'lib/base/prepared-statement.js' },
|
|
49
|
+
{ name: 'mssql', version: '<12', file: 'lib/base/prepared-statement.js' },
|
|
50
50
|
(PreparedStatement) => {
|
|
51
51
|
patcher.patch(PreparedStatement.prototype, 'prepare', {
|
|
52
52
|
name: 'mssql.PreparedStatement.prototype.prepare',
|
|
@@ -57,7 +57,7 @@ module.exports = function (core) {
|
|
|
57
57
|
);
|
|
58
58
|
|
|
59
59
|
depHooks.resolve(
|
|
60
|
-
{ name: 'mssql', file: 'lib/base/request.js' },
|
|
60
|
+
{ name: 'mssql', version: '<12', file: 'lib/base/request.js' },
|
|
61
61
|
(Request) => {
|
|
62
62
|
patcher.patch(Request.prototype, 'batch', {
|
|
63
63
|
name: 'mssql.Request.prototype.batch',
|
|
@@ -27,11 +27,11 @@ describe('protect input-tracing mssql', function () {
|
|
|
27
27
|
Request.prototype.query = sinon.stub();
|
|
28
28
|
|
|
29
29
|
core.depHooks.resolve
|
|
30
|
-
.withArgs({
|
|
30
|
+
.withArgs(sinon.match({ file: 'lib/base/prepared-statement.js' }))
|
|
31
31
|
.yields(PreparedStatement);
|
|
32
32
|
|
|
33
33
|
core.depHooks.resolve
|
|
34
|
-
.withArgs({
|
|
34
|
+
.withArgs(sinon.match({ file: 'lib/base/request.js' }))
|
|
35
35
|
.yields(Request);
|
|
36
36
|
|
|
37
37
|
require('./mssql')(core).install();
|
|
@@ -40,20 +40,18 @@ module.exports = function(core) {
|
|
|
40
40
|
|
|
41
41
|
mysqlInstr.install = function() {
|
|
42
42
|
[
|
|
43
|
-
{
|
|
44
|
-
{
|
|
45
|
-
{
|
|
43
|
+
{ name: 'mysql', version: '<3', file: 'lib/Connection.js', method: 'query' },
|
|
44
|
+
{ name: 'mysql2', version: '<4', file: 'lib/connection.js', method: 'execute' },
|
|
45
|
+
{ name: 'mysql2', version: '<4', file: 'lib/connection.js', method: 'query' }
|
|
46
46
|
].forEach(
|
|
47
|
-
({
|
|
48
|
-
depHooks.resolve({
|
|
49
|
-
const name = `${module}.Connection.prototype.${method}`;
|
|
50
|
-
|
|
47
|
+
({ name, version, file, method }) => {
|
|
48
|
+
depHooks.resolve({ name, version, file }, conn => {
|
|
51
49
|
patcher.patch(conn.prototype, method, {
|
|
52
|
-
name
|
|
50
|
+
name: `${name}.Connection.prototype.${method}`,
|
|
53
51
|
patchType,
|
|
54
52
|
pre(data) {
|
|
55
53
|
const { args, hooked, name, orig } = data;
|
|
56
|
-
const sourceContext = protect.getSourceContext(
|
|
54
|
+
const sourceContext = protect.getSourceContext();
|
|
57
55
|
|
|
58
56
|
if (!sourceContext) return;
|
|
59
57
|
|
|
@@ -32,7 +32,7 @@ module.exports = function(core) {
|
|
|
32
32
|
}
|
|
33
33
|
|
|
34
34
|
function preHook({ args, hooked, name, orig }) {
|
|
35
|
-
const sourceContext = protect.getSourceContext(
|
|
35
|
+
const sourceContext = protect.getSourceContext();
|
|
36
36
|
|
|
37
37
|
if (!sourceContext) return;
|
|
38
38
|
|
|
@@ -49,7 +49,7 @@ module.exports = function(core) {
|
|
|
49
49
|
}
|
|
50
50
|
|
|
51
51
|
function install() {
|
|
52
|
-
depHooks.resolve({ name: 'pg', file: 'lib/client.js' }, client => {
|
|
52
|
+
depHooks.resolve({ name: 'pg', version: '<9', file: 'lib/client.js' }, client => {
|
|
53
53
|
const name = 'pg.Client.prototype.query';
|
|
54
54
|
patcher.patch(client.prototype, 'query', {
|
|
55
55
|
name,
|
|
@@ -58,7 +58,7 @@ module.exports = function(core) {
|
|
|
58
58
|
});
|
|
59
59
|
});
|
|
60
60
|
|
|
61
|
-
depHooks.resolve({ name: 'pg-pool' }, pool => {
|
|
61
|
+
depHooks.resolve({ name: 'pg-pool', version: '<4' }, pool => {
|
|
62
62
|
const name = 'pg-pool.Pool.prototype.query';
|
|
63
63
|
patcher.patch(pool.prototype, 'query', {
|
|
64
64
|
name,
|
|
@@ -31,11 +31,7 @@ describe('protect input-tracing postgres', function () {
|
|
|
31
31
|
beforeEach(function () {
|
|
32
32
|
Client = function () { };
|
|
33
33
|
Client.prototype.query = sinon.stub();
|
|
34
|
-
core
|
|
35
|
-
.depHooks
|
|
36
|
-
.resolve
|
|
37
|
-
.withArgs({ name: 'pg', file: 'lib/client.js' })
|
|
38
|
-
.yields(Client);
|
|
34
|
+
core.depHooks.resolve.withArgs(sinon.match({ name: 'pg' })).yields(Client);
|
|
39
35
|
require('./postgres')(core).install();
|
|
40
36
|
});
|
|
41
37
|
|
|
@@ -79,11 +75,7 @@ describe('protect input-tracing postgres', function () {
|
|
|
79
75
|
beforeEach(function () {
|
|
80
76
|
Pool = function () { };
|
|
81
77
|
Pool.prototype.query = sinon.stub();
|
|
82
|
-
core
|
|
83
|
-
.depHooks
|
|
84
|
-
.resolve
|
|
85
|
-
.withArgs({ name: 'pg-pool' })
|
|
86
|
-
.yields(Pool);
|
|
78
|
+
core.depHooks.resolve.withArgs(sinon.match({ name: 'pg-pool' })).yields(Pool);
|
|
87
79
|
|
|
88
80
|
require('./postgres')(core).install();
|
|
89
81
|
});
|
|
@@ -33,7 +33,7 @@ module.exports = function(core) {
|
|
|
33
33
|
}
|
|
34
34
|
|
|
35
35
|
function install() {
|
|
36
|
-
depHooks.resolve({ name: 'sequelize' }, sequelize => {
|
|
36
|
+
depHooks.resolve({ name: 'sequelize', version: '<7' }, sequelize => {
|
|
37
37
|
const name = 'sequelize.prototype.query';
|
|
38
38
|
patcher.patch(sequelize.prototype, 'query', {
|
|
39
39
|
name,
|
|
@@ -41,7 +41,7 @@ module.exports = function(core) {
|
|
|
41
41
|
pre({ args, hooked, name, orig }) {
|
|
42
42
|
if (instrumentation.isLocked()) return;
|
|
43
43
|
|
|
44
|
-
const sourceContext = protect.getSourceContext(
|
|
44
|
+
const sourceContext = protect.getSourceContext();
|
|
45
45
|
|
|
46
46
|
if (!sourceContext || sourceContext.allowed) return;
|
|
47
47
|
|
|
@@ -27,7 +27,7 @@ module.exports = function(core) {
|
|
|
27
27
|
} = core;
|
|
28
28
|
|
|
29
29
|
function install() {
|
|
30
|
-
depHooks.resolve({ name: 'spdy' }, spdy => {
|
|
30
|
+
depHooks.resolve({ name: 'spdy', version: '<5' }, spdy => {
|
|
31
31
|
const name = 'spdy.response.end';
|
|
32
32
|
patcher.patch(spdy.response, 'end', {
|
|
33
33
|
name,
|
|
@@ -35,7 +35,7 @@ module.exports = function(core) {
|
|
|
35
35
|
pre({ args, obj: response, hooked }) {
|
|
36
36
|
if (instrumentation.isLocked()) return;
|
|
37
37
|
|
|
38
|
-
const sourceContext = protect.getSourceContext(
|
|
38
|
+
const sourceContext = protect.getSourceContext();
|
|
39
39
|
|
|
40
40
|
if (!sourceContext) return;
|
|
41
41
|
|
|
@@ -28,7 +28,7 @@ module.exports = function(core) {
|
|
|
28
28
|
} = core;
|
|
29
29
|
|
|
30
30
|
function install() {
|
|
31
|
-
depHooks.resolve({ name: 'sqlite3' }, sqlite3 => {
|
|
31
|
+
depHooks.resolve({ name: 'sqlite3', version: '<6' }, sqlite3 => {
|
|
32
32
|
['all', 'run', 'get', 'each', 'exec', 'prepare'].forEach((method) => {
|
|
33
33
|
const name = `sqlite3.Database.prototype.${method}`;
|
|
34
34
|
patcher.patch(sqlite3.Database.prototype, method, {
|
|
@@ -37,7 +37,7 @@ module.exports = function(core) {
|
|
|
37
37
|
pre({ args, hooked, name, orig }) {
|
|
38
38
|
if (instrumentation.isLocked()) return;
|
|
39
39
|
|
|
40
|
-
const sourceContext = protect.getSourceContext(
|
|
40
|
+
const sourceContext = protect.getSourceContext();
|
|
41
41
|
if (!sourceContext) return;
|
|
42
42
|
|
|
43
43
|
const value = args[0];
|
|
@@ -44,7 +44,7 @@ module.exports = function (core) {
|
|
|
44
44
|
const createPre = (arity) => ({ args, hooked, orig, name }) => {
|
|
45
45
|
if (instrumentation.isLocked()) return;
|
|
46
46
|
|
|
47
|
-
const sourceContext = protect.getSourceContext(
|
|
47
|
+
const sourceContext = protect.getSourceContext();
|
|
48
48
|
if (!sourceContext) return;
|
|
49
49
|
|
|
50
50
|
for (let i = 0; i < arity; i++) {
|
|
@@ -62,7 +62,7 @@ module.exports = function (core) {
|
|
|
62
62
|
|
|
63
63
|
const vmInstrumentation = inputTracing.vmInstrumentation = {
|
|
64
64
|
install() {
|
|
65
|
-
depHooks.resolve({ name: 'vm' }, (vm) => {
|
|
65
|
+
depHooks.resolve({ name: 'vm', version: '*' }, (vm) => {
|
|
66
66
|
VM_METHODS.forEach(([method, arity]) => {
|
|
67
67
|
patcher.patch(vm, method, {
|
|
68
68
|
name: `vm.${method}`,
|
|
@@ -45,7 +45,7 @@ module.exports = function (core) {
|
|
|
45
45
|
name,
|
|
46
46
|
patchType,
|
|
47
47
|
pre({ args, hooked, orig, funcKey }) {
|
|
48
|
-
const sourceContext = protect.getSourceContext(
|
|
48
|
+
const sourceContext = protect.getSourceContext();
|
|
49
49
|
const [value, options] = args;
|
|
50
50
|
|
|
51
51
|
if (!sourceContext || !value || !isString(value) || checkOptions(options)) {
|
|
@@ -78,11 +78,11 @@ module.exports = function (core) {
|
|
|
78
78
|
protect.semanticAnalysis.libxmljs = {
|
|
79
79
|
install() {
|
|
80
80
|
// libxmljs changed its API in version 1.0.0
|
|
81
|
-
depHooks.resolve({ name: 'libxmljs', version: '>=1' }, handler(true));
|
|
81
|
+
depHooks.resolve({ name: 'libxmljs', version: '>=1 <2' }, handler(true));
|
|
82
82
|
|
|
83
83
|
// libxmljs versions prior to 1.0.0 and libxmljs2 share the same API
|
|
84
84
|
depHooks.resolve({ name: 'libxmljs', version: '<1' }, handler(false));
|
|
85
|
-
depHooks.resolve({ name: 'libxmljs2' }, handler(false));
|
|
85
|
+
depHooks.resolve({ name: 'libxmljs2', version: '<1' }, handler(false));
|
|
86
86
|
}
|
|
87
87
|
};
|
|
88
88
|
|
|
@@ -35,13 +35,13 @@ describe('protect semantic-analysis libxmljs', function () {
|
|
|
35
35
|
};
|
|
36
36
|
|
|
37
37
|
core.depHooks.resolve
|
|
38
|
-
.withArgs({ name: 'libxmljs', version: '>=1' })
|
|
38
|
+
.withArgs({ name: 'libxmljs', version: '>=1 <2' })
|
|
39
39
|
.yields(modules['libxmljs@1'], { name: 'libxmljs', version: '1.0.9' });
|
|
40
40
|
core.depHooks.resolve
|
|
41
41
|
.withArgs({ name: 'libxmljs', version: '<1' })
|
|
42
42
|
.yields(modules['libxmljs@0'], { name: 'libxmljs', version: '0.19.10' });
|
|
43
43
|
core.depHooks.resolve
|
|
44
|
-
.withArgs({ name: 'libxmljs2' })
|
|
44
|
+
.withArgs({ name: 'libxmljs2', version: '<1' })
|
|
45
45
|
.yields(modules.libxmljs2, { name: 'libxmljs2', version: '0.32.0' });
|
|
46
46
|
|
|
47
47
|
require('../../get-source-context')(core);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@contrast/protect",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.50.0",
|
|
4
4
|
"description": "Contrast service providing framework-agnostic Protect support",
|
|
5
5
|
"license": "SEE LICENSE IN LICENSE",
|
|
6
6
|
"author": "Contrast Security <nodejs@contrastsecurity.com> (https://www.contrastsecurity.com)",
|
|
@@ -18,16 +18,16 @@
|
|
|
18
18
|
},
|
|
19
19
|
"dependencies": {
|
|
20
20
|
"@contrast/agent-lib": "^7.0.1",
|
|
21
|
-
"@contrast/common": "1.
|
|
22
|
-
"@contrast/config": "1.
|
|
23
|
-
"@contrast/core": "1.
|
|
24
|
-
"@contrast/dep-hooks": "1.
|
|
25
|
-
"@contrast/esm-hooks": "2.
|
|
26
|
-
"@contrast/instrumentation": "1.
|
|
27
|
-
"@contrast/logger": "1.
|
|
28
|
-
"@contrast/patcher": "1.
|
|
29
|
-
"@contrast/rewriter": "1.
|
|
30
|
-
"@contrast/scopes": "1.
|
|
21
|
+
"@contrast/common": "1.27.0",
|
|
22
|
+
"@contrast/config": "1.37.0",
|
|
23
|
+
"@contrast/core": "1.42.0",
|
|
24
|
+
"@contrast/dep-hooks": "1.11.0",
|
|
25
|
+
"@contrast/esm-hooks": "2.16.0",
|
|
26
|
+
"@contrast/instrumentation": "1.21.0",
|
|
27
|
+
"@contrast/logger": "1.15.0",
|
|
28
|
+
"@contrast/patcher": "1.14.0",
|
|
29
|
+
"@contrast/rewriter": "1.18.0",
|
|
30
|
+
"@contrast/scopes": "1.12.0",
|
|
31
31
|
"ipaddr.js": "^2.0.1",
|
|
32
32
|
"semver": "^7.6.0"
|
|
33
33
|
},
|
|
@@ -1,138 +0,0 @@
|
|
|
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
|
-
|
|
22
|
-
module.exports = function (core) {
|
|
23
|
-
const {
|
|
24
|
-
logger,
|
|
25
|
-
depHooks,
|
|
26
|
-
patcher,
|
|
27
|
-
protect,
|
|
28
|
-
} = core;
|
|
29
|
-
|
|
30
|
-
const express4ErrorHandler = protect.errorHandlers.express4ErrorHandler = {};
|
|
31
|
-
|
|
32
|
-
function aroundFn(name) {
|
|
33
|
-
return function around(orig, data) {
|
|
34
|
-
const [err] = data.args;
|
|
35
|
-
const sourceContext = protect.getSourceContext(name);
|
|
36
|
-
const isSecurityException = SecurityException.isSecurityException(err);
|
|
37
|
-
|
|
38
|
-
if (isSecurityException && sourceContext) {
|
|
39
|
-
const blockInfo = sourceContext.securityException;
|
|
40
|
-
|
|
41
|
-
sourceContext.block(...blockInfo);
|
|
42
|
-
return;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
if (!sourceContext && isSecurityException) {
|
|
46
|
-
logger.info({ funcKey: data.funcKey }, 'source context not found; unable to handle response');
|
|
47
|
-
}
|
|
48
|
-
return orig();
|
|
49
|
-
};
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
express4ErrorHandler.install = function () {
|
|
53
|
-
depHooks.resolve({ name: 'finalhandler' }, (finalhandler) =>
|
|
54
|
-
patcher.patch(finalhandler, {
|
|
55
|
-
name: 'finalHandler',
|
|
56
|
-
patchType,
|
|
57
|
-
post(data) {
|
|
58
|
-
data.result = patcher.patch(data.result, {
|
|
59
|
-
name: 'finalHandler.returnedFunction',
|
|
60
|
-
patchType,
|
|
61
|
-
around: aroundFn('finalHandler')
|
|
62
|
-
});
|
|
63
|
-
},
|
|
64
|
-
})
|
|
65
|
-
);
|
|
66
|
-
|
|
67
|
-
depHooks.resolve({ name: 'express', version: '>=4.0.0 <5.0.0', file: 'lib/router/layer.js' }, (Layer) => {
|
|
68
|
-
patcher.patch(Layer.prototype, 'handle_error', {
|
|
69
|
-
name: 'Layer.prototype.handle_error',
|
|
70
|
-
patchType,
|
|
71
|
-
around: aroundFn('express.Layer.handle_error')
|
|
72
|
-
});
|
|
73
|
-
|
|
74
|
-
// This should be revisited after the research ticket NODE-2556
|
|
75
|
-
Object.defineProperty(Layer.prototype, 'handle', {
|
|
76
|
-
enumerable: true,
|
|
77
|
-
configurable: true,
|
|
78
|
-
get() {
|
|
79
|
-
return this.__handle;
|
|
80
|
-
},
|
|
81
|
-
set(fn) {
|
|
82
|
-
fn = patchFn(fn);
|
|
83
|
-
this.__handle = fn;
|
|
84
|
-
},
|
|
85
|
-
});
|
|
86
|
-
});
|
|
87
|
-
|
|
88
|
-
// This should be revisited after the research ticket NODE-2556
|
|
89
|
-
depHooks.resolve({ name: 'express', version: '>=4.0.0 <5.0.0', file: 'lib/router/index.js' }, (Router) => {
|
|
90
|
-
patcher.patch(Router.prototype.constructor, 'param', {
|
|
91
|
-
name: 'Router.prototype.constructor.param',
|
|
92
|
-
patchType,
|
|
93
|
-
pre({ args }) {
|
|
94
|
-
if (typeof args[1] === 'function') {
|
|
95
|
-
args[1] = patchFn(args[1]);
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
});
|
|
99
|
-
});
|
|
100
|
-
|
|
101
|
-
// This should be revisited after the research ticket NODE-2556
|
|
102
|
-
function patchFn(fn) {
|
|
103
|
-
return patcher.patch(fn, {
|
|
104
|
-
name: 'express.route-handler',
|
|
105
|
-
patchType,
|
|
106
|
-
around(orig, data) {
|
|
107
|
-
const ret = orig();
|
|
108
|
-
if (ret && ret.catch) {
|
|
109
|
-
return ret.catch((err) => {
|
|
110
|
-
const sourceContext = protect.getSourceContext();
|
|
111
|
-
const isSecurityException = SecurityException.isSecurityException(err);
|
|
112
|
-
|
|
113
|
-
if (isSecurityException && sourceContext) {
|
|
114
|
-
const blockInfo = sourceContext.securityException;
|
|
115
|
-
|
|
116
|
-
sourceContext.block(...blockInfo);
|
|
117
|
-
return;
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
if (!sourceContext && isSecurityException) {
|
|
121
|
-
logger.info({ funcKey: data.funcKey }, 'source context not found; unable to handle response');
|
|
122
|
-
return;
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
throw err;
|
|
126
|
-
});
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
return ret;
|
|
130
|
-
}
|
|
131
|
-
});
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
};
|
|
136
|
-
|
|
137
|
-
return express4ErrorHandler;
|
|
138
|
-
};
|
|
@@ -1,238 +0,0 @@
|
|
|
1
|
-
/* eslint-disable object-shorthand */
|
|
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 express4', function () {
|
|
12
|
-
let core, store, errorHandlerInstr;
|
|
13
|
-
|
|
14
|
-
beforeEach(function () {
|
|
15
|
-
core = mocks.core();
|
|
16
|
-
core.config = mocks.config();
|
|
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
|
-
store = {
|
|
25
|
-
protect: {
|
|
26
|
-
block: sinon.stub(),
|
|
27
|
-
securityException: ['block', 'cmd-injection']
|
|
28
|
-
}
|
|
29
|
-
};
|
|
30
|
-
|
|
31
|
-
sinon.spy(core.patcher, 'patch');
|
|
32
|
-
});
|
|
33
|
-
|
|
34
|
-
describe('finalhandler', function () {
|
|
35
|
-
let finalhandler, returnedFn;
|
|
36
|
-
|
|
37
|
-
beforeEach(function () {
|
|
38
|
-
finalhandler = function () {
|
|
39
|
-
return function () { };
|
|
40
|
-
};
|
|
41
|
-
|
|
42
|
-
core.depHooks.resolve.withArgs({ name: 'finalhandler' }).yields(finalhandler);
|
|
43
|
-
|
|
44
|
-
errorHandlerInstr = require('./express4')(core);
|
|
45
|
-
errorHandlerInstr.install();
|
|
46
|
-
|
|
47
|
-
const patchedFinalhandler = core.patcher.patch.getCall(0).returnValue;
|
|
48
|
-
returnedFn = patchedFinalhandler();
|
|
49
|
-
});
|
|
50
|
-
|
|
51
|
-
it('should block the request when there is SecurityException', function () {
|
|
52
|
-
const error = SecurityException.create();
|
|
53
|
-
|
|
54
|
-
core.scopes.sources.run(store, () => {
|
|
55
|
-
returnedFn(error);
|
|
56
|
-
expect(core.logger.info).not.to.have.been.called;
|
|
57
|
-
expect(store.protect.block).to.have.been.calledWith('block', 'cmd-injection');
|
|
58
|
-
});
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
it('should not block the request when there is SecurityException but sourceContext is missing', function () {
|
|
62
|
-
const error = SecurityException.create();
|
|
63
|
-
|
|
64
|
-
core.scopes.sources.run({}, () => {
|
|
65
|
-
returnedFn(error);
|
|
66
|
-
expect(core.logger.info).to.have.been.calledWith(
|
|
67
|
-
{ funcKey: 'protect-error-handling:finalHandler.returnedFunction' },
|
|
68
|
-
'source context not found; unable to handle response',
|
|
69
|
-
);
|
|
70
|
-
});
|
|
71
|
-
});
|
|
72
|
-
|
|
73
|
-
it('should skip the instrumentation when there is no SecurityException', function () {
|
|
74
|
-
const error = new Error('Error');
|
|
75
|
-
|
|
76
|
-
core.scopes.sources.run({}, () => {
|
|
77
|
-
returnedFn(error);
|
|
78
|
-
expect(core.logger.info).not.to.have.been.called;
|
|
79
|
-
expect(store.protect.block).not.to.have.been.called;
|
|
80
|
-
});
|
|
81
|
-
});
|
|
82
|
-
});
|
|
83
|
-
|
|
84
|
-
describe('Layer.prototype.handle_error', function () {
|
|
85
|
-
let Layer;
|
|
86
|
-
|
|
87
|
-
beforeEach(function () {
|
|
88
|
-
Layer = function () { };
|
|
89
|
-
Layer.prototype.handle_error = function () { };
|
|
90
|
-
|
|
91
|
-
core.depHooks.resolve.withArgs({ name: 'express', version: '>=4.0.0 <5.0.0', file: 'lib/router/layer.js' }).yields(Layer);
|
|
92
|
-
|
|
93
|
-
errorHandlerInstr = require('./express4')(core);
|
|
94
|
-
errorHandlerInstr.install();
|
|
95
|
-
});
|
|
96
|
-
|
|
97
|
-
it('should block the request when there is SecurityException', function () {
|
|
98
|
-
const error = SecurityException.create();
|
|
99
|
-
|
|
100
|
-
core.scopes.sources.run(store, () => {
|
|
101
|
-
Layer.prototype.handle_error(error);
|
|
102
|
-
expect(core.logger.info).not.to.have.been.called;
|
|
103
|
-
expect(store.protect.block).to.have.been.calledWith('block', 'cmd-injection');
|
|
104
|
-
});
|
|
105
|
-
});
|
|
106
|
-
|
|
107
|
-
it('should not block the request when there is SecurityException but sourceContext is missing', function () {
|
|
108
|
-
const error = SecurityException.create();
|
|
109
|
-
|
|
110
|
-
core.scopes.sources.run({}, () => {
|
|
111
|
-
Layer.prototype.handle_error(error);
|
|
112
|
-
expect(core.logger.info).to.have.been.calledWith(
|
|
113
|
-
{ funcKey: 'protect-error-handling:Layer.prototype.handle_error' },
|
|
114
|
-
'source context not found; unable to handle response'
|
|
115
|
-
);
|
|
116
|
-
});
|
|
117
|
-
});
|
|
118
|
-
|
|
119
|
-
it('should skip the instrumentation when there is no SecurityException', function () {
|
|
120
|
-
const error = new Error('Error');
|
|
121
|
-
|
|
122
|
-
core.scopes.sources.run({}, () => {
|
|
123
|
-
Layer.prototype.handle_error(error);
|
|
124
|
-
expect(core.logger.info).not.to.have.been.called;
|
|
125
|
-
expect(store.protect.block).not.to.have.been.called;
|
|
126
|
-
});
|
|
127
|
-
});
|
|
128
|
-
});
|
|
129
|
-
|
|
130
|
-
describe('Layer.prototype.handle', function () {
|
|
131
|
-
const sampleFn = function () { };
|
|
132
|
-
let Layer;
|
|
133
|
-
|
|
134
|
-
beforeEach(function () {
|
|
135
|
-
Layer = function () { };
|
|
136
|
-
|
|
137
|
-
core.depHooks.resolve.withArgs({ name: 'express', version: '>=4.0.0 <5.0.0', file: 'lib/router/layer.js' }).yields(Layer);
|
|
138
|
-
|
|
139
|
-
errorHandlerInstr = require('./express4')(core);
|
|
140
|
-
errorHandlerInstr.install();
|
|
141
|
-
});
|
|
142
|
-
|
|
143
|
-
it('should patch the function that is being set for `handle`', function () {
|
|
144
|
-
Layer.prototype.handle = sampleFn;
|
|
145
|
-
|
|
146
|
-
expect(core.patcher.patch).to.have.been.calledWith(sampleFn, sinon.match.object);
|
|
147
|
-
});
|
|
148
|
-
|
|
149
|
-
it('should return the patched function for the `handle` get', function () {
|
|
150
|
-
Layer.prototype.handle = sampleFn;
|
|
151
|
-
|
|
152
|
-
expect(Layer.prototype.handle).to.equal(Layer.prototype.__handle);
|
|
153
|
-
});
|
|
154
|
-
});
|
|
155
|
-
|
|
156
|
-
describe('Router.prototype.constructor.param', function () {
|
|
157
|
-
let Router;
|
|
158
|
-
const sampleFn = function () { };
|
|
159
|
-
const throwingHandler = (error) => async function () {
|
|
160
|
-
await Promise.reject(error);
|
|
161
|
-
};
|
|
162
|
-
|
|
163
|
-
beforeEach(function () {
|
|
164
|
-
Router = function () { };
|
|
165
|
-
Router.prototype.constructor = {
|
|
166
|
-
param: function () { },
|
|
167
|
-
};
|
|
168
|
-
|
|
169
|
-
core.depHooks.resolve.withArgs({ name: 'express', version: '>=4.0.0 <5.0.0', file: 'lib/router/index.js' }).yields(Router);
|
|
170
|
-
|
|
171
|
-
core.patcher.patch.resetHistory();
|
|
172
|
-
|
|
173
|
-
errorHandlerInstr = require('./express4')(core);
|
|
174
|
-
errorHandlerInstr.install();
|
|
175
|
-
});
|
|
176
|
-
|
|
177
|
-
it('should patch the function for the param property', function () {
|
|
178
|
-
Router.prototype.constructor.param('sampleFn', sampleFn);
|
|
179
|
-
|
|
180
|
-
expect(core.patcher.patch).to.have.been.calledWith(sampleFn, sinon.match.object);
|
|
181
|
-
});
|
|
182
|
-
|
|
183
|
-
it('should block the request when there is SecurityException', async function () {
|
|
184
|
-
const error = SecurityException.create();
|
|
185
|
-
const fn = throwingHandler(error);
|
|
186
|
-
|
|
187
|
-
Router.prototype.constructor.param('fn', fn);
|
|
188
|
-
|
|
189
|
-
const patchedFn = core.patcher.patch.getCall(1).returnValue;
|
|
190
|
-
|
|
191
|
-
await core.scopes.sources.run(store, async () => {
|
|
192
|
-
await patchedFn();
|
|
193
|
-
});
|
|
194
|
-
|
|
195
|
-
expect(core.logger.info).not.to.have.been.called;
|
|
196
|
-
expect(store.protect.block).to.have.been.calledWith('block', 'cmd-injection');
|
|
197
|
-
});
|
|
198
|
-
|
|
199
|
-
it('should not block the request when there is SecurityException but sourceContext is missing', async function () {
|
|
200
|
-
const error = SecurityException.create();
|
|
201
|
-
const fn = throwingHandler(error);
|
|
202
|
-
|
|
203
|
-
Router.prototype.constructor.param('fn', fn);
|
|
204
|
-
|
|
205
|
-
const patchedFn = core.patcher.patch.getCall(1).returnValue;
|
|
206
|
-
|
|
207
|
-
await core.scopes.sources.run({}, async () => {
|
|
208
|
-
await patchedFn();
|
|
209
|
-
});
|
|
210
|
-
|
|
211
|
-
expect(core.logger.info).to.have.been.calledWith(
|
|
212
|
-
{ funcKey: 'protect-error-handling:express.route-handler' },
|
|
213
|
-
'source context not found; unable to handle response',
|
|
214
|
-
);
|
|
215
|
-
});
|
|
216
|
-
|
|
217
|
-
it('should skip the instrumentation when there is no SecurityException', async function () {
|
|
218
|
-
const error = new Error('Error');
|
|
219
|
-
const fn = throwingHandler(error);
|
|
220
|
-
|
|
221
|
-
Router.prototype.constructor.param('fn', fn);
|
|
222
|
-
|
|
223
|
-
const patchedFn = core.patcher.patch.getCall(1).returnValue;
|
|
224
|
-
|
|
225
|
-
try {
|
|
226
|
-
await core.scopes.sources.run(store, async () => {
|
|
227
|
-
await patchedFn();
|
|
228
|
-
});
|
|
229
|
-
} catch (err) {
|
|
230
|
-
expect(err).to.equal(error);
|
|
231
|
-
expect(core.logger.info).not.to.have.been.called;
|
|
232
|
-
expect(store.protect.block).not.to.have.been.called;
|
|
233
|
-
}
|
|
234
|
-
});
|
|
235
|
-
});
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
});
|