Files
holesail-browser/extension/dashboard/data/hostname-validator.js
T
Raven Scott f1e98a7edd
CI / Build & Test (push) Successful in 2m54s
docs: add CONTRIBUTING.md, CHANGELOG.md, and JSDoc to entire codebase
Add docs/CONTRIBUTING.md covering the build system, dev workflow, all
npm scripts, how to add new native host message types, code style, and
debugging guidance.

Add CHANGELOG.md at the project root documenting all features and fixes
across the 1.0.0 release.

Add JSDoc (@param, @returns) to all previously undocumented exported
functions across 35 JS files:
- native-host/holesail-manager/ (index, virtual-hosts, service-tunnels,
  servers, port-allocator)
- native-host top-level managers (startup, connect-proxy, https-proxy,
  certificate-authority, ssh-manager, rdp-manager)
- extension/background/ (logs, native-messaging, proxy, message-router)
- extension/dashboard/core/ (utils, navigation, init)
- extension/dashboard/ui/ (modal, toast, state-tag)
- extension/dashboard/pages/ (all 10 page files)
- extension/dashboard/refresh.js, events.js
- extension/dashboard/data/hostname-validator.js
- scripts/ (build-host, run-install)
2026-03-01 00:40:53 -05:00

56 lines
2.3 KiB
JavaScript

// Depends on: data/tlds.js (REAL_TLDS, REAL_SLD_TLDS)
/**
* Validate a virtual host hostname.
* Rules:
* 1. Must have at least 2 dots (3 labels minimum: host.second.tld)
* 2. Each label: only [a-z0-9-], no leading/trailing hyphen, non-empty
* 3. The last two labels (base domain, e.g. "hole.sail") must not be a
* real public TLD or second-level public suffix
* Returns { ok: true } or { ok: false, error: string }
*/
function isValidVhostHostname(hostname) {
if (!hostname) return { ok: false, error: 'Hostname is required' };
const labels = hostname.split('.');
if (labels.length < 3) {
return { ok: false, error: 'Hostname must have the form host.second.tld (e.g. myapp.hole.sail) — single-dot names are not allowed' };
}
for (const label of labels) {
if (!label) return { ok: false, error: 'Hostname contains empty labels' };
if (!/^[a-z0-9-]+$/.test(label)) return { ok: false, error: 'Hostname contains invalid characters — only letters, digits and hyphens allowed' };
if (label.startsWith('-') || label.endsWith('-')) return { ok: false, error: 'Hostname labels must not start or end with a hyphen' };
}
const tld = labels[labels.length - 1];
const baseDomain = labels.slice(-2).join('.');
if (REAL_TLDS.has(tld) || REAL_SLD_TLDS.has(baseDomain)) {
return { ok: false, error: 'The TLD ".' + baseDomain + '" is a real registered domain — use a private TLD like .hole.sail or .my.internal' };
}
return { ok: true };
}
/**
* Extract the two-label base domain from a hostname.
* @example extractBaseDomain('myapp.hole.sail') // => 'hole.sail'
* @param {string} hostname
* @returns {string}
*/
function extractBaseDomain(hostname) {
const parts = hostname.split('.');
return parts.slice(-2).join('.');
}
/**
* Extract unique two-label base domains from a list of virtual host objects.
* Always includes `hole.sail` as the baseline TLD.
* @param {Array<{hostname: string}>} virtualHosts
* @returns {string[]} Array of TLD suffixes with leading dot (e.g. `['.hole.sail', '.hs']`).
*/
function extractActiveTlds(virtualHosts) {
const seen = new Set();
seen.add('hole.sail'); // always include baseline
for (const v of (virtualHosts || [])) {
if (v.hostname) seen.add(extractBaseDomain(v.hostname));
}
return Array.from(seen).map(b => '.' + b);
}