@contrast/route-coverage 1.50.0 → 1.52.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/index.d.ts +2 -2
- package/lib/index.js +12 -2
- package/lib/install/express.js +535 -0
- package/lib/install/fastify/fastify-express.js +71 -0
- package/lib/install/fastify/fastify-middie.js +67 -0
- package/lib/install/{fastify.js → fastify/fastify.js} +32 -22
- package/lib/install/{express → fastify}/index.js +6 -5
- package/lib/install/graphql.js +6 -1
- package/lib/install/koa.js +39 -45
- package/lib/install/socket.io.js +127 -0
- package/lib/utils/route-info.js +26 -1
- package/package.json +8 -8
- package/lib/install/express/express4.js +0 -157
- package/lib/install/express/express5.js +0 -538
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright: 2025 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
|
+
'use strict';
|
|
16
|
+
|
|
17
|
+
const { RouteType } = require('@contrast/common');
|
|
18
|
+
const { patchType, formatHandler } = require('./../../utils/route-info');
|
|
19
|
+
const isArray = (arr) => Array.isArray(arr);
|
|
20
|
+
module.exports = function init(core) {
|
|
21
|
+
const { patcher, depHooks, routeCoverage } = core;
|
|
22
|
+
|
|
23
|
+
return core.routeCoverage.fastifyMiddie = {
|
|
24
|
+
install() {
|
|
25
|
+
depHooks.resolve({ name: '@fastify/middie', version: '*', file: 'lib/engine.js' }, (middie) => patcher.patch(middie, {
|
|
26
|
+
name: 'fastifyMiddie',
|
|
27
|
+
patchType,
|
|
28
|
+
post(data) {
|
|
29
|
+
patcher.patch(data.result, 'use', {
|
|
30
|
+
name: 'use',
|
|
31
|
+
patchType,
|
|
32
|
+
pre(data) {
|
|
33
|
+
const [url, fn] = data.args;
|
|
34
|
+
if (!url || !fn || !core.config.getEffectiveValue('assess.report_middleware_routes')) return;
|
|
35
|
+
|
|
36
|
+
const middleware = isArray(fn) ? fn : [fn];
|
|
37
|
+
const formattedPath = isArray(url) ? `[${url.join(', ')}]` : url;
|
|
38
|
+
const patchedMiddleware = middleware.map((f) => {
|
|
39
|
+
const formattedHandler = formatHandler(f);
|
|
40
|
+
const signature = `fastify.use(${formattedPath}, ${formattedHandler})`;
|
|
41
|
+
|
|
42
|
+
const routeInfo = {
|
|
43
|
+
signature,
|
|
44
|
+
url: formattedPath,
|
|
45
|
+
method: 'use',
|
|
46
|
+
normalizedUrl: formattedPath,
|
|
47
|
+
type: RouteType.MIDDLEWARE,
|
|
48
|
+
framework: 'fastify'
|
|
49
|
+
};
|
|
50
|
+
routeCoverage.discover(routeInfo);
|
|
51
|
+
|
|
52
|
+
return patcher.patch(f, {
|
|
53
|
+
name: 'middleware',
|
|
54
|
+
patchType,
|
|
55
|
+
post() {
|
|
56
|
+
routeCoverage.observe(routeInfo);
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
data.args[1] = patchedMiddleware;
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
}));
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
};
|
|
@@ -14,9 +14,12 @@
|
|
|
14
14
|
*/
|
|
15
15
|
'use strict';
|
|
16
16
|
|
|
17
|
-
const { getFastifyMethods } = require('
|
|
18
|
-
const {
|
|
19
|
-
|
|
17
|
+
const { getFastifyMethods } = require('../../utils/methods');
|
|
18
|
+
const {
|
|
19
|
+
primordials: { StringPrototypeToLowerCase, StringPrototypeSplit },
|
|
20
|
+
RouteType,
|
|
21
|
+
} = require('@contrast/common');
|
|
22
|
+
const { patchType, formatHandler } = require('./../../utils/route-info');
|
|
20
23
|
|
|
21
24
|
// Spec: https://contrast.atlassian.net/wiki/spaces/NOD/pages/3454861621/Node.js+Agent+Route+Signatures#Fastify
|
|
22
25
|
module.exports = function init(core) {
|
|
@@ -35,18 +38,19 @@ module.exports = function init(core) {
|
|
|
35
38
|
return route?.[kRoutePrefix];
|
|
36
39
|
}
|
|
37
40
|
|
|
38
|
-
function createRouteInfo(method, url, fullyDeclared) {
|
|
41
|
+
function createRouteInfo(method, url, fullyDeclared, type, handler) {
|
|
39
42
|
method = StringPrototypeToLowerCase.call(method);
|
|
40
43
|
|
|
41
44
|
const signature = fullyDeclared
|
|
42
|
-
? `fastify.route({ method:
|
|
43
|
-
: `fastify.${method}(
|
|
45
|
+
? `fastify.route({ method: ${method}, url: ${url}, handler: ${formatHandler(handler)} })`
|
|
46
|
+
: `fastify.${method}(${url}, ${formatHandler(handler)})`;
|
|
44
47
|
|
|
45
48
|
const routeInfo = {
|
|
46
49
|
signature,
|
|
47
50
|
url,
|
|
48
51
|
method,
|
|
49
52
|
normalizedUrl: url,
|
|
53
|
+
type,
|
|
50
54
|
framework: 'fastify'
|
|
51
55
|
};
|
|
52
56
|
return routeInfo;
|
|
@@ -54,14 +58,18 @@ module.exports = function init(core) {
|
|
|
54
58
|
|
|
55
59
|
function patchHandler(route, handle, routeInfo) {
|
|
56
60
|
const pre = ({ args }) => {
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
61
|
+
let req, type;
|
|
62
|
+
if (args[0]?.constructor?.name == 'WebSocket') {
|
|
63
|
+
req = args[1];
|
|
64
|
+
type = RouteType.MESSAGE_BROKER;
|
|
65
|
+
} else {
|
|
66
|
+
req = args[0];
|
|
67
|
+
type = RouteType.HTTP;
|
|
68
|
+
}
|
|
62
69
|
const method = StringPrototypeToLowerCase.call(req.raw?.method);
|
|
63
70
|
const [url] = StringPrototypeSplit.call(req.url, /\?/);
|
|
64
|
-
|
|
71
|
+
|
|
72
|
+
routeCoverage.observe({ ...routeInfo, method, type, url });
|
|
65
73
|
};
|
|
66
74
|
|
|
67
75
|
const name = `fastify.route.${handle}`;
|
|
@@ -81,28 +89,30 @@ module.exports = function init(core) {
|
|
|
81
89
|
}
|
|
82
90
|
}
|
|
83
91
|
|
|
84
|
-
function discoverAndPatch(method, path,
|
|
92
|
+
function discoverAndPatch(method, path, routeObj, handle, handler, methods, fullyDeclared) {
|
|
93
|
+
const type = routeObj?.options?.websocket ? RouteType.MESSAGE_BROKER : RouteType.HTTP;
|
|
94
|
+
|
|
85
95
|
if (Array.isArray(method)) {
|
|
86
96
|
// If all valid methods are included in `method` then .all shorthand was most likely used
|
|
87
97
|
if (methods.every(m => method.includes(m))) {
|
|
88
|
-
const routeInfo = createRouteInfo('all', path, fullyDeclared);
|
|
98
|
+
const routeInfo = createRouteInfo('all', path, fullyDeclared, type, handler);
|
|
89
99
|
routeCoverage.discover(routeInfo);
|
|
90
|
-
patchHandler(
|
|
100
|
+
patchHandler(routeObj, handle, routeInfo);
|
|
91
101
|
} else {
|
|
92
102
|
method.forEach((verb) => {
|
|
93
|
-
const routeInfo = createRouteInfo(verb, path, fullyDeclared);
|
|
103
|
+
const routeInfo = createRouteInfo(verb, path, fullyDeclared, type, handler);
|
|
94
104
|
routeCoverage.discover(routeInfo);
|
|
95
|
-
patchHandler(
|
|
105
|
+
patchHandler(routeObj, handle, routeInfo);
|
|
96
106
|
});
|
|
97
107
|
}
|
|
98
108
|
} else {
|
|
99
|
-
const routeInfo = createRouteInfo(method, path, fullyDeclared);
|
|
109
|
+
const routeInfo = createRouteInfo(method, path, fullyDeclared, type, handler);
|
|
100
110
|
routeCoverage.discover(routeInfo);
|
|
101
|
-
patchHandler(
|
|
111
|
+
patchHandler(routeObj, handle, routeInfo);
|
|
102
112
|
}
|
|
103
113
|
}
|
|
104
114
|
|
|
105
|
-
return core.routeCoverage.
|
|
115
|
+
return core.routeCoverage.fastifyCore = {
|
|
106
116
|
install() {
|
|
107
117
|
/**
|
|
108
118
|
* There are some subtle differences between fastify minor versions the instrumentation must account for
|
|
@@ -148,7 +158,7 @@ module.exports = function init(core) {
|
|
|
148
158
|
let handle = 'handler';
|
|
149
159
|
if (!handler && typeof options === 'function') handle = 'options';
|
|
150
160
|
|
|
151
|
-
discoverAndPatch(method, path, routeObj ? data.args[0] : data.args, handle, fastifyMethods, false);
|
|
161
|
+
discoverAndPatch(method, path, routeObj ? data.args[0] : data.args, handle, options || handler, fastifyMethods, false);
|
|
152
162
|
}
|
|
153
163
|
});
|
|
154
164
|
|
|
@@ -165,7 +175,7 @@ module.exports = function init(core) {
|
|
|
165
175
|
|
|
166
176
|
const prefix = getPrefix(data.obj);
|
|
167
177
|
const path = prefix ? prefix + url : url;
|
|
168
|
-
discoverAndPatch(method, path, routeArgs, 'handler', fastifyMethods, true);
|
|
178
|
+
discoverAndPatch(method, path, routeArgs, 'handler', handler, fastifyMethods, true);
|
|
169
179
|
}
|
|
170
180
|
});
|
|
171
181
|
}
|
|
@@ -18,14 +18,15 @@
|
|
|
18
18
|
const { callChildComponentMethodsSync } = require('@contrast/common');
|
|
19
19
|
|
|
20
20
|
module.exports = function(core) {
|
|
21
|
-
const
|
|
21
|
+
const fastifyRouteCoverage = core.routeCoverage.fastify = {
|
|
22
22
|
install() {
|
|
23
|
-
callChildComponentMethodsSync(
|
|
23
|
+
callChildComponentMethodsSync(fastifyRouteCoverage, 'install');
|
|
24
24
|
},
|
|
25
25
|
};
|
|
26
26
|
|
|
27
|
-
require('./
|
|
28
|
-
require('./
|
|
27
|
+
require('./fastify')(core);
|
|
28
|
+
require('./fastify-express')(core);
|
|
29
|
+
require('./fastify-middie')(core);
|
|
29
30
|
|
|
30
|
-
return
|
|
31
|
+
return fastifyRouteCoverage;
|
|
31
32
|
};
|
package/lib/install/graphql.js
CHANGED
|
@@ -14,7 +14,10 @@
|
|
|
14
14
|
*/
|
|
15
15
|
'use strict';
|
|
16
16
|
|
|
17
|
-
const {
|
|
17
|
+
const {
|
|
18
|
+
RouteType,
|
|
19
|
+
primordials: { ArrayPrototypeJoin },
|
|
20
|
+
} = require('@contrast/common');
|
|
18
21
|
const { patchType } = require('./../utils/route-info');
|
|
19
22
|
|
|
20
23
|
module.exports = function init(core) {
|
|
@@ -61,6 +64,7 @@ module.exports = function init(core) {
|
|
|
61
64
|
method,
|
|
62
65
|
normalizedUrl,
|
|
63
66
|
signature,
|
|
67
|
+
type: RouteType.HTTP, // todo: extend existing types
|
|
64
68
|
framework: 'graphql',
|
|
65
69
|
});
|
|
66
70
|
});
|
|
@@ -77,6 +81,7 @@ module.exports = function init(core) {
|
|
|
77
81
|
method: store.sourceInfo?.method,
|
|
78
82
|
normalizedUrl,
|
|
79
83
|
signature,
|
|
84
|
+
type: RouteType.HTTP, // todo: extend existing types
|
|
80
85
|
url: normalizedUrl,
|
|
81
86
|
framework: 'graphql',
|
|
82
87
|
});
|
package/lib/install/koa.js
CHANGED
|
@@ -15,28 +15,15 @@
|
|
|
15
15
|
'use strict';
|
|
16
16
|
|
|
17
17
|
const { METHODS } = require('./../utils/methods');
|
|
18
|
-
const { isString, primordials: { StringPrototypeToLowerCase, StringPrototypeSplit } } = require('@contrast/common');
|
|
19
|
-
const {
|
|
18
|
+
const { isString, RouteType, primordials: { StringPrototypeToLowerCase, StringPrototypeSplit } } = require('@contrast/common');
|
|
19
|
+
const { patchType, formatHandler } = require('./../utils/route-info');
|
|
20
20
|
|
|
21
21
|
// Spec: https://contrast.atlassian.net/wiki/spaces/NOD/pages/3454861621/Node.js+Agent+Route+Signatures#Koa
|
|
22
22
|
module.exports = function init(core) {
|
|
23
23
|
const { patcher, depHooks, routeCoverage } = core;
|
|
24
|
-
|
|
25
|
-
function createRouteInfo(method, url) {
|
|
26
|
-
method = StringPrototypeToLowerCase.call(method);
|
|
27
|
-
const routeInfo = {
|
|
28
|
-
signature: createSignature(url, method),
|
|
29
|
-
url,
|
|
30
|
-
method,
|
|
31
|
-
normalizedUrl: url,
|
|
32
|
-
framework: 'koa'
|
|
33
|
-
};
|
|
34
|
-
return routeInfo;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
24
|
return core.routeCoverage.koa = {
|
|
38
25
|
install() {
|
|
39
|
-
depHooks.resolve({ name: 'koa', version: '>=2.3.0 <
|
|
26
|
+
depHooks.resolve({ name: 'koa', version: '>=2.3.0 <4' }, (Koa, pkgMeta) => {
|
|
40
27
|
// Koa uses its own routing library @koa/router to define routes before
|
|
41
28
|
// mounting them on the app with .use so instrumenting use and traversing
|
|
42
29
|
// the constructed routes is the more technically correct approach than
|
|
@@ -48,40 +35,47 @@ module.exports = function init(core) {
|
|
|
48
35
|
if (args?.length === 0) return;
|
|
49
36
|
const [router] = args;
|
|
50
37
|
|
|
51
|
-
if (!router?.router)
|
|
38
|
+
if (!router?.router) {
|
|
39
|
+
core.logger.debug('no routes detected in koa router stack: %s@%s', pkgMeta.name, pkgMeta.version);
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
52
42
|
|
|
53
43
|
router.router.stack.forEach((Layer) => {
|
|
54
|
-
const { methods, path } = Layer;
|
|
55
|
-
if (!path || !isString(path)) return;
|
|
44
|
+
const { methods, path, stack } = Layer;
|
|
45
|
+
if (!path || !isString(path) || !stack || stack.length === 0) return;
|
|
56
46
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
47
|
+
const patchedMiddleware = [];
|
|
48
|
+
stack.forEach((handler) => {
|
|
49
|
+
const method = methods.length === 0 ? 'use' : METHODS.every(m => methods.includes(m)) ? 'all' : StringPrototypeToLowerCase.call(methods[methods.length - 1]);
|
|
50
|
+
if (method === 'use' && !core.config.getEffectiveValue('assess.report_middleware_routes')) return;
|
|
51
|
+
const routeInfo = {
|
|
52
|
+
signature: `Router.${method}(${path}, ${formatHandler(handler)})`,
|
|
53
|
+
method,
|
|
54
|
+
url: path,
|
|
55
|
+
normalizedUrl: path,
|
|
56
|
+
framework: 'koa',
|
|
57
|
+
type: method === 'use' ? RouteType.MIDDLEWARE : RouteType.HTTP
|
|
58
|
+
};
|
|
65
59
|
routeCoverage.discover(routeInfo);
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
60
|
+
const patchedHandler = patcher.patch(handler, {
|
|
61
|
+
name: 'handler',
|
|
62
|
+
patchType,
|
|
63
|
+
pre(data) {
|
|
64
|
+
const { request } = data.args[0];
|
|
65
|
+
if (!request) return;
|
|
72
66
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
Layer.stack =
|
|
67
|
+
const { method, url } = request;
|
|
68
|
+
if (!method | !url) return;
|
|
69
|
+
routeCoverage.observe({
|
|
70
|
+
...routeInfo,
|
|
71
|
+
method: StringPrototypeToLowerCase.call(method),
|
|
72
|
+
url: StringPrototypeSplit.call(url, /\?/)[0]
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
});
|
|
76
|
+
patchedMiddleware.push(patchedHandler);
|
|
77
|
+
});
|
|
78
|
+
Layer.stack = patchedMiddleware;
|
|
85
79
|
});
|
|
86
80
|
}
|
|
87
81
|
});
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright: 2025 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
|
+
'use strict';
|
|
16
|
+
|
|
17
|
+
const { set, RouteType, primordials } = require('@contrast/common');
|
|
18
|
+
const Core = require('@contrast/core/lib/ioc/core');
|
|
19
|
+
const { patchType } = require('../utils/route-info');
|
|
20
|
+
|
|
21
|
+
const COMPONENT_NAME = 'routeCoverage.socketio';
|
|
22
|
+
const FRAMEWORK = 'socket.io';
|
|
23
|
+
const kServerMountPath = Symbol('cs:socket.io.path');
|
|
24
|
+
const kServerRouteSignature = Symbol('cs:socket.io.route-signature');
|
|
25
|
+
|
|
26
|
+
module.exports = Core.makeComponent({
|
|
27
|
+
name: COMPONENT_NAME,
|
|
28
|
+
factory: (core) => new SocketIORouteCoverage(core),
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
class SocketIORouteCoverage {
|
|
32
|
+
constructor(core) {
|
|
33
|
+
Object.defineProperty(this, 'core', { value: core });
|
|
34
|
+
set(core, COMPONENT_NAME, this);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
makePath(mountPath) {
|
|
38
|
+
// we append trailing "/" since that's part of socket.io protocol
|
|
39
|
+
return mountPath.endsWith('/') ? mountPath : `${mountPath}/`;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
makeSignature(mountPath) {
|
|
43
|
+
return `Socket.IO ${mountPath}`;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
install() {
|
|
47
|
+
const self = this;
|
|
48
|
+
const {
|
|
49
|
+
depHooks,
|
|
50
|
+
patcher,
|
|
51
|
+
routeCoverage,
|
|
52
|
+
} = this.core;
|
|
53
|
+
|
|
54
|
+
depHooks.resolve(
|
|
55
|
+
{ name: 'socket.io', version: '4' },
|
|
56
|
+
/** @param {import('socket.io-4')} xport the exported socket.io module */
|
|
57
|
+
(xport) => {
|
|
58
|
+
patcher.patch(xport.Server.prototype, 'initEngine', {
|
|
59
|
+
name: 'socket.io.Server.prototype.initEngine',
|
|
60
|
+
patchType,
|
|
61
|
+
post(data) {
|
|
62
|
+
if (!this._path) return;
|
|
63
|
+
const [httpServer] = data.args;
|
|
64
|
+
const path = self.makePath(this._path);
|
|
65
|
+
const signature = self.makeSignature(path);
|
|
66
|
+
|
|
67
|
+
this.eio[kServerMountPath] = path;
|
|
68
|
+
this.eio[kServerRouteSignature] = signature;
|
|
69
|
+
|
|
70
|
+
routeCoverage.discover({
|
|
71
|
+
framework: FRAMEWORK,
|
|
72
|
+
signature,
|
|
73
|
+
method: 'all',
|
|
74
|
+
normalizedUri: path,
|
|
75
|
+
type: RouteType.MESSAGE_BROKER,
|
|
76
|
+
url: path,
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
// handle observation for HTTP polling; this doesn't emit when upgrading to ws://
|
|
80
|
+
httpServer.on(
|
|
81
|
+
'request',
|
|
82
|
+
/** @param {import('http').IncomingMessage} req */
|
|
83
|
+
(req /*, res */) => {
|
|
84
|
+
if (req.url?.startsWith?.(path)) {
|
|
85
|
+
routeCoverage.observe({
|
|
86
|
+
framework: FRAMEWORK,
|
|
87
|
+
method: primordials.StringPrototypeToLowerCase.call(req.method),
|
|
88
|
+
normalizedUrl: path,
|
|
89
|
+
signature,
|
|
90
|
+
type: RouteType.MESSAGE_BROKER,
|
|
91
|
+
url: path,
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
);
|
|
99
|
+
|
|
100
|
+
// this is to handle observations for websocket upgrades; 1 upgrade request counts as single observation
|
|
101
|
+
depHooks.resolve(
|
|
102
|
+
{ name: 'engine.io', version: '6' },
|
|
103
|
+
/** @param {import('engine.io')} xport the exported engine.io module */
|
|
104
|
+
(xport) => {
|
|
105
|
+
patcher.patch(xport.Server.prototype, 'onWebSocket', {
|
|
106
|
+
name: 'engine.io.Server.prototype.onWebSocket',
|
|
107
|
+
patchType,
|
|
108
|
+
pre(data) {
|
|
109
|
+
const eioServer = this;
|
|
110
|
+
const [req] = data.args;
|
|
111
|
+
|
|
112
|
+
if (!eioServer[kServerMountPath] || !eioServer[kServerRouteSignature]) return;
|
|
113
|
+
|
|
114
|
+
routeCoverage.observe({
|
|
115
|
+
framework: FRAMEWORK,
|
|
116
|
+
method: req.method,
|
|
117
|
+
normalizedUrl: eioServer[kServerMountPath],
|
|
118
|
+
signature: eioServer[kServerRouteSignature],
|
|
119
|
+
type: RouteType.MESSAGE_BROKER,
|
|
120
|
+
url: eioServer[kServerMountPath],
|
|
121
|
+
});
|
|
122
|
+
},
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
);
|
|
126
|
+
}
|
|
127
|
+
}
|
package/lib/utils/route-info.js
CHANGED
|
@@ -15,6 +15,8 @@
|
|
|
15
15
|
'use strict';
|
|
16
16
|
|
|
17
17
|
const patchType = 'route-coverage';
|
|
18
|
+
const { funcInfo } = require('@contrast/fn-inspect');
|
|
19
|
+
const { primordials: { StringPrototypeReplace, StringPrototypeSubstring } } = require('@contrast/common');
|
|
18
20
|
|
|
19
21
|
/**
|
|
20
22
|
* Creates a formatted "signature" for a route
|
|
@@ -28,4 +30,27 @@ function createSignature(path, method = '', obj = 'Router', handler = '[Function
|
|
|
28
30
|
return `${obj}.${method}('${path}', ${handler})`;
|
|
29
31
|
}
|
|
30
32
|
|
|
31
|
-
|
|
33
|
+
/**
|
|
34
|
+
* Creates a formatted handler signature for a route
|
|
35
|
+
* @param {function} handler
|
|
36
|
+
* @param {string} appDir
|
|
37
|
+
* @return {string} formatted handler
|
|
38
|
+
*/
|
|
39
|
+
function formatHandler(handler, appDir) {
|
|
40
|
+
const info = funcInfo(handler);
|
|
41
|
+
if (!info) return '[Function]';
|
|
42
|
+
|
|
43
|
+
let file = info.file ?
|
|
44
|
+
StringPrototypeReplace.call(info.file, appDir, '') :
|
|
45
|
+
'';
|
|
46
|
+
if (file.length > 30) {
|
|
47
|
+
file = `...${StringPrototypeSubstring.call(file, file.length - 40)}`;
|
|
48
|
+
}
|
|
49
|
+
const handlerName = info.method || handler.name || 'anonymous';
|
|
50
|
+
const formattedHandler = (file && Number.isFinite(info.lineNumber) && Number.isFinite(info.column)) ?
|
|
51
|
+
`[${handlerName} ${file} ${info.lineNumber}:${info.column}]` :
|
|
52
|
+
`[Function: ${handlerName}]`; // what util.inspect(handler) would return
|
|
53
|
+
return formattedHandler;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
module.exports = { createSignature, patchType, formatHandler };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@contrast/route-coverage",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.52.0",
|
|
4
4
|
"description": "Handles route discovery and observation",
|
|
5
5
|
"license": "SEE LICENSE IN LICENSE",
|
|
6
6
|
"author": "Contrast Security <nodejs@contrastsecurity.com> (https://www.contrastsecurity.com)",
|
|
@@ -20,14 +20,14 @@
|
|
|
20
20
|
"test": "bash ../scripts/test.sh"
|
|
21
21
|
},
|
|
22
22
|
"dependencies": {
|
|
23
|
-
"@contrast/common": "1.
|
|
24
|
-
"@contrast/config": "1.
|
|
25
|
-
"@contrast/core": "1.
|
|
26
|
-
"@contrast/dep-hooks": "1.
|
|
23
|
+
"@contrast/common": "1.38.0",
|
|
24
|
+
"@contrast/config": "1.54.1",
|
|
25
|
+
"@contrast/core": "1.59.1",
|
|
26
|
+
"@contrast/dep-hooks": "1.28.1",
|
|
27
27
|
"@contrast/fn-inspect": "^5.0.2",
|
|
28
|
-
"@contrast/logger": "1.
|
|
29
|
-
"@contrast/patcher": "1.
|
|
30
|
-
"@contrast/scopes": "1.
|
|
28
|
+
"@contrast/logger": "1.32.1",
|
|
29
|
+
"@contrast/patcher": "1.31.1",
|
|
30
|
+
"@contrast/scopes": "1.29.1",
|
|
31
31
|
"semver": "^7.6.0"
|
|
32
32
|
}
|
|
33
33
|
}
|