fix(cli): generate a UI password for bare --ui-password in daemon/serve mode

The grand tunnel restructuring removed the CLI's auto-generated UI
password, so `openchamber -d --ui-password` (no value) silently started
an unauthenticated server instead of creating a password as in 1.8.1.

Restore generation for an explicit --ui-password flag without a value:
the password is generated before either launch path, passed to the
daemon/foreground process via OPENCHAMBER_UI_PASSWORD, persisted in the
instance state file, and surfaced once in human/quiet/json output.

Refs OPE-216
This commit is contained in:
Serhii Dziupin
2026-08-05 11:24:10 +03:00
parent 34c221b07f
commit 41a2e3781d
5 changed files with 108 additions and 7 deletions
+30
View File
@@ -1,5 +1,6 @@
import dgram from 'dgram';
import os from 'os';
import { randomInt } from 'node:crypto';
import { EXIT_CODE, TunnelCliError } from './cli-errors.js';
import {
getUnauthenticatedLanErrorMessage,
@@ -125,6 +126,33 @@ function hasUiPasswordConfigured(password) {
return typeof password === 'string' && password.trim().length > 0;
}
// Ambiguous-character-free alphabet so the printed password is easy to type
// from a phone or another machine. Mirrors the pre-refactor CLI alphabet.
const UI_PASSWORD_CHARSET = 'ABCDEFGHJKLMNPQRSTUVWXYZabcdefghjkmnpqrstuvwxyz23456789';
function generateUiPassword(length = 16) {
let password = '';
for (let i = 0; i < length; i++) {
password += UI_PASSWORD_CHARSET[randomInt(UI_PASSWORD_CHARSET.length)];
}
return password;
}
// Resolves the effective UI password for a serve: a configured password wins;
// an explicit `--ui-password` flag without a value gets a freshly generated
// password so daemon/foreground serves never silently drop the requested
// protection. The caller must surface `generated` passwords to the user once
// and persist them in the instance state file the server-side reads.
function resolveServeUiPassword({ uiPassword, explicitUiPassword }) {
if (hasUiPasswordConfigured(uiPassword)) {
return { password: uiPassword, generated: false };
}
if (explicitUiPassword === true) {
return { password: generateUiPassword(), generated: true };
}
return { password: undefined, generated: false };
}
function assertAuthenticatedNetworkExposure({ host, uiPassword }) {
const bindHost = resolveConfiguredBindHost(host);
if (hasUiPasswordConfigured(uiPassword)) {
@@ -150,5 +178,7 @@ export {
detectLanIPv4Address,
assertSafeBrowserPort,
hasUiPasswordConfigured,
generateUiPassword,
resolveServeUiPassword,
assertAuthenticatedNetworkExposure,
};