@contrast/agent 4.32.18 → 4.32.20

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.
@@ -16,6 +16,8 @@ Copyright: 2023 Contrast Security, Inc
16
16
 
17
17
  const agentEmitter = require('../../agent-emitter');
18
18
  const httpCommon = require('./common');
19
+ const logger = require('../../core/logger')('contrast:response-scanning');
20
+
19
21
  let setup = false;
20
22
 
21
23
  module.exports = {
@@ -25,16 +27,20 @@ module.exports = {
25
27
  }
26
28
  // TODO add generic res.end hook
27
29
  agentEmitter.on('send', function({ request, responseHeaders, body }) {
28
- // not setting default value in arg because null does not evaluate to false
29
- body = body || '';
30
+ if (typeof body !== 'object' || body === null) {
31
+ // not setting default value in arg because null does not evaluate to false
32
+ body = body || '';
30
33
 
31
- if (httpCommon.isParseableResponse(responseHeaders)) {
32
- agentEmitter.emit('parseableResponse', {
33
- request,
34
- responseHeaders,
35
- body,
36
- bodyLC: body.toLowerCase()
37
- });
34
+ if (httpCommon.isParseableResponse(responseHeaders)) {
35
+ agentEmitter.emit('parseableResponse', {
36
+ request,
37
+ responseHeaders,
38
+ body,
39
+ bodyLC: body.toLowerCase()
40
+ });
41
+ }
42
+ } else {
43
+ logger.warn('The contrast agent expects the response body to be a string or null.');
38
44
  }
39
45
  });
40
46
 
@@ -20,6 +20,7 @@ Copyright: 2023 Contrast Security, Inc
20
20
  * We will parse the url and only check the protocol, host, path
21
21
  */
22
22
 
23
+ const logger = require('../../core/logger')('contrast:hooks:ssrf-url');
23
24
  const { isString } = require('../../util/is-string');
24
25
  const tracker = require('../../tracker');
25
26
  const { URL } = require('url');
@@ -42,7 +43,7 @@ module.exports = ({ common: { isVulnerable } }) => {
42
43
  * @param {string} params.name name of tag to add
43
44
  * @return {TagRange[]} params.list of TagRanges including the new tag
44
45
  */
45
- ssrfUrl.addTag = function({ tags, start, stop, name }) {
46
+ ssrfUrl.addTag = function ({ tags, start, stop, name }) {
46
47
  const tagRange = new TagRange(start, stop, name);
47
48
 
48
49
  return tagRangeUtil.add(tags, tagRange);
@@ -57,7 +58,7 @@ module.exports = ({ common: { isVulnerable } }) => {
57
58
  * @param {Object} disallowedTags for sink
58
59
  * @return {Boolean} if input is vulnerable or not
59
60
  */
60
- ssrfUrl.handle = function({ input, requiredTags, disallowedTags }) {
61
+ ssrfUrl.handle = function ({ input, requiredTags, disallowedTags }) {
61
62
  if (!isString(input)) {
62
63
  return false;
63
64
  }
@@ -68,27 +69,36 @@ module.exports = ({ common: { isVulnerable } }) => {
68
69
  return false;
69
70
  }
70
71
 
71
- const url = new URL(input);
72
- const qpIndex = url.search ? input.indexOf(url.search) : -1;
73
- // start search after protocol + // so it doesn't match `/` as first in the http://
74
- const pathIndex = input.indexOf(url.pathname, url.protocol.length + 2);
72
+ try {
73
+ // we define a default base here so that `input` can be a relative path
74
+ const url = new URL(input, 'abc://xyz');
75
+ const qpIndex = url.search ? input.indexOf(url.search) : -1;
76
+ const pathIndex = url.pathname ? input.indexOf(
77
+ url.pathname,
78
+ // start search after protocol + // so it doesn't match `/` as first in the http://
79
+ // if our generated base is used, protocol will be `abc` and won't be present in the input
80
+ url.protocol === 'abc:' ? 0 : url.protocol.length + 2
81
+ ) : -1;
75
82
 
76
- if (pathIndex >= 0) {
77
- contrastProps.tagRanges = ssrfUrl.addTag({
78
- tags: contrastProps.tagRanges,
79
- start: pathIndex,
80
- stop: pathIndex + url.pathname.length - 1,
81
- name: PATH_TAG
82
- });
83
- }
83
+ if (pathIndex >= 0) {
84
+ contrastProps.tagRanges = ssrfUrl.addTag({
85
+ tags: contrastProps.tagRanges,
86
+ start: pathIndex,
87
+ stop: pathIndex + url.pathname.length - 1,
88
+ name: PATH_TAG
89
+ });
90
+ }
84
91
 
85
- if (qpIndex >= 0) {
86
- contrastProps.tagRanges = ssrfUrl.addTag({
87
- tags: contrastProps.tagRanges,
88
- start: qpIndex,
89
- stop: qpIndex + url.search.length - 1,
90
- name: QUERY_TAG
91
- });
92
+ if (qpIndex >= 0) {
93
+ contrastProps.tagRanges = ssrfUrl.addTag({
94
+ tags: contrastProps.tagRanges,
95
+ start: qpIndex,
96
+ stop: qpIndex + url.search.length - 1,
97
+ name: QUERY_TAG
98
+ });
99
+ }
100
+ } catch (err) {
101
+ logger.warn('Unable to parse url %s, err: %o', err);
92
102
  }
93
103
 
94
104
  return isVulnerable({ input, requiredTags, disallowedTags });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@contrast/agent",
3
- "version": "4.32.18",
3
+ "version": "4.32.20",
4
4
  "description": "Node.js security instrumentation by Contrast Security",
5
5
  "keywords": [
6
6
  "security",