whistle 2.10.2 → 2.10.3
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/assets/menu.html +2 -0
- package/assets/modal.html +2 -0
- package/assets/tab.html +3 -3
- package/bin/whistle.js +5 -5
- package/biz/webui/cgi-bin/download.js +1 -1
- package/biz/webui/cgi-bin/rules/clear-dns-cache.js +7 -0
- package/biz/webui/htdocs/index.html +1 -1
- package/biz/webui/htdocs/js/index.js +53 -51
- package/biz/webui/lib/index.js +32 -2
- package/lib/config.js +3 -4
- package/lib/handlers/file-proxy.js +1 -1
- package/lib/https/ca.js +31 -0
- package/lib/https/index.js +3 -2
- package/lib/index.js +0 -2
- package/lib/inspectors/data.js +119 -13
- package/lib/inspectors/req.js +5 -3
- package/lib/plugins/get-plugins-sync.js +15 -11
- package/lib/plugins/get-plugins.js +11 -12
- package/lib/plugins/load-plugin.js +1 -1
- package/lib/plugins/module-paths.js +4 -0
- package/lib/plugins/util.js +8 -0
- package/lib/rules/dns.js +4 -0
- package/lib/rules/rules.js +2 -3
- package/lib/service/composer.js +10 -5
- package/lib/service/service.js +14 -10
- package/lib/service/util.js +3 -42
- package/lib/socket-mgr.js +2 -16
- package/lib/util/common.js +44 -10
- package/lib/util/index.js +24 -4
- package/package.json +2 -2
- package/biz/webui/cgi-bin/import-remote.js +0 -37
package/lib/service/service.js
CHANGED
|
@@ -32,7 +32,7 @@ var TEMP_PLUGINS_PATH = path.join(common.getWhistlePath(), '.temp_plugins');
|
|
|
32
32
|
var SESSIONS_FILE_RE = /\.(txt|json|saz)$/i;
|
|
33
33
|
var limiter = new Limiter({ concurrency: 10 });
|
|
34
34
|
var MAX_PLUGINS = 10;
|
|
35
|
-
var TEMP_FILES_PATH;
|
|
35
|
+
var TEMP_FILES_PATH = common.TEMP_FILES_PATH;
|
|
36
36
|
var SAVED_SESSIONS_PATH;
|
|
37
37
|
var LIMIT_SIZE = 1024 * 1024 * 128;
|
|
38
38
|
var MAX_TEMP_SIZE = 1024 * 1024 * 12;
|
|
@@ -47,7 +47,6 @@ var hasWhistleToken;
|
|
|
47
47
|
var whistleIdFile;
|
|
48
48
|
var storage = multer.memoryStorage();
|
|
49
49
|
var INVALID_NAME_RE = /[\u001e\u001f\u200e\u200f\u200d\u200c\u202a\u202d\u202e\u202c\u206e\u206f\u206b\u206a\u206d\u206c'<>:"\\/|?*]+/g;
|
|
50
|
-
var SPACE_RE = /\s+/g;
|
|
51
50
|
var upload = multer({
|
|
52
51
|
storage: storage,
|
|
53
52
|
fieldSize: LIMIT_SIZE
|
|
@@ -91,7 +90,7 @@ process.on('data', function(data) {
|
|
|
91
90
|
});
|
|
92
91
|
|
|
93
92
|
function getFilename(filename, time, count) {
|
|
94
|
-
filename = common.isString(filename) ? filename.replace(INVALID_NAME_RE, '').replace(
|
|
93
|
+
filename = common.isString(filename) ? filename.replace(INVALID_NAME_RE, '').replace(/\s+/g, ' ').substring(0, 64).trim() : '';
|
|
95
94
|
time = time || Date.now();
|
|
96
95
|
return common.getMonth(time) + '/' + filename + '_' + count + '_' + time;
|
|
97
96
|
}
|
|
@@ -108,7 +107,7 @@ function saveSessions(data, cb) {
|
|
|
108
107
|
return cb(err);
|
|
109
108
|
}
|
|
110
109
|
var filename = getFilename(data.filename, 0, count);
|
|
111
|
-
|
|
110
|
+
common.writeFile(path.join(SAVED_SESSIONS_PATH, filename), buf, function(e) {
|
|
112
111
|
!e && saveData({ type: 'savedData', filename: filename, buffer: buf } );
|
|
113
112
|
cb(e);
|
|
114
113
|
});
|
|
@@ -167,7 +166,8 @@ function getTempFiles(list, cb) {
|
|
|
167
166
|
}
|
|
168
167
|
};
|
|
169
168
|
list = list.forEach(function(filename) {
|
|
170
|
-
|
|
169
|
+
filename = common.getTempFile(filename);
|
|
170
|
+
if (!filename) {
|
|
171
171
|
return execCb();
|
|
172
172
|
}
|
|
173
173
|
filename = path.join(TEMP_FILES_PATH, filename);
|
|
@@ -194,7 +194,7 @@ function writeTempPlugin(plugin, data, cb) {
|
|
|
194
194
|
plugin = plugin.split('/').pop();
|
|
195
195
|
var tempPath = path.join(TEMP_PLUGINS_PATH, plugin);
|
|
196
196
|
ensureTempPluginsDir();
|
|
197
|
-
|
|
197
|
+
common.writeFile(tempPath, data, function(e) {
|
|
198
198
|
cb(e, e ? null : tempPath);
|
|
199
199
|
});
|
|
200
200
|
}
|
|
@@ -308,6 +308,10 @@ function sessionsHandler() {
|
|
|
308
308
|
function abort() {
|
|
309
309
|
res.destroy();
|
|
310
310
|
}
|
|
311
|
+
if (req.headers[common.WHISTLE_UID_HEADER] !== config.uid) {
|
|
312
|
+
return res.status(403).end('Forbidden');
|
|
313
|
+
}
|
|
314
|
+
delete req.headers[common.WHISTLE_UID_HEADER];
|
|
311
315
|
next();
|
|
312
316
|
});
|
|
313
317
|
app.get('/service/*', forwardRequest);
|
|
@@ -456,8 +460,9 @@ function sessionsHandler() {
|
|
|
456
460
|
});
|
|
457
461
|
}
|
|
458
462
|
var filename = req.query.filename;
|
|
459
|
-
|
|
460
|
-
|
|
463
|
+
var tempFile = common.getTempFile(filename);
|
|
464
|
+
if (tempFile) {
|
|
465
|
+
filename = path.join(TEMP_FILES_PATH, tempFile);
|
|
461
466
|
}
|
|
462
467
|
getFile(filename, function(em, data) {
|
|
463
468
|
if (em) {
|
|
@@ -470,7 +475,7 @@ function sessionsHandler() {
|
|
|
470
475
|
app.use('/cgi-bin/temp/create',
|
|
471
476
|
jsonParser,
|
|
472
477
|
function(req, res) {
|
|
473
|
-
|
|
478
|
+
common.writeTempFile(getContent(req.body), function(e, filename) {
|
|
474
479
|
if (e) {
|
|
475
480
|
return res.json({ ec: 2, em: e.message });
|
|
476
481
|
}
|
|
@@ -609,7 +614,6 @@ function checkWhistleId(cb) {
|
|
|
609
614
|
module.exports = function (options, callback) {
|
|
610
615
|
config = options;
|
|
611
616
|
whistleIdFile = path.join(options.baseDir, options.storage || '', '.whistle_id');
|
|
612
|
-
TEMP_FILES_PATH = options.TEMP_FILES_PATH;
|
|
613
617
|
SAVED_SESSIONS_PATH = options.SAVED_SESSIONS_PATH;
|
|
614
618
|
dataCenter.setup(options);
|
|
615
619
|
composer.setup(options);
|
package/lib/service/util.js
CHANGED
|
@@ -1,7 +1,5 @@
|
|
|
1
|
-
var crypto = require('crypto');
|
|
2
1
|
var path = require('path');
|
|
3
2
|
var fs = require('fs');
|
|
4
|
-
var fse = require('fs-extra2');
|
|
5
3
|
var zlib = require('../util/zlib');
|
|
6
4
|
var common = require('../util/common');
|
|
7
5
|
|
|
@@ -426,16 +424,6 @@ function parseFrames(res, content, callback) {
|
|
|
426
424
|
|
|
427
425
|
exports.parseFrames = parseFrames;
|
|
428
426
|
|
|
429
|
-
function getHexHash(ctn, key) {
|
|
430
|
-
return crypto
|
|
431
|
-
.createHmac('sha256', key || 'whistle_temp_files')
|
|
432
|
-
.update(ctn || '').digest('hex').toLowerCase();
|
|
433
|
-
}
|
|
434
|
-
|
|
435
|
-
function descSorter(a, b) {
|
|
436
|
-
return a > b ? -1 : 1;
|
|
437
|
-
}
|
|
438
|
-
|
|
439
427
|
var MAX_SESSIONS_FILES = 2000;
|
|
440
428
|
var SAVED_SESSIONS_FILE_RE = /_([1-9]\d*)_(\d+)$/;
|
|
441
429
|
var DIR_RE = /^\d{6}$/;
|
|
@@ -492,7 +480,9 @@ exports.getSavedList = function(savedDir, cb) {
|
|
|
492
480
|
list.push(dir);
|
|
493
481
|
}
|
|
494
482
|
});
|
|
495
|
-
list = list.sort(
|
|
483
|
+
list = list.sort(function (a, b) {
|
|
484
|
+
return a > b ? -1 : 1;
|
|
485
|
+
}).slice(0, 12).map(function(dir) {
|
|
496
486
|
return path.join(savedDir, dir);
|
|
497
487
|
});
|
|
498
488
|
readSessionsFiles(list, cb);
|
|
@@ -504,32 +494,3 @@ var SHARE_TYPE_RE = /Share$/;
|
|
|
504
494
|
exports.isShareType = function(type) {
|
|
505
495
|
return SHARE_TYPE_RE.test(type);
|
|
506
496
|
};
|
|
507
|
-
|
|
508
|
-
function writeRetry(filepath, data, callback, retry) {
|
|
509
|
-
fse.outputFile(filepath, data, function(e) {
|
|
510
|
-
if (!e || retry) {
|
|
511
|
-
return callback(e);
|
|
512
|
-
}
|
|
513
|
-
writeRetry(filepath, data, callback, true);
|
|
514
|
-
});
|
|
515
|
-
}
|
|
516
|
-
|
|
517
|
-
function writeFile(filepath, data, callback) {
|
|
518
|
-
common.getStat(filepath, function(_, stat) {
|
|
519
|
-
if (!stat || !stat.isFile()) {
|
|
520
|
-
return writeRetry(filepath, data, callback);
|
|
521
|
-
}
|
|
522
|
-
callback();
|
|
523
|
-
});
|
|
524
|
-
}
|
|
525
|
-
|
|
526
|
-
exports.writeFile = writeFile;
|
|
527
|
-
|
|
528
|
-
function writeTempFile(dir, value, callback) {
|
|
529
|
-
var filename = getHexHash(value);
|
|
530
|
-
writeFile(path.join(dir, filename), value, function(err) {
|
|
531
|
-
callback(err, filename);
|
|
532
|
-
});
|
|
533
|
-
}
|
|
534
|
-
|
|
535
|
-
exports.writeTempFile = writeTempFile;
|
package/lib/socket-mgr.js
CHANGED
|
@@ -7,28 +7,14 @@ var extend = require('extend');
|
|
|
7
7
|
var pendingReqList = [];
|
|
8
8
|
var INTERVAL = 22 * 1000;
|
|
9
9
|
var proxy;
|
|
10
|
-
var index = 0;
|
|
11
|
-
var MAX_PAYLOAD = 1024 * 1024;
|
|
12
10
|
var conns = {};
|
|
13
11
|
var PING = Buffer.from('iQA=', 'base64');
|
|
14
12
|
var PONG = Buffer.from('ioAn6ubf', 'base64');
|
|
15
13
|
var PAUSE_STATUS = 1;
|
|
16
14
|
var IGNORE_STATUS = 2;
|
|
17
15
|
var MAX_COMPOSE_FRAME_COUNT = 5;
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
++index;
|
|
21
|
-
if (index > 999) {
|
|
22
|
-
index = 0;
|
|
23
|
-
}
|
|
24
|
-
if (index > 99) {
|
|
25
|
-
return Date.now() + '-' + index;
|
|
26
|
-
}
|
|
27
|
-
if (index > 9) {
|
|
28
|
-
return Date.now() + '-0' + index;
|
|
29
|
-
}
|
|
30
|
-
return Date.now() + '-00' + index;
|
|
31
|
-
}
|
|
16
|
+
var getFrameId = util.getFrameId;
|
|
17
|
+
var MAX_PAYLOAD = util.MAX_FRAME_PAYLOAD;
|
|
32
18
|
|
|
33
19
|
exports = module.exports = function (p) {
|
|
34
20
|
proxy = p;
|
package/lib/util/common.js
CHANGED
|
@@ -70,10 +70,16 @@ var ALLOW_ACK = 'x-whistle-allow-tunnel-ack';
|
|
|
70
70
|
var PLUS_RE = /\+/g;
|
|
71
71
|
var TOKEN = '\r\u0000\n\u0003\r';
|
|
72
72
|
var PROP_SEP_RE = /(\\*)([|&]|\\[stnrfv])/g;
|
|
73
|
-
var TEMP_FILE_RE = /^[\da-f]{64}
|
|
73
|
+
var TEMP_FILE_RE = /^(?:\/?temp\/)?([\da-f]{64})(?:\.[\w.-]+)?$/;
|
|
74
|
+
var TEMP_FILES_PATH = path.join(getWhistlePath(), 'temp_files');
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
exports.WHISTLE_UID_HEADER = '_x-whistle-uid_';
|
|
78
|
+
|
|
79
|
+
exports.getTempFile = function(str) {
|
|
80
|
+
var match = TEMP_FILE_RE.exec(str);
|
|
81
|
+
return match && match[1];
|
|
74
82
|
|
|
75
|
-
exports.isTempFile = function(str) {
|
|
76
|
-
return TEMP_FILE_RE.test(str);
|
|
77
83
|
};
|
|
78
84
|
|
|
79
85
|
var PROPS_CHAR = {
|
|
@@ -135,6 +141,7 @@ exports.parseQuery = function (str, sep, eq, escape) {
|
|
|
135
141
|
return '';
|
|
136
142
|
};
|
|
137
143
|
|
|
144
|
+
exports.TEMP_FILES_PATH = TEMP_FILES_PATH;
|
|
138
145
|
exports.ALLOW_ACK = ALLOW_ACK;
|
|
139
146
|
exports.CONN_TIMEOUT = CONN_TIMEOUT;
|
|
140
147
|
exports.TGZ_FILE_NAME_RE = /^(?:[\w.~-]+-)?whistle\.[a-z\d_-]+-\d+\.\d+\.\d+[\w.-]*\.(?:tgz|tar(?:\.gz)?)$/;
|
|
@@ -481,14 +488,8 @@ exports.isConnect = function(options) {
|
|
|
481
488
|
);
|
|
482
489
|
};
|
|
483
490
|
|
|
484
|
-
var NO_PROTO_RE = /[^a-zA-Z0-9.-]/;
|
|
485
|
-
|
|
486
491
|
function hasProtocol(url) {
|
|
487
|
-
|
|
488
|
-
if (index <= 0) {
|
|
489
|
-
return false;
|
|
490
|
-
}
|
|
491
|
-
return !NO_PROTO_RE.test(url.substring(0, index));
|
|
492
|
+
return /^[a-zA-Z0-9.-]+:\/\//.test(url);
|
|
492
493
|
}
|
|
493
494
|
|
|
494
495
|
function setProtocol(url, isHttps) {
|
|
@@ -2001,3 +2002,36 @@ exports.getSessionInfo = function(headers) {
|
|
|
2001
2002
|
result._ = rawResult;
|
|
2002
2003
|
return result;
|
|
2003
2004
|
};
|
|
2005
|
+
|
|
2006
|
+
function writeRetry(filepath, data, callback, retry) {
|
|
2007
|
+
fse.outputFile(filepath, data, function(e) {
|
|
2008
|
+
if (!e || retry) {
|
|
2009
|
+
return callback(e);
|
|
2010
|
+
}
|
|
2011
|
+
writeRetry(filepath, data, callback, true);
|
|
2012
|
+
});
|
|
2013
|
+
}
|
|
2014
|
+
|
|
2015
|
+
function writeFile(filepath, data, callback) {
|
|
2016
|
+
getStat(filepath, function(_, stat) {
|
|
2017
|
+
if (!stat || !stat.isFile()) {
|
|
2018
|
+
return writeRetry(filepath, data, callback);
|
|
2019
|
+
}
|
|
2020
|
+
callback();
|
|
2021
|
+
});
|
|
2022
|
+
}
|
|
2023
|
+
|
|
2024
|
+
exports.writeFile = writeFile;
|
|
2025
|
+
|
|
2026
|
+
function getHexHash(ctn, key) {
|
|
2027
|
+
return crypto
|
|
2028
|
+
.createHmac('sha256', key || 'whistle_temp_files')
|
|
2029
|
+
.update(ctn || '').digest('hex').toLowerCase();
|
|
2030
|
+
}
|
|
2031
|
+
|
|
2032
|
+
exports.writeTempFile = function (value, callback) {
|
|
2033
|
+
var filename = getHexHash(value);
|
|
2034
|
+
writeFile(path.join(TEMP_FILES_PATH, filename), value, function(err) {
|
|
2035
|
+
callback(err, filename);
|
|
2036
|
+
});
|
|
2037
|
+
};
|
package/lib/util/index.js
CHANGED
|
@@ -1164,7 +1164,7 @@ function getTempFilePath(filePath, rule) {
|
|
|
1164
1164
|
var root = rule.root;
|
|
1165
1165
|
if (!root && common.TEMP_PATH_RE.test(filePath)) {
|
|
1166
1166
|
rule._suffix = RegExp.$2;
|
|
1167
|
-
return path.join(
|
|
1167
|
+
return path.join(common.TEMP_FILES_PATH, RegExp.$1);
|
|
1168
1168
|
}
|
|
1169
1169
|
return joinPath(root, decodePath(filePath));
|
|
1170
1170
|
}
|
|
@@ -3102,10 +3102,10 @@ function getCookieItem(name, cookie) {
|
|
|
3102
3102
|
if (!cookie || typeof cookie != 'object') {
|
|
3103
3103
|
return name + '=' + (cookie == null ? '' : cookie);
|
|
3104
3104
|
}
|
|
3105
|
-
var attrs = [name + '=' + escapeValue(cookie.value)];
|
|
3105
|
+
var attrs = [name + '=' + escapeValue(cookie.value || cookie.Value)];
|
|
3106
3106
|
var maxAge = cookie.maxAge || cookie.maxage ||
|
|
3107
3107
|
cookie.MaxAge || cookie['Max-Age'] || cookie['max-age'];
|
|
3108
|
-
maxAge = parseInt(
|
|
3108
|
+
maxAge = parseInt(maxAge, 10);
|
|
3109
3109
|
if (!Number.isNaN(maxAge)) {
|
|
3110
3110
|
attrs.push('Expires=' + new Date(Date.now() + maxAge * 1000).toGMTString());
|
|
3111
3111
|
attrs.push('Max-Age=' + (maxAge === EXPIRED_SEC ? 0 : maxAge));
|
|
@@ -3638,7 +3638,8 @@ function formatAuth(obj) {
|
|
|
3638
3638
|
var password = obj.password;
|
|
3639
3639
|
return {
|
|
3640
3640
|
username: username == null ? null : String(username),
|
|
3641
|
-
password: password == null ? null : String(password)
|
|
3641
|
+
password: password == null ? null : String(password),
|
|
3642
|
+
proxy: !!obj.proxy
|
|
3642
3643
|
};
|
|
3643
3644
|
}
|
|
3644
3645
|
|
|
@@ -3997,3 +3998,22 @@ exports.isHide = function(req) {
|
|
|
3997
3998
|
var d = req.disable || '';
|
|
3998
3999
|
return req.fromComposer ? checkHideProp(e, d, 'Composer') : checkHideProp(e, d, '', req._filters && req._filters.hide);
|
|
3999
4000
|
};
|
|
4001
|
+
|
|
4002
|
+
|
|
4003
|
+
var frameIndex = 0;
|
|
4004
|
+
|
|
4005
|
+
exports.MAX_FRAME_PAYLOAD = 1024 * 1024;
|
|
4006
|
+
|
|
4007
|
+
exports.getFrameId = function() {
|
|
4008
|
+
++frameIndex;
|
|
4009
|
+
if (frameIndex > 999) {
|
|
4010
|
+
frameIndex = 0;
|
|
4011
|
+
}
|
|
4012
|
+
if (frameIndex > 99) {
|
|
4013
|
+
return Date.now() + '-' + frameIndex;
|
|
4014
|
+
}
|
|
4015
|
+
if (frameIndex > 9) {
|
|
4016
|
+
return Date.now() + '-0' + frameIndex;
|
|
4017
|
+
}
|
|
4018
|
+
return Date.now() + '-00' + frameIndex;
|
|
4019
|
+
};
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "whistle",
|
|
3
3
|
"description": "HTTP, HTTP2, HTTPS, Websocket debugging proxy",
|
|
4
|
-
"version": "2.10.
|
|
4
|
+
"version": "2.10.3",
|
|
5
5
|
"dataDirname": ".whistle",
|
|
6
6
|
"localUIHost": "local.whistlejs.com",
|
|
7
7
|
"port": 8899,
|
|
@@ -53,7 +53,7 @@
|
|
|
53
53
|
"parseurl": "^1.3.1",
|
|
54
54
|
"pfork": "^0.6.3",
|
|
55
55
|
"pipestream": "^0.7.5",
|
|
56
|
-
"set-global-proxy": "^0.
|
|
56
|
+
"set-global-proxy": "^0.4.0",
|
|
57
57
|
"sni": "1.0.0",
|
|
58
58
|
"sockx": "^0.2.3",
|
|
59
59
|
"starting": "^8.0.3",
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
var util = require('../../../lib/util');
|
|
2
|
-
var loadService = require('../lib/proxy').loadService;
|
|
3
|
-
|
|
4
|
-
var MAX_LEN = 1024 * 1024 * 6;
|
|
5
|
-
var HTTP_RE = /https?:\/\/\S/i;
|
|
6
|
-
|
|
7
|
-
module.exports = function(req, res) {
|
|
8
|
-
var url = req.query.url;
|
|
9
|
-
if (HTTP_RE.test(url)) {
|
|
10
|
-
util.request({
|
|
11
|
-
url: url,
|
|
12
|
-
maxLength: MAX_LEN
|
|
13
|
-
}, function(err, body, r) {
|
|
14
|
-
if (err) {
|
|
15
|
-
var msg = err.code === 'EEXCEED' ? 'The size of response body exceeds 6MB' : err.message;
|
|
16
|
-
return res.json({ec: 2, em: msg});
|
|
17
|
-
}
|
|
18
|
-
var status = r.statusCode;
|
|
19
|
-
if (status !== 200) {
|
|
20
|
-
var em = status > 200 && status < 400 ? 'No data' : 'Request failed';
|
|
21
|
-
return res.json({ec: 2, em: em + ' (statusCode: ' + status + ')'});
|
|
22
|
-
}
|
|
23
|
-
return res.json({ec: 0, body: body});
|
|
24
|
-
});
|
|
25
|
-
} else if (util.isString(url)) {
|
|
26
|
-
loadService(function(err, options) {
|
|
27
|
-
if (err) {
|
|
28
|
-
util.sendRes(res, 500, err.stack || err);
|
|
29
|
-
} else {
|
|
30
|
-
req.url = '/cgi-bin/temp/get?filename=' + encodeURIComponent(url);
|
|
31
|
-
util.transformReq(req, res, options.port);
|
|
32
|
-
}
|
|
33
|
-
});
|
|
34
|
-
} else {
|
|
35
|
-
res.json({ec: 400, em: 'Bad url'});
|
|
36
|
-
}
|
|
37
|
-
};
|