Files
holesail-browser/extension/dashboard/data/hostname-validator.js
T
Raven Scott 6fcd9fcf2b
CI / Build & Test (push) Successful in 3m19s
eat: implement Holesail-Browser enhancement plan
- Add unit tests (hostname-validator, TLDs, payload-schemas) and integration tests for message handler registry
- Refactor native host message router into handler registry (handlers/state, tunnels, ssh, rdp, backup, ca, connections)
- Add ESLint config and npm test + lint steps in CI
- Dashboard: visibility-based refresh pause, configurable refresh interval (2s/5s/10s/paused)
- Accessibility: ARIA on nav and modals, focus trap and restore, prefers-reduced-motion
- Empty states: primary action buttons for virtual hosts, servers, service tunnels
- Native host rate limiting for backup and CA operations; update SECURITY.md
- CONTRIBUTING: "Adding a new dashboard page", dev workflow; add npm run dev script
2026-03-15 00:24:31 -04:00

60 lines
2.4 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);
}
if (typeof module !== 'undefined' && module.exports) {
module.exports = { isValidVhostHostname, extractBaseDomain, extractActiveTlds };
}