@contrast/protect 1.39.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,62 @@
1
+ 'use strict';
2
+
3
+ const { expect } = require('chai');
4
+ const sinon = require('sinon');
5
+ const proxyquire = require('proxyquire');
6
+
7
+ const MODULES = [
8
+ 'childProcess',
9
+ 'eval',
10
+ 'fs',
11
+ 'function',
12
+ 'http',
13
+ 'http2',
14
+ 'marsdb',
15
+ 'mongodb',
16
+ 'mssql',
17
+ 'mysql',
18
+ 'postgres',
19
+ 'sequelize',
20
+ 'spdy',
21
+ 'sqlite3',
22
+ 'vm',
23
+ ];
24
+
25
+ describe('protect input-tracing', function () {
26
+ let core, inputTracing;
27
+
28
+ beforeEach(function () {
29
+ core = {};
30
+ core.protect = {};
31
+
32
+ const hooksMock = (hookProp) => (core) => {
33
+ core.protect.inputTracing[hookProp] = { install: sinon.stub() };
34
+ };
35
+
36
+ inputTracing = proxyquire('.', {
37
+ './install/child-process': hooksMock('childProcess'),
38
+ './install/eval': hooksMock('eval'),
39
+ './install/fs': hooksMock('fs'),
40
+ './install/function': hooksMock('function'),
41
+ './install/http': hooksMock('http'),
42
+ './install/http2': hooksMock('http2'),
43
+ './install/marsdb': hooksMock('marsdb'),
44
+ './install/mongodb': hooksMock('mongodb'),
45
+ './install/mssql': hooksMock('mssql'),
46
+ './install/mysql': hooksMock('mysql'),
47
+ './install/postgres': hooksMock('postgres'),
48
+ './install/sequelize': hooksMock('sequelize'),
49
+ './install/spdy': hooksMock('spdy'),
50
+ './install/sqlite3': hooksMock('sqlite3'),
51
+ './install/vm': hooksMock('vm'),
52
+ })(core);
53
+ });
54
+
55
+ it('calls the install() method of the required hooks', function () {
56
+ inputTracing.install();
57
+
58
+ MODULES.forEach((module) => {
59
+ expect(inputTracing[module].install).to.have.been.called;
60
+ });
61
+ });
62
+ });
@@ -0,0 +1,133 @@
1
+ 'use strict';
2
+
3
+ const sinon = require('sinon');
4
+ const { expect } = require('chai');
5
+ const patcher = require('@contrast/patcher');
6
+ const mocks = require('@contrast/test/mocks/');
7
+
8
+ describe('protect input-tracing child-process', function () {
9
+ const cpInstr = require('./child-process');
10
+ const store = { protect: {} };
11
+ let cp, origCp, core;
12
+
13
+ beforeEach(function () {
14
+ origCp = {
15
+ exec: sinon.stub(),
16
+ execSync: sinon.stub()
17
+ };
18
+ cp = {
19
+ exec: origCp.exec,
20
+ execSync: origCp.execSync
21
+ };
22
+
23
+ core = mocks.core();
24
+ core.logger = mocks.logger();
25
+ core.depHooks = mocks.depHooks();
26
+ core.depHooks.resolve.yields(cp);
27
+ core.scopes = mocks.scopes();
28
+ core.scopes.sources = {
29
+ getStore: sinon.stub().returns(store)
30
+ };
31
+ core.patcher = patcher(core);
32
+ core.protect = mocks.protect();
33
+ require('../../get-source-context')(core);
34
+
35
+ cpInstr(core);
36
+
37
+ core.protect.inputTracing.cpInstrumentation.install();
38
+ });
39
+
40
+ describe('handleCommandInjection() is called with expected values', function () {
41
+ const methodArgs = ['foo', 'bar'];
42
+
43
+ ['exec', 'execSync'].forEach((method) => {
44
+ const name = `child_process.${method}`;
45
+
46
+ it(`${name}`, function () {
47
+ cp[method](...methodArgs);
48
+
49
+ const value = methodArgs[0];
50
+
51
+ expect(core.protect.inputTracing.handleCommandInjection).to.have.been.calledWith(
52
+ store.protect,
53
+ { name, value, stacktraceOpts: { constructorOpt: cp[method], prependFrames: [sinon.match.func] } }
54
+ );
55
+ expect(core.protect.semanticAnalysis.handleCommandInjectionCommandBackdoors).to.have.been.calledWith(
56
+ store.protect,
57
+ { name, value, stacktraceOpts: { constructorOpt: cp[method], prependFrames: [sinon.match.func] } }
58
+ );
59
+ expect(core.protect.semanticAnalysis.handleCmdInjectionSemanticChainedCommands).to.have.been.calledWith(
60
+ store.protect,
61
+ { name, value, stacktraceOpts: { constructorOpt: cp[method], prependFrames: [sinon.match.func] } }
62
+ );
63
+ expect(core.protect.semanticAnalysis.handleCmdInjectionSemanticDangerous).to.have.been.calledWith(
64
+ store.protect,
65
+ { name, value, stacktraceOpts: { constructorOpt: cp[method], prependFrames: [sinon.match.func] } }
66
+ );
67
+ expect(core.protect.semanticAnalysis.handlePathTraversalFileSecurityBypass).to.have.been.calledWith(
68
+ store.protect,
69
+ { name, value, stacktraceOpts: { constructorOpt: cp[method], prependFrames: [sinon.match.func] } }
70
+ );
71
+ });
72
+ });
73
+ });
74
+
75
+ describe('handleCommandInjection() is not called when method args are not relevant', function () {
76
+ const methodArgs = [1, undefined];
77
+
78
+ ['exec', 'execSync'].forEach((method) => {
79
+ const name = `child_process.${method}`;
80
+
81
+ it(name, function () {
82
+ cp[method](...methodArgs);
83
+
84
+ expect(core.protect.inputTracing.handleCommandInjection).not.to.have.been.called;
85
+ expect(core.protect.semanticAnalysis.handleCommandInjectionCommandBackdoors).not.to.have.been.called;
86
+ expect(core.protect.semanticAnalysis.handleCmdInjectionSemanticChainedCommands).not.to.have.been.called;
87
+ expect(core.protect.semanticAnalysis.handleCmdInjectionSemanticDangerous).not.to.have.been.called;
88
+ });
89
+ });
90
+ });
91
+
92
+ describe('handleCommandInjection() is not called when instrumentation is locked', function () {
93
+ const methodArgs = ['foo', 'bar'];
94
+
95
+ beforeEach(function () {
96
+ core.scopes.instrumentation.isLocked.returns(true);
97
+ });
98
+
99
+ ['exec', 'execSync'].forEach((method) => {
100
+ const name = `child_process.${method}`;
101
+
102
+ it(name, function () {
103
+ cp[method](...methodArgs);
104
+
105
+ expect(core.protect.inputTracing.handleCommandInjection).not.to.have.been.called;
106
+ expect(core.protect.semanticAnalysis.handleCommandInjectionCommandBackdoors).not.to.have.been.called;
107
+ expect(core.protect.semanticAnalysis.handleCmdInjectionSemanticChainedCommands).not.to.have.been.called;
108
+ expect(core.protect.semanticAnalysis.handleCmdInjectionSemanticDangerous).not.to.have.been.called;
109
+ });
110
+ });
111
+ });
112
+
113
+ describe('handleCommandInjection() is not called when there is no Protect sourceContext', function () {
114
+ const methodArgs = ['foo', 'bar'];
115
+
116
+ beforeEach(function () {
117
+ core.scopes.sources.getStore.returns({ protect: null });
118
+ });
119
+
120
+ ['exec', 'execSync'].forEach((method) => {
121
+ const name = `child_process.${method}`;
122
+
123
+ it(name, function () {
124
+ cp[method](...methodArgs);
125
+
126
+ expect(core.protect.inputTracing.handleCommandInjection).not.to.have.been.called;
127
+ expect(core.protect.semanticAnalysis.handleCommandInjectionCommandBackdoors).not.to.have.been.called;
128
+ expect(core.protect.semanticAnalysis.handleCmdInjectionSemanticChainedCommands).not.to.have.been.called;
129
+ expect(core.protect.semanticAnalysis.handleCmdInjectionSemanticDangerous).not.to.have.been.called;
130
+ });
131
+ });
132
+ });
133
+ });
@@ -0,0 +1,78 @@
1
+ 'use strict';
2
+
3
+ const sinon = require('sinon');
4
+ const { expect } = require('chai');
5
+ const patcher = require('@contrast/patcher');
6
+ const mocks = require('@contrast/test/mocks');
7
+
8
+ describe('protect input-tracing eval', function () {
9
+ const evalInstr = require('./eval');
10
+ const store = { protect: {} };
11
+ let evalArg, core;
12
+
13
+ beforeEach(function () {
14
+ evalArg = 'function() { return "hi"; }';
15
+
16
+ if (!global.ContrastMethods) {
17
+ global.ContrastMethods = {};
18
+ }
19
+ global.ContrastMethods.eval = sinon.stub();
20
+
21
+ core = mocks.core();
22
+ core.logger = mocks.logger();
23
+ core.depHooks = mocks.depHooks();
24
+ core.depHooks.resolve.yields(global.ContrastMethods.eval);
25
+ core.scopes = mocks.scopes();
26
+ sinon.stub(core.scopes.sources, 'getStore').returns(store);
27
+ core.patcher = patcher(core);
28
+ core.protect = mocks.protect();
29
+ require('../../get-source-context')(core);
30
+
31
+ evalInstr(core);
32
+
33
+ core.protect.inputTracing.evalInstrumentation.install();
34
+ });
35
+
36
+ afterEach(function () {
37
+ Reflect.deleteProperty(global.ContrastMethods);
38
+ });
39
+
40
+ it('ssjsInjection() is called with expected values', function () {
41
+ global.ContrastMethods.eval(evalArg);
42
+
43
+ expect(core.protect.inputTracing.ssjsInjection).to.have.been.calledWith(
44
+ store.protect,
45
+ { name: 'eval', value: evalArg, stacktraceOpts: { constructorOpt: sinon.match.func, prependFrames: [sinon.match.func] } }
46
+ );
47
+ });
48
+
49
+ it('ssjsInjection() is not called when method args are not relevant', function () {
50
+ global.ContrastMethods.eval(1, evalArg);
51
+
52
+ expect(core.protect.inputTracing.ssjsInjection).not.to.have.been.called;
53
+ });
54
+
55
+ it('ssjsInjection() is not called when instrumentation is locked', function () {
56
+ core.scopes.instrumentation.isLocked.returns(true);
57
+
58
+ global.ContrastMethods.eval(evalArg);
59
+
60
+ expect(core.protect.inputTracing.ssjsInjection).not.to.have.been.called;
61
+ });
62
+
63
+ it('ssjsInjection() is not called when there is no Protect sourceContext', function () {
64
+ core.scopes.sources.getStore.returns({ protect: null });
65
+
66
+ global.ContrastMethods.eval(evalArg);
67
+
68
+ expect(core.protect.inputTracing.ssjsInjection).not.to.have.been.called;
69
+ });
70
+
71
+ it('logs an error when there is no `global.ContrastMethods.eval` method', function () {
72
+ global.ContrastMethods.eval = null;
73
+
74
+ core.protect.inputTracing.evalInstrumentation.install();
75
+
76
+ expect(core.logger.error).to.have.been.calledOnceWith('Cannot install `eval` instrumentation - Contrast method DNE');
77
+ });
78
+ });
@@ -0,0 +1,108 @@
1
+ 'use strict';
2
+
3
+ const sinon = require('sinon');
4
+ const { expect } = require('chai');
5
+ const { FS_METHODS } = require('@contrast/common');
6
+ const patcher = require('@contrast/patcher');
7
+ const mocks = require('@contrast/test/mocks');
8
+
9
+ describe('protect input-tracing fs', function () {
10
+ const store = { protect: {} };
11
+ let fs, fsPromises, core;
12
+
13
+ beforeEach(function () {
14
+ fs = {
15
+ promises: {}
16
+ };
17
+ fsPromises = {};
18
+
19
+ for (const { name, promises, sync } of FS_METHODS) {
20
+ fs[name] = sinon.stub();
21
+ if (sync) {
22
+ fs[`${name}Sync`] = sinon.stub();
23
+ }
24
+ if (promises) {
25
+ fsPromises[name] = sinon.stub();
26
+ fs.promises[name] = sinon.stub();
27
+ }
28
+ }
29
+
30
+ core = mocks.core();
31
+ core.logger = mocks.logger();
32
+ core.depHooks = mocks.depHooks();
33
+ core.depHooks.resolve.withArgs({ name: 'fs' }).yields(fs);
34
+ core.depHooks.resolve.withArgs({ name: 'fs/promises' }).yields(fsPromises);
35
+ core.scopes = mocks.scopes();
36
+ sinon.stub(core.scopes.sources, 'getStore').returns(store);
37
+ core.patcher = patcher(core);
38
+ core.protect = mocks.protect();
39
+ require('../../get-source-context')(core);
40
+
41
+ require('./fs')(core).install();
42
+ });
43
+
44
+ for (const { name, promises, sync, indices } of FS_METHODS) {
45
+ const methodsUnderTest = [{ name: `fs.${name}`, getFn: () => fs[name] }];
46
+ if (sync) {
47
+ methodsUnderTest.push({ name: `fs.${name}Sync`, getFn: () => fs[`${name}Sync`] });
48
+ }
49
+ if (promises) {
50
+ methodsUnderTest.push({ name: `fsPromises.${name}`, getFn: () => fsPromises[name] });
51
+ methodsUnderTest.push({ name: `fs.promises.${name}`, getFn: () => fs.promises[name] });
52
+ }
53
+
54
+ for (const { name, getFn } of methodsUnderTest) {
55
+ describe(name, function () {
56
+ it('handlePathTraversal() is called with expected values', function () {
57
+ const method = getFn();
58
+ const methodArgs = ['foo', 'bar'];
59
+ method(...methodArgs);
60
+
61
+ for (const idx of indices) {
62
+ const value = methodArgs[idx];
63
+
64
+ expect(core.protect.inputTracing.handlePathTraversal).to.have.been.calledWith(
65
+ store.protect,
66
+ {
67
+ name,
68
+ value,
69
+ stacktraceOpts: {
70
+ constructorOpt: method,
71
+ prependFrames: [sinon.match.func]
72
+ }
73
+ }
74
+ );
75
+ }
76
+ });
77
+
78
+ it('handlePathTraversal() is not called when method args are not relevant', function () {
79
+ const method = getFn();
80
+ const methodArgs = [1, undefined];
81
+ method(...methodArgs);
82
+
83
+ expect(core.protect.inputTracing.handlePathTraversal).not.to.have.been.called;
84
+ });
85
+
86
+
87
+ it('handlePathTraversal() is not called when instrumentation is locked', function () {
88
+ const method = getFn();
89
+ const methodArgs = ['foo', 'bar'];
90
+ core.scopes.instrumentation.isLocked.returns(true);
91
+ method(...methodArgs);
92
+
93
+ expect(core.protect.inputTracing.handlePathTraversal).not.to.have.been.called;
94
+ });
95
+
96
+
97
+ it('handlePathTraversal() is not called when there is no Protect sourceContext', function () {
98
+ const method = getFn();
99
+ const methodArgs = ['foo', 'bar'];
100
+ core.scopes.sources.getStore.returns({ protect: null });
101
+ method(...methodArgs);
102
+
103
+ expect(core.protect.inputTracing.handlePathTraversal).not.to.have.been.called;
104
+ });
105
+ });
106
+ }
107
+ }
108
+ });
@@ -0,0 +1,81 @@
1
+ 'use strict';
2
+
3
+ const sinon = require('sinon');
4
+ const { expect } = require('chai');
5
+ const patcher = require('@contrast/patcher');
6
+ const mocks = require('@contrast/test/mocks');
7
+
8
+ describe('protect input-tracing function', function () {
9
+ const functionInstr = require('./function');
10
+ const store = { protect: {} };
11
+ let constructorArg, core;
12
+
13
+ beforeEach(function () {
14
+ constructorArg = 'return "hi";';
15
+
16
+ if (!global.ContrastMethods) {
17
+ global.ContrastMethods = {};
18
+ }
19
+ global.ContrastMethods.Function = sinon.stub();
20
+
21
+ core = mocks.core();
22
+ core.logger = mocks.logger();
23
+ core.depHooks = mocks.depHooks();
24
+ core.depHooks.resolve.yields(global.ContrastMethods.Function);
25
+ core.scopes = mocks.scopes();
26
+ sinon.stub(core.scopes.sources, 'getStore').returns(store);
27
+ core.patcher = patcher(core);
28
+ core.protect = mocks.protect();
29
+ require('../../get-source-context')(core);
30
+
31
+ functionInstr(core);
32
+
33
+ core.protect.inputTracing.functionInstrumentation.install();
34
+ });
35
+
36
+ afterEach(function () {
37
+ Reflect.deleteProperty(global.ContrastMethods);
38
+ });
39
+
40
+ it('ssjsInjection() is called with expected values', function () {
41
+ global.ContrastMethods.Function(constructorArg);
42
+
43
+ expect(core.protect.inputTracing.ssjsInjection).to.have.been.calledWith(
44
+ store.protect,
45
+ { name: 'Function', value: constructorArg, stacktraceOpts: { constructorOpt: sinon.match.func, prependFrames: [sinon.match.func] } }
46
+ );
47
+ });
48
+
49
+ it('ssjsInjection() is called only with the value for the function body', function () {
50
+ global.ContrastMethods.Function('a', 'return a');
51
+
52
+ expect(core.protect.inputTracing.ssjsInjection).to.have.been.calledOnceWithExactly(
53
+ store.protect,
54
+ { name: 'Function', value: 'return a', stacktraceOpts: { constructorOpt: sinon.match.func, prependFrames: [sinon.match.func] } }
55
+ );
56
+ });
57
+
58
+ it('ssjsInjection() is not called when instrumentation is locked', function () {
59
+ core.scopes.instrumentation.isLocked.returns(true);
60
+
61
+ global.ContrastMethods.Function(constructorArg);
62
+
63
+ expect(core.protect.inputTracing.ssjsInjection).not.to.have.been.called;
64
+ });
65
+
66
+ it('ssjsInjection() is not called when there is no Protect sourceContext', function () {
67
+ core.scopes.sources.getStore.returns({ protect: null });
68
+
69
+ global.ContrastMethods.Function(constructorArg);
70
+
71
+ expect(core.protect.inputTracing.ssjsInjection).not.to.have.been.called;
72
+ });
73
+
74
+ it('logs an error when there is no `global.ContrastMethods.eval` method', function () {
75
+ global.ContrastMethods.Function = null;
76
+
77
+ core.protect.inputTracing.functionInstrumentation.install();
78
+
79
+ expect(core.logger.error).to.have.been.calledOnceWith('Cannot install `Function` instrumentation - Contrast method DNE');
80
+ });
81
+ });
@@ -0,0 +1,85 @@
1
+ 'use strict';
2
+
3
+ const sinon = require('sinon');
4
+ const { expect } = require('chai');
5
+ const scopes = require('@contrast/scopes');
6
+ const patcher = require('@contrast/patcher');
7
+ const mocks = require('@contrast/test/mocks');
8
+
9
+ describe('protect input-tracing http', function () {
10
+ let core, inputTracing;
11
+ const store = { protect: {} };
12
+
13
+ beforeEach(function () {
14
+ core = mocks.core();
15
+ core.logger = mocks.logger();
16
+ core.patcher = patcher(core);
17
+ core.scopes = scopes(core);
18
+ sinon.stub(core.scopes.sources, 'getStore').returns(store);
19
+ core.depHooks = mocks.depHooks();
20
+ core.protect = mocks.protect();
21
+ require('../../get-source-context')(core);
22
+ ({ protect: { inputTracing } } = core);
23
+
24
+ });
25
+
26
+ describe('http', function () {
27
+ let http;
28
+
29
+ beforeEach(function () {
30
+ http = function () { };
31
+ http.ServerResponse = function () { };
32
+ http.ServerResponse.prototype.end = sinon.stub();
33
+ http.ServerResponse.prototype.write = sinon.stub();
34
+ core.depHooks.resolve.yields(http);
35
+ require('./http')(core).install();
36
+ });
37
+
38
+ ['write', 'end'].forEach((method) => {
39
+ describe(`instruments ServerResponse.${method}()`, function () {
40
+ it('handleReflectedXss() is called with valid expected values', function () {
41
+ const serverResponse = new http.ServerResponse();
42
+ const value = '" onmouseover="alert(3)"';
43
+ serverResponse[method](value);
44
+
45
+ expect(inputTracing.handleReflectedXss).to.have.been.calledOnce;
46
+ expect(inputTracing.handleReflectedXss).to.have.been.calledWith(
47
+ {},
48
+ {
49
+ name: `http.ServerResponse.prototype.${method}`,
50
+ value,
51
+ stacktraceOpts: {
52
+ constructorOpt: serverResponse[method]
53
+ }
54
+ }
55
+ );
56
+ });
57
+
58
+ it('handleReflectedXss() is not called if instrumentation is locked', function () {
59
+ sinon.stub(core.scopes.instrumentation, 'isLocked').returns(true);
60
+ const serverResponse = new http.ServerResponse();
61
+ const value = '" onmouseover="alert(3)"';
62
+ serverResponse[method](value);
63
+
64
+ expect(inputTracing.handleReflectedXss).not.to.have.been.called;
65
+ });
66
+
67
+ it('handleReflectedXss() is not called if there is no sourceContext', function () {
68
+ core.scopes.sources.getStore.returns({ protect: undefined });
69
+ const serverResponse = new http.ServerResponse();
70
+ const value = '" onmouseover="alert(3)"';
71
+ serverResponse[method](value);
72
+
73
+ expect(inputTracing.handleReflectedXss).not.to.have.been.called;
74
+ });
75
+
76
+ it('handleReflectedXss() is not called if there is no value', function () {
77
+ const serverResponse = new http.ServerResponse();
78
+ serverResponse[method]();
79
+
80
+ expect(inputTracing.handleReflectedXss).not.to.have.been.called;
81
+ });
82
+ });
83
+ });
84
+ });
85
+ });
@@ -0,0 +1,83 @@
1
+ 'use strict';
2
+
3
+ const sinon = require('sinon');
4
+ const { expect } = require('chai');
5
+ const scopes = require('@contrast/scopes');
6
+ const patcher = require('@contrast/patcher');
7
+ const mocks = require('@contrast/test/mocks');
8
+
9
+ describe('protect input-tracing http2', function () {
10
+ let core, inputTracing;
11
+ const store = { protect: {} };
12
+
13
+ beforeEach(function () {
14
+ core = mocks.core();
15
+ core.logger = mocks.logger();
16
+ core.patcher = patcher(core);
17
+ core.scopes = scopes(core);
18
+ sinon.stub(core.scopes.sources, 'getStore').returns(store);
19
+ core.depHooks = mocks.depHooks();
20
+ core.protect = mocks.protect();
21
+ require('../../get-source-context')(core);
22
+ ({ protect: { inputTracing } } = core);
23
+ });
24
+
25
+ describe('http2', function () {
26
+ let http2;
27
+
28
+ beforeEach(function () {
29
+ http2 = function () { };
30
+ http2.Http2ServerResponse = function () { };
31
+ http2.Http2ServerResponse.prototype.end = sinon.stub();
32
+ http2.Http2ServerResponse.prototype.write = sinon.stub();
33
+ core.depHooks.resolve.yields(http2);
34
+ require('./http2')(core).install();
35
+ });
36
+
37
+ ['write', 'end'].forEach((method) => {
38
+ it('handleReflectedXss() is not called if there is no value', function () {
39
+ const serverResponse = new http2.Http2ServerResponse();
40
+ serverResponse[method]();
41
+
42
+ expect(inputTracing.handleReflectedXss).not.to.have.been.called;
43
+ });
44
+
45
+ it('handleReflectedXss() is not called if there is no sourceContext', function () {
46
+ core.scopes.sources.getStore.returns({ protect: undefined });
47
+ const serverResponse = new http2.Http2ServerResponse();
48
+ const value = "<script>alert('xss');</script>";
49
+ serverResponse[method](value);
50
+
51
+ expect(inputTracing.handleReflectedXss).not.to.have.been.called;
52
+ });
53
+
54
+ it('handleReflectedXss() is not called if instrumentation is locked', function () {
55
+ sinon.stub(core.scopes.instrumentation, 'isLocked').returns(true);
56
+ const serverResponse = new http2.Http2ServerResponse();
57
+ const value = "<script>alert('xss');</script>";
58
+ serverResponse[method](value);
59
+
60
+ expect(inputTracing.handleReflectedXss).not.to.have.been.called;
61
+ });
62
+
63
+ describe(`instruments Http2ServerResponse.${method}()`, function () {
64
+ it('handleReflectedXss() is called with valid expected values', function () {
65
+ const serverResponse = new http2.Http2ServerResponse();
66
+ const value = "<script>alert('xss');</script>";
67
+ serverResponse[method](value);
68
+
69
+ expect(inputTracing.handleReflectedXss).to.have.been.calledOnceWith(
70
+ {},
71
+ {
72
+ name: `http2.Http2ServerResponse.prototype.${method}`,
73
+ value,
74
+ stacktraceOpts: {
75
+ constructorOpt: serverResponse[method]
76
+ }
77
+ }
78
+ );
79
+ });
80
+ });
81
+ });
82
+ });
83
+ });