@contrast/assess 1.9.0 → 1.11.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.
Files changed (116) hide show
  1. package/lib/dataflow/event-factory.js +1 -1
  2. package/lib/dataflow/index.js +1 -1
  3. package/lib/dataflow/propagation/common.js +1 -1
  4. package/lib/dataflow/propagation/index.js +3 -1
  5. package/lib/dataflow/propagation/install/JSON/index.js +1 -1
  6. package/lib/dataflow/propagation/install/JSON/parse-fn.js +1 -1
  7. package/lib/dataflow/propagation/install/JSON/parse.js +1 -1
  8. package/lib/dataflow/propagation/install/JSON/stringify.js +1 -1
  9. package/lib/dataflow/propagation/install/array-prototype-join.js +1 -1
  10. package/lib/dataflow/propagation/install/buffer.js +1 -1
  11. package/lib/dataflow/propagation/install/contrast-methods/add.js +1 -1
  12. package/lib/dataflow/propagation/install/contrast-methods/index.js +1 -1
  13. package/lib/dataflow/propagation/install/contrast-methods/number.js +1 -1
  14. package/lib/dataflow/propagation/install/contrast-methods/string.js +1 -1
  15. package/lib/dataflow/propagation/install/contrast-methods/tag.js +1 -1
  16. package/lib/dataflow/propagation/install/decode-uri-component.js +1 -1
  17. package/lib/dataflow/propagation/install/ejs/escape-xml.js +1 -1
  18. package/lib/dataflow/propagation/install/ejs/index.js +1 -1
  19. package/lib/dataflow/propagation/install/encode-uri-component.js +1 -1
  20. package/lib/dataflow/propagation/install/escape-html.js +1 -1
  21. package/lib/dataflow/propagation/install/escape.js +1 -1
  22. package/lib/dataflow/propagation/install/handlebars-utils-escape-expression.js +1 -1
  23. package/lib/dataflow/propagation/install/isnumeric-0.js +1 -1
  24. package/lib/dataflow/propagation/install/mongoose/common.js +20 -0
  25. package/lib/dataflow/propagation/install/mongoose/index.js +5 -9
  26. package/lib/dataflow/propagation/install/mongoose/schema-map.js +149 -0
  27. package/lib/dataflow/propagation/install/mongoose/schema-mixed.js +162 -0
  28. package/lib/dataflow/propagation/install/mongoose/schema-string.js +91 -37
  29. package/lib/dataflow/propagation/install/mysql-connection-escape.js +1 -1
  30. package/lib/dataflow/propagation/install/parse-int.js +1 -1
  31. package/lib/dataflow/propagation/install/path/basename.js +124 -0
  32. package/lib/dataflow/propagation/install/path/common.js +176 -0
  33. package/lib/dataflow/propagation/install/path/index.js +32 -0
  34. package/lib/dataflow/propagation/install/path/join-and-resolve.js +141 -0
  35. package/lib/dataflow/propagation/install/path/normalize.js +123 -0
  36. package/lib/dataflow/propagation/install/pug/index.js +1 -1
  37. package/lib/dataflow/propagation/install/pug-runtime-escape.js +1 -1
  38. package/lib/dataflow/propagation/install/querystring/index.js +1 -1
  39. package/lib/dataflow/propagation/install/querystring/parse.js +2 -2
  40. package/lib/dataflow/propagation/install/reg-exp-prototype-exec.js +182 -0
  41. package/lib/dataflow/propagation/install/sequelize.js +1 -1
  42. package/lib/dataflow/propagation/install/sql-template-strings.js +1 -1
  43. package/lib/dataflow/propagation/install/string/concat.js +1 -1
  44. package/lib/dataflow/propagation/install/string/format-methods.js +1 -1
  45. package/lib/dataflow/propagation/install/string/html-methods.js +1 -1
  46. package/lib/dataflow/propagation/install/string/index.js +65 -1
  47. package/lib/dataflow/propagation/install/string/match-all.js +236 -0
  48. package/lib/dataflow/propagation/install/string/match.js +84 -38
  49. package/lib/dataflow/propagation/install/string/replace.js +3 -3
  50. package/lib/dataflow/propagation/install/string/slice.js +2 -2
  51. package/lib/dataflow/propagation/install/string/split.js +2 -2
  52. package/lib/dataflow/propagation/install/string/substring.js +3 -3
  53. package/lib/dataflow/propagation/install/string/trim.js +2 -2
  54. package/lib/dataflow/propagation/install/unescape.js +1 -1
  55. package/lib/dataflow/propagation/install/url/domain-parsers.js +1 -1
  56. package/lib/dataflow/propagation/install/url/index.js +4 -1
  57. package/lib/dataflow/propagation/install/url/parse.js +131 -0
  58. package/lib/dataflow/propagation/install/url/searchParams.js +133 -0
  59. package/lib/dataflow/propagation/install/url/url.js +185 -0
  60. package/lib/dataflow/propagation/install/validator/hooks.js +1 -1
  61. package/lib/dataflow/propagation/install/validator/index.js +1 -1
  62. package/lib/dataflow/propagation/install/validator/methods.js +1 -1
  63. package/lib/dataflow/sinks/common.js +1 -1
  64. package/lib/dataflow/sinks/index.js +9 -5
  65. package/lib/dataflow/sinks/install/child-process.js +1 -1
  66. package/lib/dataflow/sinks/install/eval.js +138 -0
  67. package/lib/dataflow/sinks/install/express/index.js +1 -1
  68. package/lib/dataflow/sinks/install/express/unvalidated-redirect.js +2 -2
  69. package/lib/dataflow/sinks/install/fastify/index.js +1 -1
  70. package/lib/dataflow/sinks/install/fastify/unvalidated-redirect.js +3 -2
  71. package/lib/dataflow/sinks/install/fs.js +4 -4
  72. package/lib/dataflow/sinks/install/function.js +160 -0
  73. package/lib/dataflow/sinks/install/http/index.js +31 -0
  74. package/lib/dataflow/sinks/install/http/request.js +152 -0
  75. package/lib/dataflow/sinks/install/{http.js → http/server-response.js} +3 -3
  76. package/lib/dataflow/sinks/install/koa/index.js +1 -1
  77. package/lib/dataflow/sinks/install/koa/unvalidated-redirect.js +2 -2
  78. package/lib/dataflow/sinks/install/marsdb.js +1 -1
  79. package/lib/dataflow/sinks/install/mongodb.js +34 -46
  80. package/lib/dataflow/sinks/install/mssql.js +45 -32
  81. package/lib/dataflow/sinks/install/mysql.js +1 -1
  82. package/lib/dataflow/sinks/install/postgres.js +1 -1
  83. package/lib/dataflow/sinks/install/sequelize.js +1 -1
  84. package/lib/dataflow/sinks/install/sqlite3.js +1 -1
  85. package/lib/dataflow/sinks/install/vm.js +276 -0
  86. package/lib/dataflow/sources/common.js +1 -1
  87. package/lib/dataflow/sources/handler.js +1 -1
  88. package/lib/dataflow/sources/index.js +1 -1
  89. package/lib/dataflow/sources/install/body-parser1.js +1 -1
  90. package/lib/dataflow/sources/install/busboy1.js +1 -1
  91. package/lib/dataflow/sources/install/cookie-parser1.js +1 -1
  92. package/lib/dataflow/sources/install/express/index.js +1 -1
  93. package/lib/dataflow/sources/install/express/params.js +1 -1
  94. package/lib/dataflow/sources/install/express/parsedUrl.js +1 -1
  95. package/lib/dataflow/sources/install/fastify/fastify.js +1 -1
  96. package/lib/dataflow/sources/install/fastify/index.js +1 -1
  97. package/lib/dataflow/sources/install/formidable1.js +1 -1
  98. package/lib/dataflow/sources/install/http.js +1 -1
  99. package/lib/dataflow/sources/install/koa/index.js +1 -1
  100. package/lib/dataflow/sources/install/koa/koa-bodyparsers.js +1 -1
  101. package/lib/dataflow/sources/install/koa/koa-routers.js +1 -1
  102. package/lib/dataflow/sources/install/koa/koa2.js +1 -1
  103. package/lib/dataflow/sources/install/qs6.js +1 -1
  104. package/lib/dataflow/sources/install/querystring.js +1 -1
  105. package/lib/dataflow/tag-utils.js +71 -2
  106. package/lib/dataflow/tracker.js +1 -1
  107. package/lib/dataflow/utils/is-safe-content-type.js +1 -1
  108. package/lib/dataflow/utils/is-vulnerable.js +1 -1
  109. package/lib/index.js +1 -1
  110. package/lib/response-scanning/handlers/index.js +36 -30
  111. package/lib/response-scanning/handlers/utils.js +1 -1
  112. package/lib/response-scanning/index.js +1 -1
  113. package/lib/response-scanning/install/http.js +3 -3
  114. package/lib/session-configuration/index.js +1 -1
  115. package/lib/session-configuration/install/http.js +1 -1
  116. package/package.json +2 -2
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright: 2022 Contrast Security, Inc
2
+ * Copyright: 2023 Contrast Security, Inc
3
3
  * Contact: support@contrastsecurity.com
4
4
  * License: Commercial
5
5
 
@@ -21,7 +21,7 @@ const {
21
21
  isString
22
22
  } = require('@contrast/common');
23
23
  const { createModuleLabel } = require('../../propagation/common');
24
- const { patchType } = require('../common');
24
+ const { patchType, filterSafeTags } = require('../common');
25
25
 
26
26
  const safeTags = [
27
27
  SQL_ENCODED,
@@ -30,15 +30,18 @@ const safeTags = [
30
30
  CUSTOM_ENCODED,
31
31
  ];
32
32
 
33
+ const ruleId = SQL_INJECTION;
34
+
33
35
  module.exports = function(core) {
34
36
  const {
35
37
  depHooks,
36
38
  patcher,
39
+ config,
37
40
  scopes: { sources },
38
41
  assess: {
39
42
  dataflow: {
40
43
  tracker,
41
- sinks: { isVulnerable, isLocked, reportFindings },
44
+ sinks: { isVulnerable, isLocked, reportFindings, reportSafePositive },
42
45
  eventFactory: { createSinkEvent },
43
46
  },
44
47
  },
@@ -54,38 +57,48 @@ module.exports = function(core) {
54
57
  ) return;
55
58
 
56
59
  const strInfo = tracker.getData(data.args[0]);
57
- if (!strInfo || !isVulnerable(UNTRUSTED, safeTags, strInfo.tags)) {
58
- return;
59
- }
60
+ if (!strInfo) return;
60
61
 
61
- const event = createSinkEvent({
62
- name,
63
- moduleName: 'mssql',
64
- methodName: `PreparedStatement.prototype.${method}`,
65
- context: `mssql.PreparedStatement.${method}('${strInfo.value}')`,
66
- history: [strInfo],
67
- object: {
68
- value: `[${createModuleLabel('mssql', version)}].${obj}`,
69
- tracked: false,
70
- },
71
- args: [
72
- {
73
- value: strInfo.value,
74
- tracked: true,
62
+ if (isVulnerable(UNTRUSTED, safeTags, strInfo.tags)) {
63
+ const event = createSinkEvent({
64
+ name,
65
+ moduleName: 'mssql',
66
+ methodName: `PreparedStatement.prototype.${method}`,
67
+ context: `mssql.PreparedStatement.${method}('${strInfo.value}')`,
68
+ history: [strInfo],
69
+ object: {
70
+ value: `[${createModuleLabel('mssql', version)}].${obj}`,
71
+ tracked: false,
75
72
  },
76
- ],
77
- tags: strInfo.tags,
78
- source: 'P0',
79
- stacktraceOpts: {
80
- contructorOpt: data.hooked,
81
- prependFrames: [data.orig]
82
- },
83
- });
73
+ args: [
74
+ {
75
+ value: strInfo.value,
76
+ tracked: true,
77
+ },
78
+ ],
79
+ tags: strInfo.tags,
80
+ source: 'P0',
81
+ stacktraceOpts: {
82
+ contructorOpt: data.hooked,
83
+ prependFrames: [data.orig]
84
+ },
85
+ });
84
86
 
85
- if (event) {
86
- reportFindings({
87
- ruleId: SQL_INJECTION,
88
- sinkEvent: event,
87
+ if (event) {
88
+ reportFindings({
89
+ ruleId: SQL_INJECTION,
90
+ sinkEvent: event,
91
+ });
92
+ }
93
+ } else if (config.assess.safe_positives.enable) {
94
+ reportSafePositive({
95
+ name,
96
+ ruleId,
97
+ safeTags: filterSafeTags(safeTags, strInfo),
98
+ strInfo: {
99
+ tags: strInfo.tags,
100
+ value: strInfo.value,
101
+ }
89
102
  });
90
103
  }
91
104
  };
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright: 2022 Contrast Security, Inc
2
+ * Copyright: 2023 Contrast Security, Inc
3
3
  * Contact: support@contrastsecurity.com
4
4
  * License: Commercial
5
5
 
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright: 2022 Contrast Security, Inc
2
+ * Copyright: 2023 Contrast Security, Inc
3
3
  * Contact: support@contrastsecurity.com
4
4
  * License: Commercial
5
5
 
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright: 2022 Contrast Security, Inc
2
+ * Copyright: 2023 Contrast Security, Inc
3
3
  * Contact: support@contrastsecurity.com
4
4
  * License: Commercial
5
5
 
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright: 2022 Contrast Security, Inc
2
+ * Copyright: 2023 Contrast Security, Inc
3
3
  * Contact: support@contrastsecurity.com
4
4
  * License: Commercial
5
5
 
@@ -0,0 +1,276 @@
1
+ /*
2
+ * Copyright: 2023 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
+ const { patchType, filterSafeTags } = require('../common');
18
+ const {
19
+ isString,
20
+ DataflowTag: {
21
+ UNTRUSTED,
22
+ CUSTOM_ENCODED_TRUST_BOUNDARY_VIOLATION,
23
+ CUSTOM_ENCODED,
24
+ CUSTOM_VALIDATED_TRUST_BOUNDARY_VIOLATION,
25
+ CUSTOM_VALIDATED,
26
+ LIMITED_CHARS,
27
+ },
28
+ isNonEmptyObject,
29
+ inspect,
30
+ traverseValues,
31
+ join,
32
+ split,
33
+ } = require('@contrast/common');
34
+ const { createAdjustedQueryTags } = require('../../tag-utils');
35
+ const ruleId = 'unsafe-code-execution';
36
+ const safeTags = [
37
+ CUSTOM_ENCODED_TRUST_BOUNDARY_VIOLATION,
38
+ CUSTOM_ENCODED,
39
+ CUSTOM_VALIDATED_TRUST_BOUNDARY_VIOLATION,
40
+ CUSTOM_VALIDATED,
41
+ LIMITED_CHARS,
42
+ ];
43
+
44
+ module.exports = function(core) {
45
+ const {
46
+ config,
47
+ depHooks,
48
+ patcher,
49
+ scopes: { sources, instrumentation },
50
+ assess: {
51
+ dataflow: {
52
+ tracker,
53
+ sinks: {
54
+ isVulnerable,
55
+ runInActiveSink,
56
+ isLocked,
57
+ reportFindings,
58
+ reportSafePositive,
59
+ },
60
+ eventFactory: { createSinkEvent },
61
+ },
62
+ },
63
+ } = core;
64
+
65
+ function accumulateArgsInfo(origArgs, idxsToCheck) {
66
+ const argsInfo = [];
67
+ for (let i = 0; i < origArgs.length; i++) {
68
+ const arg = origArgs[i];
69
+ const infoObj = {
70
+ isStringArg: isString(arg),
71
+ idx: i,
72
+ sanitizedStrInfos: [],
73
+ sanitizedStrPaths: []
74
+ };
75
+
76
+ if (
77
+ !arg ||
78
+ !idxsToCheck.includes(i) ||
79
+ (!infoObj.isStringArg && !isNonEmptyObject(arg))
80
+ ) {
81
+ const inspectedArg = inspect(origArgs[i]);
82
+ infoObj.argsValue = { value: inspectedArg, tracked: false };
83
+ infoObj.ctxValue = inspectedArg;
84
+ argsInfo.push(infoObj);
85
+
86
+ continue;
87
+ }
88
+
89
+ if (infoObj.isStringArg) {
90
+ const strInfo = tracker.getData(arg);
91
+ infoObj.strInfo = strInfo;
92
+ infoObj.argsValue = {
93
+ value: strInfo ? strInfo.value : arg,
94
+ tracked: !!strInfo,
95
+ };
96
+ infoObj.ctxValue = `"${strInfo?.value || arg}"`;
97
+ infoObj.isVulnerableArg =
98
+ strInfo && isVulnerable(UNTRUSTED, safeTags, strInfo.tags);
99
+ if (strInfo && !infoObj.isVulnerableArg) {
100
+ infoObj.sanitizedStrInfos.push(strInfo);
101
+ infoObj.isSanitizedArg = true;
102
+ }
103
+ } else {
104
+ traverseValues(
105
+ arg,
106
+ (path, _type, value) => {
107
+ const strInfo = tracker.getData(value);
108
+ infoObj.strInfo = strInfo;
109
+ infoObj.isVulnerableArg =
110
+ strInfo && isVulnerable(UNTRUSTED, safeTags, strInfo.tags);
111
+ if (infoObj.isVulnerableArg) {
112
+ infoObj.vulnerableArgPath = [...path];
113
+ return true;
114
+ } else if (strInfo) {
115
+ infoObj.sanitizedStrInfos.push(strInfo);
116
+ infoObj.sanitizedStrPaths.push([...path]);
117
+ infoObj.isSanitizedArg = true;
118
+ }
119
+ },
120
+ 100
121
+ );
122
+
123
+ const inspectedArg = inspect(arg);
124
+
125
+ infoObj.argsValue = { value: inspectedArg, tracked: false };
126
+ infoObj.ctxValue = inspectedArg;
127
+ }
128
+
129
+ argsInfo.push(infoObj);
130
+ }
131
+
132
+ return argsInfo;
133
+ }
134
+
135
+ function around(next, { args: origArgs, hooked, orig, name }) {
136
+ const store = sources.getStore()?.assess;
137
+ if (
138
+ !store ||
139
+ instrumentation.isLocked() ||
140
+ isLocked('unsafe-code-execution')
141
+ ) {
142
+ return next();
143
+ }
144
+
145
+ const methodPath = split(name, '.');
146
+ const method = methodPath[methodPath.length - 1];
147
+ const idxsToCheck = method === 'runInNewContext' ? [0, 1] : [0];
148
+ const argsInfo = accumulateArgsInfo(origArgs, idxsToCheck);
149
+ const vulnerableArg = argsInfo.find((arg) => arg.isVulnerableArg);
150
+ const sanitizedArgs = argsInfo.filter((arg) => arg.isSanitizedArg);
151
+
152
+ if (!vulnerableArg && sanitizedArgs.length && config.assess.safe_positives.enable) {
153
+ const foundSafeTags = sanitizedArgs.reduce((a, c) => {
154
+ const tags = [];
155
+
156
+ c.sanitizedStrInfos.forEach((i) => {
157
+ tags.push(filterSafeTags(safeTags, i));
158
+ });
159
+
160
+ tags.forEach((tag) => a.add(...tag));
161
+ return a;
162
+ }, new Set());
163
+ const strInfo = sanitizedArgs.map((arg) => {
164
+ const value = inspect(origArgs[arg.idx], { depth: 4 });
165
+ const tags = [];
166
+ const strValues = [];
167
+ arg.sanitizedStrInfos.forEach((info, idx) => {
168
+ strValues.push(info.value);
169
+ if (arg.isStringArg) {
170
+ tags.push(info.tags);
171
+ } else {
172
+ tags.push(
173
+ createAdjustedQueryTags(
174
+ arg.sanitizedStrPaths[idx] || [],
175
+ info.tags,
176
+ info.value,
177
+ value
178
+ )
179
+ );
180
+ }
181
+ });
182
+
183
+ return {
184
+ value,
185
+ strValues,
186
+ tags,
187
+ };
188
+ });
189
+
190
+ reportSafePositive({
191
+ name,
192
+ ruleId,
193
+ safeTags: Array.from(foundSafeTags),
194
+ strInfo,
195
+ });
196
+
197
+ return name === 'vm.runInNewContext'
198
+ ? runInActiveSink('unsafe-code-execution', () => next())
199
+ : next();
200
+ }
201
+
202
+ if (vulnerableArg) {
203
+ const event = createSinkEvent({
204
+ name,
205
+ context: `${name}(${join(
206
+ argsInfo.map((a) => a.ctxValue),
207
+ ', '
208
+ )})`,
209
+ history: [vulnerableArg.strInfo],
210
+ object: {
211
+ value: methodPath.includes('prototype')
212
+ ? 'vm.Script.prototype'
213
+ : 'vm',
214
+ tracked: false,
215
+ },
216
+ moduleName: 'vm',
217
+ methodName: method,
218
+ args: argsInfo.map((a) => a.argsValue),
219
+ tags: vulnerableArg.vulnerableArgPath?.length
220
+ ? createAdjustedQueryTags(
221
+ vulnerableArg.vulnerableArgPath,
222
+ vulnerableArg.strInfo.tags,
223
+ vulnerableArg.strInfo.value,
224
+ vulnerableArg.argsValue.value
225
+ )
226
+ : vulnerableArg.strInfo.tags,
227
+ source: `P${vulnerableArg.idx}`,
228
+ stacktraceOpts: {
229
+ contructorOpt: hooked,
230
+ prependFrames: [orig],
231
+ },
232
+ });
233
+
234
+ if (event) {
235
+ reportFindings({
236
+ ruleId,
237
+ sinkEvent: event,
238
+ });
239
+ }
240
+ }
241
+
242
+ return name === 'vm.runInNewContext'
243
+ ? runInActiveSink('unsafe-code-execution', () => next())
244
+ : next();
245
+ }
246
+
247
+ core.assess.dataflow.sinks.vm = {
248
+ install() {
249
+ depHooks.resolve({ name: 'vm' }, (vm) => {
250
+ [
251
+ 'Script',
252
+ 'createScript',
253
+ 'runInContext',
254
+ 'runInThisContext',
255
+ 'createContext',
256
+ 'runInNewContext',
257
+ ].forEach((method) => {
258
+ const name = `vm.${method}`;
259
+ patcher.patch(vm, method, {
260
+ name,
261
+ patchType,
262
+ around,
263
+ });
264
+ });
265
+
266
+ patcher.patch(vm.Script.prototype, 'runInNewContext', {
267
+ name: 'vm.Script.prototype.runInNewContext',
268
+ patchType,
269
+ around,
270
+ });
271
+ });
272
+ },
273
+ };
274
+
275
+ return core.assess.dataflow.sinks.vm;
276
+ };
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright: 2022 Contrast Security, Inc
2
+ * Copyright: 2023 Contrast Security, Inc
3
3
  * Contact: support@contrastsecurity.com
4
4
  * License: Commercial
5
5
 
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright: 2022 Contrast Security, Inc
2
+ * Copyright: 2023 Contrast Security, Inc
3
3
  * Contact: support@contrastsecurity.com
4
4
  * License: Commercial
5
5
 
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright: 2022 Contrast Security, Inc
2
+ * Copyright: 2023 Contrast Security, Inc
3
3
  * Contact: support@contrastsecurity.com
4
4
  * License: Commercial
5
5
 
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright: 2022 Contrast Security, Inc
2
+ * Copyright: 2023 Contrast Security, Inc
3
3
  * Contact: support@contrastsecurity.com
4
4
  * License: Commercial
5
5
 
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright: 2022 Contrast Security, Inc
2
+ * Copyright: 2023 Contrast Security, Inc
3
3
  * Contact: support@contrastsecurity.com
4
4
  * License: Commercial
5
5
 
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright: 2022 Contrast Security, Inc
2
+ * Copyright: 2023 Contrast Security, Inc
3
3
  * Contact: support@contrastsecurity.com
4
4
  * License: Commercial
5
5
 
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright: 2022 Contrast Security, Inc
2
+ * Copyright: 2023 Contrast Security, Inc
3
3
  * Contact: support@contrastsecurity.com
4
4
  * License: Commercial
5
5
 
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright: 2022 Contrast Security, Inc
2
+ * Copyright: 2023 Contrast Security, Inc
3
3
  * Contact: support@contrastsecurity.com
4
4
  * License: Commercial
5
5
 
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright: 2022 Contrast Security, Inc
2
+ * Copyright: 2023 Contrast Security, Inc
3
3
  * Contact: support@contrastsecurity.com
4
4
  * License: Commercial
5
5
 
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright: 2022 Contrast Security, Inc
2
+ * Copyright: 2023 Contrast Security, Inc
3
3
  * Contact: support@contrastsecurity.com
4
4
  * License: Commercial
5
5
 
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright: 2022 Contrast Security, Inc
2
+ * Copyright: 2023 Contrast Security, Inc
3
3
  * Contact: support@contrastsecurity.com
4
4
  * License: Commercial
5
5
 
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright: 2022 Contrast Security, Inc
2
+ * Copyright: 2023 Contrast Security, Inc
3
3
  * Contact: support@contrastsecurity.com
4
4
  * License: Commercial
5
5
 
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright: 2022 Contrast Security, Inc
2
+ * Copyright: 2023 Contrast Security, Inc
3
3
  * Contact: support@contrastsecurity.com
4
4
  * License: Commercial
5
5
 
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright: 2022 Contrast Security, Inc
2
+ * Copyright: 2023 Contrast Security, Inc
3
3
  * Contact: support@contrastsecurity.com
4
4
  * License: Commercial
5
5
 
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright: 2022 Contrast Security, Inc
2
+ * Copyright: 2023 Contrast Security, Inc
3
3
  * Contact: support@contrastsecurity.com
4
4
  * License: Commercial
5
5
 
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright: 2022 Contrast Security, Inc
2
+ * Copyright: 2023 Contrast Security, Inc
3
3
  * Contact: support@contrastsecurity.com
4
4
  * License: Commercial
5
5
 
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright: 2022 Contrast Security, Inc
2
+ * Copyright: 2023 Contrast Security, Inc
3
3
  * Contact: support@contrastsecurity.com
4
4
  * License: Commercial
5
5
 
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright: 2022 Contrast Security, Inc
2
+ * Copyright: 2023 Contrast Security, Inc
3
3
  * Contact: support@contrastsecurity.com
4
4
  * License: Commercial
5
5
 
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright: 2022 Contrast Security, Inc
2
+ * Copyright: 2023 Contrast Security, Inc
3
3
  * Contact: support@contrastsecurity.com
4
4
  * License: Commercial
5
5
 
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright: 2022 Contrast Security, Inc
2
+ * Copyright: 2023 Contrast Security, Inc
3
3
  * Contact: support@contrastsecurity.com
4
4
  * License: Commercial
5
5
 
@@ -44,7 +44,7 @@ function atomicAppend(firstTagRanges, secondTagRanges, offset) {
44
44
 
45
45
  function atomicSubset(tags, subsetStart, len) {
46
46
  const ret = [];
47
- const subsetStop = subsetStart + len;
47
+ const subsetStop = subsetStart + len - 1;
48
48
 
49
49
  for (let idx = 0; idx < tags.length - 1; idx += 2) {
50
50
  const tagStart = tags[idx];
@@ -119,6 +119,42 @@ function atomicMerge(firstTagRanges, secondTagRanges) {
119
119
  return finalMergedRanges;
120
120
  }
121
121
 
122
+ function atomicExclude(tags, exclusionRange) {
123
+ const ret = [];
124
+ const [exclusionStart, exclusionStop] = exclusionRange;
125
+
126
+ for (let idx = 0; idx < tags.length - 1; idx += 2) {
127
+ const tagStart = tags[idx];
128
+ const tagStop = tags[idx + 1];
129
+
130
+ if (tagStop < exclusionStart) {
131
+ ret.push(tagStart, tagStop);
132
+ // exlusion is below - continue to check next range
133
+ continue;
134
+ }
135
+
136
+ if (tagStart > exclusionStop) {
137
+ ret.push(...tags.slice(idx));
138
+ // all other ranges are above exclusion so we can stop
139
+ break;
140
+ }
141
+
142
+ if (exclusionStart <= tagStart && exclusionStop < tagStop) {
143
+ ret.push(exclusionStop + 1, tagStop);
144
+ }
145
+
146
+ if (exclusionStart > tagStart) {
147
+ ret.push(tagStart, exclusionStart - 1);
148
+
149
+ if (exclusionStop < tagStop) {
150
+ ret.push(exclusionStop + 1, tagStop);
151
+ }
152
+ }
153
+ }
154
+
155
+ return ret;
156
+ }
157
+
122
158
  function createAppendTags(firstTags, secondTags, offset) {
123
159
  const ret = Object.create(null);
124
160
  const firstTagsObject = ensureObject(firstTags);
@@ -204,10 +240,43 @@ function createMergedTags(firstTags, secondTags) {
204
240
  return Object.keys(ret).length ? ret : null;
205
241
  }
206
242
 
243
+ function createTagsWithExclusion(tags, exclusionRange) {
244
+ if (!exclusionRange.length) return;
245
+
246
+ const ret = Object.create(null);
247
+ const tagsObject = ensureObject(tags);
248
+
249
+ for (const tagName of Object.keys(tagsObject)) {
250
+ const newTagRanges = atomicExclude(ensureTagsImmutable(tagsObject, tagName), exclusionRange);
251
+
252
+ newTagRanges.length && (ret[tagName] = newTagRanges);
253
+ }
254
+
255
+ return Object.keys(ret).length ? ret : null;
256
+ }
257
+
258
+ function createAdjustedQueryTags(path, tags, value, argString) {
259
+ let idx = -1;
260
+ for (const str of [...path, value]) {
261
+ // This is the case where the argument is an array
262
+ if (str === 0) continue;
263
+
264
+ idx = argString.indexOf(str, idx);
265
+ if (idx == -1) {
266
+ idx = -1;
267
+ break;
268
+ }
269
+ }
270
+
271
+ return idx > 0 ? createAppendTags([], tags, idx) : [...tags];
272
+ }
273
+
207
274
  module.exports = {
208
275
  createSubsetTags,
209
276
  createAppendTags,
210
277
  createFullLengthCopyTags,
211
278
  createMergedTags,
279
+ createTagsWithExclusion,
280
+ createAdjustedQueryTags,
212
281
  createOverlappingTags
213
282
  };
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright: 2022 Contrast Security, Inc
2
+ * Copyright: 2023 Contrast Security, Inc
3
3
  * Contact: support@contrastsecurity.com
4
4
  * License: Commercial
5
5