@contrast/protect 1.38.0 → 1.40.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 (58) hide show
  1. package/lib/error-handlers/common-handler.test.js +52 -0
  2. package/lib/error-handlers/index.test.js +32 -0
  3. package/lib/error-handlers/init-domain.test.js +34 -0
  4. package/lib/error-handlers/install/express4.test.js +238 -0
  5. package/lib/error-handlers/install/fastify.test.js +130 -0
  6. package/lib/error-handlers/install/hapi.test.js +102 -0
  7. package/lib/error-handlers/install/koa2.test.js +83 -0
  8. package/lib/error-handlers/install/restify.test.js +57 -0
  9. package/lib/get-source-context.test.js +35 -0
  10. package/lib/hardening/handlers.test.js +89 -0
  11. package/lib/hardening/index.test.js +31 -0
  12. package/lib/hardening/install/node-serialize0.test.js +61 -0
  13. package/lib/index.test.js +53 -0
  14. package/lib/input-analysis/handlers.test.js +1604 -0
  15. package/lib/input-analysis/index.test.js +45 -0
  16. package/lib/input-analysis/install/body-parser1.test.js +134 -0
  17. package/lib/input-analysis/install/busboy1.test.js +81 -0
  18. package/lib/input-analysis/install/cookie-parser1.test.js +144 -0
  19. package/lib/input-analysis/install/express4.test.js +208 -0
  20. package/lib/input-analysis/install/fastify.test.js +96 -0
  21. package/lib/input-analysis/install/formidable1.test.js +114 -0
  22. package/lib/input-analysis/install/hapi.test.js +300 -0
  23. package/lib/input-analysis/install/http.test.js +264 -0
  24. package/lib/input-analysis/install/koa-body5.test.js +92 -0
  25. package/lib/input-analysis/install/koa-bodyparser4.test.js +92 -0
  26. package/lib/input-analysis/install/koa2.test.js +259 -0
  27. package/lib/input-analysis/install/multer1.test.js +209 -0
  28. package/lib/input-analysis/install/qs6.test.js +79 -0
  29. package/lib/input-analysis/install/restify.test.js +98 -0
  30. package/lib/input-analysis/install/universal-cookie4.test.js +70 -0
  31. package/lib/input-analysis/ip-analysis.test.js +71 -0
  32. package/lib/input-analysis/virtual-patches.test.js +106 -0
  33. package/lib/input-tracing/handlers/index.test.js +1236 -0
  34. package/lib/input-tracing/index.test.js +62 -0
  35. package/lib/input-tracing/install/child-process.test.js +133 -0
  36. package/lib/input-tracing/install/eval.test.js +78 -0
  37. package/lib/input-tracing/install/fs.test.js +108 -0
  38. package/lib/input-tracing/install/function.test.js +81 -0
  39. package/lib/input-tracing/install/http.test.js +85 -0
  40. package/lib/input-tracing/install/http2.test.js +83 -0
  41. package/lib/input-tracing/install/marsdb.test.js +126 -0
  42. package/lib/input-tracing/install/mongodb.test.js +282 -0
  43. package/lib/input-tracing/install/mssql.test.js +81 -0
  44. package/lib/input-tracing/install/mysql.test.js +108 -0
  45. package/lib/input-tracing/install/postgres.test.js +125 -0
  46. package/lib/input-tracing/install/sequelize.test.js +78 -0
  47. package/lib/input-tracing/install/spdy.test.js +76 -0
  48. package/lib/input-tracing/install/sqlite3.test.js +88 -0
  49. package/lib/input-tracing/install/vm.test.js +176 -0
  50. package/lib/make-response-blocker.test.js +99 -0
  51. package/lib/make-source-context.test.js +219 -0
  52. package/lib/policy.test.js +446 -0
  53. package/lib/semantic-analysis/handlers.test.js +379 -0
  54. package/lib/semantic-analysis/index.test.js +38 -0
  55. package/lib/semantic-analysis/install/libxmljs.test.js +156 -0
  56. package/lib/semantic-analysis/utils/xml-analysis.test.js +156 -0
  57. package/lib/throw-security-exception.test.js +37 -0
  58. package/package.json +5 -5
@@ -0,0 +1,57 @@
1
+ 'use strict';
2
+
3
+ const { expect } = require('chai');
4
+ const sinon = require('sinon');
5
+ const { initProtectFixture } = require('@contrast/test/fixtures');
6
+ const SecurityException = require('../../security-exception');
7
+
8
+ describe('protect error-handlers restify v8+', function () {
9
+ let core, simulateRequestScope, Server, store;
10
+
11
+ beforeEach(function () {
12
+ ({ core, simulateRequestScope } = initProtectFixture());
13
+
14
+ Server = { prototype: { _onHandlerError: sinon.stub().returnsArg(0) } };
15
+ store = { protect: { block: sinon.stub(), securityException: ['block'] } };
16
+
17
+ core.depHooks.resolve
18
+ .withArgs({ name: 'restify', file: 'lib/server.js', version: '>=8' })
19
+ .yields(Server);
20
+
21
+ core.protect.errorHandlers.restifyErrorHandler.install();
22
+ });
23
+
24
+ it('blocks the request when a security exception is handled', function () {
25
+ const err = SecurityException.create();
26
+
27
+ simulateRequestScope(() => {
28
+ Server.prototype._onHandlerError(err);
29
+
30
+ expect(store.protect.block).to.have.been.calledWith(...store.protect.securityException);
31
+ }, store);
32
+ });
33
+
34
+ it('does not block the request when context is missing', function () {
35
+ const err = SecurityException.create();
36
+
37
+ simulateRequestScope(() => {
38
+ Server.prototype._onHandlerError(err);
39
+ expect(store.protect.block).not.to.have.been.called;
40
+ expect(core.logger.info).to.have.been.calledWith(
41
+ sinon.match.object,
42
+ 'source context not found; unable to handle response',
43
+ );
44
+ }, {});
45
+ });
46
+
47
+ it('does not handle other errors', function () {
48
+ const err = new Error();
49
+
50
+ simulateRequestScope(() => {
51
+ const result = Server.prototype._onHandlerError(err);
52
+ expect(store.protect.block).not.to.have.been.called;
53
+ expect(core.logger.info).not.to.have.been.called;
54
+ expect(result).to.equal(err);
55
+ }, store);
56
+ });
57
+ });
@@ -0,0 +1,35 @@
1
+ 'use strict';
2
+
3
+ const { expect } = require('chai');
4
+ const { initProtectFixture } = require('@contrast/test/fixtures');
5
+
6
+ describe('protect get-source-context', function () {
7
+ let core, getSourceContext;
8
+
9
+ beforeEach(function () {
10
+ ({ core } = initProtectFixture());
11
+ ({ getSourceContext } = core.protect);
12
+ });
13
+
14
+ it('returns the sourceContext when there is `allowed` property in it', function () {
15
+ core.scopes.sources.run({ protect: { test: 'store' } }, () => {
16
+ const result = getSourceContext();
17
+ expect(result).to.deep.equal({ test: 'store' });
18
+ });
19
+ });
20
+
21
+ it('logs a debug log when the sourceContext is missing and returns null', function () {
22
+ core.scopes.sources.run({ protect: null }, () => {
23
+ const result = getSourceContext();
24
+ expect(result).to.null;
25
+ });
26
+ });
27
+
28
+ it('returns null when the protect store has a thruthy `allowed` property', function () {
29
+ core.scopes.sources.run({ protect: { allowed: true } }, () => {
30
+ const result = getSourceContext();
31
+ expect(core.logger.debug).not.to.have.been.called;
32
+ expect(result).to.null;
33
+ });
34
+ });
35
+ });
@@ -0,0 +1,89 @@
1
+ 'use strict';
2
+
3
+ const { expect } = require('chai');
4
+ const mocks = require('@contrast/test/mocks');
5
+ const { create: createSecurityException } = require('../security-exception');
6
+ const handlersFactory = require('./handlers');
7
+
8
+ describe('protect hardening handlers', function () {
9
+ let core, handlers, map, sourceContext;
10
+
11
+ const exception = createSecurityException();
12
+ const sinkContext = {
13
+ name: 'node-serialize.unserialize',
14
+ value: '"foo":"_$$ND_FUNC$$_while(true)',
15
+ get stack() {
16
+ return [{
17
+ file: 'index.js',
18
+ lineNumber: 123,
19
+ method: '',
20
+ type: 'Function',
21
+ }];
22
+ },
23
+ };
24
+
25
+ function makePolicy(mode) {
26
+ return {
27
+ 'untrusted-deserialization': mode,
28
+ };
29
+ }
30
+
31
+ beforeEach(function () {
32
+ map = {};
33
+ sourceContext = {
34
+ resultsMap: map,
35
+ };
36
+ core = mocks.core();
37
+ core.protect = mocks.protect();
38
+ core.protect.hardening = {};
39
+ core.protect.throwSecurityException.throws(exception);
40
+ handlers = handlersFactory(core);
41
+ });
42
+
43
+ ['block', 'block_at_perimeter'].forEach((mode) => {
44
+ it(`${mode}`, function () {
45
+ sourceContext.policy = makePolicy(mode);
46
+
47
+ expect(function () {
48
+ handlers.handleUntrustedDeserialization(sourceContext, sinkContext);
49
+ }).to.throw(exception.constructor);
50
+
51
+ expect(map).to.have.deep.property('untrusted-deserialization', [{
52
+ blocked: true,
53
+ exploitMetadata: [{ deserializer: 'node-serialize.unserialize', command: false }],
54
+ sinkContext,
55
+ value: sinkContext.value,
56
+ }]);
57
+ });
58
+ });
59
+
60
+ it('monitor', function () {
61
+ sourceContext.policy = makePolicy('monitor');
62
+
63
+ handlers.handleUntrustedDeserialization(sourceContext, sinkContext);
64
+
65
+ expect(map).to.have.deep.property('untrusted-deserialization', [{
66
+ blocked: false,
67
+ exploitMetadata: [{ deserializer: 'node-serialize.unserialize', command: false }],
68
+ sinkContext,
69
+ value: sinkContext.value,
70
+ }]);
71
+ });
72
+
73
+ it('off', function () {
74
+ sourceContext.policy = makePolicy('off');
75
+
76
+ handlers.handleUntrustedDeserialization(sourceContext, sinkContext);
77
+
78
+ expect(map).not.to.have.deep.property('untrusted-deserialization');
79
+ });
80
+
81
+ it('returns if the result value type is incorrect', function () {
82
+ sourceContext.policy = makePolicy('monitor');
83
+ sinkContext.value = true;
84
+
85
+ handlers.handleUntrustedDeserialization(sourceContext, sinkContext);
86
+
87
+ expect(map).not.to.have.deep.property('untrusted-deserialization');
88
+ });
89
+ });
@@ -0,0 +1,31 @@
1
+ 'use strict';
2
+
3
+ const sinon = require('sinon');
4
+ const { expect } = require('chai');
5
+ const proxyquire = require('proxyquire');
6
+ const mocks = require('@contrast/test/mocks');
7
+
8
+ describe('protect hardening', function () {
9
+ let modulesMock, installStub, core, hardening;
10
+ beforeEach(function () {
11
+ installStub = sinon.stub();
12
+ modulesMock = (propertyName) => function (core) {
13
+ if (propertyName) {
14
+ core.protect.hardening[propertyName] = { install: installStub };
15
+ }
16
+ };
17
+ core = mocks.core();
18
+ core.protect = mocks.protect();
19
+
20
+ hardening = proxyquire('.', {
21
+ './handlers': modulesMock,
22
+ './install/node-serialize0': modulesMock('nodeSerialize0Instrumentation'),
23
+ // Total of 2 modules required - 1 handlers and 1 module to be installed
24
+ });
25
+ });
26
+
27
+ it('calls the install method on nodeSerialize0Instrumentation', function () {
28
+ hardening(core).install();
29
+ expect(installStub).to.have.been.calledOnce;
30
+ });
31
+ });
@@ -0,0 +1,61 @@
1
+ 'use strict';
2
+
3
+ const { expect } = require('chai');
4
+ const sinon = require('sinon');
5
+ const patcher = require('@contrast/patcher');
6
+ const scopes = require('@contrast/scopes');
7
+ const mocks = require('@contrast/test/mocks');
8
+ const instrumentationFactory = require('./node-serialize0');
9
+
10
+ describe('protect hardening node-serialize0', function () {
11
+ let core, mockLib, hardening;
12
+
13
+ beforeEach(function () {
14
+ mockLib = {
15
+ unserialize() { }
16
+ };
17
+
18
+ core = mocks.core();
19
+ core.logger = mocks.logger();
20
+ core.depHooks = mocks.depHooks();
21
+ core.depHooks
22
+ .resolve
23
+ .withArgs({ name: 'node-serialize', version: '<1.0.0' })
24
+ .yields(mockLib);
25
+ core.patcher = patcher(core);
26
+ core.scopes = scopes(core);
27
+ core.protect = mocks.protect();
28
+ require('../../get-source-context')(core);
29
+ hardening = core.protect.hardening;
30
+
31
+ instrumentationFactory(core).install();
32
+ });
33
+
34
+ it('calls hardening handler when called in sources scope', function () {
35
+ const value = '"foo":"_$$ND_FUNC$$_while(true)';
36
+ const sourceContext = {};
37
+ const store = { protect: sourceContext };
38
+ const sinkContext = {
39
+ name: 'node-serialize.unserialize',
40
+ value,
41
+ stacktraceOpts: { constructorOpt: mockLib.unserialize, prependFrames: [sinon.match.func] }
42
+ };
43
+
44
+ core.scopes.sources.run(store, function () {
45
+ mockLib.unserialize(value);
46
+ });
47
+
48
+ expect(hardening.handleUntrustedDeserialization).to.have.been.calledWith(
49
+ sourceContext,
50
+ sinkContext
51
+ );
52
+ });
53
+
54
+ it('hardening handler is not called when out of sources scope', function () {
55
+ const value = '"foo":"_$$ND_FUNC$$_while(true)';
56
+
57
+ mockLib.unserialize(value);
58
+
59
+ expect(hardening.handleUntrustedDeserialization).not.to.have.been.called;
60
+ });
61
+ });
@@ -0,0 +1,53 @@
1
+ 'use strict';
2
+
3
+ const { expect } = require('chai');
4
+ const proxyquire = require('proxyquire');
5
+ const mocks = require('@contrast/test/mocks');
6
+
7
+ const moduleMock = (moduleName, mock) => (deps) => {
8
+ deps.protect[moduleName] = mock[moduleName];
9
+ };
10
+
11
+ describe('protect', function () {
12
+ let core, protectMock, protectModule, modulesWithInstall;
13
+
14
+ beforeEach(function () {
15
+ protectMock = mocks.protect();
16
+ core = mocks.core();
17
+ core.logger = mocks.logger();
18
+ core.config = mocks.config();
19
+ core.scopes = mocks.scopes();
20
+ core.config.protect.rules = {
21
+ ['nosql-injection']: { mode: 'monitor' },
22
+ ['cmd-injection']: { mode: 'block' },
23
+ ['path-traversal']: { mode: 'off' }
24
+ };
25
+ core.config.protect.rules.disabled_rules = ['sql-injection'];
26
+ core.rewriter = mocks.rewriter();
27
+ protectModule = proxyquire('.', {
28
+ './input-analysis': moduleMock('inputAnalysis', protectMock),
29
+ './input-tracing': moduleMock('inputTracing', protectMock),
30
+ './semantic-analysis': moduleMock('semanticAnalysis', protectMock),
31
+ './hardening': moduleMock('hardening', protectMock),
32
+ './error-handlers': moduleMock('errorHandlers', protectMock),
33
+ });
34
+ modulesWithInstall = ['inputAnalysis', 'inputTracing', 'hardening', 'errorHandlers'];
35
+ });
36
+
37
+ it('installs components and initializes policy', function () {
38
+ core.config.getEffectiveValue.withArgs('protect.enable').returns(true);
39
+ protectModule(core).install();
40
+
41
+ modulesWithInstall.forEach((module) => {
42
+ expect(protectMock[module].install).to.have.been.calledOnce;
43
+ });
44
+
45
+ expect(core.protect.getPolicy()).to.deep.include({
46
+ rulesMask: 48,
47
+ 'cmd-injection': 'block',
48
+ 'nosql-injection': 'monitor',
49
+ });
50
+
51
+ expect(core.rewriter.install).to.have.been.calledWith('protect');
52
+ });
53
+ });