Skip to content

fix(onboarding): mask token/credential inputs in CLI wizard prompts#76693

Merged
clawsweeper[bot] merged 1 commit intoopenclaw:mainfrom
anurag-bg-neu:fix/onboarding-mask-credentials
May 3, 2026
Merged

fix(onboarding): mask token/credential inputs in CLI wizard prompts#76693
clawsweeper[bot] merged 1 commit intoopenclaw:mainfrom
anurag-bg-neu:fix/onboarding-mask-credentials

Conversation

@anurag-bg-neu
Copy link
Copy Markdown
Contributor

@anurag-bg-neu anurag-bg-neu commented May 3, 2026

Summary

  • Problem: API keys, gateway tokens, and gateway passwords entered during the interactive openclaw onboard wizard were echoed in cleartext into the terminal — leaving secrets in PowerShell scrollback, Start-Transcript logs, screenshots, and screen-shares. Reproduced on Windows PowerShell during a normal openclaw onboard --install-daemon session.
  • Why it matters: real-world credential leak vector. CWE-549 (missing field masking) / CWE-200 (information exposure). gh auth login, npm login, aws configure, gcloud auth all mask this for the same reason.
  • What changed: added sensitive?: boolean to WizardTextParams; clack frontend dispatches to @clack/prompts.password() when set; sensitive: true flipped at every onboarding credential prompt across the Node CLI; existing tokens/passwords preserved via a confirm with maskApiKey() masked-preview before the masked text prompt.
  • Scope boundary: only input rendering changes — secrets storage/transmission/logging after entry is untouched, no CODEOWNERS-secops paths touched, non-interactive paths bypass the prompter. macOS openclaw-mac wizard session-client masking is deliberately deferred → tracked at macOS openclaw-mac wizard CLI: honor step.sensitive for credential text prompts #76698 (see "Out-of-scope leak path" in Security Impact for rationale and maintainer-decision options).

Change Type (select all)

  • Bug fix
  • Feature
  • Refactor required for the fix
  • 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

Root Cause

  • Root cause: WizardTextParams did not expose a sensitive flag, so the clack prompter always called text() regardless of whether the prompt was asking for a credential. Every wizard text prompt — including credential entry — rendered cleartext that persisted in the terminal stream. The WizardStep type already had a sensitive?: boolean field (sketched but unwired), so the design intent was there; this PR completes the Node-side plumbing.
  • Missing detection / guardrail: no test asserted that secret-input prompts dispatch differently from regular text prompts, or that the sensitive flag propagates from caller through the wizard step contract. Adding session-level + call-site assertions.

Regression Test Plan

  • Coverage: ✅ Unit
  • Targets:
    • src/wizard/session.test.tsforwards sensitive flag to the emitted text step
    • src/wizard/setup.gateway-config.test.tskeeps OPENCLAW_GATEWAY_TOKEN in advanced flow when user confirms keeping existing
    • src/commands/onboard-remote.test.tskeeps an existing remote gateway token / password when user confirms via masked-preview prompt
    • src/commands/onboard-search.test.ts → tightened Gemini-key assertion now requires sensitive: true on the prompt step
  • Why this is the smallest reliable guardrail: the sensitive flag is the contract between wizard runner and rendering frontend. If it stops propagating, masking silently regresses everywhere downstream. Asserting at the contract level catches that without needing TTY-mocked clack.

User-visible / Behavior Changes

Credential prompts in openclaw onboard (and openclaw onboard --remote) now render as masked-bullet input. Entered values are no longer echoed into the terminal:

  • Enter Gemini API key and every other model-auth provider key (OpenAI / Anthropic / Z.AI / etc.)
  • Google Gemini API key, Brave API key, Tavily API key, Firecrawl API key, etc. (web-search providers)
  • Gateway token (blank to generate) / Gateway password (both local QuickStart and --remote onboarding)
  • API key (self-hosted OpenAI-compatible providers)
  • Enter <SKILL_ENV_VAR> (skill credential entry)

When an existing credential is detected (env var or saved config), users are asked via a confirm step with a maskApiKey() masked preview ("Use existing gateway token (env-…oken)?") before the masked text prompt — preserves "press Enter to keep current" without rendering the secret.

No config-shape changes; non-interactive onboarding paths bypass the prompter entirely and are unchanged.

Diagram

Before: o  Enter Gemini API key
        |  AIzaSyCQBy...e4CWsDI         ← cleartext echo persists in scrollback / Start-Transcript

After:  o  Use existing GEMINI_API_KEY (env: GEMINI_API_KEY, AIza…WsDI)?
        |  No
        |
        o  Enter Gemini API key
        |  ••••••••••••••••••••••       ← masked; cleartext never rendered

Security Impact (required)

  • New permissions/capabilities? No
  • Secrets/tokens handling changed? Yes — input rendering only. Cleartext is no longer placed into the terminal stream (and therefore PowerShell scrollback, Start-Transcript logs, screenshots, recordings). Storage / transmission / logging paths after submission are untouched.
  • New/changed network calls? No
  • Command/tool execution surface changed? No
  • Data access scope changed? No
  • Risk + mitigation: low. The non-sensitive code path is unchanged for every existing caller. The masked path uses @clack/prompts.password() — same library, same major version (^1.2.0) the project already depends on. Tests assert the dispatch and WizardStep.sensitive propagation.
  • CODEOWNERS check: none of the modified paths fall under @openclaw/secops ownership globs (/src/secrets/, /src/security/, /src/agents/**/*auth*.ts, /src/gateway/**/*auth*.ts). Maintainers welcome to route to secops anyway as a courtesy security review.

Out-of-scope leak path (acknowledged, tracked at #76698)

The bundled openclaw-mac wizard CLI in apps/macos/Sources/OpenClawMacCLI/WizardCommand.swift:445 still reads every text step with an echoing readLine() and displays the bracketed [initial] value, regardless of step.sensitive. That's a real residual leak for users on the macOS session-client surface (the SwiftUI app at apps/macos/Sources/OpenClaw/OnboardingWizard.swift:303 already honors sensitive via SecureField).

Not fixed in this PR because (a) authored on Windows with no Apple toolchain to run swift build / swiftlint / swiftformat --lint, (b) prior speculative iterations on #76615 kept failing macos-swift CI. Staying TDD-strict means I don't keep guessing.

Maintainer decision needed: please choose one —

Either is fine by me — the deferral just needs to be explicit and tracked, not silent.

Repro + Verification

Environment

  • OS: Windows 10 (PowerShell 5.1) — original repro environment
  • Runtime: Node v24.15
  • Provider: Google Gemini (also exercises OpenAI / Z.AI / Brave / Tavily / etc. via provider-auth-input.ts and search-setup.ts)
  • Integration: N/A — onboarding wizard
  • Config: N/A

Steps

  1. From the local checkout, run pnpm openclaw onboard --install-daemon (builds-on-the-fly via scripts/run-node.mjs).
  2. At Enter Gemini API key, paste a fake key (e.g. AIzaSyXXXXXXXXXXXXXXXXXXXXXXXXXXXX).
  3. Continue to Web search → Search provider → Gemini → Google Gemini API key and paste again.
  4. After Enter on each prompt, scroll terminal back over the entered value. Optionally Start-Transcript before step 1.

Expected

  • Prompt renders as ••••... while typing/pasting.
  • Value is not displayed in the prompt confirmation line.
  • Terminal scrollback and Start-Transcript log do not contain the cleartext.

Actual (this PR)

  • Before: cleartext key visible in scrollback and transcript.
  • After: masked input via @clack/prompts.password(); cleartext never placed into the terminal stream.

Evidence

Before masking the token

pr_2_api_auth_not_masked

After masking the token

pr_2_api_auth_masked

The "after" screenshot shows:

  • Enter Gemini API key rendering as bullets (••••...)
  • Use existing GEMINI_API_KEY (env: GEMINI_API_KEY, AIza…WsDI)? — the masked-preview confirm step firing because the env var is present
  • The maskApiKey() head/tail safe preview format (AIza…WsDI)

Human Verification (required)

What was personally verified:

  • Automated (passing on the squashed commit a3db64c265):
    • pnpm check:test-types — clean locally (this lane was red on the prior PR's CI at SHA dcfca89199; passes now that the Swift change is reverted to upstream)
    • pnpm tsgo:core — clean
    • pnpm check — 0 warnings, 0 errors across all 3 oxlint lanes plus all policy guards
    • setup.gateway-config.test.ts — 11/11 (10 prior + 1 new)
    • session.test.ts — 6/6 (5 upstream + 1 new)
    • onboard-remote.test.ts — 12/12 (10 prior + 2 new)
    • onboard-search.test.ts — 15/15 (tightened existing assertion)
  • Manual end-to-end (Windows PowerShell): screenshots above. Masked prompt + masked-preview confirm both verified.
  • Out of scope by design: macOS openclaw-mac wizard session-client masking → see "Out-of-scope leak path" above. Tracked at macOS openclaw-mac wizard CLI: honor step.sensitive for credential text prompts #76698.

Review Conversations

  • I will reply to or resolve every bot review conversation I address in this PR.
  • I will leave unresolved only the conversations that still need reviewer or maintainer judgment.

Compatibility / Migration

  • Backward compatible? Yessensitive is an optional field; all existing callers (and any third-party plugin code consuming WizardPrompter) continue to work unchanged.
  • Config/env changes? No
  • Migration needed? No

Risks and Mitigations

  • Risk: clack's password() does not accept initialValue / placeholder, so sensitive: true text prompts ignore those two WizardTextParams fields.
    • Mitigation: this is the desired behavior for credential entry — pre-filling or hint-suggesting a secret is itself a leak vector. The three call sites that previously seeded initialValue (setup.gateway-config.ts:212, onboard-remote.ts:198, 230) now branch through a prompter.confirm with maskApiKey() masked preview before falling through to the masked text prompt. Preserves the "press Enter to keep current" UX without rendering the secret.

AI assist disclosure (per CONTRIBUTING.md)

  • AI-assisted: planning, research, edits, and tests by Claude Opus 4.7 (claude-opus-4-7) via Claude Code. I reviewed every change before commit.
  • Testing degree: lightly tested — automated lanes (typecheck core + extensions + test types, lint, targeted unit tests) all green; end-to-end manual run on Windows PowerShell verified visually via the screenshots in the Evidence section. macOS swift build deliberately not in scope here — see Out-of-scope leak path above.
  • Codex review: not run locally; will address maintainers' Codex review feedback before merge.
  • I confirm understanding of what the code does.

@openclaw-barnacle openclaw-barnacle Bot added commands Command implementations size: M labels May 3, 2026
@clawsweeper
Copy link
Copy Markdown
Contributor

clawsweeper Bot commented May 3, 2026

Codex review: passed.

Summary
The PR adds sensitive support to wizard text prompts, routes sensitive Clack prompts through password(), marks onboarding credential prompts as sensitive, preserves existing gateway secrets through masked-preview confirms, and adds tests plus a changelog entry.

Reproducibility: yes. Source inspection shows current main routes onboarding credential entry through visible Clack text() prompts, and the PR body provides a concrete Windows PowerShell openclaw onboard --install-daemon reproduction with screenshots.

Next step before merge
No repair job is needed; this automerge-opted PR has no current review findings, and required exact-head checks plus mergeability should gate the merge.

Security
Cleared: The diff reduces Node CLI credential exposure without adding new dependency, permission, network, script, or supply-chain risk; the remaining bundled macOS CLI exposure is accepted and tracked separately in #76698.

Review details

Best possible solution:

Merge this Node-side masking patch through the exact-head automerge gates and keep #76698 open for the macOS CLI no-echo reader.

Do we have a high-confidence way to reproduce the issue?

Yes. Source inspection shows current main routes onboarding credential entry through visible Clack text() prompts, and the PR body provides a concrete Windows PowerShell openclaw onboard --install-daemon reproduction with screenshots.

Is this the best way to solve the issue?

Yes after the maintainer split decision. The sensitive flag plus Clack password() branch is the narrow Node-side fix, and the masked-preview confirm handles existing secrets without relying on unsupported password initial values.

What I checked:

  • Current main renders wizard text with visible Clack text input: createClackPrompter().text always calls @clack/prompts.text() with initialValue and placeholder; there is no sensitive branch on current main. (src/wizard/clack-prompter.ts:119, 0e4d28aa9e65)
  • Current main credential prompts flow through text prompts: Gateway token/password, remote gateway credentials, web-search provider keys, provider API keys, self-hosted provider keys, and skill env-var values are current-main prompter.text() credential paths. (src/wizard/setup.gateway-config.ts:212, 0e4d28aa9e65)
  • Wizard protocol already carries sensitive metadata: The current gateway wizard schema and TypeScript WizardStep type already include optional sensitive, so forwarding it from WizardTextParams is additive and matches the existing protocol shape. (src/gateway/protocol/schema/wizard.ts:73, 0e4d28aa9e65)
  • PR masks sensitive Node Clack prompts: The PR imports password from @clack/prompts and dispatches params.sensitive text prompts through password() while preserving the existing text() path for non-sensitive prompts. (src/wizard/clack-prompter.ts:119, a3db64c265e6)
  • PR marks the credential call sites and preserves existing gateway secrets: The diff adds sensitive: true to onboarding credential prompts and replaces existing gateway token/password initial values with a masked-preview confirm step before masked entry. (src/commands/onboard-remote.ts:194, a3db64c265e6)
  • Dependency contract supports the implementation: @clack/prompts@1.3.0 is the locked dependency, and its PasswordOptions expose message, optional mask, validate, and clearOnError, without initialValue or placeholder; the PR's confirm-before-password design matches that contract. (pnpm-lock.yaml:2082, 0e4d28aa9e65)

Likely related people:

  • Peter Steinberger: Available current-main blame and log history for the central wizard, protocol, onboarding, and macOS wizard files route through commit 8f20d03, which is the only local history entry for these paths in this checkout. (role: recent maintainer / current-main owner; confidence: medium; commits: 8f20d03373; files: src/wizard/clack-prompter.ts, src/wizard/prompts.ts, src/wizard/session.ts)
  • sallyom: Maintainer comment accepted merging the Node-side fix while keeping macOS openclaw-mac wizard CLI: honor step.sensitive for credential text prompts #76698 open, then enabled ClawSweeper automerge for the exact PR head. (role: maintainer reviewer / automerge approver; confidence: high; files: src/wizard/clack-prompter.ts, src/wizard/setup.gateway-config.ts, src/commands/onboard-remote.ts)

Codex review notes: model gpt-5.5, reasoning high; reviewed against 0e4d28aa9e65.

@sallyom
Copy link
Copy Markdown
Contributor

sallyom commented May 3, 2026

Maintainer note: OK to merge this Node-side fix. Keep #76698 open for the macOS CLI follow-up.

@sallyom
Copy link
Copy Markdown
Contributor

sallyom commented May 3, 2026

/clawsweeper automerge

@clawsweeper clawsweeper Bot added the clawsweeper:automerge Maintainer opted this PR into bounded ClawSweeper-reviewed automerge label May 3, 2026
@clawsweeper
Copy link
Copy Markdown
Contributor

clawsweeper Bot commented May 3, 2026

🦞🦞
ClawSweeper merged this PR after the passing review.

Source: clawsweeper[bot]
Feedback: structured ClawSweeper verdict: pass (sha=a3db64c265e6057123df63adf2905c833f30f570)
Merge status: merged by ClawSweeper automerge
Merged at: 2026-05-03T14:33:59Z
Merge commit: 727398f41a85

What merged:

  • The PR adds sensitive support to wizard text prompts, routes sensitive Clack prompts through password(), ... preserves existing gateway secrets through masked-preview confirms, and adds tests plus a changelog entry.
  • Reproducibility: yes. Source inspection shows current main routes onboarding credential entry through visibl ... y provides a concrete Windows PowerShell openclaw onboard --install-daemon reproduction with screenshots.

Automerge notes:

  • No ClawSweeper repair was needed after automerge opt-in.

The automerge loop is complete.

Automerge progress:

  • 2026-05-03 14:30:13 UTC review queued [`a3db64c265e6`](https://github.com/openclaw/openclaw/commit/a3db64c265e6057123df63adf2905c833f30f570) (queued)
  • 2026-05-03 14:33:49 UTC review passed [`a3db64c265e6`](https://github.com/openclaw/openclaw/commit/a3db64c265e6057123df63adf2905c833f30f570) (structured ClawSweeper verdict: pass (sha=a3db64c265e6057123df63adf2905c833f30f...)
  • 2026-05-03 14:34:00 UTC merged [`a3db64c265e6`](https://github.com/openclaw/openclaw/commit/a3db64c265e6057123df63adf2905c833f30f570) (merged by ClawSweeper automerge)

@clawsweeper clawsweeper Bot merged commit 727398f into openclaw:main May 3, 2026
122 of 123 checks passed
lxe pushed a commit to lxe/openclaw that referenced this pull request May 6, 2026
…penclaw#76693)

Summary:
- The PR adds `sensitive` support to wizard text prompts, routes sensitive Clack prompts through `password()`, ...  preserves existing gateway secrets through masked-preview confirms, and adds tests plus a changelog entry.
- Reproducibility: yes. Source inspection shows current main routes onboarding credential entry through visibl ... y provides a concrete Windows PowerShell `openclaw onboard --install-daemon` reproduction with screenshots.

Automerge notes:
- No ClawSweeper repair was needed after automerge opt-in.

Validation:
- ClawSweeper review passed for head a3db64c.
- Required merge gates passed before the squash merge.

Prepared head SHA: a3db64c
Review: openclaw#76693 (comment)

Co-authored-by: anurag-bg-neu <bheemappagnanamurt.a@northeastern.edu>
github-actions Bot pushed a commit to Desicool/openclaw that referenced this pull request May 9, 2026
…penclaw#76693)

Summary:
- The PR adds `sensitive` support to wizard text prompts, routes sensitive Clack prompts through `password()`, ...  preserves existing gateway secrets through masked-preview confirms, and adds tests plus a changelog entry.
- Reproducibility: yes. Source inspection shows current main routes onboarding credential entry through visibl ... y provides a concrete Windows PowerShell `openclaw onboard --install-daemon` reproduction with screenshots.

Automerge notes:
- No ClawSweeper repair was needed after automerge opt-in.

Validation:
- ClawSweeper review passed for head a3db64c.
- Required merge gates passed before the squash merge.

Prepared head SHA: a3db64c
Review: openclaw#76693 (comment)

Co-authored-by: anurag-bg-neu <bheemappagnanamurt.a@northeastern.edu>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

clawsweeper:automerge Maintainer opted this PR into bounded ClawSweeper-reviewed automerge commands Command implementations size: M

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants