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.10",
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.length !== hostname.length) {
32
+ if (safehost !== normalizedHostname) {
32
33
  res.statusCode = 400;
33
34
  res.end("Malformed HTTP Header: 'Host: " + hostname + "'");
34
35
  return;