@contrast/assess 1.1.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/coverage/lcov-report/base.css +224 -0
- package/coverage/lcov-report/block-navigation.js +87 -0
- package/coverage/lcov-report/favicon.png +0 -0
- package/coverage/lcov-report/index.html +266 -0
- package/coverage/lcov-report/lib/dataflow/common.js.html +154 -0
- package/coverage/lcov-report/lib/dataflow/event-factory.js.html +598 -0
- package/coverage/lcov-report/lib/dataflow/index.html +191 -0
- package/coverage/lcov-report/lib/dataflow/index.js.html +190 -0
- package/coverage/lcov-report/lib/dataflow/propagation/common.js.html +145 -0
- package/coverage/lcov-report/lib/dataflow/propagation/index.html +131 -0
- package/coverage/lcov-report/lib/dataflow/propagation/index.js.html +190 -0
- package/coverage/lcov-report/lib/dataflow/propagation/install/contrast-methods/index.html +131 -0
- package/coverage/lcov-report/lib/dataflow/propagation/install/contrast-methods/index.js.html +184 -0
- package/coverage/lcov-report/lib/dataflow/propagation/install/contrast-methods/plus.js.html +397 -0
- package/coverage/lcov-report/lib/dataflow/propagation/install/string/concat.js.html +478 -0
- package/coverage/lcov-report/lib/dataflow/propagation/install/string/index.html +176 -0
- package/coverage/lcov-report/lib/dataflow/propagation/install/string/index.js.html +202 -0
- package/coverage/lcov-report/lib/dataflow/propagation/install/string/replace.js.html +496 -0
- package/coverage/lcov-report/lib/dataflow/propagation/install/string/substring.js.html +415 -0
- package/coverage/lcov-report/lib/dataflow/propagation/install/string/trim.js.html +403 -0
- package/coverage/lcov-report/lib/dataflow/signatures.js.html +5923 -0
- package/coverage/lcov-report/lib/dataflow/sinks/common.js.html +145 -0
- package/coverage/lcov-report/lib/dataflow/sinks/index.html +131 -0
- package/coverage/lcov-report/lib/dataflow/sinks/index.js.html +211 -0
- package/coverage/lcov-report/lib/dataflow/sinks/install/fastify/index.html +146 -0
- package/coverage/lcov-report/lib/dataflow/sinks/install/fastify/index.js.html +172 -0
- package/coverage/lcov-report/lib/dataflow/sinks/install/fastify/unvalidated-redirect.js.html +319 -0
- package/coverage/lcov-report/lib/dataflow/sinks/install/fastify/xss.js.html +721 -0
- package/coverage/lcov-report/lib/dataflow/sinks/install/http.js.html +340 -0
- package/coverage/lcov-report/lib/dataflow/sinks/install/index.html +116 -0
- package/coverage/lcov-report/lib/dataflow/sources/common.js.html +145 -0
- package/coverage/lcov-report/lib/dataflow/sources/index.html +131 -0
- package/coverage/lcov-report/lib/dataflow/sources/index.js.html +226 -0
- package/coverage/lcov-report/lib/dataflow/sources/install/fastify.js.html +379 -0
- package/coverage/lcov-report/lib/dataflow/sources/install/http.js.html +502 -0
- package/coverage/lcov-report/lib/dataflow/sources/install/index.html +146 -0
- package/coverage/lcov-report/lib/dataflow/sources/install/qs.js.html +322 -0
- package/coverage/lcov-report/lib/dataflow/tag-utils.js.html +418 -0
- package/coverage/lcov-report/lib/dataflow/tracker.js.html +466 -0
- package/coverage/lcov-report/lib/dataflow/utils/index.html +116 -0
- package/coverage/lcov-report/lib/dataflow/utils/is-vulnerable.js.html +424 -0
- package/coverage/lcov-report/lib/index.html +116 -0
- package/coverage/lcov-report/lib/index.js.html +193 -0
- package/coverage/lcov-report/prettify.css +1 -0
- package/coverage/lcov-report/prettify.js +2 -0
- package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
- package/coverage/lcov-report/sorter.js +196 -0
- package/coverage/lcov.info +4856 -0
- package/coverage/tmp/coverage-9548-1675168551025-0.json +1 -0
- package/coverage/tmp/coverage-9551-1675168550963-0.json +1 -0
- package/coverage/tmp/coverage-9552-1675168550969-0.json +1 -0
- package/coverage/tmp/coverage-9553-1675168550970-0.json +1 -0
- package/coverage/tmp/coverage-9554-1675168550962-0.json +1 -0
- package/coverage/tmp/coverage-9555-1675168550965-0.json +1 -0
- package/coverage/tmp/coverage-9556-1675168550964-0.json +1 -0
- package/coverage/tmp/coverage-9557-1675168550969-0.json +1 -0
- package/coverage/tmp/coverage-9558-1675168550964-0.json +1 -0
- package/coverage/tmp/coverage-9559-1675168550971-0.json +1 -0
- package/lib/dataflow/event-factory.js +256 -0
- package/lib/dataflow/index.js +35 -0
- package/lib/dataflow/propagation/common.js +26 -0
- package/lib/dataflow/propagation/index.js +50 -0
- package/lib/dataflow/propagation/install/array-prototype-join.js +125 -0
- package/lib/dataflow/propagation/install/contrast-methods/add.js +104 -0
- package/lib/dataflow/propagation/install/contrast-methods/index.js +34 -0
- package/lib/dataflow/propagation/install/contrast-methods/tag.js +102 -0
- package/lib/dataflow/propagation/install/decode-uri-component.js +88 -0
- package/lib/dataflow/propagation/install/ejs/escape-xml.js +89 -0
- package/lib/dataflow/propagation/install/ejs/index.js +30 -0
- package/lib/dataflow/propagation/install/encode-uri-component.js +87 -0
- package/lib/dataflow/propagation/install/escape-html.js +89 -0
- package/lib/dataflow/propagation/install/escape.js +89 -0
- package/lib/dataflow/propagation/install/handlebars-utils-escape-expression.js +89 -0
- package/lib/dataflow/propagation/install/mysql-connection-escape.js +111 -0
- package/lib/dataflow/propagation/install/pug/index.js +64 -0
- package/lib/dataflow/propagation/install/pug-runtime-escape.js +89 -0
- package/lib/dataflow/propagation/install/sql-template-strings.js +103 -0
- package/lib/dataflow/propagation/install/string/concat.js +108 -0
- package/lib/dataflow/propagation/install/string/format-methods.js +83 -0
- package/lib/dataflow/propagation/install/string/html-methods.js +163 -0
- package/lib/dataflow/propagation/install/string/index.js +38 -0
- package/lib/dataflow/propagation/install/string/replace.js +197 -0
- package/lib/dataflow/propagation/install/string/substring.js +117 -0
- package/lib/dataflow/propagation/install/string/trim.js +115 -0
- package/lib/dataflow/propagation/install/unescape.js +90 -0
- package/lib/dataflow/propagation/install/url/domain-parsers.js +89 -0
- package/lib/dataflow/propagation/install/url/index.js +33 -0
- package/lib/dataflow/propagation/install/validator/hooks.js +172 -0
- package/lib/dataflow/propagation/install/validator/index.js +28 -0
- package/lib/dataflow/propagation/install/validator/methods.js +82 -0
- package/lib/dataflow/signatures.js +2022 -0
- package/lib/dataflow/sinks/common.js +20 -0
- package/lib/dataflow/sinks/index.js +44 -0
- package/lib/dataflow/sinks/install/fastify/index.js +30 -0
- package/lib/dataflow/sinks/install/fastify/unvalidated-redirect.js +107 -0
- package/lib/dataflow/sinks/install/http.js +119 -0
- package/lib/dataflow/sinks/install/postgres/index.js +129 -0
- package/lib/dataflow/sources/common.js +20 -0
- package/lib/dataflow/sources/handler.js +114 -0
- package/lib/dataflow/sources/index.js +35 -0
- package/lib/dataflow/sources/install/fastify/cookie.js +61 -0
- package/lib/dataflow/sources/install/fastify/fastify.js +89 -0
- package/lib/dataflow/sources/install/fastify/index.js +31 -0
- package/lib/dataflow/sources/install/http.js +181 -0
- package/lib/dataflow/sources/install/qs.js +88 -0
- package/lib/dataflow/tag-utils.js +122 -0
- package/lib/dataflow/tracker.js +127 -0
- package/lib/dataflow/utils/is-safe-content-type.js +30 -0
- package/lib/dataflow/utils/is-vulnerable.js +113 -0
- package/lib/index.js +36 -0
- package/package.json +21 -0
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright: 2022 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
|
+
module.exports = {
|
|
19
|
+
patchType: 'assess-dataflow-sink'
|
|
20
|
+
};
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright: 2022 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 { callChildComponentMethodsSync, Event } = require('@contrast/common');
|
|
19
|
+
const { isVulnerable } = require('../utils/is-vulnerable');
|
|
20
|
+
const { isSafeContentType } = require('../utils/is-safe-content-type');
|
|
21
|
+
|
|
22
|
+
module.exports = function (core) {
|
|
23
|
+
const {
|
|
24
|
+
messages
|
|
25
|
+
} = core;
|
|
26
|
+
|
|
27
|
+
const sinks = core.assess.dataflow.sinks = {
|
|
28
|
+
isVulnerable,
|
|
29
|
+
isSafeContentType,
|
|
30
|
+
reportFindings(data) {
|
|
31
|
+
messages.emit(Event.ASSESS_DATAFLOW_FINDING, data);
|
|
32
|
+
},
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
require('./install/http')(core);
|
|
36
|
+
require('./install/postgres')(core);
|
|
37
|
+
require('./install/fastify')(core);
|
|
38
|
+
|
|
39
|
+
sinks.install = function() {
|
|
40
|
+
callChildComponentMethodsSync(core.assess.dataflow.sinks, 'install');
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
return sinks;
|
|
44
|
+
};
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright: 2022 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 { callChildComponentMethodsSync } = require('@contrast/common');
|
|
19
|
+
|
|
20
|
+
module.exports = function(core) {
|
|
21
|
+
const fastify = core.assess.dataflow.sinks.fastify = {};
|
|
22
|
+
|
|
23
|
+
require('./unvalidated-redirect')(core);
|
|
24
|
+
|
|
25
|
+
fastify.install = function() {
|
|
26
|
+
callChildComponentMethodsSync(fastify, 'install');
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
return fastify;
|
|
30
|
+
};
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright: 2022 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 { isString } = require('@contrast/common');
|
|
19
|
+
const { createModuleLabel } = require('../../../propagation/common');
|
|
20
|
+
const { patchType } = require('../../common');
|
|
21
|
+
|
|
22
|
+
module.exports = function (core) {
|
|
23
|
+
const {
|
|
24
|
+
depHooks,
|
|
25
|
+
patcher,
|
|
26
|
+
scopes: { sources },
|
|
27
|
+
assess: {
|
|
28
|
+
dataflow: {
|
|
29
|
+
tracker,
|
|
30
|
+
sinks: { isVulnerable, reportFindings },
|
|
31
|
+
eventFactory: { createSinkEvent },
|
|
32
|
+
},
|
|
33
|
+
},
|
|
34
|
+
} = core;
|
|
35
|
+
const unvalidatedRedirect =
|
|
36
|
+
(core.assess.dataflow.sinks.fastify.unvalidatedRedirect = {});
|
|
37
|
+
|
|
38
|
+
const safeTags = [
|
|
39
|
+
'limited-chars',
|
|
40
|
+
'url-encoded',
|
|
41
|
+
'html-encoded',
|
|
42
|
+
'custom-validated',
|
|
43
|
+
'custom-encoded'
|
|
44
|
+
];
|
|
45
|
+
const requiredTag = 'untrusted';
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Patches `Reply.prototype.redirect` for
|
|
49
|
+
* `unvalidated-redirect` checking
|
|
50
|
+
*
|
|
51
|
+
* @param {Fastify.Reply} Reply outgoing response
|
|
52
|
+
*/
|
|
53
|
+
const registerUnvalidatedRedirectHandler = (Reply, version) => {
|
|
54
|
+
patcher.patch(Reply.prototype, 'redirect', {
|
|
55
|
+
name: 'fastify.Reply.prototype.redirect',
|
|
56
|
+
patchType,
|
|
57
|
+
post(data) {
|
|
58
|
+
const assessStore = sources.getStore()?.assess;
|
|
59
|
+
if (!assessStore) return;
|
|
60
|
+
|
|
61
|
+
const [code] = data.args;
|
|
62
|
+
// url can be first or second argument
|
|
63
|
+
const url = typeof code === 'string' ? code : data.args[1];
|
|
64
|
+
|
|
65
|
+
if (!url || !isString(url)) return;
|
|
66
|
+
|
|
67
|
+
const strInfo = tracker.getData(url);
|
|
68
|
+
if (!strInfo) return;
|
|
69
|
+
|
|
70
|
+
if (isVulnerable(requiredTag, safeTags, strInfo.tags)) {
|
|
71
|
+
const event = createSinkEvent({
|
|
72
|
+
name: 'fastify.reply.redirect',
|
|
73
|
+
object: `[${createModuleLabel('fastify', version)}].Reply`,
|
|
74
|
+
history: [strInfo],
|
|
75
|
+
args: [{
|
|
76
|
+
value: strInfo.value,
|
|
77
|
+
isTracked: true
|
|
78
|
+
}],
|
|
79
|
+
result: {
|
|
80
|
+
value: url,
|
|
81
|
+
isTracked: true,
|
|
82
|
+
},
|
|
83
|
+
tags: strInfo.tags,
|
|
84
|
+
source: 'P0',
|
|
85
|
+
stacktraceOpts: {
|
|
86
|
+
constructorOpt: data.hooked,
|
|
87
|
+
},
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
reportFindings({
|
|
91
|
+
ruleId: 'unvalidated-redirect',
|
|
92
|
+
metadata: event,
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
},
|
|
96
|
+
});
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
unvalidatedRedirect.install = function () {
|
|
100
|
+
depHooks.resolve(
|
|
101
|
+
{ name: 'fastify', file: 'lib/reply' },
|
|
102
|
+
registerUnvalidatedRedirectHandler
|
|
103
|
+
);
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
return unvalidatedRedirect;
|
|
107
|
+
};
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright: 2022 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 { Rule } = require('@contrast/common');
|
|
19
|
+
const { createObjectLabel } = require('../../propagation/common');
|
|
20
|
+
const { patchType } = require('../common');
|
|
21
|
+
|
|
22
|
+
module.exports = function(core) {
|
|
23
|
+
const {
|
|
24
|
+
depHooks,
|
|
25
|
+
patcher,
|
|
26
|
+
scopes: { sources },
|
|
27
|
+
assess: {
|
|
28
|
+
dataflow: {
|
|
29
|
+
tracker,
|
|
30
|
+
sinks: { isVulnerable, reportFindings, isSafeContentType },
|
|
31
|
+
eventFactory: { createSinkEvent },
|
|
32
|
+
},
|
|
33
|
+
},
|
|
34
|
+
} = core;
|
|
35
|
+
const http = core.assess.dataflow.sinks.http = {};
|
|
36
|
+
|
|
37
|
+
const safeTags = [
|
|
38
|
+
'alphanum-space-hyphen',
|
|
39
|
+
'cookie',
|
|
40
|
+
'header',
|
|
41
|
+
'limited-chars',
|
|
42
|
+
'html-encoded',
|
|
43
|
+
'sql-encoded',
|
|
44
|
+
'url-encoded',
|
|
45
|
+
'weak-url-encoded',
|
|
46
|
+
'custom-validated',
|
|
47
|
+
'custom-encoded'
|
|
48
|
+
];
|
|
49
|
+
const requiredTag = 'untrusted';
|
|
50
|
+
|
|
51
|
+
const preHook = (name) => (data) => {
|
|
52
|
+
const sourceContext = sources.getStore()?.assess;
|
|
53
|
+
if (!sourceContext) return;
|
|
54
|
+
|
|
55
|
+
const payload = data.args[0];
|
|
56
|
+
if (!payload) return;
|
|
57
|
+
|
|
58
|
+
const strInfo = tracker.getData(payload);
|
|
59
|
+
if (!strInfo) return;
|
|
60
|
+
|
|
61
|
+
const { contentType } = sourceContext.responseData;
|
|
62
|
+
if (contentType && isSafeContentType(contentType)) return;
|
|
63
|
+
|
|
64
|
+
if (isVulnerable(requiredTag, safeTags, strInfo.tags)) {
|
|
65
|
+
const event = createSinkEvent({
|
|
66
|
+
ruleId: Rule.REFLECTED_XSS,
|
|
67
|
+
name,
|
|
68
|
+
history: [strInfo],
|
|
69
|
+
object: {
|
|
70
|
+
isTracked: false,
|
|
71
|
+
value: createObjectLabel('http.ServerResponse')
|
|
72
|
+
},
|
|
73
|
+
args: [{
|
|
74
|
+
value: strInfo.value,
|
|
75
|
+
isTracked: true
|
|
76
|
+
}],
|
|
77
|
+
result: {
|
|
78
|
+
value: null,
|
|
79
|
+
isTracked: false,
|
|
80
|
+
},
|
|
81
|
+
tags: strInfo.tags,
|
|
82
|
+
source: 'P0',
|
|
83
|
+
stacktraceOpts: {
|
|
84
|
+
constructorOpt: data.hooked
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
if (event) {
|
|
89
|
+
reportFindings({
|
|
90
|
+
ruleId: 'reflected-xss',
|
|
91
|
+
metadata: event
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
http.install = function() {
|
|
98
|
+
depHooks.resolve({ name: 'http' }, (http) => {
|
|
99
|
+
{
|
|
100
|
+
const name = 'http.ServerResponse.prototype.write';
|
|
101
|
+
patcher.patch(http.ServerResponse.prototype, 'write', {
|
|
102
|
+
name,
|
|
103
|
+
patchType,
|
|
104
|
+
pre: preHook(name),
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
{
|
|
108
|
+
const name = 'http.ServerResponse.prototype.end';
|
|
109
|
+
patcher.patch(http.ServerResponse.prototype, 'end', {
|
|
110
|
+
name,
|
|
111
|
+
patchType,
|
|
112
|
+
pre: preHook(name),
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
});
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
return http;
|
|
119
|
+
};
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright: 2022 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 { isString } = require('@contrast/common');
|
|
19
|
+
const { patchType } = require('../../common');
|
|
20
|
+
const util = require('util');
|
|
21
|
+
|
|
22
|
+
module.exports = function (core) {
|
|
23
|
+
const {
|
|
24
|
+
depHooks,
|
|
25
|
+
patcher,
|
|
26
|
+
scopes: { sources },
|
|
27
|
+
assess: {
|
|
28
|
+
dataflow: {
|
|
29
|
+
tracker,
|
|
30
|
+
sinks: { isVulnerable, reportFindings },
|
|
31
|
+
eventFactory: { createSinkEvent },
|
|
32
|
+
},
|
|
33
|
+
},
|
|
34
|
+
} = core;
|
|
35
|
+
|
|
36
|
+
const postgres = core.assess.dataflow.sinks.postgres = {};
|
|
37
|
+
|
|
38
|
+
const safeTags = [
|
|
39
|
+
'sql-encoded',
|
|
40
|
+
'limited-chars',
|
|
41
|
+
'custom-validated',
|
|
42
|
+
'custom-encoded',
|
|
43
|
+
];
|
|
44
|
+
const requiredTag = 'untrusted';
|
|
45
|
+
|
|
46
|
+
const preHook = (methodSignature) => (data) => {
|
|
47
|
+
const assessStore = sources.getStore()?.assess;
|
|
48
|
+
if (!assessStore) return;
|
|
49
|
+
|
|
50
|
+
const query = data.args[0]?.text || data.args[0];
|
|
51
|
+
if (!query || !isString(query)) return;
|
|
52
|
+
|
|
53
|
+
const strInfo = tracker.getData(query);
|
|
54
|
+
if (!strInfo) return;
|
|
55
|
+
|
|
56
|
+
if (isVulnerable(requiredTag, safeTags, strInfo.tags)) {
|
|
57
|
+
const event = createSinkEvent({
|
|
58
|
+
name: methodSignature,
|
|
59
|
+
history: [strInfo],
|
|
60
|
+
args: [
|
|
61
|
+
{
|
|
62
|
+
value: util.inspect(data.args[0]),
|
|
63
|
+
isTracked: true,
|
|
64
|
+
},
|
|
65
|
+
],
|
|
66
|
+
result: {
|
|
67
|
+
value: data.result,
|
|
68
|
+
isTracked: false,
|
|
69
|
+
},
|
|
70
|
+
tags: strInfo.tags,
|
|
71
|
+
source: 'P0',
|
|
72
|
+
stacktraceOpts: {
|
|
73
|
+
constructorOpt: data.hooked,
|
|
74
|
+
},
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
reportFindings({
|
|
78
|
+
ruleId: 'sql-injection',
|
|
79
|
+
metadata: event,
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
postgres.install = function () {
|
|
85
|
+
const pgClientQueryPatchName = 'pg.Client.prototype.query';
|
|
86
|
+
depHooks.resolve({ name: 'pg', file: 'lib/client.js' }, client => {
|
|
87
|
+
patcher.patch(client.prototype, 'query', {
|
|
88
|
+
name: pgClientQueryPatchName,
|
|
89
|
+
patchType,
|
|
90
|
+
pre: preHook('pg/lib/client.prototype.query'),
|
|
91
|
+
});
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
const pgNativeClientQueryPatchName = 'pg.native.Client.prototype.query';
|
|
95
|
+
depHooks.resolve({ name: 'pg', file: 'lib/native/client.js' }, (client) => {
|
|
96
|
+
patcher.patch(client.prototype, 'query', {
|
|
97
|
+
name: pgNativeClientQueryPatchName,
|
|
98
|
+
patchType,
|
|
99
|
+
pre: preHook('pg/lib/native/client.prototype.query'),
|
|
100
|
+
});
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
const pgClientPatchName = `${patchType}:${pgClientQueryPatchName}.query`;
|
|
104
|
+
const pgNativeClientPatchName = `${patchType}:${pgNativeClientQueryPatchName}.query`;
|
|
105
|
+
depHooks.resolve({ name: 'pg-pool' }, (pool) => {
|
|
106
|
+
const name = 'pg-pool.Pool.prototype.query';
|
|
107
|
+
patcher.patch(pool.prototype, 'query', {
|
|
108
|
+
name,
|
|
109
|
+
patchType,
|
|
110
|
+
pre: (data) => {
|
|
111
|
+
const funcKeys = patcher.hooks.get(
|
|
112
|
+
data.obj.Client?.prototype?.query
|
|
113
|
+
)?.funcKeys;
|
|
114
|
+
|
|
115
|
+
if (
|
|
116
|
+
funcKeys &&
|
|
117
|
+
(funcKeys.has(pgClientPatchName) || funcKeys.has(pgNativeClientPatchName))
|
|
118
|
+
) {
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
preHook(name)(data);
|
|
123
|
+
},
|
|
124
|
+
});
|
|
125
|
+
});
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
return postgres;
|
|
129
|
+
};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright: 2022 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
|
+
module.exports = {
|
|
19
|
+
patchType: 'assess-dataflow-source',
|
|
20
|
+
};
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright: 2022 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 { isString, traverseValues } = require('@contrast/common');
|
|
19
|
+
|
|
20
|
+
module.exports = function(core) {
|
|
21
|
+
const {
|
|
22
|
+
assess: {
|
|
23
|
+
dataflow: {
|
|
24
|
+
sources,
|
|
25
|
+
tracker,
|
|
26
|
+
eventFactory: { createSourceEvent }
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
createSnapshot,
|
|
30
|
+
config,
|
|
31
|
+
logger,
|
|
32
|
+
} = core;
|
|
33
|
+
|
|
34
|
+
const emptyStack = Object.freeze([]);
|
|
35
|
+
|
|
36
|
+
sources.createTags = function createTags({ inputType, key, value }) {
|
|
37
|
+
if (!value?.length) {
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const stop = value.length - 1;
|
|
42
|
+
const tags = {
|
|
43
|
+
untrusted: [0, stop]
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
if (inputType === 'header' && key.toLowerCase() === 'referer') {
|
|
47
|
+
tags.header = [0, stop];
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
if (inputType === 'cookie') {
|
|
51
|
+
tags.cookie = [0, stop];
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return tags;
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
sources.handle = function({
|
|
58
|
+
name,
|
|
59
|
+
inputType = 'unknown',
|
|
60
|
+
stacktraceOpts,
|
|
61
|
+
data,
|
|
62
|
+
}) {
|
|
63
|
+
if (!data) return;
|
|
64
|
+
|
|
65
|
+
const max = config.assess.max_context_source_events;
|
|
66
|
+
const sourceContext = core.scopes.sources.getStore()?.assess;
|
|
67
|
+
|
|
68
|
+
if (!sourceContext) {
|
|
69
|
+
core.logger.trace({ inputType, name }, 'skipping assess source handling - no request context');
|
|
70
|
+
return null;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// todo: NODE-2789
|
|
74
|
+
const stack = config.assess.stacktraces === 'NONE'
|
|
75
|
+
? emptyStack
|
|
76
|
+
: createSnapshot(stacktraceOpts)();
|
|
77
|
+
|
|
78
|
+
traverseValues(data, (path, type, value, obj) => {
|
|
79
|
+
if (sourceContext.sourceEventsCount >= max) {
|
|
80
|
+
core.logger.trace({ inputType, name }, 'exiting assess source handling - %s max events exceeded', max);
|
|
81
|
+
return true;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
if (isString(value) && value.length) {
|
|
85
|
+
const key = path[path.length - 1];
|
|
86
|
+
const pathName = path.join('.');
|
|
87
|
+
const event = createSourceEvent({
|
|
88
|
+
name,
|
|
89
|
+
pathName,
|
|
90
|
+
stack,
|
|
91
|
+
inputType,
|
|
92
|
+
tags: sources.createTags({ inputType, key, value }),
|
|
93
|
+
result: { tracked: true, value },
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
if (!event) {
|
|
97
|
+
core.logger.warn({ inputType, name, pathName, value }, 'unable to create source event');
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const { extern } = tracker.track(value, event);
|
|
102
|
+
if (extern) {
|
|
103
|
+
logger.trace({ extern, key, inputType }, 'tracked');
|
|
104
|
+
obj[key] = extern;
|
|
105
|
+
sourceContext.sourceEventsCount++;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
return data;
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
return sources;
|
|
114
|
+
};
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright: 2022 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 { callChildComponentMethodsSync } = require('@contrast/common');
|
|
19
|
+
|
|
20
|
+
module.exports = function(core) {
|
|
21
|
+
const sources = core.assess.dataflow.sources = {};
|
|
22
|
+
|
|
23
|
+
// API
|
|
24
|
+
require('./handler')(core);
|
|
25
|
+
// installers
|
|
26
|
+
require('./install/http')(core);
|
|
27
|
+
require('./install/fastify')(core);
|
|
28
|
+
require('./install/qs')(core);
|
|
29
|
+
|
|
30
|
+
sources.install = function install() {
|
|
31
|
+
callChildComponentMethodsSync(sources, 'install');
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
return sources;
|
|
35
|
+
};
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright: 2022 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 { InputType } = require('@contrast/common');
|
|
19
|
+
const { patchType } = require('../../common');
|
|
20
|
+
|
|
21
|
+
module.exports = function(core) {
|
|
22
|
+
const {
|
|
23
|
+
depHooks,
|
|
24
|
+
patcher,
|
|
25
|
+
assess: { dataflow: { sources } },
|
|
26
|
+
} = core;
|
|
27
|
+
|
|
28
|
+
const source = sources.fastifyInstrumentation.cookie = {
|
|
29
|
+
install() {
|
|
30
|
+
[
|
|
31
|
+
'@fastify/cookie',
|
|
32
|
+
'fastify-cookie'
|
|
33
|
+
].forEach((moduleName) => {
|
|
34
|
+
depHooks.resolve({ name: moduleName }, (lib) => patcher.patch(lib, {
|
|
35
|
+
name: `${moduleName}.constructor`,
|
|
36
|
+
patchType,
|
|
37
|
+
post({ args: [server] }) {
|
|
38
|
+
const name = 'fastifyServer.parseCookie';
|
|
39
|
+
patcher.patch(server, 'parseCookie', {
|
|
40
|
+
name,
|
|
41
|
+
patchType,
|
|
42
|
+
post({ hooked, orig, result: cookies }) {
|
|
43
|
+
sources.handle({
|
|
44
|
+
name,
|
|
45
|
+
inputType: InputType.COOKIE_VALUE,
|
|
46
|
+
stacktraceOpts: {
|
|
47
|
+
constructorOpt: hooked,
|
|
48
|
+
prependFrames: [orig]
|
|
49
|
+
},
|
|
50
|
+
data: cookies,
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
}));
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
return source;
|
|
61
|
+
};
|