@contrast/assess 1.8.0 → 1.9.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/dataflow/event-factory.js +17 -13
- package/lib/dataflow/propagation/index.js +3 -0
- package/lib/dataflow/propagation/install/JSON/index.js +1 -0
- package/lib/dataflow/propagation/install/JSON/parse-fn.js +248 -0
- package/lib/dataflow/propagation/install/JSON/parse.js +196 -0
- package/lib/dataflow/propagation/install/JSON/stringify.js +5 -3
- package/lib/dataflow/propagation/install/array-prototype-join.js +21 -14
- package/lib/dataflow/propagation/install/buffer.js +2 -0
- package/lib/dataflow/propagation/install/contrast-methods/add.js +2 -0
- package/lib/dataflow/propagation/install/contrast-methods/index.js +1 -0
- package/lib/dataflow/propagation/install/contrast-methods/number.js +58 -0
- package/lib/dataflow/propagation/install/contrast-methods/string.js +53 -6
- package/lib/dataflow/propagation/install/contrast-methods/tag.js +3 -1
- package/lib/dataflow/propagation/install/decode-uri-component.js +9 -2
- package/lib/dataflow/propagation/install/ejs/escape-xml.js +8 -2
- package/lib/dataflow/propagation/install/encode-uri-component.js +9 -2
- package/lib/dataflow/propagation/install/escape-html.js +13 -5
- package/lib/dataflow/propagation/install/escape.js +9 -2
- package/lib/dataflow/propagation/install/handlebars-utils-escape-expression.js +11 -4
- package/lib/dataflow/propagation/install/isnumeric-0.js +59 -0
- package/lib/dataflow/propagation/install/mongoose/index.js +36 -0
- package/lib/dataflow/propagation/install/mongoose/schema-string.js +156 -0
- package/lib/dataflow/propagation/install/mysql-connection-escape.js +5 -0
- package/lib/dataflow/propagation/install/parse-int.js +60 -0
- package/lib/dataflow/propagation/install/pug-runtime-escape.js +9 -2
- package/lib/dataflow/propagation/install/querystring/parse.js +11 -9
- package/lib/dataflow/propagation/install/sequelize.js +6 -3
- package/lib/dataflow/propagation/install/sql-template-strings.js +9 -2
- package/lib/dataflow/propagation/install/string/concat.js +8 -2
- package/lib/dataflow/propagation/install/string/format-methods.js +7 -2
- package/lib/dataflow/propagation/install/string/html-methods.js +15 -5
- package/lib/dataflow/propagation/install/string/match.js +14 -9
- package/lib/dataflow/propagation/install/string/replace.js +22 -14
- package/lib/dataflow/propagation/install/string/slice.js +13 -5
- package/lib/dataflow/propagation/install/string/split.js +15 -11
- package/lib/dataflow/propagation/install/string/substring.js +16 -6
- package/lib/dataflow/propagation/install/string/trim.js +3 -0
- package/lib/dataflow/propagation/install/unescape.js +9 -2
- package/lib/dataflow/propagation/install/url/domain-parsers.js +7 -2
- package/lib/dataflow/propagation/install/validator/hooks.js +6 -2
- package/lib/dataflow/sinks/install/child-process.js +116 -50
- package/lib/dataflow/sinks/install/express/unvalidated-redirect.js +6 -3
- package/lib/dataflow/sinks/install/fastify/unvalidated-redirect.js +7 -4
- package/lib/dataflow/sinks/install/fs.js +44 -12
- package/lib/dataflow/sinks/install/http.js +5 -2
- package/lib/dataflow/sinks/install/koa/unvalidated-redirect.js +7 -4
- package/lib/dataflow/sinks/install/marsdb.js +3 -0
- package/lib/dataflow/sinks/install/mongodb.js +3 -1
- package/lib/dataflow/sinks/install/mssql.js +9 -2
- package/lib/dataflow/sinks/install/mysql.js +9 -4
- package/lib/dataflow/sinks/install/postgres.js +6 -3
- package/lib/dataflow/sinks/install/sequelize.js +7 -5
- package/lib/dataflow/sinks/install/sqlite3.js +7 -3
- package/lib/dataflow/sources/handler.js +2 -1
- package/lib/dataflow/sources/install/http.js +1 -1
- package/lib/dataflow/tag-utils.js +25 -1
- package/lib/dataflow/tracker.js +6 -6
- package/lib/index.js +2 -0
- package/lib/response-scanning/handlers/utils.js +2 -2
- package/lib/session-configuration/index.js +34 -0
- package/lib/session-configuration/install/http.js +79 -0
- package/package.json +2 -2
|
@@ -28,6 +28,7 @@ const {
|
|
|
28
28
|
LIMITED_CHARS,
|
|
29
29
|
UNTRUSTED
|
|
30
30
|
},
|
|
31
|
+
inspect
|
|
31
32
|
} = require('@contrast/common');
|
|
32
33
|
|
|
33
34
|
const safeTags = [
|
|
@@ -39,7 +40,7 @@ const safeTags = [
|
|
|
39
40
|
LIMITED_CHARS,
|
|
40
41
|
];
|
|
41
42
|
|
|
42
|
-
module.exports = function
|
|
43
|
+
module.exports = function(core) {
|
|
43
44
|
const {
|
|
44
45
|
depHooks,
|
|
45
46
|
patcher,
|
|
@@ -63,7 +64,7 @@ module.exports = function (core) {
|
|
|
63
64
|
}
|
|
64
65
|
}
|
|
65
66
|
|
|
66
|
-
const pre = (module, file, obj) => (data) => {
|
|
67
|
+
const pre = (module, file, obj, method) => (data) => {
|
|
67
68
|
const store = sources.getStore()?.assess;
|
|
68
69
|
if (
|
|
69
70
|
!store ||
|
|
@@ -81,6 +82,9 @@ module.exports = function (core) {
|
|
|
81
82
|
|
|
82
83
|
const event = createSinkEvent({
|
|
83
84
|
name: `${module}/${file}`,
|
|
85
|
+
moduleName: module,
|
|
86
|
+
methodName: `prototype.${method}`,
|
|
87
|
+
context: `${module}.${method}(${inspect(data.args[0])})`,
|
|
84
88
|
history: [strInfo],
|
|
85
89
|
object: {
|
|
86
90
|
value: `${module}.${obj}`,
|
|
@@ -96,6 +100,7 @@ module.exports = function (core) {
|
|
|
96
100
|
source: 'P0',
|
|
97
101
|
stacktraceOpts: {
|
|
98
102
|
contructorOpt: data.hooked,
|
|
103
|
+
prependFrames: [data.orig]
|
|
99
104
|
},
|
|
100
105
|
});
|
|
101
106
|
|
|
@@ -115,7 +120,7 @@ module.exports = function (core) {
|
|
|
115
120
|
patcher.patch(Connection.prototype, 'query', {
|
|
116
121
|
name: 'Connection.prototype.query',
|
|
117
122
|
patchType,
|
|
118
|
-
pre: pre('mysql', 'lib/Connection.query', 'Connection')
|
|
123
|
+
pre: pre('mysql', 'lib/Connection.query', 'Connection', 'query')
|
|
119
124
|
});
|
|
120
125
|
},
|
|
121
126
|
);
|
|
@@ -126,7 +131,7 @@ module.exports = function (core) {
|
|
|
126
131
|
patcher.patch(connection.prototype, `${method}`, {
|
|
127
132
|
name: `connection.prototype.${method}`,
|
|
128
133
|
patchType,
|
|
129
|
-
pre: pre('mysql2', `lib/connection.Connection.${method}`, 'connection')
|
|
134
|
+
pre: pre('mysql2', `lib/connection.Connection.${method}`, 'connection', method)
|
|
130
135
|
});
|
|
131
136
|
});
|
|
132
137
|
},
|
|
@@ -19,11 +19,11 @@ const util = require('util');
|
|
|
19
19
|
const {
|
|
20
20
|
DataflowTag: { UNTRUSTED, SQL_ENCODED, LIMITED_CHARS, CUSTOM_VALIDATED, CUSTOM_ENCODED },
|
|
21
21
|
Rule: { SQL_INJECTION: ruleId },
|
|
22
|
-
isString
|
|
22
|
+
isString,
|
|
23
23
|
} = require('@contrast/common');
|
|
24
24
|
const { filterSafeTags, patchType } = require('../common');
|
|
25
25
|
|
|
26
|
-
module.exports = function
|
|
26
|
+
module.exports = function(core) {
|
|
27
27
|
const {
|
|
28
28
|
config,
|
|
29
29
|
depHooks,
|
|
@@ -74,6 +74,8 @@ module.exports = function (core) {
|
|
|
74
74
|
context: `${objValue}.query(${arg0Val})`,
|
|
75
75
|
history: [strInfo],
|
|
76
76
|
name: methodSignature,
|
|
77
|
+
moduleName: `${methodSignature.includes('pool') ? 'pg-pool' : 'pg'}`,
|
|
78
|
+
methodName: 'Connection.prototype.query',
|
|
77
79
|
object: {
|
|
78
80
|
tracked: false,
|
|
79
81
|
value: objValue,
|
|
@@ -86,6 +88,7 @@ module.exports = function (core) {
|
|
|
86
88
|
source: 'P0',
|
|
87
89
|
stacktraceOpts: {
|
|
88
90
|
constructorOpt: data.hooked,
|
|
91
|
+
prependFrames: [data.orig]
|
|
89
92
|
},
|
|
90
93
|
});
|
|
91
94
|
|
|
@@ -108,7 +111,7 @@ module.exports = function (core) {
|
|
|
108
111
|
}
|
|
109
112
|
};
|
|
110
113
|
|
|
111
|
-
postgres.install = function
|
|
114
|
+
postgres.install = function() {
|
|
112
115
|
const pgClientQueryPatchName = 'pg.Client.prototype.query';
|
|
113
116
|
depHooks.resolve(
|
|
114
117
|
{ name: 'pg', file: 'lib/client.js' },
|
|
@@ -28,7 +28,7 @@ const {
|
|
|
28
28
|
} = require('@contrast/common');
|
|
29
29
|
const { patchType, filterSafeTags } = require('../common');
|
|
30
30
|
|
|
31
|
-
module.exports = function
|
|
31
|
+
module.exports = function(core) {
|
|
32
32
|
const {
|
|
33
33
|
depHooks,
|
|
34
34
|
patcher,
|
|
@@ -54,7 +54,7 @@ module.exports = function (core) {
|
|
|
54
54
|
|
|
55
55
|
const sequelize = (core.assess.dataflow.sinks.sequelize = {});
|
|
56
56
|
|
|
57
|
-
sequelize.install = function
|
|
57
|
+
sequelize.install = function() {
|
|
58
58
|
const sequelizeQueryPatchName = 'sequelize.prototype.query';
|
|
59
59
|
depHooks.resolve({ name: 'sequelize' }, (sequelize) => {
|
|
60
60
|
patcher.patch(sequelize.prototype, 'query', {
|
|
@@ -69,7 +69,7 @@ module.exports = function (core) {
|
|
|
69
69
|
|
|
70
70
|
try {
|
|
71
71
|
const queryInfo = tracker.getData(query);
|
|
72
|
-
const isVulnerableQuery = isVulnerable(requiredTag, safeTags, queryInfo.tags);
|
|
72
|
+
const isVulnerableQuery = queryInfo && isVulnerable(requiredTag, safeTags, queryInfo.tags);
|
|
73
73
|
|
|
74
74
|
if (queryInfo && !isVulnerableQuery && config.assess.safe_positives.enable) {
|
|
75
75
|
reportSafePositive({
|
|
@@ -94,8 +94,8 @@ module.exports = function (core) {
|
|
|
94
94
|
typeof args[0] === 'string' ? args[0] : inspect(args[0]);
|
|
95
95
|
const inspectedOptions = args[1] ? inspect(args[1]) : '';
|
|
96
96
|
const contextArgs = args[1]
|
|
97
|
-
?
|
|
98
|
-
: sqlValue
|
|
97
|
+
? `'${sqlValue}', ${inspectedOptions}`
|
|
98
|
+
: `'${sqlValue}'`;
|
|
99
99
|
|
|
100
100
|
const reportedArgs = [{ value: sqlValue, tracked: true }];
|
|
101
101
|
args[1] &&
|
|
@@ -104,6 +104,8 @@ module.exports = function (core) {
|
|
|
104
104
|
const event = createSinkEvent({
|
|
105
105
|
context: `sequelize.prototype.query(${contextArgs})`,
|
|
106
106
|
name: sequelizeQueryPatchName,
|
|
107
|
+
moduleName: 'sequelize',
|
|
108
|
+
methodName: 'prototype.query',
|
|
107
109
|
history: [queryInfo],
|
|
108
110
|
object: {
|
|
109
111
|
value: 'sequelize.prototype',
|
|
@@ -29,7 +29,7 @@ const safeTags = [
|
|
|
29
29
|
CUSTOM_ENCODED,
|
|
30
30
|
];
|
|
31
31
|
|
|
32
|
-
module.exports = function
|
|
32
|
+
module.exports = function(core) {
|
|
33
33
|
const {
|
|
34
34
|
depHooks,
|
|
35
35
|
patcher,
|
|
@@ -43,7 +43,7 @@ module.exports = function (core) {
|
|
|
43
43
|
},
|
|
44
44
|
} = core;
|
|
45
45
|
|
|
46
|
-
const pre = (name) => (data) => {
|
|
46
|
+
const pre = (name, method) => (data) => {
|
|
47
47
|
const store = sources.getStore()?.assess;
|
|
48
48
|
if (
|
|
49
49
|
!store ||
|
|
@@ -59,6 +59,9 @@ module.exports = function (core) {
|
|
|
59
59
|
|
|
60
60
|
const event = createSinkEvent({
|
|
61
61
|
name,
|
|
62
|
+
moduleName: 'sqlite3',
|
|
63
|
+
methodName: `Database.prototype.${method}`,
|
|
64
|
+
context: `db.${method}('${strInfo.value}')`,
|
|
62
65
|
history: [strInfo],
|
|
63
66
|
object: {
|
|
64
67
|
value: '[Module<sqlite3>].Database',
|
|
@@ -74,6 +77,7 @@ module.exports = function (core) {
|
|
|
74
77
|
source: 'P0',
|
|
75
78
|
stacktraceOpts: {
|
|
76
79
|
contructorOpt: data.hooked,
|
|
80
|
+
prependFrames: [data.orig]
|
|
77
81
|
},
|
|
78
82
|
});
|
|
79
83
|
|
|
@@ -93,7 +97,7 @@ module.exports = function (core) {
|
|
|
93
97
|
patcher.patch(sqlite3.Database.prototype, method, {
|
|
94
98
|
name,
|
|
95
99
|
patchType,
|
|
96
|
-
pre: pre(name)
|
|
100
|
+
pre: pre(name, method)
|
|
97
101
|
});
|
|
98
102
|
});
|
|
99
103
|
});
|
|
@@ -19,6 +19,7 @@ const {
|
|
|
19
19
|
InputType,
|
|
20
20
|
DataflowTag,
|
|
21
21
|
isString,
|
|
22
|
+
join,
|
|
22
23
|
} = require('@contrast/common');
|
|
23
24
|
|
|
24
25
|
module.exports = function(core) {
|
|
@@ -115,7 +116,7 @@ module.exports = function(core) {
|
|
|
115
116
|
}
|
|
116
117
|
|
|
117
118
|
traverse(_data, (path, fieldName, value, obj) => {
|
|
118
|
-
const pathName =
|
|
119
|
+
const pathName = join(path, '.');
|
|
119
120
|
|
|
120
121
|
if (sourceContext.sourceEventsCount >= max) {
|
|
121
122
|
core.logger.trace({ inputType, name }, 'exiting assess source handling - %s max events exceeded', max);
|
|
@@ -80,7 +80,7 @@ module.exports = function(core) {
|
|
|
80
80
|
pre(data) {
|
|
81
81
|
const [name = '', value] = data.args;
|
|
82
82
|
if (toLowerCase(name) === 'content-type' && value) {
|
|
83
|
-
|
|
83
|
+
scopes.sources.getStore().assess.responseData.contentType = value;
|
|
84
84
|
}
|
|
85
85
|
}
|
|
86
86
|
});
|
|
@@ -134,6 +134,29 @@ function createAppendTags(firstTags, secondTags, offset) {
|
|
|
134
134
|
return Object.keys(ret).length ? ret : null;
|
|
135
135
|
}
|
|
136
136
|
|
|
137
|
+
function createOverlappingTags(tags, startIndex, endIndex) {
|
|
138
|
+
const overlappingTags = {};
|
|
139
|
+
|
|
140
|
+
Object.entries(tags).forEach(([tag, tagRanges]) => {
|
|
141
|
+
const overlappingRanges = [];
|
|
142
|
+
|
|
143
|
+
for (let i = 0; i < tagRanges.length; i += 2) {
|
|
144
|
+
const start = tagRanges[i];
|
|
145
|
+
const end = tagRanges[i + 1];
|
|
146
|
+
|
|
147
|
+
if (end >= startIndex && start <= endIndex) {
|
|
148
|
+
overlappingRanges.push([start, end]);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
if (overlappingRanges.length > 0) {
|
|
153
|
+
overlappingTags[tag] = overlappingRanges;
|
|
154
|
+
}
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
return overlappingTags;
|
|
158
|
+
}
|
|
159
|
+
|
|
137
160
|
/**
|
|
138
161
|
* assumes:
|
|
139
162
|
* - no mutation of arguments
|
|
@@ -185,5 +208,6 @@ module.exports = {
|
|
|
185
208
|
createSubsetTags,
|
|
186
209
|
createAppendTags,
|
|
187
210
|
createFullLengthCopyTags,
|
|
188
|
-
createMergedTags
|
|
211
|
+
createMergedTags,
|
|
212
|
+
createOverlappingTags
|
|
189
213
|
};
|
package/lib/dataflow/tracker.js
CHANGED
|
@@ -34,7 +34,8 @@ module.exports = function tracker(core) {
|
|
|
34
34
|
|
|
35
35
|
function getData(value) {
|
|
36
36
|
if (typeof value === 'string') {
|
|
37
|
-
|
|
37
|
+
const props = distringuish.getProperties(value);
|
|
38
|
+
return props?.untracked ? null : props;
|
|
38
39
|
}
|
|
39
40
|
|
|
40
41
|
return objMap.get(value) || null;
|
|
@@ -126,11 +127,10 @@ module.exports = function tracker(core) {
|
|
|
126
127
|
if (typeof value === 'string') {
|
|
127
128
|
const props = distringuish.getProperties(value);
|
|
128
129
|
if (props) {
|
|
129
|
-
Object.
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
delete props.resultTracked;
|
|
130
|
+
for (const key of Object.keys(props)) {
|
|
131
|
+
delete props[key];
|
|
132
|
+
}
|
|
133
|
+
props.untracked = true;
|
|
134
134
|
}
|
|
135
135
|
return distringuish.internalize(value);
|
|
136
136
|
}
|
package/lib/index.js
CHANGED
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
'use strict';
|
|
17
17
|
|
|
18
18
|
const { callChildComponentMethodsSync } = require('@contrast/common');
|
|
19
|
+
const sessionConfiguration = require('./session-configuration');
|
|
19
20
|
const dataflow = require('./dataflow');
|
|
20
21
|
const responseScanning = require('./response-scanning');
|
|
21
22
|
|
|
@@ -26,6 +27,7 @@ module.exports = function assess(core) {
|
|
|
26
27
|
|
|
27
28
|
// Does this order matter? Probably not
|
|
28
29
|
// 1. dataflow
|
|
30
|
+
sessionConfiguration(core);
|
|
29
31
|
dataflow(core);
|
|
30
32
|
responseScanning(core);
|
|
31
33
|
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
|
|
16
16
|
'use strict';
|
|
17
17
|
|
|
18
|
-
const { join, substring, toLowerCase, split, trim } = require('@contrast/common');
|
|
18
|
+
const { join, substring, toLowerCase, split, trim, replace } = require('@contrast/common');
|
|
19
19
|
|
|
20
20
|
//
|
|
21
21
|
// General HTML utils
|
|
@@ -32,7 +32,7 @@ const reHasUnescapedHtml = RegExp(reUnescapedHtml.source);
|
|
|
32
32
|
|
|
33
33
|
function escapeHtml(string) {
|
|
34
34
|
return (string && reHasUnescapedHtml.test(string))
|
|
35
|
-
?
|
|
35
|
+
? replace(string, reUnescapedHtml, (chr) => htmlEscapes[chr])
|
|
36
36
|
: (string || '');
|
|
37
37
|
}
|
|
38
38
|
|
|
@@ -0,0 +1,34 @@
|
|
|
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
|
+
|
|
20
|
+
module.exports = function(core) {
|
|
21
|
+
const { messages } = core;
|
|
22
|
+
const sessionConfiguration = core.assess.sessionConfiguration = {
|
|
23
|
+
reportFindings(_sourceContext, vulnerabilityMetadata) {
|
|
24
|
+
messages.emit(Event.ASSESS_SESSION_CONFIGURATION_FINDING, vulnerabilityMetadata);
|
|
25
|
+
},
|
|
26
|
+
};
|
|
27
|
+
require('./install/http')(core);
|
|
28
|
+
|
|
29
|
+
sessionConfiguration.install = function() {
|
|
30
|
+
callChildComponentMethodsSync(sessionConfiguration, 'install');
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
return sessionConfiguration;
|
|
34
|
+
};
|
|
@@ -0,0 +1,79 @@
|
|
|
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 { SessionConfigurationRule, split, toLowerCase } = require('@contrast/common');
|
|
19
|
+
|
|
20
|
+
module.exports = function(core) {
|
|
21
|
+
const {
|
|
22
|
+
depHooks,
|
|
23
|
+
patcher,
|
|
24
|
+
scopes: { sources },
|
|
25
|
+
assess: {
|
|
26
|
+
sessionConfiguration: {
|
|
27
|
+
reportFindings
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
} = core;
|
|
31
|
+
const http = core.assess.sessionConfiguration.httpInstrumentation = {};
|
|
32
|
+
|
|
33
|
+
const patchType = 'session-configuration';
|
|
34
|
+
|
|
35
|
+
http.install = function() {
|
|
36
|
+
[
|
|
37
|
+
{ name: 'http', responseObj: 'ServerResponse' },
|
|
38
|
+
{ name: 'https', responseObj: 'ServerResponse' },
|
|
39
|
+
{ name: 'http2', responseObj: 'Http2ServerResponse' }
|
|
40
|
+
].forEach(({ name, responseObj }) => {
|
|
41
|
+
depHooks.resolve({ name }, (module) => {
|
|
42
|
+
patcher.patch(module[responseObj].prototype, 'setHeader', {
|
|
43
|
+
name: `${name}.${responseObj}.prototype.setHeader`,
|
|
44
|
+
patchType,
|
|
45
|
+
post(data) {
|
|
46
|
+
const sourceContext = sources.getStore()?.assess;
|
|
47
|
+
if (!sourceContext) return;
|
|
48
|
+
|
|
49
|
+
const [key, val] = data.args;
|
|
50
|
+
if (key === 'Set-Cookie') {
|
|
51
|
+
const [cookies] = val;
|
|
52
|
+
const parsedCookies = split(toLowerCase(cookies), '; ');
|
|
53
|
+
|
|
54
|
+
if (!parsedCookies.includes('httponly')) {
|
|
55
|
+
reportFindings(sourceContext, {
|
|
56
|
+
ruleId: SessionConfigurationRule.HTTPONLY,
|
|
57
|
+
props: {
|
|
58
|
+
evidence: cookies
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
if (!parsedCookies.includes('secure')) {
|
|
64
|
+
reportFindings(sourceContext, {
|
|
65
|
+
ruleId: SessionConfigurationRule.SECURE_FLAG_MISSING,
|
|
66
|
+
props: {
|
|
67
|
+
evidence: cookies
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
});
|
|
75
|
+
});
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
return http;
|
|
79
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@contrast/assess",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.9.0",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
"dependencies": {
|
|
16
16
|
"@contrast/distringuish": "^4.1.0",
|
|
17
17
|
"@contrast/scopes": "1.4.0",
|
|
18
|
-
"@contrast/common": "1.
|
|
18
|
+
"@contrast/common": "1.12.0",
|
|
19
19
|
"parseurl": "^1.3.3"
|
|
20
20
|
}
|
|
21
21
|
}
|