@contrast/assess 1.40.0 → 1.42.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/crypto-analysis/install/crypto.js +4 -5
- package/lib/crypto-analysis/install/crypto.test.js +1 -1
- package/lib/crypto-analysis/install/math.js +2 -4
- package/lib/dataflow/propagation/install/JSON/parse.js +2 -3
- package/lib/dataflow/propagation/install/JSON/stringify.js +3 -4
- package/lib/dataflow/propagation/install/array-prototype-join.js +2 -3
- package/lib/dataflow/propagation/install/buffer.js +3 -4
- package/lib/dataflow/propagation/install/contrast-methods/add.js +2 -3
- package/lib/dataflow/propagation/install/contrast-methods/number.js +2 -3
- package/lib/dataflow/propagation/install/contrast-methods/string.js +2 -3
- package/lib/dataflow/propagation/install/contrast-methods/tag.js +2 -3
- package/lib/dataflow/propagation/install/decode-uri-component.js +2 -3
- package/lib/dataflow/propagation/install/ejs/escape-xml.js +3 -4
- package/lib/dataflow/propagation/install/ejs/template.js +3 -4
- package/lib/dataflow/propagation/install/ejs/template.test.js +1 -1
- package/lib/dataflow/propagation/install/encode-uri.js +2 -3
- package/lib/dataflow/propagation/install/escape-html.js +3 -4
- package/lib/dataflow/propagation/install/escape.js +2 -3
- package/lib/dataflow/propagation/install/fastify-send.js +3 -3
- package/lib/dataflow/propagation/install/fastify-send.test.js +1 -3
- package/lib/dataflow/propagation/install/handlebars-utils-escape-expression.js +3 -4
- package/lib/dataflow/propagation/install/isnumeric-0.js +1 -1
- package/lib/dataflow/propagation/install/joi/any.js +1 -1
- package/lib/dataflow/propagation/install/joi/any.test.js +1 -1
- package/lib/dataflow/propagation/install/joi/array.test.js +5 -5
- package/lib/dataflow/propagation/install/joi/boolean.js +3 -3
- package/lib/dataflow/propagation/install/joi/boolean.test.js +1 -1
- package/lib/dataflow/propagation/install/joi/expression.js +3 -3
- package/lib/dataflow/propagation/install/joi/expression.test.js +1 -1
- package/lib/dataflow/propagation/install/joi/index.js +3 -3
- package/lib/dataflow/propagation/install/joi/keys.js +3 -3
- package/lib/dataflow/propagation/install/joi/number.js +3 -3
- package/lib/dataflow/propagation/install/joi/number.test.js +1 -1
- package/lib/dataflow/propagation/install/joi/object.js +1 -1
- package/lib/dataflow/propagation/install/joi/object.test.js +1 -1
- package/lib/dataflow/propagation/install/joi/ref.test.js +4 -4
- package/lib/dataflow/propagation/install/joi/string-schema.js +4 -4
- package/lib/dataflow/propagation/install/joi/string-schema.test.js +4 -4
- package/lib/dataflow/propagation/install/joi/values.js +3 -3
- package/lib/dataflow/propagation/install/mongoose/schema-map.js +4 -4
- package/lib/dataflow/propagation/install/mongoose/schema-mixed.js +4 -4
- package/lib/dataflow/propagation/install/mongoose/schema-string.js +4 -4
- package/lib/dataflow/propagation/install/mustache-escape.js +3 -4
- package/lib/dataflow/propagation/install/mustache-escape.test.js +1 -1
- package/lib/dataflow/propagation/install/mysql-connection-escape.js +22 -14
- package/lib/dataflow/propagation/install/mysql-connection-escape.test.js +1 -1
- package/lib/dataflow/propagation/install/parse-int.js +2 -3
- package/lib/dataflow/propagation/install/path/basename.js +3 -4
- package/lib/dataflow/propagation/install/path/dirname.js +3 -4
- package/lib/dataflow/propagation/install/path/extname.js +3 -4
- package/lib/dataflow/propagation/install/path/format.js +3 -4
- package/lib/dataflow/propagation/install/path/index.test.js +1 -1
- package/lib/dataflow/propagation/install/path/join-and-resolve.js +3 -4
- package/lib/dataflow/propagation/install/path/normalize.js +4 -5
- package/lib/dataflow/propagation/install/path/parse.js +3 -4
- package/lib/dataflow/propagation/install/path/relative.js +4 -5
- package/lib/dataflow/propagation/install/path/toNamespacedPath.js +3 -4
- package/lib/dataflow/propagation/install/pug/index.js +3 -4
- package/lib/dataflow/propagation/install/pug-runtime-escape.js +3 -4
- package/lib/dataflow/propagation/install/querystring/escape.js +3 -4
- package/lib/dataflow/propagation/install/querystring/escape.test.js +1 -1
- package/lib/dataflow/propagation/install/querystring/parse.js +3 -4
- package/lib/dataflow/propagation/install/querystring/parse.test.js +1 -1
- package/lib/dataflow/propagation/install/querystring/stringify.js +3 -4
- package/lib/dataflow/propagation/install/querystring/stringify.test.js +1 -1
- package/lib/dataflow/propagation/install/reg-exp-prototype-exec.js +2 -3
- package/lib/dataflow/propagation/install/send.js +3 -3
- package/lib/dataflow/propagation/install/sequelize/query-generator.js +3 -3
- package/lib/dataflow/propagation/install/sequelize/query-generator.test.js +2 -1
- package/lib/dataflow/propagation/install/sequelize/sql-string.js +5 -5
- package/lib/dataflow/propagation/install/sql-template-strings.js +3 -3
- package/lib/dataflow/propagation/install/string/concat.js +2 -3
- package/lib/dataflow/propagation/install/string/format-methods.js +2 -3
- package/lib/dataflow/propagation/install/string/html-methods.js +3 -4
- package/lib/dataflow/propagation/install/string/match-all.js +2 -3
- package/lib/dataflow/propagation/install/string/match.js +2 -3
- package/lib/dataflow/propagation/install/string/replace.js +2 -3
- package/lib/dataflow/propagation/install/string/slice.js +2 -3
- package/lib/dataflow/propagation/install/string/split.js +2 -3
- package/lib/dataflow/propagation/install/string/substring.js +2 -3
- package/lib/dataflow/propagation/install/string/trim.js +2 -3
- package/lib/dataflow/propagation/install/unescape.js +2 -3
- package/lib/dataflow/propagation/install/url/domain-parsers.js +3 -4
- package/lib/dataflow/propagation/install/url/parse.js +3 -4
- package/lib/dataflow/propagation/install/url/parse.test.js +2 -2
- package/lib/dataflow/propagation/install/url/searchParams.js +3 -4
- package/lib/dataflow/propagation/install/url/url.js +3 -4
- package/lib/dataflow/propagation/install/util-format.js +3 -4
- package/lib/dataflow/propagation/install/validator/hooks.js +9 -9
- package/lib/dataflow/sinks/install/child-process.js +5 -6
- package/lib/dataflow/sinks/install/eval.js +2 -3
- package/lib/dataflow/sinks/install/express/reflected-xss.js +3 -4
- package/lib/dataflow/sinks/install/express/unvalidated-redirect.js +3 -4
- package/lib/dataflow/sinks/install/fastify/unvalidated-redirect.js +3 -4
- package/lib/dataflow/sinks/install/fs.js +4 -5
- package/lib/dataflow/sinks/install/fs.test.js +2 -2
- package/lib/dataflow/sinks/install/function.js +2 -3
- package/lib/dataflow/sinks/install/hapi/unvalidated-redirect.js +3 -4
- package/lib/dataflow/sinks/install/http/request.js +3 -4
- package/lib/dataflow/sinks/install/http/request.test.js +2 -2
- package/lib/dataflow/sinks/install/http/server-response.js +5 -6
- package/lib/dataflow/sinks/install/http/server-response.test.js +3 -3
- package/lib/dataflow/sinks/install/koa/unvalidated-redirect.js +3 -4
- package/lib/dataflow/sinks/install/libxmljs.js +4 -5
- package/lib/dataflow/sinks/install/libxmljs.test.js +2 -2
- package/lib/dataflow/sinks/install/marsdb.js +3 -4
- package/lib/dataflow/sinks/install/marsdb.test.js +3 -3
- package/lib/dataflow/sinks/install/mongodb.js +3 -4
- package/lib/dataflow/sinks/install/mongodb.test.js +2 -6
- package/lib/dataflow/sinks/install/mssql.js +4 -5
- package/lib/dataflow/sinks/install/mssql.test.js +2 -2
- package/lib/dataflow/sinks/install/mysql.js +4 -5
- package/lib/dataflow/sinks/install/mysql.test.js +2 -11
- package/lib/dataflow/sinks/install/node-serialize.js +3 -4
- package/lib/dataflow/sinks/install/node-serialize.test.js +1 -3
- package/lib/dataflow/sinks/install/postgres.js +5 -6
- package/lib/dataflow/sinks/install/postgres.test.js +3 -9
- package/lib/dataflow/sinks/install/restify.js +3 -4
- package/lib/dataflow/sinks/install/restify.test.js +3 -5
- package/lib/dataflow/sinks/install/sequelize.js +3 -4
- package/lib/dataflow/sinks/install/sqlite3.js +3 -4
- package/lib/dataflow/sinks/install/vm.js +3 -4
- package/lib/dataflow/sources/install/body-parser1.js +2 -4
- package/lib/dataflow/sources/install/body-parser1.test.js +4 -8
- package/lib/dataflow/sources/install/busboy.js +3 -4
- package/lib/dataflow/sources/install/busboy.test.js +2 -2
- package/lib/dataflow/sources/install/cookie-parser1.js +2 -4
- package/lib/dataflow/sources/install/cookie-parser1.test.js +2 -4
- package/lib/dataflow/sources/install/express/params.js +56 -38
- package/lib/dataflow/sources/install/express/params.test.js +80 -73
- package/lib/dataflow/sources/install/express/parsedUrl.js +45 -29
- package/lib/dataflow/sources/install/express/parsedUrl.test.js +71 -29
- package/lib/dataflow/sources/install/fastify/fastify.js +2 -3
- package/lib/dataflow/sources/install/fastify/fastify.test.js +3 -6
- package/lib/dataflow/sources/install/formidable1.js +2 -3
- package/lib/dataflow/sources/install/hapi/hapi.js +1 -2
- package/lib/dataflow/sources/install/http.js +2 -3
- package/lib/dataflow/sources/install/http.test.js +2 -2
- package/lib/dataflow/sources/install/koa/koa-bodyparsers.js +3 -5
- package/lib/dataflow/sources/install/koa/koa-multer.js +3 -4
- package/lib/dataflow/sources/install/koa/koa-multer.test.js +1 -1
- package/lib/dataflow/sources/install/koa/koa-routers.js +3 -4
- package/lib/dataflow/sources/install/koa/koa2.js +2 -4
- package/lib/dataflow/sources/install/multer1.js +2 -3
- package/lib/dataflow/sources/install/multer1.test.js +1 -3
- package/lib/dataflow/sources/install/qs6.js +2 -4
- package/lib/dataflow/sources/install/querystring.js +2 -3
- package/lib/dataflow/sources/install/restify/fieldedTextBodyParser.js +2 -3
- package/lib/dataflow/sources/install/restify/fieldedTextBodyParser.test.js +1 -1
- package/lib/dataflow/sources/install/restify/jsonBodyParser.js +2 -3
- package/lib/dataflow/sources/install/restify/jsonBodyParser.test.js +1 -1
- package/lib/dataflow/sources/install/restify/router.js +2 -4
- package/lib/dataflow/sources/install/restify/router.test.js +4 -6
- package/lib/get-source-context.js +77 -37
- package/lib/get-source-context.test.js +106 -53
- package/lib/index.d.ts +3 -9
- package/lib/response-scanning/install/http.js +3 -3
- package/lib/response-scanning/install/http.test.js +2 -2
- package/lib/session-configuration/install/express-session.js +1 -1
- package/lib/session-configuration/install/express-session.test.js +1 -3
- package/lib/session-configuration/install/fastify-cookie.js +1 -1
- package/lib/session-configuration/install/fastify-cookie.test.js +1 -3
- package/lib/session-configuration/install/koa.js +1 -1
- package/lib/session-configuration/install/koa.test.js +1 -1
- package/package.json +11 -11
- package/lib/constants.js +0 -26
- package/lib/dataflow/sinks/install/fs-original.js +0 -170
|
@@ -16,7 +16,6 @@
|
|
|
16
16
|
'use strict';
|
|
17
17
|
|
|
18
18
|
const { InputType: { URL_PARAMETER } } = require('@contrast/common');
|
|
19
|
-
const { InstrumentationType: { SOURCE } } = require('../../../../constants');
|
|
20
19
|
const { patchType } = require('../../common');
|
|
21
20
|
|
|
22
21
|
module.exports = function init(core) {
|
|
@@ -33,16 +32,15 @@ module.exports = function init(core) {
|
|
|
33
32
|
return core.assess.dataflow.sources.restifyInstrumentation.router = {
|
|
34
33
|
install() {
|
|
35
34
|
depHooks.resolve(
|
|
36
|
-
{ name: 'restify', file: 'lib/router.js', version: '>=8' },
|
|
35
|
+
{ name: 'restify', file: 'lib/router.js', version: '>=8 <12' },
|
|
37
36
|
(Router) => {
|
|
38
37
|
patcher.patch(Router.prototype, 'lookup', {
|
|
39
38
|
name: 'restify.Router.prototype.lookup',
|
|
40
39
|
patchType,
|
|
41
40
|
post({ args: [req], hooked, orig, name, funcKey }) {
|
|
42
|
-
const sourceContext = getSourceContext(
|
|
41
|
+
const sourceContext = getSourceContext();
|
|
43
42
|
|
|
44
43
|
if (!sourceContext) {
|
|
45
|
-
logger.error({ funcKey }, 'unable to handle source. Missing `sourceContext`');
|
|
46
44
|
return;
|
|
47
45
|
}
|
|
48
46
|
|
|
@@ -17,7 +17,7 @@ describe('assess dataflow sources restify router', function () {
|
|
|
17
17
|
req = {};
|
|
18
18
|
|
|
19
19
|
core.depHooks.resolve
|
|
20
|
-
.withArgs({ name: 'restify', file: 'lib/router.js', version: '>=8' })
|
|
20
|
+
.withArgs({ name: 'restify', file: 'lib/router.js', version: '>=8 <12' })
|
|
21
21
|
.yields(Router);
|
|
22
22
|
|
|
23
23
|
init(core).install();
|
|
@@ -27,11 +27,9 @@ describe('assess dataflow sources restify router', function () {
|
|
|
27
27
|
simulateRequestScope(() => {
|
|
28
28
|
Router.prototype.lookup(req);
|
|
29
29
|
|
|
30
|
-
expect(handle).not.
|
|
31
|
-
expect(core.logger.
|
|
32
|
-
|
|
33
|
-
'unable to handle source. Missing `sourceContext`'
|
|
34
|
-
);
|
|
30
|
+
expect(handle).not.called;
|
|
31
|
+
expect(core.logger.trace.callCount).greaterThan(0);
|
|
32
|
+
expect(core.logger.trace.lastCall.args[0]).includes('Assess intentionally disabled');
|
|
35
33
|
}, { assess: { policy: null } });
|
|
36
34
|
});
|
|
37
35
|
|
|
@@ -12,16 +12,17 @@
|
|
|
12
12
|
* engineered, modified, repackaged, sold, redistributed or otherwise used in a
|
|
13
13
|
* way not consistent with the End User License Agreement.
|
|
14
14
|
*/
|
|
15
|
-
|
|
15
|
+
// @ts-check
|
|
16
16
|
'use strict';
|
|
17
17
|
|
|
18
|
-
|
|
18
|
+
/** @typedef {import('@contrast/assess').SourceContext} SourceContext */
|
|
19
19
|
|
|
20
20
|
/**
|
|
21
21
|
* @param {{
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
* scopes: import('@contrast/scopes').Scopes
|
|
22
|
+
* readonly config: import('@contrast/config').Config;
|
|
23
|
+
* readonly logger: import('@contrast/logger').Logger;
|
|
24
|
+
* readonly scopes: import('@contrast/scopes').Scopes;
|
|
25
|
+
* assess: import('@contrast/assess').Assess;
|
|
25
26
|
* }} core
|
|
26
27
|
*/
|
|
27
28
|
module.exports = function(core) {
|
|
@@ -32,42 +33,81 @@ module.exports = function(core) {
|
|
|
32
33
|
} = core;
|
|
33
34
|
|
|
34
35
|
/**
|
|
35
|
-
*
|
|
36
|
-
*
|
|
36
|
+
* Used by all propagator instrumentation to make sure that:
|
|
37
|
+
* - the assess store is available
|
|
38
|
+
* - assess is enabled for this request
|
|
39
|
+
* - instrumentation is not locked
|
|
40
|
+
*
|
|
41
|
+
* Any code that does not use this function and directly accesses the assess
|
|
42
|
+
* source context will recurse until the stack is blown if the following
|
|
43
|
+
* checks are not correctly made.
|
|
44
|
+
* @returns {SourceContext | null}
|
|
37
45
|
*/
|
|
38
|
-
|
|
46
|
+
core.assess.getPropagatorContext = function getPropagatorContext() {
|
|
47
|
+
if (instrumentation.isLocked()) return null;
|
|
48
|
+
|
|
49
|
+
// the following logging used to be done by the caller, but has been moved
|
|
50
|
+
// here as opposed to overloading `ctx.policy` with a special value so the
|
|
51
|
+
// caller could determine whether no source context was available or the
|
|
52
|
+
// request is being intentionally excluded. A negative of this is that the
|
|
53
|
+
// function name is not available to be included in the log.
|
|
54
|
+
const ctx = sources.getStore()?.assess;
|
|
55
|
+
if (!ctx) return null;
|
|
56
|
+
|
|
57
|
+
// there is a context, but if policy is null then assess is intentionally
|
|
58
|
+
// disabled (i.e., url exclusion or the request is not sampled).
|
|
59
|
+
if (!ctx.policy) {
|
|
60
|
+
core.logger.trace('Assess intentionally disabled for this request');
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (ctx.propagationEventsCount > config.assess.max_propagation_events) return null;
|
|
65
|
+
|
|
66
|
+
return ctx;
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* @param {import('@contrast/common').Rule} ruleId
|
|
71
|
+
* @returns {SourceContext | null}
|
|
72
|
+
*/
|
|
73
|
+
core.assess.getSinkContext = function getSinkContext(ruleId) {
|
|
74
|
+
if (instrumentation.isLocked()) return null;
|
|
75
|
+
|
|
39
76
|
const ctx = sources.getStore()?.assess;
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
// E.g. Uncommenting any line below will cause a stack overflow:
|
|
46
|
-
// 'asdf'.concat()
|
|
47
|
-
// console.log() // even though this is deadzoned, we haven't checked whether instrumentation is locked yet
|
|
48
|
-
//
|
|
49
|
-
// policy will not exist if assess is altogether disabled for the active request e.g. url exclusion
|
|
50
|
-
if (!ctx?.policy || instrumentation.isLocked()) return null;
|
|
51
|
-
// </unsafe> but still be careful
|
|
52
|
-
|
|
53
|
-
switch (type) {
|
|
54
|
-
case InstrumentationType.PROPAGATOR: {
|
|
55
|
-
if (ctx.propagationEventsCount > config.assess.max_propagation_events) return null;
|
|
56
|
-
break;
|
|
57
|
-
}
|
|
58
|
-
case InstrumentationType.SOURCE: {
|
|
59
|
-
if (ctx.sourceEventsCount > config.assess.max_context_source_events) return null;
|
|
60
|
-
break;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
case InstrumentationType.RULE: {
|
|
64
|
-
const [ruleId] = rest;
|
|
65
|
-
if (!ruleId) break;
|
|
66
|
-
if (!ctx.policy?.enabledRules?.has?.(ruleId) || ruleScopes.isLocked(ruleId)) return null;
|
|
67
|
-
break;
|
|
68
|
-
}
|
|
77
|
+
if (!ctx) return null;
|
|
78
|
+
|
|
79
|
+
if (!ctx.policy) {
|
|
80
|
+
core.logger.trace('Assess intentionally disabled for this request');
|
|
81
|
+
return null;
|
|
69
82
|
}
|
|
70
83
|
|
|
84
|
+
if (ruleId && !ctx.policy?.enabledRules?.has?.(ruleId) || ruleScopes.isLocked(ruleId)) return null;
|
|
85
|
+
|
|
71
86
|
return ctx;
|
|
72
87
|
};
|
|
88
|
+
|
|
89
|
+
/** @returns {SourceContext | null} */
|
|
90
|
+
core.assess.getSourceContext = function getSourceContext() {
|
|
91
|
+
if (instrumentation.isLocked()) return null;
|
|
92
|
+
|
|
93
|
+
const ctx = sources.getStore()?.assess;
|
|
94
|
+
if (!ctx) {
|
|
95
|
+
// because this is a real error, and we don't have the function name
|
|
96
|
+
// that the caller previously logged, we generate a stack trace to
|
|
97
|
+
// capture that information.
|
|
98
|
+
const err = new Error('No source context found');
|
|
99
|
+
core.logger.warn({ err }, 'assess running outside of request scope');
|
|
100
|
+
return null;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
if (!ctx.policy) {
|
|
104
|
+
core.logger.trace('Assess intentionally disabled for this request');
|
|
105
|
+
return null;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
if (ctx.sourceEventsCount > config.assess.max_context_source_events) return null;
|
|
109
|
+
|
|
110
|
+
return ctx;
|
|
111
|
+
};
|
|
112
|
+
|
|
73
113
|
};
|
|
@@ -3,9 +3,29 @@
|
|
|
3
3
|
const { expect } = require('chai');
|
|
4
4
|
const { Event } = require('@contrast/common');
|
|
5
5
|
const { initAssessFixture } = require('@contrast/test/fixtures');
|
|
6
|
-
const
|
|
7
|
-
|
|
8
|
-
|
|
6
|
+
const sinon = require('sinon');
|
|
7
|
+
|
|
8
|
+
function execStoreAssertions(assessStore) {
|
|
9
|
+
expect(assessStore).to.be.an('object').and.deep.include({
|
|
10
|
+
reqData: {
|
|
11
|
+
ip: '127.0.0.1',
|
|
12
|
+
httpVersion: '1.1',
|
|
13
|
+
method: 'get',
|
|
14
|
+
headers: {
|
|
15
|
+
'content-type': 'text/html',
|
|
16
|
+
language: 'en',
|
|
17
|
+
referer: 'http://fake.url.foo',
|
|
18
|
+
},
|
|
19
|
+
uriPath: '/index',
|
|
20
|
+
queries: '_id=123',
|
|
21
|
+
contentType: 'text/html'
|
|
22
|
+
},
|
|
23
|
+
responseData: {},
|
|
24
|
+
sourceEventsCount: 0,
|
|
25
|
+
propagationEventsCount: 0,
|
|
26
|
+
});
|
|
27
|
+
expect(assessStore.policy.enabledRules).to.have.length.greaterThan(5);
|
|
28
|
+
}
|
|
9
29
|
|
|
10
30
|
describe('assess getSourceContext', function () {
|
|
11
31
|
let core, simulateRequestScope;
|
|
@@ -14,86 +34,74 @@ describe('assess getSourceContext', function () {
|
|
|
14
34
|
({ core, simulateRequestScope } = initAssessFixture());
|
|
15
35
|
});
|
|
16
36
|
|
|
17
|
-
function
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
'content-type': 'text/html',
|
|
25
|
-
language: 'en',
|
|
26
|
-
referer: 'http://fake.url.foo',
|
|
27
|
-
},
|
|
28
|
-
uriPath: '/index',
|
|
29
|
-
queries: '_id=123',
|
|
30
|
-
contentType: 'text/html'
|
|
31
|
-
},
|
|
32
|
-
responseData: {},
|
|
33
|
-
sourceEventsCount: 0,
|
|
34
|
-
propagationEventsCount: 0,
|
|
37
|
+
describe('getPropagatorContext()', function() {
|
|
38
|
+
it('returns null when instrumentation is locked', function () {
|
|
39
|
+
sinon.stub(core.scopes.instrumentation, 'isLocked').returns(true);
|
|
40
|
+
|
|
41
|
+
simulateRequestScope(() => {
|
|
42
|
+
expect(core.assess.getPropagatorContext()).to.be.null;
|
|
43
|
+
});
|
|
35
44
|
});
|
|
36
|
-
expect(assessStore.policy.enabledRules).to.have.length.greaterThan(5);
|
|
37
|
-
}
|
|
38
45
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
46
|
+
it('returns null when not in request scope', function() {
|
|
47
|
+
expect(core.assess.getPropagatorContext()).to.be.null;
|
|
48
|
+
});
|
|
42
49
|
|
|
43
|
-
|
|
44
|
-
it('returns assess store when in request scope', function() {
|
|
50
|
+
it('returns null when assess is disabled by policy', function() {
|
|
45
51
|
simulateRequestScope(() => {
|
|
46
|
-
|
|
47
|
-
|
|
52
|
+
expect(core.assess.getPropagatorContext()).to.be.null;
|
|
53
|
+
expect(core.logger.trace).to.have.been.calledWith('Assess intentionally disabled for this request');
|
|
54
|
+
}, { assess: { policy: undefined } });
|
|
48
55
|
});
|
|
49
|
-
});
|
|
50
56
|
|
|
51
|
-
|
|
52
|
-
it('returns assess store when max source event threshold is not met', function() {
|
|
57
|
+
it('returns assess store when max propagation event threshold is not met', function() {
|
|
53
58
|
simulateRequestScope(() => {
|
|
54
|
-
execStoreAssertions(core.assess.
|
|
59
|
+
execStoreAssertions(core.assess.getPropagatorContext());
|
|
55
60
|
});
|
|
56
61
|
});
|
|
57
62
|
|
|
58
|
-
it('returns
|
|
63
|
+
it('returns null when max propagation event threshold is exceeded', function() {
|
|
64
|
+
core.config.setValue('assess.max_propagation_events', 10);
|
|
65
|
+
|
|
59
66
|
simulateRequestScope(() => {
|
|
60
|
-
core.
|
|
61
|
-
|
|
62
|
-
expect(core.assess.getSourceContext(SOURCE)).to.be.null;
|
|
63
|
-
});
|
|
67
|
+
expect(core.assess.getPropagatorContext()).to.be.null;
|
|
68
|
+
}, { assess: { propagationEventsCount: 11 } });
|
|
64
69
|
});
|
|
65
70
|
});
|
|
66
71
|
|
|
67
|
-
describe('
|
|
68
|
-
it('returns
|
|
72
|
+
describe('getSinkContext()', function() {
|
|
73
|
+
it('returns null when instrumentation is locked', function () {
|
|
74
|
+
sinon.stub(core.scopes.instrumentation, 'isLocked').returns(true);
|
|
75
|
+
|
|
69
76
|
simulateRequestScope(() => {
|
|
70
|
-
|
|
77
|
+
expect(core.assess.getSinkContext()).to.be.null;
|
|
71
78
|
});
|
|
72
79
|
});
|
|
73
80
|
|
|
74
|
-
it('returns
|
|
81
|
+
it('returns null when not in request scope', function() {
|
|
82
|
+
expect(core.assess.getSinkContext()).to.be.null;
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
it('returns null when assess is disabled by policy', function() {
|
|
75
86
|
simulateRequestScope(() => {
|
|
76
|
-
core.
|
|
77
|
-
core.
|
|
78
|
-
|
|
79
|
-
});
|
|
87
|
+
expect(core.assess.getSinkContext()).to.be.null;
|
|
88
|
+
expect(core.logger.trace).to.have.been.calledWith('Assess intentionally disabled for this request');
|
|
89
|
+
}, { assess: { policy: undefined } });
|
|
80
90
|
});
|
|
81
|
-
});
|
|
82
91
|
|
|
83
|
-
describe('.getSourceContext(SINK?, ruleId?)', function() {
|
|
84
92
|
it('returns assess store when ruleId is not passed', function() {
|
|
85
93
|
simulateRequestScope(() => {
|
|
86
|
-
execStoreAssertions(core.assess.getSourceContext(
|
|
94
|
+
execStoreAssertions(core.assess.getSourceContext());
|
|
87
95
|
});
|
|
88
96
|
});
|
|
89
97
|
|
|
90
98
|
it('returns assess store when ruleId provided is enabled in the policy', function() {
|
|
91
99
|
simulateRequestScope(() => {
|
|
92
|
-
execStoreAssertions(core.assess.
|
|
100
|
+
execStoreAssertions(core.assess.getSinkContext('reflected-xss'));
|
|
93
101
|
});
|
|
94
102
|
});
|
|
95
103
|
|
|
96
|
-
it('returns
|
|
104
|
+
it('returns null when ruleId provided is not enabled in the policy', function() {
|
|
97
105
|
core.messages.emit(Event.SERVER_SETTINGS_UPDATE, {
|
|
98
106
|
assess: {
|
|
99
107
|
['reflected-xss']: { enable: false },
|
|
@@ -101,8 +109,53 @@ describe('assess getSourceContext', function () {
|
|
|
101
109
|
});
|
|
102
110
|
|
|
103
111
|
simulateRequestScope(() => {
|
|
104
|
-
expect(core.assess.
|
|
112
|
+
expect(core.assess.getSinkContext('reflected-xss')).to.be.null;
|
|
105
113
|
});
|
|
106
114
|
});
|
|
107
115
|
});
|
|
116
|
+
|
|
117
|
+
describe('getSourceContext()', function() {
|
|
118
|
+
it('returns null when instrumentation is locked', function () {
|
|
119
|
+
sinon.stub(core.scopes.instrumentation, 'isLocked').returns(true);
|
|
120
|
+
|
|
121
|
+
simulateRequestScope(() => {
|
|
122
|
+
expect(core.assess.getSourceContext()).to.be.null;
|
|
123
|
+
});
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
it('returns null when not in request scope', function() {
|
|
127
|
+
expect(core.assess.getSourceContext()).to.be.null;
|
|
128
|
+
expect(core.logger.warn).to.have.been.calledWithMatch(
|
|
129
|
+
sinon.match.object,
|
|
130
|
+
'assess running outside of request scope',
|
|
131
|
+
);
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
it('returns null when assess is disabled by policy', function() {
|
|
135
|
+
simulateRequestScope(() => {
|
|
136
|
+
expect(core.assess.getSourceContext()).to.be.null;
|
|
137
|
+
expect(core.logger.trace).to.have.been.calledWith('Assess intentionally disabled for this request');
|
|
138
|
+
}, { assess: { policy: undefined } });
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
it('returns assess store when in request scope', function() {
|
|
142
|
+
simulateRequestScope(() => {
|
|
143
|
+
execStoreAssertions(core.assess.getSourceContext());
|
|
144
|
+
});
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
it('returns assess store when max source event threshold is not met', function() {
|
|
148
|
+
simulateRequestScope(() => {
|
|
149
|
+
execStoreAssertions(core.assess.getSourceContext());
|
|
150
|
+
});
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
it('returns null when max source event threshold is exceeded', function() {
|
|
154
|
+
core.config.setValue('assess.max_context_source_events', 10);
|
|
155
|
+
|
|
156
|
+
simulateRequestScope(() => {
|
|
157
|
+
expect(core.assess.getSourceContext()).to.be.null;
|
|
158
|
+
}, { assess: { sourceEventsCount: 11 } });
|
|
159
|
+
});
|
|
160
|
+
});
|
|
108
161
|
});
|
package/lib/index.d.ts
CHANGED
|
@@ -37,12 +37,6 @@ export interface Core extends _Core {
|
|
|
37
37
|
metrics: any;
|
|
38
38
|
}
|
|
39
39
|
|
|
40
|
-
export enum InstrumentationType {
|
|
41
|
-
SOURCE = 'source',
|
|
42
|
-
PROPAGATOR = 'propagator',
|
|
43
|
-
RULE = 'rule',
|
|
44
|
-
}
|
|
45
|
-
|
|
46
40
|
export interface SourceContext {
|
|
47
41
|
reqData: object,
|
|
48
42
|
responseData: {
|
|
@@ -74,14 +68,14 @@ export interface RuleState {
|
|
|
74
68
|
|
|
75
69
|
export interface Assess {
|
|
76
70
|
getPolicy(): Policy,
|
|
77
|
-
|
|
71
|
+
getPropagatorContext(): SourceContext | null,
|
|
72
|
+
getSinkContext(ruleId: Rule): SourceContext | null,
|
|
73
|
+
getSourceContext(): SourceContext | null,
|
|
78
74
|
makeSourceContext(req: IncomingMessage, res: ServerResponse): SourceContext,
|
|
79
75
|
ruleScopes: RuleScopes,
|
|
80
76
|
ruleState: RuleState,
|
|
81
77
|
}
|
|
82
78
|
|
|
83
|
-
export function getSourceContext(instrType?: InstrumentationType, ops?: any): SourceContext;
|
|
84
|
-
|
|
85
79
|
declare function init(core: Core): Assess;
|
|
86
80
|
|
|
87
81
|
export = init;
|
|
@@ -89,7 +89,7 @@ module.exports = function(core) {
|
|
|
89
89
|
}
|
|
90
90
|
|
|
91
91
|
http.install = function() {
|
|
92
|
-
depHooks.resolve({ name: 'http' }, (http) => {
|
|
92
|
+
depHooks.resolve({ name: 'http', version: '*' }, (http) => {
|
|
93
93
|
{
|
|
94
94
|
const name = 'http.ServerResponse.prototype.write';
|
|
95
95
|
patcher.patch(http.ServerResponse.prototype, 'write', {
|
|
@@ -116,7 +116,7 @@ module.exports = function(core) {
|
|
|
116
116
|
}
|
|
117
117
|
});
|
|
118
118
|
|
|
119
|
-
depHooks.resolve({ name: 'http2' }, (http2) => {
|
|
119
|
+
depHooks.resolve({ name: 'http2', version: '*' }, (http2) => {
|
|
120
120
|
// todo: NODE-3467
|
|
121
121
|
{
|
|
122
122
|
const name = 'http2.Http2ServerResponse.prototype.write';
|
|
@@ -147,7 +147,7 @@ module.exports = function(core) {
|
|
|
147
147
|
}
|
|
148
148
|
});
|
|
149
149
|
|
|
150
|
-
depHooks.resolve({ name: 'spdy', file: 'lib/spdy/response.js' }, (response) => {
|
|
150
|
+
depHooks.resolve({ name: 'spdy', version: '<5', file: 'lib/spdy/response.js' }, (response) => {
|
|
151
151
|
patcher.patch(response, 'end', {
|
|
152
152
|
name: 'spdy.response.end',
|
|
153
153
|
patchType: 'test',
|
|
@@ -95,10 +95,10 @@ describe('assess response scanning sinks http', function () {
|
|
|
95
95
|
|
|
96
96
|
http = require('./http')(core);
|
|
97
97
|
core.depHooks.resolve
|
|
98
|
-
.withArgs({ name: 'http' })
|
|
98
|
+
.withArgs(sinon.match({ name: 'http' }))
|
|
99
99
|
.yields(httpModule);
|
|
100
100
|
core.depHooks.resolve
|
|
101
|
-
.withArgs({ name: 'http2' })
|
|
101
|
+
.withArgs(sinon.match({ name: 'http2' }))
|
|
102
102
|
.yields(http2Module);
|
|
103
103
|
|
|
104
104
|
http.install();
|
|
@@ -41,7 +41,7 @@ module.exports = function (core) {
|
|
|
41
41
|
const expressSession = core.assess.sessionConfiguration.expressSession = {};
|
|
42
42
|
|
|
43
43
|
expressSession.install = function () {
|
|
44
|
-
return depHooks.resolve({ name: 'express-session' }, (session) => {
|
|
44
|
+
return depHooks.resolve({ name: 'express-session', version: '<2' }, (session) => {
|
|
45
45
|
// Return the hooked function as the export.
|
|
46
46
|
const hooked = patcher.patch(session, {
|
|
47
47
|
name: 'express.hookedSessionConstructor',
|
|
@@ -16,9 +16,7 @@ describe('assess session-configuration http', function () {
|
|
|
16
16
|
|
|
17
17
|
beforeEach(function () {
|
|
18
18
|
({ core, simulateRequestScope } = initAssessFixture());
|
|
19
|
-
core.depHooks.resolve
|
|
20
|
-
.withArgs({ name: 'express-session' })
|
|
21
|
-
.yields(ExpressSession);
|
|
19
|
+
core.depHooks.resolve.yields(ExpressSession);
|
|
22
20
|
|
|
23
21
|
sinon.spy(core.assess.sessionConfiguration, 'handleHttpOnly');
|
|
24
22
|
sinon.spy(core.assess.sessionConfiguration, 'handleSecure');
|
|
@@ -40,7 +40,7 @@ module.exports = function (core) {
|
|
|
40
40
|
|
|
41
41
|
return core.assess.sessionConfiguration.fastifyCookie = {
|
|
42
42
|
install () {
|
|
43
|
-
depHooks.resolve({ name: '@fastify/cookie' }, (_export) => {
|
|
43
|
+
depHooks.resolve({ name: '@fastify/cookie', version: '<12' }, (_export) => {
|
|
44
44
|
const patched = patcher.patch(_export, {
|
|
45
45
|
name: 'express.hookedSessionConstructor',
|
|
46
46
|
patchType,
|
|
@@ -26,9 +26,7 @@ describe('assess session-configuration @fastify/cookie', function () {
|
|
|
26
26
|
addHook: sinon.stub().yields({}, reply),
|
|
27
27
|
};
|
|
28
28
|
|
|
29
|
-
core.depHooks.resolve
|
|
30
|
-
.withArgs({ name: '@fastify/cookie' })
|
|
31
|
-
.yields(mockExport);
|
|
29
|
+
core.depHooks.resolve.yields(mockExport);
|
|
32
30
|
|
|
33
31
|
sinon.stub(core.assess.sessionConfiguration, 'reportFindings');
|
|
34
32
|
core.assess.sessionConfiguration.fastifyCookie.install();
|
|
@@ -39,7 +39,7 @@ module.exports = function (core) {
|
|
|
39
39
|
|
|
40
40
|
return core.assess.sessionConfiguration.koa = {
|
|
41
41
|
install () {
|
|
42
|
-
depHooks.resolve({ name: 'koa', version: '>=2.3.0' }, (Koa) => {
|
|
42
|
+
depHooks.resolve({ name: 'koa', version: '>=2.3.0 <3' }, (Koa) => {
|
|
43
43
|
patcher.patch(Koa.prototype, 'use', {
|
|
44
44
|
name: 'Koa.Application',
|
|
45
45
|
patchType,
|
|
@@ -30,7 +30,7 @@ describe('assess sessionConfiguration Koa', function () {
|
|
|
30
30
|
ctxMock.cookies = { set: sinon.stub() };
|
|
31
31
|
|
|
32
32
|
core.depHooks.resolve
|
|
33
|
-
.withArgs({ name: 'koa', version: '>=2.3.0' })
|
|
33
|
+
.withArgs({ name: 'koa', version: '>=2.3.0 <3' })
|
|
34
34
|
.yields(koaMock);
|
|
35
35
|
|
|
36
36
|
sinon.stub(core.assess.sessionConfiguration, 'reportFindings');
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@contrast/assess",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.42.0",
|
|
4
4
|
"description": "Contrast service providing framework-agnostic Assess support",
|
|
5
5
|
"license": "SEE LICENSE IN LICENSE",
|
|
6
6
|
"author": "Contrast Security <nodejs@contrastsecurity.com> (https://www.contrastsecurity.com)",
|
|
@@ -17,17 +17,17 @@
|
|
|
17
17
|
"test": "../scripts/test.sh"
|
|
18
18
|
},
|
|
19
19
|
"dependencies": {
|
|
20
|
-
"@contrast/common": "1.
|
|
21
|
-
"@contrast/config": "1.
|
|
22
|
-
"@contrast/core": "1.
|
|
23
|
-
"@contrast/dep-hooks": "1.
|
|
20
|
+
"@contrast/common": "1.27.0",
|
|
21
|
+
"@contrast/config": "1.37.0",
|
|
22
|
+
"@contrast/core": "1.42.0",
|
|
23
|
+
"@contrast/dep-hooks": "1.11.0",
|
|
24
24
|
"@contrast/distringuish": "^5.1.0",
|
|
25
|
-
"@contrast/instrumentation": "1.
|
|
26
|
-
"@contrast/logger": "1.
|
|
27
|
-
"@contrast/patcher": "1.
|
|
28
|
-
"@contrast/rewriter": "1.
|
|
29
|
-
"@contrast/route-coverage": "1.
|
|
30
|
-
"@contrast/scopes": "1.
|
|
25
|
+
"@contrast/instrumentation": "1.21.0",
|
|
26
|
+
"@contrast/logger": "1.15.0",
|
|
27
|
+
"@contrast/patcher": "1.14.0",
|
|
28
|
+
"@contrast/rewriter": "1.18.0",
|
|
29
|
+
"@contrast/route-coverage": "1.32.0",
|
|
30
|
+
"@contrast/scopes": "1.12.0",
|
|
31
31
|
"semver": "^7.6.0"
|
|
32
32
|
}
|
|
33
33
|
}
|
package/lib/constants.js
DELETED
|
@@ -1,26 +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 InstrumentationType = {
|
|
19
|
-
SOURCE: 'SOURCE',
|
|
20
|
-
PROPAGATOR: 'PROPAGATOR',
|
|
21
|
-
RULE: 'RULE',
|
|
22
|
-
};
|
|
23
|
-
|
|
24
|
-
module.exports = {
|
|
25
|
-
InstrumentationType,
|
|
26
|
-
};
|