roster-server 2.4.10 → 2.4.12
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/index.js
CHANGED
|
@@ -322,6 +322,7 @@ class Roster {
|
|
|
322
322
|
return;
|
|
323
323
|
}
|
|
324
324
|
|
|
325
|
+
const siteKeyForDomain = (domain) => this.defaultPort === 443 ? domain : `${domain}:443`;
|
|
325
326
|
const sites = fs.readdirSync(this.wwwPath, { withFileTypes: true })
|
|
326
327
|
.filter(dirent => dirent.isDirectory());
|
|
327
328
|
|
|
@@ -350,7 +351,7 @@ class Roster {
|
|
|
350
351
|
continue;
|
|
351
352
|
}
|
|
352
353
|
this.domains.push(domain);
|
|
353
|
-
this.sites[domain] = siteApp;
|
|
354
|
+
this.sites[siteKeyForDomain(domain)] = siteApp;
|
|
354
355
|
const root = wildcardRoot(domain);
|
|
355
356
|
if (root) this.wildcardZones.add(root);
|
|
356
357
|
log.info(`(✔) Loaded wildcard site: https://${domain}${type === 'static' ? ' (static)' : ''}`);
|
|
@@ -358,7 +359,7 @@ class Roster {
|
|
|
358
359
|
const domainEntries = [domain, `www.${domain}`];
|
|
359
360
|
this.domains.push(...domainEntries);
|
|
360
361
|
domainEntries.forEach(d => {
|
|
361
|
-
this.sites[d] = siteApp;
|
|
362
|
+
this.sites[siteKeyForDomain(d)] = siteApp;
|
|
362
363
|
});
|
|
363
364
|
log.info(`(✔) Loaded site: https://${domain}${type === 'static' ? ' (static)' : ''}`);
|
|
364
365
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "roster-server",
|
|
3
|
-
"version": "2.4.
|
|
3
|
+
"version": "2.4.12",
|
|
4
4
|
"description": "👾 RosterServer - A domain host router to host multiple HTTPS.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"repository": {
|
|
@@ -36,6 +36,7 @@
|
|
|
36
36
|
"@greenlock/manager": "^3.1.0",
|
|
37
37
|
"@root/acme": "^3.1.0",
|
|
38
38
|
"@root/csr": "^0.8.1",
|
|
39
|
+
"@root/encoding": "^1.0.1",
|
|
39
40
|
"@root/keypairs": "^0.10.0",
|
|
40
41
|
"@root/mkdirp": "^1.0.0",
|
|
41
42
|
"@root/request": "^1.6.1",
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
const PHP_PATH = /(?:^|\/)[^/]*\.php(?:$|\/)/i;
|
|
4
4
|
const SCANNER_PATH = /(?:^|\/)(?:\.git|\.hg|\.svn|\.ssh|\.aws|cgi-bin|server-status|vendor\/phpunit|wp-admin|wp-content|wp-includes|wp-json)(?:\/|$)/i;
|
|
5
|
-
const SCANNER_FILE = /(?:^|\/)(?:\.env(?:\.[^/]*)?|\.htaccess|composer\.(?:json|lock)|web\.config)(?:\/|$)/i;
|
|
5
|
+
const SCANNER_FILE = /(?:^|\/)(?:\.env(?:\.[^/]*)?|\.htaccess|composer\.(?:json|lock)|security\.txt|web\.config)(?:\/|$)/i;
|
|
6
6
|
|
|
7
7
|
function requirePositiveInteger(options, name) {
|
|
8
8
|
const value = options[name];
|
|
@@ -8,6 +8,7 @@ const http = require('http');
|
|
|
8
8
|
const os = require('os');
|
|
9
9
|
const Roster = require('../index.js');
|
|
10
10
|
const { createScannerBlocker } = require('../plugins/scanner-blocker.js');
|
|
11
|
+
const HttpsMiddleware = require('../vendor/greenlock-express/https-middleware.js');
|
|
11
12
|
const {
|
|
12
13
|
wildcardRoot,
|
|
13
14
|
hostMatchesWildcard,
|
|
@@ -40,6 +41,37 @@ function httpGet(host, port, pathname = '/') {
|
|
|
40
41
|
});
|
|
41
42
|
}
|
|
42
43
|
|
|
44
|
+
describe('vendored HTTPS hostname middleware', () => {
|
|
45
|
+
function invoke(host) {
|
|
46
|
+
let appCalled = false;
|
|
47
|
+
let body;
|
|
48
|
+
const req = { headers: { host }, socket: {} };
|
|
49
|
+
const res = {
|
|
50
|
+
end(value) {
|
|
51
|
+
body = value;
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
HttpsMiddleware.create({}, () => {
|
|
55
|
+
appCalled = true;
|
|
56
|
+
})(req, res);
|
|
57
|
+
return { appCalled, body, req, res };
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
it('accepts a valid Host header with a numeric port', () => {
|
|
61
|
+
const result = invoke('example.com:8880');
|
|
62
|
+
assert.strictEqual(result.appCalled, true);
|
|
63
|
+
assert.strictEqual(result.res.statusCode, undefined);
|
|
64
|
+
assert.strictEqual(result.req.headers.host, 'example.com');
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it('rejects a Host header with a non-numeric port', () => {
|
|
68
|
+
const result = invoke('example.com:invalid');
|
|
69
|
+
assert.strictEqual(result.appCalled, false);
|
|
70
|
+
assert.strictEqual(result.res.statusCode, 400);
|
|
71
|
+
assert.match(result.body, /Malformed HTTP Header/);
|
|
72
|
+
});
|
|
73
|
+
});
|
|
74
|
+
|
|
43
75
|
describe('wildcardRoot', () => {
|
|
44
76
|
it('returns root domain for *.example.com', () => {
|
|
45
77
|
assert.strictEqual(wildcardRoot('*.example.com'), 'example.com');
|
|
@@ -481,6 +513,51 @@ describe('Roster local mode (local: true)', () => {
|
|
|
481
513
|
});
|
|
482
514
|
|
|
483
515
|
describe('Roster loadSites', () => {
|
|
516
|
+
it('keeps discovered sites on 443 when the same domain is registered on other ports', async () => {
|
|
517
|
+
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'roster-test-'));
|
|
518
|
+
const wwwPath = path.join(tmpDir, 'www');
|
|
519
|
+
const siteDir = path.join(wwwPath, 'persist.example.com');
|
|
520
|
+
fs.mkdirSync(siteDir, { recursive: true });
|
|
521
|
+
fs.writeFileSync(path.join(siteDir, 'index.html'), '<html>static-443</html>', 'utf8');
|
|
522
|
+
|
|
523
|
+
const roster = new Roster({ wwwPath, local: true, port: 8880 });
|
|
524
|
+
roster.register('persist.example.com', () => (req, res) => {
|
|
525
|
+
res.writeHead(200);
|
|
526
|
+
res.end('app-8880');
|
|
527
|
+
});
|
|
528
|
+
roster.register('persist.example.com:8882', () => (req, res) => {
|
|
529
|
+
res.writeHead(200);
|
|
530
|
+
res.end('socket-8882');
|
|
531
|
+
});
|
|
532
|
+
|
|
533
|
+
try {
|
|
534
|
+
await roster.init();
|
|
535
|
+
|
|
536
|
+
const dispatch = (port) => {
|
|
537
|
+
let statusCode;
|
|
538
|
+
let body = '';
|
|
539
|
+
const res = {
|
|
540
|
+
writeHead: (status) => { statusCode = status; },
|
|
541
|
+
end: (value) => { body = value ? value.toString() : ''; }
|
|
542
|
+
};
|
|
543
|
+
roster.requestHandler(port)(
|
|
544
|
+
{ headers: { host: 'persist.example.com' }, method: 'GET', url: '/' },
|
|
545
|
+
res
|
|
546
|
+
);
|
|
547
|
+
return { statusCode, body };
|
|
548
|
+
};
|
|
549
|
+
|
|
550
|
+
assert.deepStrictEqual(dispatch(443), {
|
|
551
|
+
statusCode: 200,
|
|
552
|
+
body: '<html>static-443</html>'
|
|
553
|
+
});
|
|
554
|
+
assert.deepStrictEqual(dispatch(8880), { statusCode: 200, body: 'app-8880' });
|
|
555
|
+
assert.deepStrictEqual(dispatch(8882), { statusCode: 200, body: 'socket-8882' });
|
|
556
|
+
} finally {
|
|
557
|
+
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
558
|
+
}
|
|
559
|
+
});
|
|
560
|
+
|
|
484
561
|
it('loads site from www directory and registers domain + www', async () => {
|
|
485
562
|
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'roster-test-'));
|
|
486
563
|
const wwwPath = path.join(tmpDir, 'www');
|
|
@@ -61,9 +61,11 @@ describe('scanner-blocker plugin', () => {
|
|
|
61
61
|
const urls = [
|
|
62
62
|
'/install.php',
|
|
63
63
|
'/user-new\\.php',
|
|
64
|
+
'/wp-login.php',
|
|
64
65
|
'/wp-json',
|
|
65
66
|
'/.git/config',
|
|
66
|
-
'/%252eenv'
|
|
67
|
+
'/%252eenv',
|
|
68
|
+
'/security.txt'
|
|
67
69
|
];
|
|
68
70
|
|
|
69
71
|
for (const [index, url] of urls.entries()) {
|
|
@@ -20,6 +20,7 @@ SanitizeHost.create = function(gl, app) {
|
|
|
20
20
|
var hostname = HttpMiddleware.getHostname(req);
|
|
21
21
|
// Replace the hostname, and get the safe version
|
|
22
22
|
var safehost = HttpMiddleware.sanitizeHostname(req);
|
|
23
|
+
var normalizedHostname = hostname.toLowerCase().replace(/:\d+$/, "");
|
|
23
24
|
|
|
24
25
|
// if no hostname, move along
|
|
25
26
|
if (!hostname) {
|
|
@@ -28,7 +29,7 @@ SanitizeHost.create = function(gl, app) {
|
|
|
28
29
|
}
|
|
29
30
|
|
|
30
31
|
// if there were unallowed characters, complain
|
|
31
|
-
if (safehost
|
|
32
|
+
if (safehost !== normalizedHostname) {
|
|
32
33
|
res.statusCode = 400;
|
|
33
34
|
res.end("Malformed HTTP Header: 'Host: " + hostname + "'");
|
|
34
35
|
return;
|