@mbtest/mountebank 2.9.2-beta.9050
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/LICENSE +21 -0
- package/README.md +94 -0
- package/bin/mb +136 -0
- package/package.json +71 -0
- package/releases.json +52 -0
- package/src/cli/api.js +112 -0
- package/src/cli/cli.js +420 -0
- package/src/controllers/configController.js +64 -0
- package/src/controllers/feedController.js +115 -0
- package/src/controllers/homeController.js +58 -0
- package/src/controllers/imposterController.js +328 -0
- package/src/controllers/impostersController.js +215 -0
- package/src/controllers/logsController.js +52 -0
- package/src/models/behaviors.js +553 -0
- package/src/models/behaviorsValidator.js +186 -0
- package/src/models/compatibility.js +133 -0
- package/src/models/dryRunValidator.js +261 -0
- package/src/models/filesystemBackedImpostersRepository.js +908 -0
- package/src/models/http/baseHttpServer.js +207 -0
- package/src/models/http/headersMap.js +87 -0
- package/src/models/http/httpProxy.js +230 -0
- package/src/models/http/httpRequest.js +82 -0
- package/src/models/http/httpServer.js +18 -0
- package/src/models/http/index.js +18 -0
- package/src/models/https/cert/mb-cert.pem +20 -0
- package/src/models/https/cert/mb-csr.pem +16 -0
- package/src/models/https/cert/mb-key.pem +27 -0
- package/src/models/https/httpsServer.js +42 -0
- package/src/models/https/index.js +18 -0
- package/src/models/imposter.js +243 -0
- package/src/models/imposterPrinter.js +120 -0
- package/src/models/impostersRepository.js +49 -0
- package/src/models/inMemoryImpostersRepository.js +418 -0
- package/src/models/jsonpath.js +44 -0
- package/src/models/mbConnection.js +107 -0
- package/src/models/predicates.js +438 -0
- package/src/models/protocols.js +242 -0
- package/src/models/responseResolver.js +398 -0
- package/src/models/smtp/index.js +16 -0
- package/src/models/smtp/smtpRequest.js +60 -0
- package/src/models/smtp/smtpServer.js +109 -0
- package/src/models/tcp/index.js +18 -0
- package/src/models/tcp/tcpProxy.js +110 -0
- package/src/models/tcp/tcpRequest.js +23 -0
- package/src/models/tcp/tcpServer.js +156 -0
- package/src/models/tcp/tcpValidator.js +19 -0
- package/src/models/xpath.js +95 -0
- package/src/mountebank.js +245 -0
- package/src/public/images/arrow_down.png +0 -0
- package/src/public/images/arrow_up.png +0 -0
- package/src/public/images/book.jpg +0 -0
- package/src/public/images/dataflow.png +0 -0
- package/src/public/images/favicon.ico +0 -0
- package/src/public/images/forkme_right_orange_ff7600.png +0 -0
- package/src/public/images/mountebank.png +0 -0
- package/src/public/images/overview.gif +0 -0
- package/src/public/images/quote.png +0 -0
- package/src/public/images/tw-logo.png +0 -0
- package/src/public/scripts/jquery/jquery-3.6.1.min.js +2 -0
- package/src/public/scripts/urlHashHandler.js +31 -0
- package/src/public/stylesheets/application.css +424 -0
- package/src/public/stylesheets/ie.css +14 -0
- package/src/public/stylesheets/imposters.css +121 -0
- package/src/public/stylesheets/jqueryui/1.10.4/themes/smoothness/jquery-ui.css +1178 -0
- package/src/util/combinators.js +68 -0
- package/src/util/date.js +51 -0
- package/src/util/errors.js +55 -0
- package/src/util/helpers.js +131 -0
- package/src/util/inherit.js +28 -0
- package/src/util/ip.js +54 -0
- package/src/util/logger.js +83 -0
- package/src/util/middleware.js +256 -0
- package/src/util/scopedLogger.js +47 -0
- package/src/views/_footer.ejs +20 -0
- package/src/views/_header.ejs +113 -0
- package/src/views/_imposter.ejs +8 -0
- package/src/views/config.ejs +71 -0
- package/src/views/docs/api/behaviors/copy.ejs +427 -0
- package/src/views/docs/api/behaviors/decorate.ejs +182 -0
- package/src/views/docs/api/behaviors/lookup.ejs +220 -0
- package/src/views/docs/api/behaviors/shellTransform.ejs +153 -0
- package/src/views/docs/api/behaviors/wait.ejs +121 -0
- package/src/views/docs/api/behaviors.ejs +141 -0
- package/src/views/docs/api/contracts/addStub-description.ejs +10 -0
- package/src/views/docs/api/contracts/addStub.ejs +10 -0
- package/src/views/docs/api/contracts/config-description.ejs +32 -0
- package/src/views/docs/api/contracts/config.ejs +23 -0
- package/src/views/docs/api/contracts/home-description.ejs +18 -0
- package/src/views/docs/api/contracts/home.ejs +13 -0
- package/src/views/docs/api/contracts/imposter-description.ejs +439 -0
- package/src/views/docs/api/contracts/imposter.ejs +182 -0
- package/src/views/docs/api/contracts/imposters-description.ejs +13 -0
- package/src/views/docs/api/contracts/imposters.ejs +13 -0
- package/src/views/docs/api/contracts/logs-description.ejs +3 -0
- package/src/views/docs/api/contracts/logs.ejs +14 -0
- package/src/views/docs/api/contracts/stub-description.ejs +4 -0
- package/src/views/docs/api/contracts/stub.ejs +7 -0
- package/src/views/docs/api/contracts/stubs-description.ejs +4 -0
- package/src/views/docs/api/contracts/stubs.ejs +11 -0
- package/src/views/docs/api/contracts.ejs +133 -0
- package/src/views/docs/api/errors.ejs +64 -0
- package/src/views/docs/api/fault/connectionReset.ejs +31 -0
- package/src/views/docs/api/fault/randomDataThenClose.ejs +31 -0
- package/src/views/docs/api/faults.ejs +57 -0
- package/src/views/docs/api/injection.ejs +426 -0
- package/src/views/docs/api/json.ejs +205 -0
- package/src/views/docs/api/jsonpath.ejs +210 -0
- package/src/views/docs/api/mocks.ejs +130 -0
- package/src/views/docs/api/overview.ejs +968 -0
- package/src/views/docs/api/predicates/and.ejs +62 -0
- package/src/views/docs/api/predicates/contains.ejs +64 -0
- package/src/views/docs/api/predicates/deepEquals.ejs +114 -0
- package/src/views/docs/api/predicates/endsWith.ejs +66 -0
- package/src/views/docs/api/predicates/equals.ejs +125 -0
- package/src/views/docs/api/predicates/exists.ejs +118 -0
- package/src/views/docs/api/predicates/inject.ejs +67 -0
- package/src/views/docs/api/predicates/matches.ejs +66 -0
- package/src/views/docs/api/predicates/not.ejs +52 -0
- package/src/views/docs/api/predicates/or.ejs +79 -0
- package/src/views/docs/api/predicates/startsWith.ejs +62 -0
- package/src/views/docs/api/predicates.ejs +382 -0
- package/src/views/docs/api/proxies.ejs +191 -0
- package/src/views/docs/api/proxy/addDecorateBehavior.ejs +115 -0
- package/src/views/docs/api/proxy/addWaitBehavior.ejs +96 -0
- package/src/views/docs/api/proxy/injectHeaders.ejs +91 -0
- package/src/views/docs/api/proxy/predicateGenerators.ejs +600 -0
- package/src/views/docs/api/proxy/proxyModes.ejs +495 -0
- package/src/views/docs/api/stubs.ejs +391 -0
- package/src/views/docs/api/xpath.ejs +281 -0
- package/src/views/docs/cli/configFiles.ejs +133 -0
- package/src/views/docs/cli/customFormatters.ejs +53 -0
- package/src/views/docs/cli/help.ejs +6 -0
- package/src/views/docs/cli/replay.ejs +42 -0
- package/src/views/docs/cli/restart.ejs +10 -0
- package/src/views/docs/cli/save.ejs +68 -0
- package/src/views/docs/cli/start.ejs +234 -0
- package/src/views/docs/cli/stop.ejs +32 -0
- package/src/views/docs/commandLine.ejs +93 -0
- package/src/views/docs/communityExtensions.ejs +233 -0
- package/src/views/docs/gettingStarted.ejs +146 -0
- package/src/views/docs/mentalModel.ejs +51 -0
- package/src/views/docs/protocols/custom.ejs +231 -0
- package/src/views/docs/protocols/http.ejs +238 -0
- package/src/views/docs/protocols/https.ejs +246 -0
- package/src/views/docs/protocols/smtp.ejs +142 -0
- package/src/views/docs/protocols/tcp.ejs +431 -0
- package/src/views/docs/security.ejs +38 -0
- package/src/views/faqs.ejs +65 -0
- package/src/views/feed.ejs +33 -0
- package/src/views/imposter.ejs +22 -0
- package/src/views/imposters.ejs +33 -0
- package/src/views/index.ejs +89 -0
- package/src/views/license.ejs +30 -0
- package/src/views/logs.ejs +77 -0
- package/src/views/releases/v1.1.0.ejs +55 -0
- package/src/views/releases/v1.1.36.ejs +84 -0
- package/src/views/releases/v1.1.72.ejs +92 -0
- package/src/views/releases/v1.10.0.ejs +108 -0
- package/src/views/releases/v1.11.0.ejs +109 -0
- package/src/views/releases/v1.12.0.ejs +96 -0
- package/src/views/releases/v1.13.0.ejs +118 -0
- package/src/views/releases/v1.14.0.ejs +107 -0
- package/src/views/releases/v1.14.1.ejs +94 -0
- package/src/views/releases/v1.15.0.ejs +113 -0
- package/src/views/releases/v1.16.0.ejs +104 -0
- package/src/views/releases/v1.2.0.ejs +78 -0
- package/src/views/releases/v1.2.103.ejs +86 -0
- package/src/views/releases/v1.2.122.ejs +86 -0
- package/src/views/releases/v1.2.30.ejs +84 -0
- package/src/views/releases/v1.2.45.ejs +84 -0
- package/src/views/releases/v1.2.56.ejs +79 -0
- package/src/views/releases/v1.3.0.ejs +86 -0
- package/src/views/releases/v1.3.1.ejs +100 -0
- package/src/views/releases/v1.4.0.ejs +96 -0
- package/src/views/releases/v1.4.1.ejs +103 -0
- package/src/views/releases/v1.4.2.ejs +100 -0
- package/src/views/releases/v1.4.3.ejs +113 -0
- package/src/views/releases/v1.5.0.ejs +104 -0
- package/src/views/releases/v1.5.1.ejs +91 -0
- package/src/views/releases/v1.6.0.ejs +109 -0
- package/src/views/releases/v1.7.0.ejs +113 -0
- package/src/views/releases/v1.7.1.ejs +90 -0
- package/src/views/releases/v1.7.2.ejs +96 -0
- package/src/views/releases/v1.8.0.ejs +121 -0
- package/src/views/releases/v1.9.0.ejs +111 -0
- package/src/views/releases/v2.0.0.ejs +159 -0
- package/src/views/releases/v2.1.0.ejs +121 -0
- package/src/views/releases/v2.1.1.ejs +106 -0
- package/src/views/releases/v2.1.2.ejs +84 -0
- package/src/views/releases/v2.2.0.ejs +115 -0
- package/src/views/releases/v2.2.1.ejs +102 -0
- package/src/views/releases/v2.3.0.ejs +121 -0
- package/src/views/releases/v2.3.1.ejs +100 -0
- package/src/views/releases/v2.3.2.ejs +102 -0
- package/src/views/releases/v2.3.3.ejs +97 -0
- package/src/views/releases/v2.4.0.ejs +114 -0
- package/src/views/releases/v2.5.0.ejs +51 -0
- package/src/views/releases/v2.6.0.ejs +35 -0
- package/src/views/releases/v2.7.0.ejs +32 -0
- package/src/views/releases/v2.8.0.ejs +36 -0
- package/src/views/releases/v2.8.1.ejs +7 -0
- package/src/views/releases/v2.8.2.ejs +26 -0
- package/src/views/releases/v2.9.0.ejs +32 -0
- package/src/views/releases/v2.9.1.ejs +10 -0
- package/src/views/releases.ejs +26 -0
- package/src/views/sitemap.ejs +36 -0
- package/src/views/support.ejs +14 -0
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Helpful combinators
|
|
5
|
+
* For the non-pedants, a combinator is basically just a function with no free variables.
|
|
6
|
+
* For the non-pedants, "no free variables" means that the combinator does not have dependencies
|
|
7
|
+
* on things outside the function (e.g. it only depends on the function parameters).
|
|
8
|
+
* A strict definition of combinators requires functions as input parameters, but I loosen that here.
|
|
9
|
+
* That definition really only serves mathematical modeling of state in pure functional terms
|
|
10
|
+
* @module
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Curries a function parameters, which is to say that it returns a function with reduced arity.
|
|
15
|
+
* @example
|
|
16
|
+
* function sum (x, y) { return x + y; }
|
|
17
|
+
* curry(sum, 1)(2); // returns 3
|
|
18
|
+
* curry(sum, 1, 2)(); // returns 3
|
|
19
|
+
* @param {Function} fn - The function to curry
|
|
20
|
+
* @param {...*} args - The arguments to curry
|
|
21
|
+
* @returns {Function}
|
|
22
|
+
*/
|
|
23
|
+
function curry (fn) {
|
|
24
|
+
const args = Array.prototype.slice.call(arguments, 1);
|
|
25
|
+
return function () {
|
|
26
|
+
const nextArgs = Array.prototype.slice.call(arguments),
|
|
27
|
+
allArgs = args.concat(nextArgs);
|
|
28
|
+
|
|
29
|
+
return fn.apply(null, allArgs);
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Composes two or more functions
|
|
35
|
+
* @example
|
|
36
|
+
* function increment (i) { return i + 1; }
|
|
37
|
+
* function double (i) { return i * 2; }
|
|
38
|
+
* function triple (i) { return i * 3; }
|
|
39
|
+
* combinators.compose(increment, double, triple)(1); // returns 7
|
|
40
|
+
* @param {...Function} args - The functions to compose
|
|
41
|
+
* @returns {Function} A single function that represents the composition of the functions provided
|
|
42
|
+
*/
|
|
43
|
+
function compose () {
|
|
44
|
+
const args = Array.prototype.slice.call(arguments).reverse();
|
|
45
|
+
return obj => args.reduce((result, F) => F(result), obj);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
module.exports = {
|
|
49
|
+
/**
|
|
50
|
+
* Returns what was passed in unchanged, occasionally useful as the default transformation function
|
|
51
|
+
* to avoid special case logic
|
|
52
|
+
* @param {Object} i - The input
|
|
53
|
+
* @returns {Object} Exactly what was passed in
|
|
54
|
+
*/
|
|
55
|
+
identity: i => i,
|
|
56
|
+
/**
|
|
57
|
+
* Ignores its parameters, and instead always returns a constant value
|
|
58
|
+
* @param {Object} k - The constant to return
|
|
59
|
+
* @returns {Function} - A function that will always return the constant
|
|
60
|
+
*/
|
|
61
|
+
constant: k => () => k,
|
|
62
|
+
/**
|
|
63
|
+
* A function that does nothing, occasionally useful to avoid special case logic
|
|
64
|
+
*/
|
|
65
|
+
noop: () => {},
|
|
66
|
+
compose,
|
|
67
|
+
curry
|
|
68
|
+
};
|
package/src/util/date.js
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/** @module */
|
|
4
|
+
|
|
5
|
+
function toEpochWithoutTime (text) {
|
|
6
|
+
// be sure to exclude time so we get accurate text
|
|
7
|
+
const dateTextWithoutTime = new Date(Date.parse(text)).toDateString();
|
|
8
|
+
return Date.parse(dateTextWithoutTime);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function sameMonth (firstEpoch, secondEpoch) {
|
|
12
|
+
const first = new Date(firstEpoch),
|
|
13
|
+
second = new Date(secondEpoch);
|
|
14
|
+
|
|
15
|
+
return first.getFullYear() === second.getFullYear() && first.getMonth() === second.getMonth();
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Translates the distance between two dates within a month of each other to human readable text
|
|
20
|
+
* @param {string} thenText - The start date
|
|
21
|
+
* @param {string} testNowText - Ignore, used for testing purposes only.
|
|
22
|
+
* @returns {string}
|
|
23
|
+
*/
|
|
24
|
+
function howLongAgo (thenText, testNowText) {
|
|
25
|
+
const nowText = testNowText ? testNowText : new Date(Date.now()).toISOString(), // testNow is just for testing purposes
|
|
26
|
+
then = toEpochWithoutTime(thenText),
|
|
27
|
+
now = toEpochWithoutTime(nowText),
|
|
28
|
+
millisecondsInDay = 24 * 60 * 60 * 1000,
|
|
29
|
+
daysAgo = Math.floor((now - then) / millisecondsInDay);
|
|
30
|
+
|
|
31
|
+
if (daysAgo === 0) {
|
|
32
|
+
return 'today';
|
|
33
|
+
}
|
|
34
|
+
else if (daysAgo === 1) {
|
|
35
|
+
return 'yesterday';
|
|
36
|
+
}
|
|
37
|
+
else if (daysAgo < 7) {
|
|
38
|
+
return 'this week';
|
|
39
|
+
}
|
|
40
|
+
else if (daysAgo < 14) {
|
|
41
|
+
return 'last week';
|
|
42
|
+
}
|
|
43
|
+
else if (sameMonth(then, now)) {
|
|
44
|
+
return 'this month';
|
|
45
|
+
}
|
|
46
|
+
else {
|
|
47
|
+
return '';
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
module.exports = { howLongAgo };
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Error types returned by the API
|
|
5
|
+
* @module
|
|
6
|
+
*/
|
|
7
|
+
const inherit = require('./inherit.js'),
|
|
8
|
+
helpers = require('./helpers.js');
|
|
9
|
+
|
|
10
|
+
function createError (code, message, options) {
|
|
11
|
+
const result = inherit.from(Error, { code, message });
|
|
12
|
+
|
|
13
|
+
if (options) {
|
|
14
|
+
Object.keys(options).forEach(key => {
|
|
15
|
+
result[key] = options[key];
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
return result;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function create (code) {
|
|
22
|
+
return (message, options) => createError(code, message, options);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function createWithMessage (code, message) {
|
|
26
|
+
return options => createError(code, message, options);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Produces a JSON.stringify-able Error object
|
|
30
|
+
// (because message is on the prototype, it doesn't show by default)
|
|
31
|
+
function details (error) {
|
|
32
|
+
const prototypeProperties = {};
|
|
33
|
+
|
|
34
|
+
['message', 'name', 'stack'].forEach(key => {
|
|
35
|
+
if (error[key]) {
|
|
36
|
+
prototypeProperties[key] = error[key];
|
|
37
|
+
}
|
|
38
|
+
});
|
|
39
|
+
return helpers.merge(error, prototypeProperties);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
module.exports = {
|
|
43
|
+
ValidationError: create('bad data'),
|
|
44
|
+
InjectionError: create('invalid injection'),
|
|
45
|
+
ResourceConflictError: create('resource conflict'),
|
|
46
|
+
InsufficientAccessError: createWithMessage('insufficient access', 'Run mb in superuser mode if you want access'),
|
|
47
|
+
InvalidProxyError: create('invalid proxy'),
|
|
48
|
+
MissingResourceError: create('no such resource'),
|
|
49
|
+
InvalidJSONError: createWithMessage('invalid JSON', 'Unable to parse body as JSON'),
|
|
50
|
+
CommunicationError: createWithMessage('communication', 'Error communicating with mountebank'),
|
|
51
|
+
ProtocolError: create('cannot start server'),
|
|
52
|
+
DatabaseError: create('corrupted database'),
|
|
53
|
+
UnauthorizedError: createWithMessage('unauthorized', 'If you set the apiKey option, make sure you are sending the correct apiKey in the x-api-key header.'),
|
|
54
|
+
details
|
|
55
|
+
};
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/** @module */
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Returns true if obj is a defined value
|
|
7
|
+
* @param {Object} obj - the value to test
|
|
8
|
+
* @returns {boolean}
|
|
9
|
+
*/
|
|
10
|
+
function defined (obj) {
|
|
11
|
+
return typeof obj !== 'undefined';
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Returns true if obj is a non-null object
|
|
16
|
+
* Checking for typeof 'object' without checking for nulls
|
|
17
|
+
* is a very common source of bugs
|
|
18
|
+
* @param {Object} obj - the value to test
|
|
19
|
+
* @returns {boolean}
|
|
20
|
+
*/
|
|
21
|
+
function isObject (obj) {
|
|
22
|
+
return typeof obj === 'object' && obj !== null;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Returns the text used for logging purposes related to this socket
|
|
27
|
+
* @param {Object} socket - the socket
|
|
28
|
+
* @returns {string}
|
|
29
|
+
*/
|
|
30
|
+
function socketName (socket) {
|
|
31
|
+
let result = socket.remoteAddress;
|
|
32
|
+
if (socket.remotePort) {
|
|
33
|
+
result += `:${socket.remotePort}`;
|
|
34
|
+
}
|
|
35
|
+
return result;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Returns a deep clone of obj
|
|
40
|
+
* @param {Object} obj - the object to clone
|
|
41
|
+
* @returns {Object}
|
|
42
|
+
*/
|
|
43
|
+
function clone (obj) {
|
|
44
|
+
return JSON.parse(JSON.stringify(obj));
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Returns a new object combining the two parameters
|
|
49
|
+
* @param {Object} defaults - The base object
|
|
50
|
+
* @param {Object} overrides - The object to merge from. Where the same property exists in both defaults
|
|
51
|
+
* and overrides, the values for overrides will be used
|
|
52
|
+
* @returns {Object}
|
|
53
|
+
*/
|
|
54
|
+
function merge (defaults, overrides) {
|
|
55
|
+
const result = clone(defaults);
|
|
56
|
+
Object.keys(overrides).forEach(key => {
|
|
57
|
+
if (typeof overrides[key] === 'object' && overrides[key] !== null) {
|
|
58
|
+
result[key] = merge(result[key] || {}, overrides[key]);
|
|
59
|
+
}
|
|
60
|
+
else {
|
|
61
|
+
result[key] = overrides[key];
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
return result;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Sets a value of nested key string descriptor inside a Object.
|
|
69
|
+
* It changes the passed object.
|
|
70
|
+
* Ex:
|
|
71
|
+
* let obj = {a: {b:{c:'initial'}}}
|
|
72
|
+
* setNestedKey(obj, ['a', 'b', 'c'], 'changed-value')
|
|
73
|
+
* assert(obj === {a: {b:{c:'changed-value'}}})
|
|
74
|
+
*
|
|
75
|
+
* @param {Object} obj Object to set the nested key
|
|
76
|
+
* @param {Array} path An array to describe the path(Ex: ['a', 'b', 'c'])
|
|
77
|
+
* @param {Object} value Any value
|
|
78
|
+
* @returns {undefined}
|
|
79
|
+
* from https://stackoverflow.com/a/49754647
|
|
80
|
+
*/
|
|
81
|
+
function setDeep (obj, path, value) {
|
|
82
|
+
if (path.length === 1) {
|
|
83
|
+
obj[path] = value;
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
setDeep(obj[path[0]], path.slice(1), value);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function simulateFault (socket, faultConfig, logger) {
|
|
90
|
+
if (typeof faultConfig === 'undefined') {
|
|
91
|
+
return false;
|
|
92
|
+
}
|
|
93
|
+
if (faultConfig === 'CONNECTION_RESET_BY_PEER') {
|
|
94
|
+
logger.debug('Closing the connection');
|
|
95
|
+
socket.destroy();
|
|
96
|
+
return true;
|
|
97
|
+
}
|
|
98
|
+
else if (faultConfig === 'RANDOM_DATA_THEN_CLOSE') {
|
|
99
|
+
logger.debug('Sending garbage data then closing the connection');
|
|
100
|
+
socket.write(Buffer.from('Htijy%@tWXJ/hQ#[Q:7G@dH4"gu[QaX&', 'utf-8'));
|
|
101
|
+
socket.destroy();
|
|
102
|
+
return true;
|
|
103
|
+
}
|
|
104
|
+
else {
|
|
105
|
+
logger.error('Unexpected fault type [' + faultConfig + '], expected either CONNECTION_RESET_BY_PEER or RANDOM_DATA_THEN_CLOSE');
|
|
106
|
+
return false;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Remove specific key and value from object
|
|
112
|
+
* @param {Object} obj Object to filter
|
|
113
|
+
* @param {Array|Object|String} filter keys to remove
|
|
114
|
+
* @returns {Object}
|
|
115
|
+
*/
|
|
116
|
+
|
|
117
|
+
function objFilter (obj, filter) {
|
|
118
|
+
|
|
119
|
+
if (typeof filter === 'string') {
|
|
120
|
+
delete obj[filter];
|
|
121
|
+
}
|
|
122
|
+
else if (Array.isArray(filter)) {
|
|
123
|
+
filter.filter(keyFilter => obj[keyFilter]).forEach(keyFilter => objFilter(obj, keyFilter));
|
|
124
|
+
}
|
|
125
|
+
else {
|
|
126
|
+
Object.keys(filter).filter(keyFilter => obj[keyFilter]).forEach(keyFilter => objFilter(obj[keyFilter], filter[keyFilter]));
|
|
127
|
+
}
|
|
128
|
+
return obj;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
module.exports = { defined, isObject, socketName, clone, merge, setDeep, simulateFault, objFilter };
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/** @module */
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Crockford-style prototypical inheritance, which basically allows me to completely
|
|
7
|
+
* avoid the new and this operators, which I have an unnatural aversion to
|
|
8
|
+
* @param {Object} proto - the object to inherit from
|
|
9
|
+
* @param {Object} [obj] - properties to merge into the newly created object as own properties
|
|
10
|
+
* @returns {Object}
|
|
11
|
+
*/
|
|
12
|
+
function from (proto, obj) {
|
|
13
|
+
// allow either inherit.from(EventEmitter) or inherit.from({key: 'value'})
|
|
14
|
+
if (typeof proto === 'function') {
|
|
15
|
+
proto = new proto();
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
obj = obj || {};
|
|
19
|
+
function F () {}
|
|
20
|
+
F.prototype = proto;
|
|
21
|
+
const result = new F();
|
|
22
|
+
Object.keys(obj).forEach(key => {
|
|
23
|
+
result[key] = obj[key];
|
|
24
|
+
});
|
|
25
|
+
return result;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
module.exports = { from };
|
package/src/util/ip.js
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const os = require('os');
|
|
4
|
+
|
|
5
|
+
function getLocalIPs () {
|
|
6
|
+
const interfaces = os.networkInterfaces(),
|
|
7
|
+
result = [];
|
|
8
|
+
|
|
9
|
+
Object.keys(interfaces).forEach(name => {
|
|
10
|
+
interfaces[name].forEach(ip => {
|
|
11
|
+
if (ip.internal) {
|
|
12
|
+
result.push(ip.address);
|
|
13
|
+
if (ip.family === 'IPv4') {
|
|
14
|
+
// Prefix for IPv4 address mapped to a compliant IPv6 scheme
|
|
15
|
+
result.push(`::ffff:${ip.address}`);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
});
|
|
19
|
+
});
|
|
20
|
+
return result;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function ipWithoutZoneId (ip) {
|
|
24
|
+
return ip.replace(/%\w+/, '').toLowerCase();
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function createIPVerification (options) {
|
|
28
|
+
const allowedIPs = getLocalIPs();
|
|
29
|
+
|
|
30
|
+
if (!options.localOnly) {
|
|
31
|
+
options.ipWhitelist.forEach(ip => { allowedIPs.push(ip.toLowerCase()); });
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
if (allowedIPs.indexOf('*') >= 0) {
|
|
35
|
+
return () => true;
|
|
36
|
+
}
|
|
37
|
+
else {
|
|
38
|
+
return (ip, logger) => {
|
|
39
|
+
if (typeof ip === 'undefined') {
|
|
40
|
+
logger.error('Blocking request because no IP address provided. This is likely a bug in the protocol implementation.');
|
|
41
|
+
return false;
|
|
42
|
+
}
|
|
43
|
+
else {
|
|
44
|
+
const allowed = allowedIPs.some(allowedIP => allowedIP === ipWithoutZoneId(ip));
|
|
45
|
+
if (!allowed) {
|
|
46
|
+
logger.warn(`Blocking incoming connection from ${ip}. Turn off --localOnly or add to --ipWhitelist to allow`);
|
|
47
|
+
}
|
|
48
|
+
return allowed;
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
module.exports = { createIPVerification };
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
const path = require('path'),
|
|
3
|
+
fs = require('fs-extra'),
|
|
4
|
+
winston = require('winston'),
|
|
5
|
+
scopedLogger = require('./scopedLogger.js');
|
|
6
|
+
|
|
7
|
+
function initializeLogfile (filename) {
|
|
8
|
+
// Ensure new logfile on startup so the /logs only shows for this process
|
|
9
|
+
const extension = path.extname(filename),
|
|
10
|
+
pattern = new RegExp(`${extension}$`),
|
|
11
|
+
newFilename = filename.replace(pattern, `1${extension}`);
|
|
12
|
+
|
|
13
|
+
if (fs.existsSync(filename)) {
|
|
14
|
+
fs.renameSync(filename, newFilename);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function logFormat (config) {
|
|
19
|
+
const template = config.replace(/\$/g, '') // prevent injection attacks
|
|
20
|
+
.replace(/%level/g, '${info.level}')
|
|
21
|
+
.replace(/%message/g, '${info.message}')
|
|
22
|
+
.replace(/%timestamp/g, '${info.timestamp}');
|
|
23
|
+
|
|
24
|
+
// eslint-disable-next-line no-new-func
|
|
25
|
+
return new Function('info', `return \`${template}\`;`);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function createWinstonFormat (format, config) {
|
|
29
|
+
const formatters = [format.timestamp()];
|
|
30
|
+
if (config.colorize) {
|
|
31
|
+
formatters.push(format.colorize());
|
|
32
|
+
}
|
|
33
|
+
if (config.format === 'json') {
|
|
34
|
+
formatters.push(format.json());
|
|
35
|
+
}
|
|
36
|
+
else if (config.format === 'simple') {
|
|
37
|
+
formatters.push(format.simple());
|
|
38
|
+
}
|
|
39
|
+
else {
|
|
40
|
+
formatters.push(format.printf(logFormat(config.format)));
|
|
41
|
+
}
|
|
42
|
+
return format.combine(...formatters);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function createLogger (options) {
|
|
46
|
+
if (!options.log) {
|
|
47
|
+
options.log = { level: 'info' };
|
|
48
|
+
}
|
|
49
|
+
if (!options.log.transports) {
|
|
50
|
+
options.log.transports = {
|
|
51
|
+
file: {
|
|
52
|
+
path: 'mb.log',
|
|
53
|
+
format: 'json'
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const winstonLogger = winston.createLogger({ level: options.log.level }),
|
|
59
|
+
logger = scopedLogger.create(winstonLogger, `[mb:${options.port}] `),
|
|
60
|
+
consoleConfig = options.log.transports.console,
|
|
61
|
+
fileConfig = options.log.transports.file;
|
|
62
|
+
|
|
63
|
+
if (consoleConfig) {
|
|
64
|
+
winstonLogger.add(new winston.transports.Console({
|
|
65
|
+
format: createWinstonFormat(winston.format, consoleConfig)
|
|
66
|
+
}));
|
|
67
|
+
}
|
|
68
|
+
if (fileConfig) {
|
|
69
|
+
initializeLogfile(fileConfig.path);
|
|
70
|
+
winstonLogger.add(new winston.transports.File({
|
|
71
|
+
filename: fileConfig.path,
|
|
72
|
+
maxsize: '20m',
|
|
73
|
+
maxFiles: 5,
|
|
74
|
+
tailable: true,
|
|
75
|
+
format: createWinstonFormat(winston.format, fileConfig)
|
|
76
|
+
}));
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
return logger;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
module.exports = { createLogger };
|
|
83
|
+
|