Skip to content

macOS: add remote gateway token UI without clobbering config#40187

Merged
ngutman merged 6 commits intomainfrom
fix/macos-remote-gateway-token-ui
Mar 8, 2026
Merged

macOS: add remote gateway token UI without clobbering config#40187
ngutman merged 6 commits intomainfrom
fix/macos-remote-gateway-token-ui

Conversation

@ngutman
Copy link
Contributor

@ngutman ngutman commented Mar 8, 2026

Summary

  • Supersedes macOS: add remote gateway token field for remote mode #34614 and carries forward the original macOS remote token UI work from @cgdusek.
  • Add a remote gateway token field to macOS Settings and onboarding advanced remote setup for gateway.remote.token.
  • Preserve existing non-plaintext gateway.remote.token values unless the user explicitly edits the field, instead of silently clobbering them during app-state sync.
  • Warn when the loaded remote token value is not plaintext, while keeping the macOS app plaintext-only for remote token auth.

Change Type (select all)

  • Bug fix
  • Feature
  • Refactor
  • Docs
  • Security hardening
  • Chore/infra

Scope (select all touched areas)

  • Gateway / orchestration
  • Skills / tool execution
  • Auth / tokens
  • Memory / storage
  • Integrations
  • API / contracts
  • UI / DX
  • CI/CD / infra

Linked Issue/PR

User-visible / Behavior Changes

  • macOS Settings > General > Remote now includes a Gateway token field.
  • Onboarding advanced remote setup now includes a Gateway token field.
  • Existing unsupported non-plaintext gateway.remote.token config values are preserved until explicitly replaced.
  • The UI now warns when the loaded gateway.remote.token value is not plaintext and cannot be used directly by the macOS app.

Security Impact (required)

  • New permissions/capabilities? (No)
  • Secrets/tokens handling changed? (Yes)
  • New/changed network calls? (No)
  • Command/tool execution surface changed? (No)
  • Data access scope changed? (No)
  • If any Yes, explain risk + mitigation:
    • Risk: token input in UI could accidentally store whitespace or clear config unexpectedly.
    • Mitigation: token input is trimmed, blank values only remove gateway.remote.token after explicit user edit, and unsupported existing token shapes are preserved when untouched.

Repro + Verification

Environment

  • OS: macOS (local dev)
  • Runtime/container: Swift Package (apps/macos)
  • Model/provider: N/A
  • Integration/channel (if any): N/A
  • Relevant config (redacted): gateway.mode=remote, gateway.remote.*

Steps

  1. Open macOS Settings in Remote mode or onboarding advanced remote setup.
  2. Enter a plaintext gateway token or load a config containing a non-string gateway.remote.token.
  3. Confirm plaintext tokens persist correctly and unsupported non-string values are preserved until explicitly replaced.

Expected

  • Remote token can be set without manual config edits.
  • Existing unsupported token shapes are not silently removed by unrelated remote config changes.

Actual

  • Implemented and covered by updated tests below.

Evidence

  • Failing test/log before + passing after
  • Trace/log snippets
  • Screenshot/recording
  • Perf numbers (if relevant)

Human Verification (required)

  • Verified scenarios:
    • swift test --package-path apps/macos --filter AppStateRemoteConfigTests
    • swift test --package-path apps/macos --filter GatewayEndpointStoreTests
    • swift test --package-path apps/macos --filter SettingsViewSmokeTests
    • swift test --package-path apps/macos --filter OnboardingViewSmokeTests
  • Edge cases checked:
    • Token is trimmed before persistence.
    • Blank plaintext tokens remove gateway.remote.token.
    • Object-valued gateway.remote.token is preserved unless the user explicitly edits the field.
  • What you did not verify:
    • Full swift test --package-path apps/macos run is not currently stable on this host due unrelated baseline failures/flakes (ExecAllowlistTests fixture mismatch; LowCoverageViewSmokeTests exclusivity crash).

Review Conversations

  • I replied to or resolved every bot review conversation I addressed in this PR.
  • I left unresolved only the conversations that still need reviewer or maintainer judgment.

Compatibility / Migration

  • Backward compatible? (Yes)
  • Config/env changes? (No)
  • Migration needed? (No)
  • If yes, exact upgrade steps:

Failure Recovery (if this breaks)

  • How to disable/revert this change quickly:
    • Remove gateway.remote.token from config and/or revert this PR.
  • Files/config to restore:
    • apps/macos/Sources/OpenClaw/AppState.swift
    • apps/macos/Sources/OpenClaw/GatewayRemoteConfig.swift
    • apps/macos/Sources/OpenClaw/GeneralSettings.swift
    • apps/macos/Sources/OpenClaw/OnboardingView+Pages.swift
  • Known bad symptoms reviewers should watch for:
    • Remote token field appears but does not persist to config.
    • Existing non-string gateway.remote.token values disappear after unrelated remote config edits.

Risks and Mitigations

  • Risk: Exposing token input in UI could increase accidental shoulder-surfing.
    • Mitigation: Uses SecureField and keeps scope limited to remote gateway token entry.

@aisle-research-bot
Copy link

aisle-research-bot bot commented Mar 8, 2026

🔒 Aisle Security Analysis

We found 1 potential security issue(s) in this PR:

# Severity Title
1 🟡 Medium Remote gateway token persisted in plaintext JSON config with no permission hardening

1. 🟡 Remote gateway token persisted in plaintext JSON config with no permission hardening

Property Value
Severity Medium
CWE CWE-522
Location apps/macos/Sources/OpenClaw/OpenClawConfigFile.swift:36-55

Description

The new remote gateway token feature writes the user-provided gateway.remote.token value into the main config file on disk.

  • Input: SecureField(... text: $state.remoteToken) in settings/onboarding updates AppState.remoteToken.
  • Propagation: when remoteTokenDirty is true, AppState.updatedRemoteGatewayConfig(...) sets remote["token"] = remoteToken.
  • Sink (disk persistence): OpenClawConfigFile.saveDict(...) serializes and writes the full config dictionary (including gateway.remote.token) to OpenClawPaths.configURL (default ~/.openclaw/openclaw.json) using Data.write(..., .atomic).
  • Issue: saveDict / directory creation do not set restrictive POSIX permissions (e.g., 0600 for the file, 0700 for the directory). On macOS, the default create/write permissions are umask-dependent and commonly result in files like 0644 and directories like 0755, which may be readable by other local users if the home directory is not private. This stores an authentication token in plaintext and increases exposure via local reads and backups.

Vulnerable sink code:

try FileManager().createDirectory(
    at: url.deletingLastPathComponent(),
    withIntermediateDirectories: true)
try data.write(to: url, options: [.atomic])

Recommendation

Avoid persisting authentication tokens in plaintext config files.

Preferred fix: store the remote gateway token in Keychain and keep only a reference (or no token at all) in openclaw.json.

If a config-file token must exist for compatibility, harden on-disk permissions:

  1. Ensure the state dir (~/.openclaw) is 0700.
  2. Ensure the config file is 0600 after write (and ideally create it with 0600 initially).

Example (after data.write):

try FileManager().setAttributes([
  .posixPermissions: 0o700,
], ofItemAtPath: url.deletingLastPathComponent().path)

try FileManager().setAttributes([
  .posixPermissions: 0o600,
], ofItemAtPath: url.path)

Also consider:

  • Excluding the token from backups by moving it to Keychain.
  • Minimizing writes (avoid saving on each keystroke; save on explicit “Apply”/“Save” action).

Analyzed PR: #40187 at commit e645e87

Last updated on: 2026-03-08T19:48:56Z

@openclaw-barnacle openclaw-barnacle bot added app: macos App: macos size: M maintainer Maintainer-authored PR labels Mar 8, 2026
@ngutman ngutman force-pushed the fix/macos-remote-gateway-token-ui branch from 16c6dfe to 279d59c Compare March 8, 2026 19:20
@greptile-apps
Copy link
Contributor

greptile-apps bot commented Mar 8, 2026

Greptile Summary

This PR adds a Gateway token field (gateway.remote.token) to macOS Settings and onboarding advanced-remote setup, fixing a pre-existing bug where any remote config sync would silently overwrite non-plaintext token values (e.g., $secretRef objects).

Core implementation:

  • Introduces remoteTokenDirty flag to gate writes behind explicit user edits
  • Adds TokenValue enum distinguishing missing / plaintext / unsupported shapes
  • Uses isApplyingRemoteTokenConfig re-entrancy guard to prevent the didSet observer from marking the token dirty during programmatic reloads
  • GatewayRemoteConfig.resolveTokenValue / resolveTokenString centralize token-reading logic
  • updatedRemoteGatewayConfig only touches the "token" key when remoteTokenDirty is true, preserving non-string tokens during unrelated syncs

Test coverage:
Token trimming, blank-clears-key, object-token preservation across mode/transport changes, and explicit-replacement scenarios are covered by targeted unit tests.

Known issues:
CHANGELOG entry references #34614 (superseded PR) instead of current PR #40187.

Confidence Score: 4/5

  • Safe to merge; the token-preservation bug fix is correct and well-tested with only a minor CHANGELOG reference error to fix.
  • The core logic — dirty-flag gating writes, isApplyingRemoteTokenConfig re-entrancy guard, and TokenValue enum — is sound and backed by targeted unit tests. No security or data-loss risks identified. The only verified issue is a CHANGELOG typo (PR reference), which is trivial to correct.
  • CHANGELOG.md requires PR number correction on line 13.

Last reviewed commit: 279d59c

@@ -11,6 +11,7 @@ Docs: https://docs.openclaw.ai
- Talk mode: add top-level `talk.silenceTimeoutMs` config so Talk waits a configurable amount of silence before auto-sending the current transcript, while keeping each platform's existing default pause window when unset. (#39607) Thanks @danodoesdesign. Fixes #17147.
- CLI/install: include the short git commit hash in `openclaw --version` output when metadata is available, and keep installer version checks compatible with the decorated format. (#39712) thanks @sourman.
- Docs/Web search: restore $5/month free-credit details, replace defunct "Data for Search"/"Data for AI" plan names with current "Search" plan, and note legacy subscription validity in Brave setup docs. Follows up on #26860. (#40111) Thanks @remusao.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The changelog entry cites #34614 (the PR this work supersedes), but should reference the current PR #40187 for correct issue tracking.

Suggested change
- Docs/Web search: restore $5/month free-credit details, replace defunct "Data for Search"/"Data for AI" plan names with current "Search" plan, and note legacy subscription validity in Brave setup docs. Follows up on #26860. (#40111) Thanks @remusao.
- macOS/onboarding: add a remote gateway token field for remote mode, preserve existing non-plaintext `gateway.remote.token` config values until explicitly replaced, and warn when the loaded token shape cannot be used directly from the macOS app. (#40187)
Prompt To Fix With AI
This is a comment left during a code review.
Path: CHANGELOG.md
Line: 13

Comment:
The changelog entry cites `#34614` (the PR this work supersedes), but should reference the current PR `#40187` for correct issue tracking.

```suggestion
- macOS/onboarding: add a remote gateway token field for remote mode, preserve existing non-plaintext `gateway.remote.token` config values until explicitly replaced, and warn when the loaded token shape cannot be used directly from the macOS app. (#40187)
```

How can I resolve this? If you propose a fix, please make it concise.

@ngutman ngutman force-pushed the fix/macos-remote-gateway-token-ui branch from 279d59c to e645e87 Compare March 8, 2026 19:28
@ngutman ngutman merged commit 92726d9 into main Mar 8, 2026
10 of 11 checks passed
@ngutman ngutman deleted the fix/macos-remote-gateway-token-ui branch March 8, 2026 19:28
@ngutman
Copy link
Contributor Author

ngutman commented Mar 8, 2026

Landed via temp rebase onto main.

  • Gate: pnpm check && pnpm build && swift test --package-path apps/macos --filter AppStateRemoteConfigTests && swift test --package-path apps/macos --filter GatewayEndpointStoreTests && swift test --package-path apps/macos --filter SettingsViewSmokeTests && swift test --package-path apps/macos --filter OnboardingViewSmokeTests
  • Land commit: e645e87
  • Merge commit: 92726d9

Thanks @cgdusek!

@Mushy-Snugglebites-badonkadonk

Nice! I was trying to implement something like this too...but I feared I would have weakened security the way I was going about it; buffer overflow via random strings in the input box. So, I pulled my PR. 😅

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

app: macos App: macos maintainer Maintainer-authored PR size: M

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: macOS companion app fails to pass gateway auth token over SSH tunnel — no UI field, no auto-read from remote config

3 participants