@contrast/protect 1.54.0 → 1.54.2
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.
- package/package.json +14 -11
- package/lib/error-handlers/common-handler.test.js +0 -52
- package/lib/error-handlers/index.test.js +0 -32
- package/lib/error-handlers/init-domain.test.js +0 -22
- package/lib/error-handlers/install/express.test.js +0 -290
- package/lib/error-handlers/install/fastify.test.js +0 -130
- package/lib/error-handlers/install/hapi.test.js +0 -102
- package/lib/error-handlers/install/koa2.test.js +0 -83
- package/lib/error-handlers/install/restify.test.js +0 -57
- package/lib/get-source-context.test.js +0 -35
- package/lib/hardening/handlers.test.js +0 -89
- package/lib/hardening/index.test.js +0 -31
- package/lib/hardening/install/node-serialize0.test.js +0 -58
- package/lib/index.test.js +0 -53
- package/lib/input-analysis/handlers.test.js +0 -1604
- package/lib/input-analysis/index.test.js +0 -45
- package/lib/input-analysis/install/body-parser1.test.js +0 -134
- package/lib/input-analysis/install/busboy1.test.js +0 -81
- package/lib/input-analysis/install/cookie-parser1.test.js +0 -144
- package/lib/input-analysis/install/express.test.js +0 -241
- package/lib/input-analysis/install/fastify.test.js +0 -96
- package/lib/input-analysis/install/formidable1.test.js +0 -114
- package/lib/input-analysis/install/hapi.test.js +0 -292
- package/lib/input-analysis/install/http.test.js +0 -270
- package/lib/input-analysis/install/koa-body5.test.js +0 -92
- package/lib/input-analysis/install/koa-bodyparser4.test.js +0 -92
- package/lib/input-analysis/install/koa2.test.js +0 -259
- package/lib/input-analysis/install/multer1.test.js +0 -209
- package/lib/input-analysis/install/qs6.test.js +0 -79
- package/lib/input-analysis/install/restify.test.js +0 -98
- package/lib/input-analysis/install/universal-cookie4.test.js +0 -70
- package/lib/input-analysis/ip-analysis.test.js +0 -71
- package/lib/input-analysis/virtual-patches.test.js +0 -106
- package/lib/input-tracing/handlers/index.test.js +0 -1237
- package/lib/input-tracing/index.test.js +0 -62
- package/lib/input-tracing/install/child-process.test.js +0 -133
- package/lib/input-tracing/install/eval.test.js +0 -78
- package/lib/input-tracing/install/fs.test.js +0 -108
- package/lib/input-tracing/install/function.test.js +0 -81
- package/lib/input-tracing/install/http.test.js +0 -85
- package/lib/input-tracing/install/http2.test.js +0 -83
- package/lib/input-tracing/install/marsdb.test.js +0 -126
- package/lib/input-tracing/install/mongodb.test.js +0 -280
- package/lib/input-tracing/install/mssql.test.js +0 -81
- package/lib/input-tracing/install/mysql.test.js +0 -108
- package/lib/input-tracing/install/postgres.test.js +0 -117
- package/lib/input-tracing/install/sequelize.test.js +0 -78
- package/lib/input-tracing/install/spdy.test.js +0 -76
- package/lib/input-tracing/install/sqlite3.test.js +0 -88
- package/lib/input-tracing/install/vm.test.js +0 -176
- package/lib/make-response-blocker.test.js +0 -99
- package/lib/make-source-context.test.js +0 -219
- package/lib/policy.test.js +0 -446
- package/lib/semantic-analysis/handlers.test.js +0 -379
- package/lib/semantic-analysis/index.test.js +0 -38
- package/lib/semantic-analysis/install/libxmljs.test.js +0 -156
- package/lib/semantic-analysis/utils/xml-analysis.test.js +0 -156
- package/lib/throw-security-exception.test.js +0 -37
|
@@ -1,117 +0,0 @@
|
|
|
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 postgres', function () {
|
|
10
|
-
const sourceContext = {};
|
|
11
|
-
const store = { protect: sourceContext };
|
|
12
|
-
const query = 'SELECT "foo"';
|
|
13
|
-
|
|
14
|
-
let core, inputTracing;
|
|
15
|
-
|
|
16
|
-
beforeEach(function () {
|
|
17
|
-
core = mocks.core();
|
|
18
|
-
core.logger = mocks.logger();
|
|
19
|
-
core.patcher = patcher(core);
|
|
20
|
-
core.scopes = scopes(core);
|
|
21
|
-
sinon.stub(core.scopes.sources, 'getStore').returns(store);
|
|
22
|
-
core.depHooks = mocks.depHooks();
|
|
23
|
-
core.protect = mocks.protect();
|
|
24
|
-
require('../../get-source-context')(core);
|
|
25
|
-
({ protect: { inputTracing } } = core);
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
describe('pg.Client.prototype.query', function () {
|
|
29
|
-
let Client;
|
|
30
|
-
|
|
31
|
-
beforeEach(function () {
|
|
32
|
-
Client = function () { };
|
|
33
|
-
Client.prototype.query = sinon.stub();
|
|
34
|
-
core.depHooks.resolve.withArgs(sinon.match({ name: 'pg' })).yields(Client);
|
|
35
|
-
require('./postgres')(core).install();
|
|
36
|
-
});
|
|
37
|
-
|
|
38
|
-
it('handleSqlInjection() is called with expected values', function () {
|
|
39
|
-
const conn = new Client();
|
|
40
|
-
conn.query(query);
|
|
41
|
-
conn.query({ text: query });
|
|
42
|
-
|
|
43
|
-
const sinkContext = { name: 'pg.Client.prototype.query', value: query };
|
|
44
|
-
|
|
45
|
-
expect(inputTracing.handleSqlInjection.callCount).to.equal(2);
|
|
46
|
-
[0, 1].forEach(callNum => {
|
|
47
|
-
expect(inputTracing.handleSqlInjection.getCall(callNum).calledWithExactly(store, sinkContext));
|
|
48
|
-
});
|
|
49
|
-
});
|
|
50
|
-
|
|
51
|
-
it('handleSqlInjection() is not called if there is no sourceContext', function () {
|
|
52
|
-
core.scopes.sources.getStore.returns({ protect: undefined });
|
|
53
|
-
|
|
54
|
-
const conn = new Client();
|
|
55
|
-
conn.query(query);
|
|
56
|
-
conn.query({ text: query });
|
|
57
|
-
|
|
58
|
-
expect(inputTracing.handleSqlInjection).not.to.have.been.called;
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
it('handleSqlInjection() is not called if the sql value is empty or not a string', function () {
|
|
62
|
-
const conn = new Client();
|
|
63
|
-
conn.query('');
|
|
64
|
-
conn.query(100);
|
|
65
|
-
conn.query({ text: '' });
|
|
66
|
-
conn.query({ text: 100 });
|
|
67
|
-
|
|
68
|
-
expect(inputTracing.handleSqlInjection).not.to.have.been.called;
|
|
69
|
-
});
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
describe('pg-pool.Pool.prototype.query', function () {
|
|
73
|
-
let Pool;
|
|
74
|
-
|
|
75
|
-
beforeEach(function () {
|
|
76
|
-
Pool = function () { };
|
|
77
|
-
Pool.prototype.query = sinon.stub();
|
|
78
|
-
core.depHooks.resolve.withArgs(sinon.match({ name: 'pg-pool' })).yields(Pool);
|
|
79
|
-
|
|
80
|
-
require('./postgres')(core).install();
|
|
81
|
-
});
|
|
82
|
-
|
|
83
|
-
it('handleSqlInjection() is called with expected values', function () {
|
|
84
|
-
const pool = new Pool();
|
|
85
|
-
pool.query(query);
|
|
86
|
-
pool.query({ text: query });
|
|
87
|
-
|
|
88
|
-
const sinkContext = { name: 'pg-pool.Pool.prototype.query', value: query };
|
|
89
|
-
|
|
90
|
-
expect(inputTracing.handleSqlInjection.callCount).to.equal(2);
|
|
91
|
-
[0, 1].forEach(callNum => {
|
|
92
|
-
expect(inputTracing.handleSqlInjection.getCall(callNum).calledWithExactly(store, sinkContext));
|
|
93
|
-
});
|
|
94
|
-
});
|
|
95
|
-
|
|
96
|
-
it('handleSqlInjection() is not called if there is no sourceContext', function () {
|
|
97
|
-
core.scopes.sources.getStore.returns({ protect: undefined });
|
|
98
|
-
|
|
99
|
-
const pool = new Pool();
|
|
100
|
-
const value = 'SELECT "foo"';
|
|
101
|
-
pool.query(value);
|
|
102
|
-
pool.query({ text: value });
|
|
103
|
-
|
|
104
|
-
expect(inputTracing.handleSqlInjection).not.to.have.been.called;
|
|
105
|
-
});
|
|
106
|
-
|
|
107
|
-
it('handleSqlInjection() is not called if the sql value is empty or not a string', function () {
|
|
108
|
-
const pool = new Pool();
|
|
109
|
-
pool.query('');
|
|
110
|
-
pool.query(100);
|
|
111
|
-
pool.query({ text: '' });
|
|
112
|
-
pool.query({ text: 100 });
|
|
113
|
-
|
|
114
|
-
expect(inputTracing.handleSqlInjection).not.to.have.been.called;
|
|
115
|
-
});
|
|
116
|
-
});
|
|
117
|
-
});
|
|
@@ -1,78 +0,0 @@
|
|
|
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 sequelize', 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
|
-
core.depHooks = mocks.depHooks();
|
|
19
|
-
core.protect = mocks.protect();
|
|
20
|
-
require('../../get-source-context')(core);
|
|
21
|
-
({ protect: { inputTracing } } = core);
|
|
22
|
-
|
|
23
|
-
sinon.stub(core.scopes.sources, 'getStore').returns(store);
|
|
24
|
-
});
|
|
25
|
-
|
|
26
|
-
describe('sequelize', function () {
|
|
27
|
-
let sequelize;
|
|
28
|
-
|
|
29
|
-
beforeEach(function () {
|
|
30
|
-
sequelize = function () { };
|
|
31
|
-
sequelize.prototype.query = sinon.stub();
|
|
32
|
-
core.depHooks.resolve.yields(sequelize);
|
|
33
|
-
require('./sequelize')(core).install();
|
|
34
|
-
});
|
|
35
|
-
|
|
36
|
-
describe('instruments sequelize.query()', function () {
|
|
37
|
-
it('handleSqlInjection() is called with valid expected values', function () {
|
|
38
|
-
const conn = new sequelize();
|
|
39
|
-
const value = 'SELECT "foo"';
|
|
40
|
-
conn.query(value);
|
|
41
|
-
conn.query({ query: value });
|
|
42
|
-
|
|
43
|
-
expect(inputTracing.handleSqlInjection).to.have.been.calledTwice;
|
|
44
|
-
expect(inputTracing.handleSqlInjection).to.have.been.calledWith(
|
|
45
|
-
{},
|
|
46
|
-
{ name: 'sequelize.prototype.query', value, stacktraceOpts: { constructorOpt: conn.query, prependFrames: [sinon.match.func] } }
|
|
47
|
-
);
|
|
48
|
-
});
|
|
49
|
-
|
|
50
|
-
it('handleSqlInjection() is not called if instrumentation is locked', function () {
|
|
51
|
-
sinon.stub(core.scopes.instrumentation, 'isLocked').returns(true);
|
|
52
|
-
const conn = new sequelize();
|
|
53
|
-
const value = 'SELECT "foo"';
|
|
54
|
-
conn.query(value);
|
|
55
|
-
|
|
56
|
-
expect(inputTracing.handleSqlInjection).not.to.have.been.called;
|
|
57
|
-
});
|
|
58
|
-
|
|
59
|
-
it('handleSqlInjection() is not called if there is no sourceContext', function () {
|
|
60
|
-
core.scopes.sources.getStore.returns({ protect: undefined });
|
|
61
|
-
const conn = new sequelize();
|
|
62
|
-
const value = 'SELECT "foo"';
|
|
63
|
-
conn.query(value);
|
|
64
|
-
|
|
65
|
-
expect(inputTracing.handleSqlInjection).not.to.have.been.called;
|
|
66
|
-
});
|
|
67
|
-
|
|
68
|
-
it('handleSqlInjection() is not called if the sql value is empty or not a string', function () {
|
|
69
|
-
const conn = new sequelize();
|
|
70
|
-
conn.query('');
|
|
71
|
-
conn.query(100);
|
|
72
|
-
conn.query({ query: '' });
|
|
73
|
-
|
|
74
|
-
expect(inputTracing.handleSqlInjection).not.to.have.been.called;
|
|
75
|
-
});
|
|
76
|
-
});
|
|
77
|
-
});
|
|
78
|
-
});
|
|
@@ -1,76 +0,0 @@
|
|
|
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 spdy', 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('spdy', function () {
|
|
26
|
-
let spdy;
|
|
27
|
-
|
|
28
|
-
beforeEach(function () {
|
|
29
|
-
spdy = function () {};
|
|
30
|
-
spdy.response = {};
|
|
31
|
-
spdy.response.end = sinon.stub();
|
|
32
|
-
spdy.response.spdyStream = { once: sinon.stub() };
|
|
33
|
-
core.depHooks.resolve.yields(spdy);
|
|
34
|
-
require('./spdy')(core).install();
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
it('handleReflectedXss() is not called if there is no value', function () {
|
|
38
|
-
spdy.response.end();
|
|
39
|
-
|
|
40
|
-
expect(inputTracing.handleReflectedXss).not.to.have.been.called;
|
|
41
|
-
});
|
|
42
|
-
|
|
43
|
-
it('handleReflectedXss() is not called if there is no sourceContext', function () {
|
|
44
|
-
core.scopes.sources.getStore.returns({ protect: undefined });
|
|
45
|
-
const value = "<script>alert('xss');</script>";
|
|
46
|
-
spdy.response.end(value);
|
|
47
|
-
|
|
48
|
-
expect(inputTracing.handleReflectedXss).not.to.have.been.called;
|
|
49
|
-
});
|
|
50
|
-
|
|
51
|
-
it('handleReflectedXss() is not called if instrumentation is locked', function () {
|
|
52
|
-
sinon.stub(core.scopes.instrumentation, 'isLocked').returns(true);
|
|
53
|
-
const value = "<script>alert('xss');</script>";
|
|
54
|
-
spdy.response.end(value);
|
|
55
|
-
|
|
56
|
-
expect(inputTracing.handleReflectedXss).not.to.have.been.called;
|
|
57
|
-
});
|
|
58
|
-
|
|
59
|
-
it('handleReflectedXss() is called with valid expected values', function () {
|
|
60
|
-
const value = "<script>alert('xss');</script>";
|
|
61
|
-
spdy.response.end(value);
|
|
62
|
-
|
|
63
|
-
expect(inputTracing.handleReflectedXss).to.have.been.calledOnceWith(
|
|
64
|
-
{},
|
|
65
|
-
{
|
|
66
|
-
name: 'spdy.response.end',
|
|
67
|
-
value,
|
|
68
|
-
stacktraceOpts: {
|
|
69
|
-
constructorOpt: spdy.response.end
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
);
|
|
73
|
-
});
|
|
74
|
-
|
|
75
|
-
});
|
|
76
|
-
});
|
|
@@ -1,88 +0,0 @@
|
|
|
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 sqlite3', function () {
|
|
10
|
-
const store = { protect: {} };
|
|
11
|
-
|
|
12
|
-
let core, inputTracing;
|
|
13
|
-
|
|
14
|
-
beforeEach(function () {
|
|
15
|
-
core = mocks.core();
|
|
16
|
-
core.logger = mocks.logger();
|
|
17
|
-
core.patcher = patcher(core);
|
|
18
|
-
core.scopes = scopes(core);
|
|
19
|
-
core.depHooks = mocks.depHooks();
|
|
20
|
-
core.protect = mocks.protect();
|
|
21
|
-
require('../../get-source-context')(core);
|
|
22
|
-
({ protect: { inputTracing } } = core);
|
|
23
|
-
|
|
24
|
-
sinon.stub(core.scopes.sources, 'getStore').returns(store);
|
|
25
|
-
});
|
|
26
|
-
|
|
27
|
-
describe('sqlite3', function () {
|
|
28
|
-
let sqlite3;
|
|
29
|
-
|
|
30
|
-
beforeEach(function () {
|
|
31
|
-
sqlite3 = {
|
|
32
|
-
// eslint-disable-next-line object-shorthand
|
|
33
|
-
Database: function () { }
|
|
34
|
-
};
|
|
35
|
-
sqlite3.Database.prototype = {
|
|
36
|
-
all: sinon.stub(),
|
|
37
|
-
run: sinon.stub(),
|
|
38
|
-
get: sinon.stub(),
|
|
39
|
-
each: sinon.stub(),
|
|
40
|
-
exec: sinon.stub(),
|
|
41
|
-
prepare: sinon.stub()
|
|
42
|
-
};
|
|
43
|
-
core.depHooks.resolve.yields(sqlite3);
|
|
44
|
-
require('./sqlite3')(core).install();
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
['all', 'run', 'get', 'each', 'exec', 'prepare'].forEach((method) => {
|
|
48
|
-
describe(`instruments connection.${method}()`, function () {
|
|
49
|
-
it('handleSqlInjection() is called with valid expected values', function () {
|
|
50
|
-
const db = new sqlite3.Database();
|
|
51
|
-
const value = 'SELECT "foo"';
|
|
52
|
-
db[method](value);
|
|
53
|
-
|
|
54
|
-
expect(inputTracing.handleSqlInjection).to.have.been.calledWith(
|
|
55
|
-
{},
|
|
56
|
-
{ name: `sqlite3.Database.prototype.${method}`, value, stacktraceOpts: { constructorOpt: db[method], prependFrames: [sinon.match.func] } }
|
|
57
|
-
);
|
|
58
|
-
});
|
|
59
|
-
|
|
60
|
-
it('handleSqlInjection() is not called if instrumentation is locked', function () {
|
|
61
|
-
sinon.stub(core.scopes.instrumentation, 'isLocked').returns(true);
|
|
62
|
-
const db = new sqlite3.Database();
|
|
63
|
-
const value = 'SELECT "foo"';
|
|
64
|
-
db[method](value);
|
|
65
|
-
|
|
66
|
-
expect(inputTracing.handleSqlInjection).not.to.have.been.called;
|
|
67
|
-
});
|
|
68
|
-
|
|
69
|
-
it('handleSqlInjection() is not called if there is no sourceContext', function () {
|
|
70
|
-
core.scopes.sources.getStore.returns({ protect: undefined });
|
|
71
|
-
const db = new sqlite3.Database();
|
|
72
|
-
const value = 'SELECT "foo"';
|
|
73
|
-
db[method](value);
|
|
74
|
-
|
|
75
|
-
expect(inputTracing.handleSqlInjection).not.to.have.been.called;
|
|
76
|
-
});
|
|
77
|
-
|
|
78
|
-
it('handleSqlInjection() is not called if the sql value is empty or not a string', function () {
|
|
79
|
-
const db = new sqlite3.Database();
|
|
80
|
-
db[method]('');
|
|
81
|
-
db[method](100);
|
|
82
|
-
|
|
83
|
-
expect(inputTracing.handleSqlInjection).not.to.have.been.called;
|
|
84
|
-
});
|
|
85
|
-
});
|
|
86
|
-
});
|
|
87
|
-
});
|
|
88
|
-
});
|
|
@@ -1,176 +0,0 @@
|
|
|
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 vm', function () {
|
|
9
|
-
const store = { protect: {} };
|
|
10
|
-
let vm, origVm, core;
|
|
11
|
-
|
|
12
|
-
const testSetup = [
|
|
13
|
-
{
|
|
14
|
-
methods: ['Script', 'compileFunction', 'runInContext', 'runInNewContext', 'runInThisContext'],
|
|
15
|
-
vulnerableArgs: [{ type: 'string', index: 0 }],
|
|
16
|
-
},
|
|
17
|
-
{
|
|
18
|
-
methods: ['runInContext', 'runInNewContext'],
|
|
19
|
-
vulnerableArgs: [{ type: 'string', index: 0 }, { type: 'object', index: 1 }],
|
|
20
|
-
},
|
|
21
|
-
{
|
|
22
|
-
methods: ['createContext'],
|
|
23
|
-
vulnerableArgs: [{ type: 'object', index: 0 }],
|
|
24
|
-
},
|
|
25
|
-
{
|
|
26
|
-
methods: ['Script.prototype.runInContext', 'Script.prototype.runInNewContext'],
|
|
27
|
-
vulnerableArgs: [{ type: 'object', index: 0 }],
|
|
28
|
-
}
|
|
29
|
-
];
|
|
30
|
-
|
|
31
|
-
function methodPick(vm, method) {
|
|
32
|
-
let fn = vm[method];
|
|
33
|
-
|
|
34
|
-
if (method.includes('.')) {
|
|
35
|
-
fn = vm;
|
|
36
|
-
method.split('.').forEach((prop) => {
|
|
37
|
-
fn = fn[prop];
|
|
38
|
-
});
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
return fn;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
beforeEach(function () {
|
|
45
|
-
vm = {};
|
|
46
|
-
origVm = {
|
|
47
|
-
Script: sinon.stub(),
|
|
48
|
-
createContext: sinon.stub(),
|
|
49
|
-
compileFunction: sinon.stub(),
|
|
50
|
-
runInContext: sinon.stub(),
|
|
51
|
-
runInNewContext: sinon.stub(),
|
|
52
|
-
runInThisContext: sinon.stub(),
|
|
53
|
-
};
|
|
54
|
-
Object.assign(vm, origVm);
|
|
55
|
-
origVm['Script.prototype.runInContext'] = sinon.stub();
|
|
56
|
-
origVm['Script.prototype.runInNewContext'] = sinon.stub();
|
|
57
|
-
vm.Script.prototype.runInContext = origVm['Script.prototype.runInContext'];
|
|
58
|
-
vm.Script.prototype.runInNewContext = origVm['Script.prototype.runInNewContext'];
|
|
59
|
-
|
|
60
|
-
core = mocks.core();
|
|
61
|
-
core.logger = mocks.logger();
|
|
62
|
-
core.depHooks = mocks.depHooks();
|
|
63
|
-
core.depHooks.resolve.yields(vm);
|
|
64
|
-
core.scopes = mocks.scopes();
|
|
65
|
-
sinon.stub(core.scopes.sources, 'getStore').returns(store);
|
|
66
|
-
core.patcher = patcher(core);
|
|
67
|
-
core.protect = mocks.protect();
|
|
68
|
-
require('../../get-source-context')(core);
|
|
69
|
-
|
|
70
|
-
require('./vm')(core);
|
|
71
|
-
|
|
72
|
-
core.protect.inputTracing.vmInstrumentation.install();
|
|
73
|
-
});
|
|
74
|
-
|
|
75
|
-
describe('ssjsInjection() is called with expected values', function () {
|
|
76
|
-
testSetup.forEach(({ methods, vulnerableArgs }) => {
|
|
77
|
-
const methodArgs = [];
|
|
78
|
-
|
|
79
|
-
vulnerableArgs.forEach((arg) => {
|
|
80
|
-
const argValue = arg.type == 'string' ? 'function() { return "hi"; }' : { prop: 'foobar' };
|
|
81
|
-
methodArgs[arg.index] = argValue;
|
|
82
|
-
});
|
|
83
|
-
|
|
84
|
-
methods.forEach((method) => {
|
|
85
|
-
const name = `vm.${method}`;
|
|
86
|
-
|
|
87
|
-
it(name, function () {
|
|
88
|
-
const fn = methodPick(vm, method);
|
|
89
|
-
fn(...methodArgs);
|
|
90
|
-
|
|
91
|
-
expect(core.protect.inputTracing.ssjsInjection).to.have.callCount(vulnerableArgs.length);
|
|
92
|
-
vulnerableArgs.forEach(({ index }) => {
|
|
93
|
-
const value = methodArgs[index];
|
|
94
|
-
|
|
95
|
-
expect(core.protect.inputTracing.ssjsInjection).to.have.been.calledWith(
|
|
96
|
-
store.protect,
|
|
97
|
-
{ name, value, stacktraceOpts: { constructorOpt: fn, prependFrames: [origVm[method]] } }
|
|
98
|
-
);
|
|
99
|
-
});
|
|
100
|
-
});
|
|
101
|
-
});
|
|
102
|
-
});
|
|
103
|
-
});
|
|
104
|
-
|
|
105
|
-
describe('ssjsInjection() is not called when method args are not relevant', function () {
|
|
106
|
-
const methodArgs = [1, undefined];
|
|
107
|
-
|
|
108
|
-
testSetup.forEach(({ methods }) => {
|
|
109
|
-
methods.forEach((method) => {
|
|
110
|
-
const name = `vm.${method}`;
|
|
111
|
-
|
|
112
|
-
it(name, function () {
|
|
113
|
-
const fn = methodPick(vm, method);
|
|
114
|
-
|
|
115
|
-
fn(...methodArgs);
|
|
116
|
-
|
|
117
|
-
expect(core.protect.inputTracing.ssjsInjection).not.to.have.been.called;
|
|
118
|
-
});
|
|
119
|
-
});
|
|
120
|
-
});
|
|
121
|
-
});
|
|
122
|
-
|
|
123
|
-
describe('ssjsInjection() is not called when instrumentation is locked', function () {
|
|
124
|
-
beforeEach(function () {
|
|
125
|
-
core.scopes.instrumentation.isLocked.returns(true);
|
|
126
|
-
});
|
|
127
|
-
|
|
128
|
-
testSetup.forEach(({ methods, vulnerableArgs }) => {
|
|
129
|
-
const methodArgs = [];
|
|
130
|
-
|
|
131
|
-
vulnerableArgs.forEach((arg) => {
|
|
132
|
-
const argValue = arg.type == 'string' ? 'function() { return "hi"; }' : { prop: 'function() { return "hi"; }' };
|
|
133
|
-
methodArgs[arg.index] = argValue;
|
|
134
|
-
});
|
|
135
|
-
|
|
136
|
-
methods.forEach((method) => {
|
|
137
|
-
const name = `vm.${method}`;
|
|
138
|
-
|
|
139
|
-
it(name, function () {
|
|
140
|
-
const fn = methodPick(vm, method);
|
|
141
|
-
|
|
142
|
-
fn(...methodArgs);
|
|
143
|
-
|
|
144
|
-
expect(core.protect.inputTracing.ssjsInjection).not.to.have.been.called;
|
|
145
|
-
});
|
|
146
|
-
});
|
|
147
|
-
});
|
|
148
|
-
});
|
|
149
|
-
|
|
150
|
-
describe('ssjsInjection() is not called when there is no Protect sourceContext', function () {
|
|
151
|
-
beforeEach(function () {
|
|
152
|
-
core.scopes.sources.getStore.returns({ protect: null });
|
|
153
|
-
});
|
|
154
|
-
|
|
155
|
-
testSetup.forEach(({ methods, vulnerableArgs }) => {
|
|
156
|
-
const methodArgs = [];
|
|
157
|
-
|
|
158
|
-
vulnerableArgs.forEach((arg) => {
|
|
159
|
-
const argValue = arg.type == 'string' ? 'function() { return "hi"; }' : { prop: 'function() { return "hi"; }' };
|
|
160
|
-
methodArgs[arg.index] = argValue;
|
|
161
|
-
});
|
|
162
|
-
|
|
163
|
-
methods.forEach((method) => {
|
|
164
|
-
const name = `vm.${method}`;
|
|
165
|
-
|
|
166
|
-
it(name, function () {
|
|
167
|
-
const fn = methodPick(vm, method);
|
|
168
|
-
|
|
169
|
-
fn(...methodArgs);
|
|
170
|
-
|
|
171
|
-
expect(core.protect.inputTracing.ssjsInjection).not.to.have.been.called;
|
|
172
|
-
});
|
|
173
|
-
});
|
|
174
|
-
});
|
|
175
|
-
});
|
|
176
|
-
});
|
|
@@ -1,99 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
const sinon = require('sinon');
|
|
4
|
-
const { expect } = require('chai');
|
|
5
|
-
const { symbols: { kMetrics } } = require('@contrast/common');
|
|
6
|
-
const mocks = require('@contrast/test/mocks');
|
|
7
|
-
const patcher = require('@contrast/patcher');
|
|
8
|
-
|
|
9
|
-
describe('protect make-response-blocker', function () {
|
|
10
|
-
|
|
11
|
-
let res, core, makeResponseBlocker;
|
|
12
|
-
beforeEach(function () {
|
|
13
|
-
|
|
14
|
-
res = {
|
|
15
|
-
end: sinon.stub(),
|
|
16
|
-
headersSent: false,
|
|
17
|
-
writeHead: sinon.stub()
|
|
18
|
-
};
|
|
19
|
-
|
|
20
|
-
core = mocks.core();
|
|
21
|
-
core.logger = mocks.logger();
|
|
22
|
-
core.patcher = patcher(core);
|
|
23
|
-
core.protect = {};
|
|
24
|
-
|
|
25
|
-
sinon.spy(core.patcher, 'unwrap');
|
|
26
|
-
makeResponseBlocker = require('./make-response-blocker')(core);
|
|
27
|
-
});
|
|
28
|
-
|
|
29
|
-
it('Sends a blocked response', function () {
|
|
30
|
-
const block = makeResponseBlocker(res);
|
|
31
|
-
block('block', 'sql-injection');
|
|
32
|
-
expect(core.patcher.unwrap).to.have.been.calledWith(res.writeHead);
|
|
33
|
-
expect(core.patcher.unwrap).to.have.been.calledWith(res.end);
|
|
34
|
-
expect(res.writeHead).to.have.been.calledWith(403);
|
|
35
|
-
expect(res.end).to.have.been.calledWith('');
|
|
36
|
-
expect(core.logger.info).to.have.been.calledWith(
|
|
37
|
-
{ mode: 'BLOCK', ruleId: 'sql-injection' },
|
|
38
|
-
'Request blocked'
|
|
39
|
-
);
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
it('Sets blocked property', function () {
|
|
43
|
-
const block = makeResponseBlocker(res);
|
|
44
|
-
block('block', 'sql-injection');
|
|
45
|
-
expect(makeResponseBlocker.blocked.has(res)).to.be.true;
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
it('Set core.protect.makeResponseBlocker', function () {
|
|
49
|
-
expect(core.protect.makeResponseBlocker).to.equal(makeResponseBlocker);
|
|
50
|
-
});
|
|
51
|
-
|
|
52
|
-
it('Blocks on the same "res" object only once', function () {
|
|
53
|
-
const block = makeResponseBlocker(res);
|
|
54
|
-
block('block', 'sql-injection');
|
|
55
|
-
block('block', 'cmd-injection');
|
|
56
|
-
expect(makeResponseBlocker.blocked.has(res));
|
|
57
|
-
expect(res.end).to.have.been.calledOnce;
|
|
58
|
-
expect(res.writeHead).to.have.been.calledOnce;
|
|
59
|
-
expect(core.logger.info).to.have.been.calledOnce;
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
it('Adds multiple responses to blocked set', function () {
|
|
63
|
-
const res1 = Object.assign({}, res);
|
|
64
|
-
const res2 = Object.assign({}, res);
|
|
65
|
-
const block1 = makeResponseBlocker(res1);
|
|
66
|
-
const block2 = makeResponseBlocker(res2);
|
|
67
|
-
block1('block', 'sql-injection');
|
|
68
|
-
block2('block', 'cmd-injection');
|
|
69
|
-
expect(makeResponseBlocker.blocked.has(res1));
|
|
70
|
-
expect(makeResponseBlocker.blocked.has(res2));
|
|
71
|
-
});
|
|
72
|
-
|
|
73
|
-
it('Does not call writeHead when headers already sent', function () {
|
|
74
|
-
const block = makeResponseBlocker(res);
|
|
75
|
-
res.headersSent = true;
|
|
76
|
-
block('block_at_perimeter', 'reflected-xss');
|
|
77
|
-
expect(res.writeHead).not.to.have.been.called;
|
|
78
|
-
expect(res.end).to.have.been.calledWith('');
|
|
79
|
-
});
|
|
80
|
-
|
|
81
|
-
it('Logs error when exception thrown', function () {
|
|
82
|
-
const err = new Error('error');
|
|
83
|
-
res.end.throws(err);
|
|
84
|
-
const block = makeResponseBlocker(res);
|
|
85
|
-
block('block', 'path-traversal');
|
|
86
|
-
expect(core.logger.error).to.have.been.calledWith(
|
|
87
|
-
{ err, mode: 'BLOCK', ruleId: 'path-traversal' },
|
|
88
|
-
'Error blocking request'
|
|
89
|
-
);
|
|
90
|
-
});
|
|
91
|
-
|
|
92
|
-
it('removes the `metrics` object if present', function () {
|
|
93
|
-
const block = makeResponseBlocker(res);
|
|
94
|
-
res[kMetrics] = { timeout: setTimeout(sinon.stub(), 1) };
|
|
95
|
-
block('block', 'sql-injection');
|
|
96
|
-
|
|
97
|
-
expect(res).not.to.have.property(kMetrics);
|
|
98
|
-
});
|
|
99
|
-
});
|