@contrast/protect 1.49.0 → 1.51.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 (56) hide show
  1. package/lib/error-handlers/index.js +1 -1
  2. package/lib/error-handlers/index.test.js +1 -1
  3. package/lib/error-handlers/init-domain.js +4 -39
  4. package/lib/error-handlers/init-domain.test.js +3 -15
  5. package/lib/error-handlers/install/express.js +162 -0
  6. package/lib/error-handlers/install/express.test.js +290 -0
  7. package/lib/error-handlers/install/hapi.js +2 -2
  8. package/lib/error-handlers/install/hapi.test.js +2 -2
  9. package/lib/error-handlers/install/koa2.js +1 -1
  10. package/lib/error-handlers/install/restify.js +1 -1
  11. package/lib/error-handlers/install/restify.test.js +1 -1
  12. package/lib/hardening/install/node-serialize0.js +2 -2
  13. package/lib/hardening/install/node-serialize0.test.js +1 -4
  14. package/lib/index.d.ts +3 -3
  15. package/lib/input-analysis/index.js +1 -1
  16. package/lib/input-analysis/index.test.js +1 -1
  17. package/lib/input-analysis/install/body-parser1.js +2 -2
  18. package/lib/input-analysis/install/busboy1.js +1 -1
  19. package/lib/input-analysis/install/cookie-parser1.js +1 -1
  20. package/lib/input-analysis/install/{express4.js → express.js} +61 -20
  21. package/lib/input-analysis/install/{express4.test.js → express.test.js} +92 -59
  22. package/lib/input-analysis/install/formidable1.js +1 -1
  23. package/lib/input-analysis/install/hapi.js +1 -1
  24. package/lib/input-analysis/install/hapi.test.js +6 -14
  25. package/lib/input-analysis/install/http.js +2 -9
  26. package/lib/input-analysis/install/koa-body5.js +1 -1
  27. package/lib/input-analysis/install/koa-bodyparser4.js +1 -1
  28. package/lib/input-analysis/install/koa2.js +5 -5
  29. package/lib/input-analysis/install/multer1.js +1 -1
  30. package/lib/input-analysis/install/qs6.js +1 -1
  31. package/lib/input-analysis/install/restify.js +1 -1
  32. package/lib/input-analysis/install/restify.test.js +1 -1
  33. package/lib/input-analysis/install/universal-cookie4.js +1 -1
  34. package/lib/input-tracing/install/child-process.js +1 -1
  35. package/lib/input-tracing/install/fs.js +2 -2
  36. package/lib/input-tracing/install/fs.test.js +2 -2
  37. package/lib/input-tracing/install/http.js +2 -2
  38. package/lib/input-tracing/install/http2.js +2 -2
  39. package/lib/input-tracing/install/marsdb.js +2 -2
  40. package/lib/input-tracing/install/marsdb.test.js +1 -1
  41. package/lib/input-tracing/install/mongodb.js +2 -2
  42. package/lib/input-tracing/install/mongodb.test.js +2 -4
  43. package/lib/input-tracing/install/mssql.js +3 -3
  44. package/lib/input-tracing/install/mssql.test.js +2 -2
  45. package/lib/input-tracing/install/mysql.js +7 -9
  46. package/lib/input-tracing/install/postgres.js +3 -3
  47. package/lib/input-tracing/install/postgres.test.js +2 -10
  48. package/lib/input-tracing/install/sequelize.js +2 -2
  49. package/lib/input-tracing/install/spdy.js +2 -2
  50. package/lib/input-tracing/install/sqlite3.js +2 -2
  51. package/lib/input-tracing/install/vm.js +2 -2
  52. package/lib/semantic-analysis/install/libxmljs.js +3 -3
  53. package/lib/semantic-analysis/install/libxmljs.test.js +2 -2
  54. package/package.json +12 -14
  55. package/lib/error-handlers/install/express4.js +0 -138
  56. package/lib/error-handlers/install/express4.test.js +0 -238
@@ -43,7 +43,7 @@ module.exports = function(core) {
43
43
  }
44
44
 
45
45
  function install() {
46
- depHooks.resolve({ name: 'marsdb' }, marsdb => {
46
+ depHooks.resolve({ name: 'marsdb', version: '<1' }, marsdb => {
47
47
  methods.forEach((method) => {
48
48
  const name = `marsdb.Collection.prototype.${method}`;
49
49
 
@@ -52,7 +52,7 @@ module.exports = function(core) {
52
52
  patchType,
53
53
  pre({ args, hooked, orig }) {
54
54
  const value = getCursorQueryData(args);
55
- const sourceContext = getSourceContext(name);
55
+ const sourceContext = getSourceContext();
56
56
 
57
57
  if (
58
58
  !sourceContext ||
@@ -34,7 +34,7 @@ describe('protect input-tracing marsdb', function () {
34
34
  mockMarsDb.Collection.prototype[method] = () => { };
35
35
  });
36
36
 
37
- core.depHooks.resolve.withArgs({ name: 'marsdb' }).yields(mockMarsDb);
37
+ core.depHooks.resolve.yields(mockMarsDb);
38
38
 
39
39
  marsdbInstr = require('./marsdb')(core);
40
40
  });
@@ -142,7 +142,7 @@ module.exports = function(core) {
142
142
  const argsIdxsToCheck = vulnerableArgIdxs || [0];
143
143
  return function(next, data) {
144
144
  const { args, name, hooked, orig } = data;
145
- const sourceCtx = getSourceContext(name);
145
+ const sourceCtx = getSourceContext();
146
146
 
147
147
  if (instrumentation.isLocked() || !sourceCtx) {
148
148
  return next();
@@ -158,7 +158,7 @@ module.exports = function(core) {
158
158
  }
159
159
 
160
160
  instr.install = function() {
161
- depHooks.resolve({ name: 'mongodb' }, (mongodb, version) => {
161
+ depHooks.resolve({ name: 'mongodb', version: '<7' }, (mongodb, version) => {
162
162
  patchCollection(mongodb, version);
163
163
  patchDatabase(mongodb, version);
164
164
  });
@@ -39,9 +39,7 @@ describe('protect input-tracing mongodb', function() {
39
39
  ({ core, simulateRequestScope } = initProtectFixture());
40
40
  require('../../get-source-context')(core);
41
41
 
42
- core.depHooks.resolve
43
- .withArgs({ name: 'mongodb' })
44
- .yields({ Collection, Db });
42
+ core.depHooks.resolve.yields({ Collection, Db });
45
43
 
46
44
  instr = require('./mongodb')(core);
47
45
  instr.install();
@@ -247,7 +245,7 @@ describe('protect input-tracing mongodb', function() {
247
245
  command() { }
248
246
  }
249
247
  core.depHooks.resolve
250
- .withArgs({ name: 'mongodb' })
248
+ .withArgs(sinon.match({ name: 'mongodb' }))
251
249
  .yields({ Collection, Db }, 'v5.x.x');
252
250
 
253
251
  const instr = require('./mongodb')(core);
@@ -27,7 +27,7 @@ module.exports = function (core) {
27
27
  } = core;
28
28
 
29
29
  const pre = ({ args, hooked, name, orig }) => {
30
- const sourceContext = protect.getSourceContext(name);
30
+ const sourceContext = protect.getSourceContext();
31
31
  const [value] = args;
32
32
  if (!sourceContext || !value || !isString(value)) return;
33
33
 
@@ -46,7 +46,7 @@ module.exports = function (core) {
46
46
  core.protect.inputTracing.mssqlInstrumentation = {
47
47
  install() {
48
48
  depHooks.resolve(
49
- { name: 'mssql', file: 'lib/base/prepared-statement.js' },
49
+ { name: 'mssql', version: '<12', file: 'lib/base/prepared-statement.js' },
50
50
  (PreparedStatement) => {
51
51
  patcher.patch(PreparedStatement.prototype, 'prepare', {
52
52
  name: 'mssql.PreparedStatement.prototype.prepare',
@@ -57,7 +57,7 @@ module.exports = function (core) {
57
57
  );
58
58
 
59
59
  depHooks.resolve(
60
- { name: 'mssql', file: 'lib/base/request.js' },
60
+ { name: 'mssql', version: '<12', file: 'lib/base/request.js' },
61
61
  (Request) => {
62
62
  patcher.patch(Request.prototype, 'batch', {
63
63
  name: 'mssql.Request.prototype.batch',
@@ -27,11 +27,11 @@ describe('protect input-tracing mssql', function () {
27
27
  Request.prototype.query = sinon.stub();
28
28
 
29
29
  core.depHooks.resolve
30
- .withArgs({ name: 'mssql', file: 'lib/base/prepared-statement.js' })
30
+ .withArgs(sinon.match({ file: 'lib/base/prepared-statement.js' }))
31
31
  .yields(PreparedStatement);
32
32
 
33
33
  core.depHooks.resolve
34
- .withArgs({ name: 'mssql', file: 'lib/base/request.js' })
34
+ .withArgs(sinon.match({ file: 'lib/base/request.js' }))
35
35
  .yields(Request);
36
36
 
37
37
  require('./mssql')(core).install();
@@ -40,20 +40,18 @@ module.exports = function(core) {
40
40
 
41
41
  mysqlInstr.install = function() {
42
42
  [
43
- { module: 'mysql', file: 'lib/Connection.js', method: 'query' },
44
- { module: 'mysql2', file: 'lib/connection.js', method: 'execute' },
45
- { module: 'mysql2', file: 'lib/connection.js', method: 'query' }
43
+ { name: 'mysql', version: '<3', file: 'lib/Connection.js', method: 'query' },
44
+ { name: 'mysql2', version: '<4', file: 'lib/connection.js', method: 'execute' },
45
+ { name: 'mysql2', version: '<4', file: 'lib/connection.js', method: 'query' }
46
46
  ].forEach(
47
- ({ module, file, method }) => {
48
- depHooks.resolve({ module, file, method }, conn => {
49
- const name = `${module}.Connection.prototype.${method}`;
50
-
47
+ ({ name, version, file, method }) => {
48
+ depHooks.resolve({ name, version, file }, conn => {
51
49
  patcher.patch(conn.prototype, method, {
52
- name,
50
+ name: `${name}.Connection.prototype.${method}`,
53
51
  patchType,
54
52
  pre(data) {
55
53
  const { args, hooked, name, orig } = data;
56
- const sourceContext = protect.getSourceContext(name);
54
+ const sourceContext = protect.getSourceContext();
57
55
 
58
56
  if (!sourceContext) return;
59
57
 
@@ -32,7 +32,7 @@ module.exports = function(core) {
32
32
  }
33
33
 
34
34
  function preHook({ args, hooked, name, orig }) {
35
- const sourceContext = protect.getSourceContext(name);
35
+ const sourceContext = protect.getSourceContext();
36
36
 
37
37
  if (!sourceContext) return;
38
38
 
@@ -49,7 +49,7 @@ module.exports = function(core) {
49
49
  }
50
50
 
51
51
  function install() {
52
- depHooks.resolve({ name: 'pg', file: 'lib/client.js' }, client => {
52
+ depHooks.resolve({ name: 'pg', version: '<9', file: 'lib/client.js' }, client => {
53
53
  const name = 'pg.Client.prototype.query';
54
54
  patcher.patch(client.prototype, 'query', {
55
55
  name,
@@ -58,7 +58,7 @@ module.exports = function(core) {
58
58
  });
59
59
  });
60
60
 
61
- depHooks.resolve({ name: 'pg-pool' }, pool => {
61
+ depHooks.resolve({ name: 'pg-pool', version: '<4' }, pool => {
62
62
  const name = 'pg-pool.Pool.prototype.query';
63
63
  patcher.patch(pool.prototype, 'query', {
64
64
  name,
@@ -31,11 +31,7 @@ describe('protect input-tracing postgres', function () {
31
31
  beforeEach(function () {
32
32
  Client = function () { };
33
33
  Client.prototype.query = sinon.stub();
34
- core
35
- .depHooks
36
- .resolve
37
- .withArgs({ name: 'pg', file: 'lib/client.js' })
38
- .yields(Client);
34
+ core.depHooks.resolve.withArgs(sinon.match({ name: 'pg' })).yields(Client);
39
35
  require('./postgres')(core).install();
40
36
  });
41
37
 
@@ -79,11 +75,7 @@ describe('protect input-tracing postgres', function () {
79
75
  beforeEach(function () {
80
76
  Pool = function () { };
81
77
  Pool.prototype.query = sinon.stub();
82
- core
83
- .depHooks
84
- .resolve
85
- .withArgs({ name: 'pg-pool' })
86
- .yields(Pool);
78
+ core.depHooks.resolve.withArgs(sinon.match({ name: 'pg-pool' })).yields(Pool);
87
79
 
88
80
  require('./postgres')(core).install();
89
81
  });
@@ -33,7 +33,7 @@ module.exports = function(core) {
33
33
  }
34
34
 
35
35
  function install() {
36
- depHooks.resolve({ name: 'sequelize' }, sequelize => {
36
+ depHooks.resolve({ name: 'sequelize', version: '<7' }, sequelize => {
37
37
  const name = 'sequelize.prototype.query';
38
38
  patcher.patch(sequelize.prototype, 'query', {
39
39
  name,
@@ -41,7 +41,7 @@ module.exports = function(core) {
41
41
  pre({ args, hooked, name, orig }) {
42
42
  if (instrumentation.isLocked()) return;
43
43
 
44
- const sourceContext = protect.getSourceContext(name);
44
+ const sourceContext = protect.getSourceContext();
45
45
 
46
46
  if (!sourceContext || sourceContext.allowed) return;
47
47
 
@@ -27,7 +27,7 @@ module.exports = function(core) {
27
27
  } = core;
28
28
 
29
29
  function install() {
30
- depHooks.resolve({ name: 'spdy' }, spdy => {
30
+ depHooks.resolve({ name: 'spdy', version: '<5' }, spdy => {
31
31
  const name = 'spdy.response.end';
32
32
  patcher.patch(spdy.response, 'end', {
33
33
  name,
@@ -35,7 +35,7 @@ module.exports = function(core) {
35
35
  pre({ args, obj: response, hooked }) {
36
36
  if (instrumentation.isLocked()) return;
37
37
 
38
- const sourceContext = protect.getSourceContext(name);
38
+ const sourceContext = protect.getSourceContext();
39
39
 
40
40
  if (!sourceContext) return;
41
41
 
@@ -28,7 +28,7 @@ module.exports = function(core) {
28
28
  } = core;
29
29
 
30
30
  function install() {
31
- depHooks.resolve({ name: 'sqlite3' }, sqlite3 => {
31
+ depHooks.resolve({ name: 'sqlite3', version: '<6' }, sqlite3 => {
32
32
  ['all', 'run', 'get', 'each', 'exec', 'prepare'].forEach((method) => {
33
33
  const name = `sqlite3.Database.prototype.${method}`;
34
34
  patcher.patch(sqlite3.Database.prototype, method, {
@@ -37,7 +37,7 @@ module.exports = function(core) {
37
37
  pre({ args, hooked, name, orig }) {
38
38
  if (instrumentation.isLocked()) return;
39
39
 
40
- const sourceContext = protect.getSourceContext(name);
40
+ const sourceContext = protect.getSourceContext();
41
41
  if (!sourceContext) return;
42
42
 
43
43
  const value = args[0];
@@ -44,7 +44,7 @@ module.exports = function (core) {
44
44
  const createPre = (arity) => ({ args, hooked, orig, name }) => {
45
45
  if (instrumentation.isLocked()) return;
46
46
 
47
- const sourceContext = protect.getSourceContext(name);
47
+ const sourceContext = protect.getSourceContext();
48
48
  if (!sourceContext) return;
49
49
 
50
50
  for (let i = 0; i < arity; i++) {
@@ -62,7 +62,7 @@ module.exports = function (core) {
62
62
 
63
63
  const vmInstrumentation = inputTracing.vmInstrumentation = {
64
64
  install() {
65
- depHooks.resolve({ name: 'vm' }, (vm) => {
65
+ depHooks.resolve({ name: 'vm', version: '*' }, (vm) => {
66
66
  VM_METHODS.forEach(([method, arity]) => {
67
67
  patcher.patch(vm, method, {
68
68
  name: `vm.${method}`,
@@ -45,7 +45,7 @@ module.exports = function (core) {
45
45
  name,
46
46
  patchType,
47
47
  pre({ args, hooked, orig, funcKey }) {
48
- const sourceContext = protect.getSourceContext(name);
48
+ const sourceContext = protect.getSourceContext();
49
49
  const [value, options] = args;
50
50
 
51
51
  if (!sourceContext || !value || !isString(value) || checkOptions(options)) {
@@ -78,11 +78,11 @@ module.exports = function (core) {
78
78
  protect.semanticAnalysis.libxmljs = {
79
79
  install() {
80
80
  // libxmljs changed its API in version 1.0.0
81
- depHooks.resolve({ name: 'libxmljs', version: '>=1' }, handler(true));
81
+ depHooks.resolve({ name: 'libxmljs', version: '>=1 <2' }, handler(true));
82
82
 
83
83
  // libxmljs versions prior to 1.0.0 and libxmljs2 share the same API
84
84
  depHooks.resolve({ name: 'libxmljs', version: '<1' }, handler(false));
85
- depHooks.resolve({ name: 'libxmljs2' }, handler(false));
85
+ depHooks.resolve({ name: 'libxmljs2', version: '<1' }, handler(false));
86
86
  }
87
87
  };
88
88
 
@@ -35,13 +35,13 @@ describe('protect semantic-analysis libxmljs', function () {
35
35
  };
36
36
 
37
37
  core.depHooks.resolve
38
- .withArgs({ name: 'libxmljs', version: '>=1' })
38
+ .withArgs({ name: 'libxmljs', version: '>=1 <2' })
39
39
  .yields(modules['libxmljs@1'], { name: 'libxmljs', version: '1.0.9' });
40
40
  core.depHooks.resolve
41
41
  .withArgs({ name: 'libxmljs', version: '<1' })
42
42
  .yields(modules['libxmljs@0'], { name: 'libxmljs', version: '0.19.10' });
43
43
  core.depHooks.resolve
44
- .withArgs({ name: 'libxmljs2' })
44
+ .withArgs({ name: 'libxmljs2', version: '<1' })
45
45
  .yields(modules.libxmljs2, { name: 'libxmljs2', version: '0.32.0' });
46
46
 
47
47
  require('../../get-source-context')(core);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@contrast/protect",
3
- "version": "1.49.0",
3
+ "version": "1.51.0",
4
4
  "description": "Contrast service providing framework-agnostic Protect support",
5
5
  "license": "SEE LICENSE IN LICENSE",
6
6
  "author": "Contrast Security <nodejs@contrastsecurity.com> (https://www.contrastsecurity.com)",
@@ -18,20 +18,18 @@
18
18
  },
19
19
  "dependencies": {
20
20
  "@contrast/agent-lib": "^7.0.1",
21
- "@contrast/common": "1.26.0",
22
- "@contrast/config": "1.36.0",
23
- "@contrast/core": "1.41.1",
24
- "@contrast/dep-hooks": "1.10.0",
25
- "@contrast/esm-hooks": "2.15.1",
26
- "@contrast/instrumentation": "1.20.0",
27
- "@contrast/logger": "1.14.0",
28
- "@contrast/patcher": "1.13.0",
29
- "@contrast/rewriter": "1.17.1",
30
- "@contrast/scopes": "1.11.0",
21
+ "@contrast/common": "1.28.0",
22
+ "@contrast/config": "1.38.0",
23
+ "@contrast/core": "1.43.0",
24
+ "@contrast/dep-hooks": "1.12.0",
25
+ "@contrast/esm-hooks": "2.17.0",
26
+ "@contrast/instrumentation": "1.22.0",
27
+ "@contrast/logger": "1.16.0",
28
+ "@contrast/patcher": "1.15.0",
29
+ "@contrast/rewriter": "1.19.0",
30
+ "@contrast/scopes": "1.13.0",
31
+ "async-hook-domain": "^4.0.1",
31
32
  "ipaddr.js": "^2.0.1",
32
33
  "semver": "^7.6.0"
33
- },
34
- "optionalDependencies": {
35
- "async-hook-domain": "^3.0.2"
36
34
  }
37
35
  }
@@ -1,138 +0,0 @@
1
- /*
2
- * Copyright: 2024 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 SecurityException = require('../../security-exception');
19
- const { patchType } = require('../constants');
20
-
21
-
22
- module.exports = function (core) {
23
- const {
24
- logger,
25
- depHooks,
26
- patcher,
27
- protect,
28
- } = core;
29
-
30
- const express4ErrorHandler = protect.errorHandlers.express4ErrorHandler = {};
31
-
32
- function aroundFn(name) {
33
- return function around(orig, data) {
34
- const [err] = data.args;
35
- const sourceContext = protect.getSourceContext(name);
36
- const isSecurityException = SecurityException.isSecurityException(err);
37
-
38
- if (isSecurityException && sourceContext) {
39
- const blockInfo = sourceContext.securityException;
40
-
41
- sourceContext.block(...blockInfo);
42
- return;
43
- }
44
-
45
- if (!sourceContext && isSecurityException) {
46
- logger.info({ funcKey: data.funcKey }, 'source context not found; unable to handle response');
47
- }
48
- return orig();
49
- };
50
- }
51
-
52
- express4ErrorHandler.install = function () {
53
- depHooks.resolve({ name: 'finalhandler' }, (finalhandler) =>
54
- patcher.patch(finalhandler, {
55
- name: 'finalHandler',
56
- patchType,
57
- post(data) {
58
- data.result = patcher.patch(data.result, {
59
- name: 'finalHandler.returnedFunction',
60
- patchType,
61
- around: aroundFn('finalHandler')
62
- });
63
- },
64
- })
65
- );
66
-
67
- depHooks.resolve({ name: 'express', version: '>=4.0.0 <5.0.0', file: 'lib/router/layer.js' }, (Layer) => {
68
- patcher.patch(Layer.prototype, 'handle_error', {
69
- name: 'Layer.prototype.handle_error',
70
- patchType,
71
- around: aroundFn('express.Layer.handle_error')
72
- });
73
-
74
- // This should be revisited after the research ticket NODE-2556
75
- Object.defineProperty(Layer.prototype, 'handle', {
76
- enumerable: true,
77
- configurable: true,
78
- get() {
79
- return this.__handle;
80
- },
81
- set(fn) {
82
- fn = patchFn(fn);
83
- this.__handle = fn;
84
- },
85
- });
86
- });
87
-
88
- // This should be revisited after the research ticket NODE-2556
89
- depHooks.resolve({ name: 'express', version: '>=4.0.0 <5.0.0', file: 'lib/router/index.js' }, (Router) => {
90
- patcher.patch(Router.prototype.constructor, 'param', {
91
- name: 'Router.prototype.constructor.param',
92
- patchType,
93
- pre({ args }) {
94
- if (typeof args[1] === 'function') {
95
- args[1] = patchFn(args[1]);
96
- }
97
- }
98
- });
99
- });
100
-
101
- // This should be revisited after the research ticket NODE-2556
102
- function patchFn(fn) {
103
- return patcher.patch(fn, {
104
- name: 'express.route-handler',
105
- patchType,
106
- around(orig, data) {
107
- const ret = orig();
108
- if (ret && ret.catch) {
109
- return ret.catch((err) => {
110
- const sourceContext = protect.getSourceContext();
111
- const isSecurityException = SecurityException.isSecurityException(err);
112
-
113
- if (isSecurityException && sourceContext) {
114
- const blockInfo = sourceContext.securityException;
115
-
116
- sourceContext.block(...blockInfo);
117
- return;
118
- }
119
-
120
- if (!sourceContext && isSecurityException) {
121
- logger.info({ funcKey: data.funcKey }, 'source context not found; unable to handle response');
122
- return;
123
- }
124
-
125
- throw err;
126
- });
127
- }
128
-
129
- return ret;
130
- }
131
- });
132
- }
133
-
134
-
135
- };
136
-
137
- return express4ErrorHandler;
138
- };