Harden remote API security boundaries
This commit is contained in:
@@ -22,6 +22,8 @@ export const DesktopNetworkSettings: React.FC = () => {
|
||||
const [draftValue, setDraftValue] = React.useState(false);
|
||||
const [savedPassword, setSavedPassword] = React.useState('');
|
||||
const [draftPassword, setDraftPassword] = React.useState('');
|
||||
const [lanAccessActive, setLanAccessActive] = React.useState(false);
|
||||
const [lanAccessBlockedReason, setLanAccessBlockedReason] = React.useState<string | null>(null);
|
||||
const [isLoading, setIsLoading] = React.useState(true);
|
||||
const [isSaving, setIsSaving] = React.useState(false);
|
||||
const [launchAtLoginSupported, setLaunchAtLoginSupported] = React.useState(false);
|
||||
@@ -50,6 +52,8 @@ export const DesktopNetworkSettings: React.FC = () => {
|
||||
const data = (await response.json().catch(() => null)) as null | {
|
||||
desktopLanAccessEnabled?: unknown;
|
||||
desktopUiPassword?: unknown;
|
||||
desktopLanAccessActive?: unknown;
|
||||
desktopLanAccessBlockedReason?: unknown;
|
||||
};
|
||||
if (cancelled) {
|
||||
return;
|
||||
@@ -61,6 +65,10 @@ export const DesktopNetworkSettings: React.FC = () => {
|
||||
setDraftValue(enabled);
|
||||
setSavedPassword(password);
|
||||
setDraftPassword(password);
|
||||
setLanAccessActive(data?.desktopLanAccessActive === true);
|
||||
setLanAccessBlockedReason(
|
||||
typeof data?.desktopLanAccessBlockedReason === 'string' ? data.desktopLanAccessBlockedReason : null
|
||||
);
|
||||
setError(null);
|
||||
} catch (cause) {
|
||||
if (!cancelled) {
|
||||
@@ -135,12 +143,22 @@ export const DesktopNetworkSettings: React.FC = () => {
|
||||
}
|
||||
return Number.isFinite(parsed) && parsed > 0 ? parsed : null;
|
||||
}, []);
|
||||
const lanUrl = draftValue && lanAddress && currentPort ? `http://${lanAddress}:${currentPort}` : null;
|
||||
const lanUrl = draftValue && lanAccessActive && lanAddress && currentPort ? `http://${lanAddress}:${currentPort}` : null;
|
||||
const lanRequiresPassword = draftValue && !draftPassword.trim();
|
||||
const lanBlockedByMissingPassword = savedValue && !lanAccessActive && lanAccessBlockedReason === 'missing-password';
|
||||
const saveDisabled = isLoading || isSaving || !isDirty || lanRequiresPassword;
|
||||
|
||||
const handleToggle = React.useCallback(() => {
|
||||
setDraftValue((current) => !current);
|
||||
}, []);
|
||||
|
||||
const handlePasswordChange = React.useCallback((value: string) => {
|
||||
setDraftPassword(value);
|
||||
if (!value.trim()) {
|
||||
setDraftValue(false);
|
||||
}
|
||||
}, []);
|
||||
|
||||
const handleLaunchAtLoginToggle = React.useCallback(async () => {
|
||||
if (!launchAtLoginSupported || isSavingLaunchAtLogin) {
|
||||
return;
|
||||
@@ -252,9 +270,11 @@ export const DesktopNetworkSettings: React.FC = () => {
|
||||
type="password"
|
||||
className="h-7 max-w-sm"
|
||||
value={draftPassword}
|
||||
onChange={(event) => setDraftPassword(event.target.value)}
|
||||
onChange={(event) => handlePasswordChange(event.target.value)}
|
||||
placeholder={t('settings.openchamber.desktopPassword.field.passwordPlaceholder')}
|
||||
disabled={isLoading || isSaving}
|
||||
required={draftValue}
|
||||
aria-invalid={lanRequiresPassword}
|
||||
/>
|
||||
<div className="typography-micro text-muted-foreground/70">
|
||||
{t('settings.openchamber.desktopPassword.field.passwordDescription')}
|
||||
@@ -288,6 +308,11 @@ export const DesktopNetworkSettings: React.FC = () => {
|
||||
<div className="typography-micro text-[var(--status-warning)]/85">
|
||||
{t('settings.openchamber.desktopNetwork.field.warning')}
|
||||
</div>
|
||||
{lanRequiresPassword || lanBlockedByMissingPassword ? (
|
||||
<div className="typography-micro text-[var(--status-warning)]/85">
|
||||
{t('settings.openchamber.desktopNetwork.field.passwordRequiredWarning')}
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -309,7 +334,7 @@ export const DesktopNetworkSettings: React.FC = () => {
|
||||
type="button"
|
||||
size="xs"
|
||||
onClick={handleSaveAndRestart}
|
||||
disabled={isLoading || isSaving || !isDirty}
|
||||
disabled={saveDisabled}
|
||||
className="shrink-0 !font-normal"
|
||||
>
|
||||
{isSaving ? t('settings.common.actions.saving') : t('settings.openchamber.desktopNetwork.actions.saveAndRestart')}
|
||||
|
||||
Reference in New Issue
Block a user