@contrast/assess 1.65.0 → 1.67.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/configuration-analysis/install/apollo-server.js +1 -1
- package/lib/configuration-analysis/install/graphql-yoga.js +1 -1
- package/lib/dataflow/propagation/index.js +0 -2
- package/lib/dataflow/sinks/install/fs.js +8 -15
- package/lib/response-scanning/handlers/index.js +6 -4
- package/package.json +12 -12
- package/lib/dataflow/propagation/install/fastify-send.js +0 -60
- package/lib/dataflow/propagation/install/send.js +0 -57
|
@@ -39,7 +39,7 @@ module.exports = function (core) {
|
|
|
39
39
|
const apolloServer = core.assess.configurationAnalysis.apolloServer = {};
|
|
40
40
|
|
|
41
41
|
apolloServer.install = function () {
|
|
42
|
-
return depHooks.resolve({ name: '@apollo/server', version: '>=4'
|
|
42
|
+
return depHooks.resolve({ name: '@apollo/server', version: '>=4' }, (xport) => {
|
|
43
43
|
if (!xport.ApolloServer) return;
|
|
44
44
|
patcher.patch(xport, 'ApolloServer', {
|
|
45
45
|
name: '@apollo/server.ApolloServer',
|
|
@@ -39,7 +39,7 @@ module.exports = function (core) {
|
|
|
39
39
|
const graphqlYoga = core.assess.configurationAnalysis.graphqlYoga = {};
|
|
40
40
|
|
|
41
41
|
graphqlYoga.install = function () {
|
|
42
|
-
return depHooks.resolve({ name: '@graphql-yoga/plugin-disable-introspection', version: '*'
|
|
42
|
+
return depHooks.resolve({ name: '@graphql-yoga/plugin-disable-introspection', version: '*' }, (xport) => patcher.patch(xport, 'useDisableIntrospection', {
|
|
43
43
|
name: '@graphql-yoga/plugin-disable-introspection.useDisableIntrospection',
|
|
44
44
|
patchType,
|
|
45
45
|
post(data) {
|
|
@@ -34,7 +34,6 @@ module.exports = function(core) {
|
|
|
34
34
|
require('./install/encode-uri')(core);
|
|
35
35
|
require('./install/escape-html')(core);
|
|
36
36
|
require('./install/escape')(core);
|
|
37
|
-
require('./install/fastify-send')(core);
|
|
38
37
|
require('./install/handlebars-utils-escape-expression')(core);
|
|
39
38
|
require('./install/isnumeric-0')(core);
|
|
40
39
|
require('./install/mustache-escape')(core);
|
|
@@ -48,7 +47,6 @@ module.exports = function(core) {
|
|
|
48
47
|
require('./install/path')(core);
|
|
49
48
|
require('./install/reg-exp-prototype-exec')(core);
|
|
50
49
|
require('./install/joi')(core);
|
|
51
|
-
require('./install/send')(core);
|
|
52
50
|
require('./install/util-format')(core);
|
|
53
51
|
|
|
54
52
|
propagation.install = function() {
|
|
@@ -41,6 +41,7 @@ module.exports = function(core) {
|
|
|
41
41
|
tracker,
|
|
42
42
|
sinks: { isVulnerable, reportFindings },
|
|
43
43
|
},
|
|
44
|
+
ruleScopes
|
|
44
45
|
},
|
|
45
46
|
} = core;
|
|
46
47
|
|
|
@@ -60,12 +61,12 @@ module.exports = function(core) {
|
|
|
60
61
|
}, []);
|
|
61
62
|
}
|
|
62
63
|
|
|
63
|
-
const
|
|
64
|
+
const around = (name, method, moduleName = 'fs', fullMethodName = '') => (next, data) => {
|
|
64
65
|
const { name: methodName, indices } = method;
|
|
65
|
-
if (!getSinkContext(ruleId)) return;
|
|
66
|
+
if (!getSinkContext(ruleId)) return next();
|
|
66
67
|
|
|
67
68
|
const values = getValues(indices, data.args);
|
|
68
|
-
if (!values.length) return;
|
|
69
|
+
if (!values.length) return next();
|
|
69
70
|
|
|
70
71
|
const args = values.map((v) => {
|
|
71
72
|
const strInfo = tracker.getData(v);
|
|
@@ -111,6 +112,7 @@ module.exports = function(core) {
|
|
|
111
112
|
});
|
|
112
113
|
}
|
|
113
114
|
}
|
|
115
|
+
return ruleScopes.run(ruleId, next);
|
|
114
116
|
};
|
|
115
117
|
|
|
116
118
|
core.assess.dataflow.sinks.pathTraversal = {
|
|
@@ -123,7 +125,7 @@ module.exports = function(core) {
|
|
|
123
125
|
patcher.patch(fs, method.name, {
|
|
124
126
|
name,
|
|
125
127
|
patchType,
|
|
126
|
-
|
|
128
|
+
around: around(name, method),
|
|
127
129
|
});
|
|
128
130
|
}
|
|
129
131
|
|
|
@@ -134,19 +136,10 @@ module.exports = function(core) {
|
|
|
134
136
|
patcher.patch(fs, syncName, {
|
|
135
137
|
name,
|
|
136
138
|
patchType,
|
|
137
|
-
|
|
139
|
+
around: around(name, method, 'fs', syncName),
|
|
138
140
|
});
|
|
139
141
|
}
|
|
140
142
|
}
|
|
141
|
-
|
|
142
|
-
if (method.promises && fs.promises && fs.promises[method.name]) {
|
|
143
|
-
const name = `fs.promises.${method.name}`;
|
|
144
|
-
patcher.patch(fs.promises, method.name, {
|
|
145
|
-
name,
|
|
146
|
-
patchType,
|
|
147
|
-
pre: pre(name, method, 'fs.promises'),
|
|
148
|
-
});
|
|
149
|
-
}
|
|
150
143
|
}
|
|
151
144
|
});
|
|
152
145
|
|
|
@@ -157,7 +150,7 @@ module.exports = function(core) {
|
|
|
157
150
|
patcher.patch(fsPromises, method.name, {
|
|
158
151
|
name,
|
|
159
152
|
patchType,
|
|
160
|
-
|
|
153
|
+
around: around(name, method, 'fsPromises'),
|
|
161
154
|
});
|
|
162
155
|
}
|
|
163
156
|
}
|
|
@@ -17,9 +17,9 @@
|
|
|
17
17
|
|
|
18
18
|
const {
|
|
19
19
|
primordials: {
|
|
20
|
+
StringPrototypeSubstring,
|
|
20
21
|
StringPrototypeToLowerCase,
|
|
21
22
|
JSONStringify,
|
|
22
|
-
StringPrototypeSubstring,
|
|
23
23
|
},
|
|
24
24
|
ResponseScanningRule,
|
|
25
25
|
} = require('@contrast/common');
|
|
@@ -261,7 +261,7 @@ module.exports = function(core) {
|
|
|
261
261
|
});
|
|
262
262
|
};
|
|
263
263
|
|
|
264
|
-
responseScanning.handleXPoweredByHeader = function(sourceContext, resHeaders) {
|
|
264
|
+
responseScanning.handleXPoweredByHeader = function (sourceContext, resHeaders) {
|
|
265
265
|
if (!sourceContext.policy?.isRuleEnabled(X_POWERED_BY_HEADER)) return;
|
|
266
266
|
|
|
267
267
|
const headerName = 'x-powered-by';
|
|
@@ -269,11 +269,13 @@ module.exports = function(core) {
|
|
|
269
269
|
|
|
270
270
|
if (header) {
|
|
271
271
|
header = StringPrototypeToLowerCase.call(header);
|
|
272
|
-
|
|
273
272
|
reportFindings(sourceContext, {
|
|
274
273
|
ruleId: ResponseScanningRule.X_POWERED_BY_HEADER,
|
|
275
274
|
vulnerabilityMetadata: {
|
|
276
|
-
|
|
275
|
+
snippet: header,
|
|
276
|
+
// the UI vuln title will read: X-Powered-By Header Enabled in Route
|
|
277
|
+
// ^^^^^
|
|
278
|
+
path: 'Route',
|
|
277
279
|
}
|
|
278
280
|
});
|
|
279
281
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@contrast/assess",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.67.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)",
|
|
@@ -20,18 +20,18 @@
|
|
|
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.39.0",
|
|
24
|
+
"@contrast/config": "1.55.0",
|
|
25
|
+
"@contrast/core": "1.60.0",
|
|
26
|
+
"@contrast/dep-hooks": "1.29.0",
|
|
27
27
|
"@contrast/distringuish": "^6.0.2",
|
|
28
|
-
"@contrast/instrumentation": "1.
|
|
29
|
-
"@contrast/logger": "1.
|
|
30
|
-
"@contrast/patcher": "1.
|
|
31
|
-
"@contrast/rewriter": "1.
|
|
32
|
-
"@contrast/route-coverage": "1.
|
|
33
|
-
"@contrast/scopes": "1.
|
|
34
|
-
"@contrast/sources": "1.
|
|
28
|
+
"@contrast/instrumentation": "1.39.0",
|
|
29
|
+
"@contrast/logger": "1.33.0",
|
|
30
|
+
"@contrast/patcher": "1.32.0",
|
|
31
|
+
"@contrast/rewriter": "1.37.0",
|
|
32
|
+
"@contrast/route-coverage": "1.53.0",
|
|
33
|
+
"@contrast/scopes": "1.30.0",
|
|
34
|
+
"@contrast/sources": "1.6.0",
|
|
35
35
|
"semver": "^7.6.0"
|
|
36
36
|
}
|
|
37
37
|
}
|
|
@@ -1,60 +0,0 @@
|
|
|
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 { primordials: { StringPrototypeSlice } } = require('@contrast/common');
|
|
18
|
-
const { patchType } = require('../common');
|
|
19
|
-
|
|
20
|
-
module.exports = function (core) {
|
|
21
|
-
const {
|
|
22
|
-
depHooks,
|
|
23
|
-
patcher,
|
|
24
|
-
} = core;
|
|
25
|
-
|
|
26
|
-
return core.assess.dataflow.propagation.fastifySend = {
|
|
27
|
-
install() {
|
|
28
|
-
depHooks.resolve({ name: '@fastify/send', version: '<3', file: 'lib/SendStream.js' }, (SendStream) => {
|
|
29
|
-
patcher.patch(SendStream.prototype, 'sendFile', {
|
|
30
|
-
name: '@fastify/send/lib/SendStream.js',
|
|
31
|
-
patchType,
|
|
32
|
-
usePerf: 'sync',
|
|
33
|
-
pre(data) {
|
|
34
|
-
const { args } = data;
|
|
35
|
-
// (†) This is a minimal propagator that just untracks the argument. There are
|
|
36
|
-
// no propagation events generated and no expensive tag range computations. We
|
|
37
|
-
// don't get the propagator context before proceeding since it could lead to false
|
|
38
|
-
// positives e.g. if the number of propagation events exceeds the configured limit.
|
|
39
|
-
const untrackedPath = StringPrototypeSlice.call(` ${args[0]}`, 1);
|
|
40
|
-
args[0] = untrackedPath;
|
|
41
|
-
},
|
|
42
|
-
});
|
|
43
|
-
});
|
|
44
|
-
|
|
45
|
-
depHooks.resolve({ name: '@fastify/send', version: '>=3 <5', file: 'lib/send.js' }, (send) => {
|
|
46
|
-
patcher.patch(send, 'send', {
|
|
47
|
-
name: '@fastify/send/lib/send.js',
|
|
48
|
-
patchType,
|
|
49
|
-
usePerf: 'sync',
|
|
50
|
-
pre(data) {
|
|
51
|
-
const { args } = data;
|
|
52
|
-
// (†)
|
|
53
|
-
const untrackedPath = StringPrototypeSlice.call(` ${args[1]}`, 1);
|
|
54
|
-
args[1] = untrackedPath;
|
|
55
|
-
},
|
|
56
|
-
});
|
|
57
|
-
});
|
|
58
|
-
}
|
|
59
|
-
};
|
|
60
|
-
};
|
|
@@ -1,57 +0,0 @@
|
|
|
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 { patchType } = require('../common');
|
|
18
|
-
const { primordials: { StringPrototypeSlice } } = require('@contrast/common');
|
|
19
|
-
|
|
20
|
-
module.exports = function (core) {
|
|
21
|
-
const {
|
|
22
|
-
depHooks,
|
|
23
|
-
patcher,
|
|
24
|
-
} = core;
|
|
25
|
-
|
|
26
|
-
const send = {};
|
|
27
|
-
core.assess.dataflow.propagation.send = send;
|
|
28
|
-
|
|
29
|
-
function patchSendModule(sendModuleExport) {
|
|
30
|
-
return patcher.patch(sendModuleExport, {
|
|
31
|
-
name: 'send',
|
|
32
|
-
patchType,
|
|
33
|
-
usePerf: 'sync',
|
|
34
|
-
post(data) {
|
|
35
|
-
patcher.patch(data.result, 'sendFile', {
|
|
36
|
-
name: 'send.sendFile',
|
|
37
|
-
patchType,
|
|
38
|
-
pre(data) {
|
|
39
|
-
const { args } = data;
|
|
40
|
-
// This is a minimal propagator that just untracks the argument. There are
|
|
41
|
-
// no propagation events generated and no expensive tag range computations. We
|
|
42
|
-
// don't get the propagator context before proceeding since it could lead to false
|
|
43
|
-
// positives e.g. if the number of propagation events exceeds the configured limit.
|
|
44
|
-
const untrackedPath = StringPrototypeSlice.call(` ${args[0]}`, 1);
|
|
45
|
-
args[0] = untrackedPath;
|
|
46
|
-
},
|
|
47
|
-
});
|
|
48
|
-
},
|
|
49
|
-
});
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
send.install = function () {
|
|
53
|
-
depHooks.resolve({ name: 'send', version: '<2' }, patchSendModule);
|
|
54
|
-
};
|
|
55
|
-
|
|
56
|
-
return send;
|
|
57
|
-
};
|