OverviewFilesCommitsIssuesDocsReleasesPeers

hypermosh-shell / test / firewall.test.mjs

54 lines · javascript ref:
// firewall regression test — the root cause of "hypermosh just does not connect".
//
// A bare `hypermosh serve --hyperident` builds an EMPTY allow-list, which the
// firewall reads as DENY-ALL. Before the fix that produced a silent hang / a
// bare PEER_CONNECTION_FAILED with no explanation. This pins the behaviour:
//   • empty allow-list  -> client is rejected (connection never establishes)
//   • --disable-firewall -> client connects (control frame received)
import DHT from 'hyperdht'
import { serveSession, connectSession } from '../lib/session.mjs'
import { execSync } from 'child_process'

let hasMosh = true
try { execSync('command -v mosh-server', { stdio: 'ignore' }) } catch { hasMosh = false }

let failures = 0
const ok = (cond, msg) => { if (cond) console.log('    ✅ ' + msg); else { console.log('    ❌ ' + msg); failures++ } }

// Single-machine self-hairpin DHT connects can be slow cold, so give the
// expected-connect case generous headroom (matches session.test's 40s tolerance).
async function trial (allow, { timeoutMs, retries }) {
  const serverKp = DHT.keyPair()
  const s = await serveSession({ keyPair: serverKp, allow })
  const clientKp = DHT.keyPair()
  const outcome = await new Promise((resolve) => {
    let done = false
    const finish = (r) => { if (!done) { done = true; resolve(r) } }
    const t = setTimeout(() => finish('deny'), timeoutMs) // never established => treated as deny/hang
    const session = connectSession({
      remoteKey: serverKp.publicKey, keyPair: clientKp, retries,
      start: { term: 'xterm', cmd: ['true'] },
      onstarted: () => { clearTimeout(t); finish('connect') },
      onerror: () => { clearTimeout(t); finish('deny') }
    })
    setTimeout(() => { try { session.close() } catch {} }, timeoutMs + 500)
  })
  await s.close()
  return outcome
}

console.log('[firewall] empty allow-list denies; --disable-firewall connects')

// DENY: empty allow-list (what a plain `serve --hyperident` produces).
ok((await trial([], { timeoutMs: 20000, retries: 1 })) === 'deny', 'empty allow-list rejects the client (root-cause behaviour pinned)')

// ALLOW: firewall disabled. Requires mosh-server to reach the onstarted control frame.
if (hasMosh) {
  ok((await trial(true, { timeoutMs: 45000, retries: 3 })) === 'connect', '--disable-firewall lets the client through to a session')
} else {
  console.log('    ⏭  disable-firewall connect check SKIPPED (mosh-server not installed)')
}

console.log(failures === 0 ? '\n✅ firewall test passed' : '\n❌ firewall test failed')
process.exit(failures === 0 ? 0 : 1)