@contrast/assess 1.17.0 → 1.19.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 (53) hide show
  1. package/lib/constants.js +26 -0
  2. package/lib/crypto-analysis/common.js +20 -0
  3. package/lib/crypto-analysis/index.js +44 -0
  4. package/lib/crypto-analysis/install/crypto.js +151 -0
  5. package/lib/crypto-analysis/install/math.js +99 -0
  6. package/lib/dataflow/propagation/index.js +2 -1
  7. package/lib/dataflow/propagation/install/JSON/parse.js +12 -11
  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/ejs/escape-xml.js +2 -2
  11. package/lib/dataflow/propagation/install/ejs/index.js +1 -0
  12. package/lib/dataflow/propagation/install/ejs/template.js +77 -0
  13. package/lib/dataflow/propagation/install/sequelize/index.js +31 -0
  14. package/lib/dataflow/propagation/install/sequelize/query-generator.js +90 -0
  15. package/lib/dataflow/propagation/install/{sequelize.js → sequelize/sql-string.js} +3 -3
  16. package/lib/dataflow/propagation/install/util-format.js +126 -0
  17. package/lib/dataflow/sinks/index.js +1 -0
  18. package/lib/dataflow/sinks/install/child-process.js +20 -14
  19. package/lib/dataflow/sinks/install/eval.js +16 -14
  20. package/lib/dataflow/sinks/install/express/unvalidated-redirect.js +14 -8
  21. package/lib/dataflow/sinks/install/fastify/unvalidated-redirect.js +12 -5
  22. package/lib/dataflow/sinks/install/fs.js +7 -7
  23. package/lib/dataflow/sinks/install/function.js +8 -12
  24. package/lib/dataflow/sinks/install/http/request.js +16 -8
  25. package/lib/dataflow/sinks/install/http/server-response.js +11 -2
  26. package/lib/dataflow/sinks/install/koa/unvalidated-redirect.js +15 -8
  27. package/lib/dataflow/sinks/install/libxmljs.js +15 -10
  28. package/lib/dataflow/sinks/install/marsdb.js +13 -8
  29. package/lib/dataflow/sinks/install/mongodb.js +25 -15
  30. package/lib/dataflow/sinks/install/mssql.js +20 -9
  31. package/lib/dataflow/sinks/install/mysql.js +15 -8
  32. package/lib/dataflow/sinks/install/node-serialize.js +101 -0
  33. package/lib/dataflow/sinks/install/postgres.js +17 -4
  34. package/lib/dataflow/sinks/install/sequelize.js +16 -9
  35. package/lib/dataflow/sinks/install/sqlite3.js +20 -7
  36. package/lib/dataflow/sinks/install/vm.js +19 -17
  37. package/lib/dataflow/sources/install/http.js +14 -42
  38. package/lib/dataflow/sources/install/koa/index.js +1 -0
  39. package/lib/dataflow/sources/install/koa/koa-multer.js +102 -0
  40. package/lib/dataflow/sources/install/multer1.js +25 -51
  41. package/lib/dataflow/sources/install/querystring.js +1 -4
  42. package/lib/event-factory.js +47 -0
  43. package/lib/get-policy.js +68 -0
  44. package/lib/get-source-context.js +62 -0
  45. package/lib/index.d.ts +50 -0
  46. package/lib/index.js +20 -19
  47. package/lib/make-source-context.js +74 -0
  48. package/lib/response-scanning/handlers/index.js +55 -28
  49. package/lib/response-scanning/install/http.js +13 -7
  50. package/lib/rule-scopes.js +48 -0
  51. package/lib/session-configuration/handlers.js +4 -3
  52. package/lib/session-configuration/install/express-session.js +8 -2
  53. package/package.json +2 -2
@@ -16,7 +16,6 @@
16
16
  'use strict';
17
17
  const { patchType, filterSafeTags } = require('../common');
18
18
  const {
19
- isString,
20
19
  DataflowTag: {
21
20
  UNTRUSTED,
22
21
  CUSTOM_ENCODED_TRUST_BOUNDARY_VIOLATION,
@@ -25,14 +24,17 @@ const {
25
24
  CUSTOM_VALIDATED,
26
25
  LIMITED_CHARS,
27
26
  },
28
- Rule: { UNSAFE_CODE_EXECUTION },
29
- isNonEmptyObject,
27
+ Rule: { UNSAFE_CODE_EXECUTION: ruleId },
30
28
  inspect,
31
- traverseValues,
29
+ isNonEmptyObject,
30
+ isString,
32
31
  join,
33
32
  split,
33
+ traverseValues,
34
34
  } = require('@contrast/common');
35
+ const { InstrumentationType: { RULE } } = require('../../../constants');
35
36
  const { createAdjustedQueryTags } = require('../../tag-utils');
37
+
36
38
  const safeTags = [
37
39
  CUSTOM_ENCODED_TRUST_BOUNDARY_VIOLATION,
38
40
  CUSTOM_ENCODED,
@@ -41,13 +43,20 @@ const safeTags = [
41
43
  LIMITED_CHARS,
42
44
  ];
43
45
 
46
+ /**
47
+ * @param {{
48
+ * assess: import('@contrast/assess').Assess,
49
+ * config: import('@contrast/config').Config,
50
+ * }} core
51
+ * @returns {import('@contrast/common').Installable}
52
+ */
44
53
  module.exports = function (core) {
45
54
  const {
46
55
  config,
47
56
  depHooks,
48
57
  patcher,
49
- scopes: { sources, instrumentation },
50
58
  assess: {
59
+ getSourceContext,
51
60
  eventFactory: { createSinkEvent },
52
61
  dataflow: {
53
62
  tracker,
@@ -133,14 +142,7 @@ module.exports = function (core) {
133
142
  }
134
143
 
135
144
  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
- }
145
+ if (!getSourceContext(RULE, ruleId) || isLocked(ruleId)) return next();
144
146
 
145
147
  const methodPath = split(name, '.');
146
148
  const method = methodPath[methodPath.length - 1];
@@ -189,13 +191,13 @@ module.exports = function (core) {
189
191
 
190
192
  reportSafePositive({
191
193
  name,
192
- ruleId: UNSAFE_CODE_EXECUTION,
194
+ ruleId,
193
195
  safeTags: Array.from(foundSafeTags),
194
196
  strInfo,
195
197
  });
196
198
 
197
199
  return name === 'vm.runInNewContext'
198
- ? runInActiveSink(UNSAFE_CODE_EXECUTION, () => next())
200
+ ? runInActiveSink(ruleId, () => next())
199
201
  : next();
200
202
  }
201
203
 
@@ -233,14 +235,14 @@ module.exports = function (core) {
233
235
 
234
236
  if (event) {
235
237
  reportFindings({
236
- ruleId: UNSAFE_CODE_EXECUTION,
238
+ ruleId,
237
239
  sinkEvent: event,
238
240
  });
239
241
  }
240
242
  }
241
243
 
242
244
  return name === 'vm.runInNewContext'
243
- ? runInActiveSink(UNSAFE_CODE_EXECUTION, () => next())
245
+ ? runInActiveSink(ruleId, () => next())
244
246
  : next();
245
247
  }
246
248
 
@@ -17,12 +17,17 @@
17
17
  const { patchType } = require('../common');
18
18
  const { toLowerCase, InputType } = require('@contrast/common');
19
19
 
20
+ /**
21
+ * @param {{
22
+ * assess: import('@contrast/assess').Assess,
23
+ * }} core
24
+ */
20
25
  module.exports = function(core) {
21
26
  const {
22
- scopes,
27
+ assess: { dataflow, makeSourceContext },
23
28
  instrumentation: { instrument },
24
- assess: { dataflow },
25
29
  patcher,
30
+ scopes,
26
31
  } = core;
27
32
 
28
33
  const logger = core.logger.child('contrast:assess');
@@ -34,19 +39,20 @@ module.exports = function(core) {
34
39
  function around(next, data) {
35
40
  const [type] = data.args;
36
41
 
37
- if (type !== 'request') {
38
- return next();
39
- }
42
+ if (type !== 'request') return next();
40
43
 
41
44
  try {
42
45
  const [, req, res] = data.args;
43
46
  const store = scopes.sources.getStore();
44
47
 
45
48
  if (!store) {
46
- logger.debug('cannot acquire store for assess request handling');
47
- return next();
49
+ // this would indicate that sources did not install correctly
50
+ throw new Error('async request store not found');
48
51
  }
49
52
 
53
+ store.assess = makeSourceContext(req, res);
54
+ if (!store.assess) return;
55
+
50
56
  patcher.patch(res, 'writeHead', {
51
57
  name: 'write-head',
52
58
  patchType,
@@ -86,27 +92,7 @@ module.exports = function(core) {
86
92
  });
87
93
  }
88
94
 
89
- let uriPath, queries;
90
- const ix = req.url.indexOf('?');
91
-
92
- if (ix >= 0) {
93
- uriPath = req.url.slice(0, ix);
94
- queries = req.url.slice(ix + 1);
95
- } else {
96
- uriPath = req.url;
97
- queries = '';
98
- }
99
-
100
- const headers = {};
101
95
  const sourceName = 'ClientRequest';
102
-
103
- store.assess = {
104
- responseData: {},
105
- sourceEventsCount: 0,
106
- propagationEventsCount: 0,
107
- findings: {},
108
- };
109
-
110
96
  const sourceInfo = {
111
97
  name: sourceName,
112
98
  stacktraceOpts: {
@@ -141,24 +127,10 @@ module.exports = function(core) {
141
127
 
142
128
  for (let i = 0; i < req.rawHeaders.length; i += 2) {
143
129
  const header = toLowerCase(req.rawHeaders[i]);
144
- headers[header] = req.rawHeaders[i + 1];
145
130
  req.rawHeaders[i + 1] = req.headers[header];
146
131
  }
147
-
148
- const contentType = headers['content-type'] && toLowerCase(headers['content-type']);
149
-
150
- store.assess.reqData = {
151
- ip: req.socket.remoteAddress,
152
- httpVersion: req.httpVersion,
153
- method: req.method,
154
- headers,
155
- uriPath,
156
- queries,
157
- contentType,
158
- };
159
-
160
132
  } catch (err) {
161
- logger.error({ err }, 'Error during assess request handling');
133
+ logger.error({ err }, 'Error during Assess request handling');
162
134
  }
163
135
 
164
136
  setImmediate(() => {
@@ -22,6 +22,7 @@ module.exports = function(core) {
22
22
 
23
23
  require('./koa2')(core);
24
24
  require('./koa-bodyparsers')(core);
25
+ require('./koa-multer')(core);
25
26
  require('./koa-routers')(core);
26
27
 
27
28
  koaSources.install = function install() {
@@ -0,0 +1,102 @@
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 } = require('../../common');
18
+ const { InputType } = require('@contrast/common');
19
+
20
+ module.exports = (core) => {
21
+ const {
22
+ depHooks,
23
+ logger,
24
+ patcher,
25
+ scopes,
26
+ assess: { dataflow: { sources } },
27
+ } = core;
28
+
29
+ function handler(req, constructorOpt) {
30
+ const sourceContext = scopes.sources.getStore()?.assess;
31
+ if (!sourceContext) return;
32
+
33
+ function handle(context, data, key) {
34
+ try {
35
+ sources.handle({
36
+ context,
37
+ data,
38
+ keys: [key],
39
+ name: 'multer',
40
+ inputType: InputType.BODY,
41
+ sourceContext,
42
+ stacktraceOpts: {
43
+ constructorOpt,
44
+ },
45
+ });
46
+ } catch (err) {
47
+ logger.error({ err }, 'error handling Koa multer Assess dataflow %s.%s source', context, key);
48
+ }
49
+ }
50
+
51
+ if (req.file) {
52
+ handle('req', req, 'file');
53
+ }
54
+
55
+ if (Array.isArray(req.files)) {
56
+ for (let i = 0; i < req.files.length; i++) {
57
+ handle('req.files', req.files[i], i);
58
+ }
59
+ }
60
+
61
+ if (req.body && Object.keys(req.body).length) {
62
+ handle('req', req, 'body');
63
+ }
64
+ }
65
+
66
+ function install() {
67
+ ['koa-multer', '@koa/multer'].forEach((name) => {
68
+ depHooks.resolve(
69
+ { name }, (_export) => {
70
+ const origMulter = _export;
71
+ return patcher.patch(_export, {
72
+ name,
73
+ patchType,
74
+ post(data) {
75
+ const { args, hooked } = data;
76
+ const instance = origMulter.apply(this, args);
77
+ const origMake = instance._makeMiddleware;
78
+ instance._makeMiddleware = function _makeMiddleware(...args) {
79
+ const origMulterMiddleware = origMake.apply(this, args);
80
+ return function multerMiddleware(req, res, origNext) {
81
+
82
+ const next = function(...args) {
83
+ handler(req, hooked);
84
+ return origNext.apply(this, args);
85
+ };
86
+ return origMulterMiddleware.apply(this, [req, res, next]);
87
+ };
88
+ };
89
+ data.result = instance;
90
+ }
91
+ });
92
+ }
93
+ );
94
+ });
95
+ }
96
+
97
+ const koaMulterInstrumentation = sources.koaInstrumentation.koaMulter = {
98
+ install
99
+ };
100
+
101
+ return koaMulterInstrumentation;
102
+ };
@@ -23,36 +23,20 @@ module.exports = (core) => {
23
23
  depHooks,
24
24
  patcher,
25
25
  logger,
26
- assess,
27
- scopes: { wrap },
26
+ assess: { dataflow: { sources } },
27
+ scopes,
28
28
  } = core;
29
29
 
30
- function handleFile(sourceContext, constructorOpt, req) {
31
- try {
32
- assess.dataflow.sources.handle({
33
- context: 'req',
34
- data: req,
35
- keys: ['file'],
36
- name: 'multer',
37
- inputType: InputType.BODY,
38
- sourceContext,
39
- stacktraceOpts: {
40
- constructorOpt,
41
- },
42
- });
43
- } catch (err) {
44
- logger.error({ err }, 'error handling multer Assess dataflow req.file source');
45
- }
46
- }
30
+ function handler(req, constructorOpt) {
31
+ const sourceContext = scopes.sources.getStore()?.assess;
32
+ if (!sourceContext) return;
47
33
 
48
- function handleFiles(sourceContext, constructorOpt, req) {
49
- let i;
50
- for (i = 0; i < req.files.length; i++) {
34
+ function handle(context, data, key) {
51
35
  try {
52
- assess.dataflow.sources.handle({
53
- context: 'req.files',
54
- data: req.files[i],
55
- keys: [i],
36
+ sources.handle({
37
+ context,
38
+ data,
39
+ keys: [key],
56
40
  name: 'multer',
57
41
  inputType: InputType.BODY,
58
42
  sourceContext,
@@ -61,26 +45,22 @@ module.exports = (core) => {
61
45
  },
62
46
  });
63
47
  } catch (err) {
64
- logger.error({ err }, 'error handling multer Assess dataflow req.files[%s] source', i);
48
+ logger.error({ err }, 'error handling multer Assess dataflow %s.%s source', context, key);
65
49
  }
66
50
  }
67
- }
68
51
 
69
- function handleBody(sourceContext, constructorOpt, req) {
70
- try {
71
- assess.dataflow.sources.handle({
72
- context: 'req',
73
- data: req,
74
- keys: ['body'],
75
- name: 'multer',
76
- inputType: InputType.BODY,
77
- sourceContext,
78
- stacktraceOpts: {
79
- constructorOpt,
80
- },
81
- });
82
- } catch (err) {
83
- logger.error({ err }, 'error handling multer Assess dataflow req.body source');
52
+ if (req.file) {
53
+ handle('req', req, 'file');
54
+ }
55
+
56
+ if (Array.isArray(req.files)) {
57
+ for (let i = 0; i < req.files.length; i++) {
58
+ handle('req.files', req.files[i], i);
59
+ }
60
+ }
61
+
62
+ if (req.body && Object.keys(req.body).length) {
63
+ handle('req', req, 'body');
84
64
  }
85
65
  }
86
66
 
@@ -97,14 +77,8 @@ module.exports = (core) => {
97
77
  patchType,
98
78
  pre(data) {
99
79
  const [req, , next, hooked] = data.args;
100
-
101
- const sourceContext = core.scopes.sources.getStore()?.assess;
102
- if (!sourceContext) return;
103
-
104
- data.args[2] = wrap(function (...args) {
105
- if (req.file) handleFile(sourceContext, hooked, req);
106
- if (Array.isArray(req.files)) handleFiles(sourceContext, hooked, req);
107
- if (req.body && Object.keys(req.body).length) handleBody(sourceContext, hooked, req.body);
80
+ data.args[2] = scopes.wrap(function (...args) {
81
+ handler(req, hooked);
108
82
  next(...args);
109
83
  });
110
84
  },
@@ -32,10 +32,7 @@ module.exports = (core) => {
32
32
  const sourceContext = core.scopes.sources.getStore()?.assess;
33
33
  const inputType = InputType.QUERYSTRING;
34
34
 
35
- if (!sourceContext) {
36
- logger.error({ name }, 'unable to handle source. Missing `sourceContext`');
37
- return;
38
- }
35
+ if (!sourceContext) return;
39
36
 
40
37
  if (sourceContext.parsedQuery) {
41
38
  logger.trace({ name }, 'values already tracked');
@@ -270,5 +270,52 @@ module.exports = function(core) {
270
270
  return event;
271
271
  };
272
272
 
273
+
274
+ /**
275
+ * @param {{
276
+ * context: string,
277
+ * name: string,
278
+ * moduleName: string,
279
+ * methodName: string,
280
+ * object: { value: any, tracked: boolean },
281
+ * args: any[],
282
+ * result: { value: vany, tracked: boolean },
283
+ * source: string,
284
+ * stacktraceOpts: { constructorOpt?: Function},
285
+ * }} data
286
+ * @returns {any}
287
+ */
288
+ eventFactory.createCryptoAnalysisEvent = function(data) {
289
+ const {
290
+ name = '',
291
+ source,
292
+ stacktraceOpts,
293
+ } = data;
294
+
295
+ if (!name) {
296
+ logger.debug({ data }, 'no sink event name');
297
+ return null;
298
+ }
299
+
300
+ if (!source || !source.match(annotationRegExp)) {
301
+ logger.debug({ data }, 'malformed or missing sink event source field');
302
+ return null;
303
+ }
304
+
305
+ let stack;
306
+ if (config.assess.stacktraces !== 'NONE') {
307
+ stack = createSnapshot(stacktraceOpts)();
308
+ } else {
309
+ stack = [];
310
+ }
311
+
312
+ data.stack = stack;
313
+ data.time = Date.now();
314
+
315
+ eventFactory.createdEvents.add(data);
316
+
317
+ return data;
318
+ };
319
+
273
320
  return eventFactory;
274
321
  };
@@ -0,0 +1,68 @@
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
+
18
+ const {
19
+ Rule,
20
+ ResponseScanningRule,
21
+ SessionConfigurationRule,
22
+ Event,
23
+ } = require('@contrast/common');
24
+
25
+ const rulesIds = Object.values({
26
+ ...Rule,
27
+ ...ResponseScanningRule,
28
+ ...SessionConfigurationRule,
29
+ });
30
+
31
+ /**
32
+ * @param {{
33
+ * config: import('@contrast/config').Config,
34
+ * logger: import('@contrast/logger').Logger,
35
+ * messages: import('@contrast/common').Messages,
36
+ * }} core
37
+ * @returns {import('@contrast/common').Installable}
38
+ */
39
+ module.exports = function assess(core) {
40
+ const { logger, messages } = core;
41
+
42
+ const enabledRules = new Set(rulesIds);
43
+
44
+ messages.on(Event.SERVER_SETTINGS_UPDATE, (msg) => {
45
+ if (!msg.assess) return;
46
+
47
+ for (const ruleId of rulesIds) {
48
+ const enable = msg.assess[ruleId]?.enable;
49
+ if (enable === true) {
50
+ enabledRules.add(ruleId);
51
+ if (ruleId === Rule.NOSQL_INJECTION) enabledRules.add(Rule.NOSQL_INJECTION_MONGO);
52
+ } else if (enable === false) {
53
+ if (ruleId === Rule.NOSQL_INJECTION) enabledRules.delete(Rule.NOSQL_INJECTION_MONGO);
54
+ enabledRules.delete(ruleId);
55
+ }
56
+ }
57
+ logger.info({
58
+ enabledRules: Array.from(enabledRules)
59
+ }, 'Assess policy updated');
60
+ });
61
+
62
+ return core.assess.getPolicy = function getPolicy() {
63
+ // creates copy of local policy for request store
64
+ return {
65
+ enabledRules: new Set(enabledRules),
66
+ };
67
+ };
68
+ };
@@ -0,0 +1,62 @@
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
+
18
+ const { InstrumentationType } = require('./constants');
19
+
20
+ /**
21
+ * @param {{
22
+ * assess: import('@contrast/assess').Assess,
23
+ * config: import('@contrast/config').Config,
24
+ * scopes: import('@contrast/scopes').Scopes,
25
+ * }} core
26
+ */
27
+ module.exports = function(core) {
28
+ const {
29
+ config,
30
+ scopes: { sources, instrumentation },
31
+ assess: { ruleScopes },
32
+ } = core;
33
+
34
+ /**
35
+ * @param {import('./constants.js').InstrumentationType} type
36
+ * @returns {import('@contrast/assess').SourceContext|null} the assess store
37
+ */
38
+ return core.assess.getSourceContext = function getSourceContext(type, ...rest) {
39
+ const ctx = sources.getStore()?.assess;
40
+
41
+ if (!ctx || instrumentation.isLocked()) return null;
42
+
43
+ switch (type) {
44
+ case InstrumentationType.PROPAGATOR: {
45
+ if (ctx.propagationEventsCount > config.assess.max_propagation_events) return null;
46
+ break;
47
+ }
48
+ case InstrumentationType.SOURCE: {
49
+ if (ctx.sourceEventsCount > config.assess.max_context_source_events) return null;
50
+ break;
51
+ }
52
+ case InstrumentationType.RULE: {
53
+ const [ruleId] = rest;
54
+ if (!ruleId) break;
55
+ if (!ctx.policy.enabledRules.has(ruleId) || ruleScopes.isLocked(ruleId)) return null;
56
+ break;
57
+ }
58
+ }
59
+
60
+ return ctx;
61
+ };
62
+ };
package/lib/index.d.ts ADDED
@@ -0,0 +1,50 @@
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
+ import { IncomingMessage, ServerResponse } from 'node:http';
16
+ import { Rule } from '@contrast/common';
17
+
18
+ export enum InstrumentationType {
19
+ SOURCE = 'source',
20
+ PROPAGATOR = 'propagator',
21
+ RULE = 'rule',
22
+ }
23
+
24
+ export interface SourceContext {
25
+ reqData: object,
26
+ responseData: {
27
+ contentType: string,
28
+ },
29
+ policy: {
30
+ enabledRules: Set<string>,
31
+ }
32
+ }
33
+
34
+ export interface Policy {
35
+ enabledRules: Set<Rule>,
36
+ }
37
+
38
+ export interface RuleScopes {
39
+ run(ruleId: Rule, cb: void): void;
40
+ isLocked(ruleId: Rule): boolean;
41
+ }
42
+
43
+ export interface Assess {
44
+ getPolicy(): Policy,
45
+ getSourceContext(instrType?: InstrumentationType, opts?: any): SourceContext,
46
+ makeSourceContext(req: IncomingMessage, res: ServerResponse): SourceContext,
47
+ ruleScopes: RuleScopes,
48
+ }
49
+
50
+ export function getSourceContext(instrType?: InstrumentationType, ops?: any): SourceContext;