fix(security): redact gateway auth token from console output - #1954
fix(security): redact gateway auth token from console output#1954ColinM-sys wants to merge 6 commits into
Conversation
The gateway auth token was printed in plaintext as part of the dashboard URL (#token=<64-char-hex>) during onboard. This token controls full access to the OpenClaw dashboard — agent commands, workspace read/write, and action execution. In CI/CD environments (GitHub Actions, GitLab CI), where --non-interactive onboard runs, this token persists in build logs that are often readable by all project members or publicly visible on public repos. Terminal scrollback and piped output also retain the plaintext token. Redact the token in console output by showing only the first 4 characters. Users who need the full token can retrieve it via: nemoclaw <name> connect jq '.gateway.auth.token' /sandbox/.openclaw/openclaw.json The buildControlUiUrls function gains a forDisplay parameter: - forDisplay=true: token redacted (safe for stdout/logs) - forDisplay=false (default): full token (backward compatible) Signed-off-by: ColinM-sys <cmcdonough@50words.com>
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughprintDashboardUi now uses the real Changes
Sequence DiagramsequenceDiagram
actor User
participant Onboard as agent-onboard
participant Dashboard as dashboard
participant Shell as CLI
User->>Onboard: request onboarding / UI URLs
Onboard->>Dashboard: buildControlUiUrls(token, port, forDisplay=true)
Dashboard->>Dashboard: redact token -> maskedToken
Dashboard-->>Onboard: return URLs containing maskedToken
Onboard->>Shell: print redacted UI URLs
Onboard->>Shell: print jq command to retrieve full token
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
src/lib/agent-onboard.ts (1)
220-220: Make the token retrieval command directly runnable.Use the actual sandbox name instead of
<sandbox>so users can copy/paste the command without manual edits.✏️ Suggested tweak
export function printDashboardUi( - _sandboxName: string, + sandboxName: string, token: string | null, agent: AgentDefinition, deps: { @@ - console.log(` To get the full token: nemoclaw <sandbox> connect → jq '.gateway.auth.token' /sandbox/.openclaw/openclaw.json`); + console.log(` To get the full token: nemoclaw ${sandboxName} connect → jq '.gateway.auth.token' /sandbox/.openclaw/openclaw.json`);Also applies to: 239-239
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/lib/agent-onboard.ts` at line 220, The token-retrieval shell command currently embeds the literal placeholder "<sandbox>" making it non-runnable; replace that placeholder with the actual sandbox name variable (e.g., _sandboxName) using string interpolation or template literals wherever the command is constructed (references around _sandboxName at the spots flagged, including the second occurrence near line 239) so the emitted command contains the real sandbox name and is copy/paste runnable.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/lib/dashboard.ts`:
- Around line 45-47: The redactToken helper currently returns short tokens
unredacted and only masks up to 20 characters of longer tokens; update
redactToken(token: string, forDisplay: boolean) so that when forDisplay is true
it always masks all characters except the first four (if token.length > 4) and
masks the entire token when token.length <= 4, i.e., replace the
Math.min(...,20) logic with token.length - 4 (or full token length for short
tokens) so no token data can leak; touch the redactToken function and its
parameters (token, forDisplay) to implement this behavior.
---
Nitpick comments:
In `@src/lib/agent-onboard.ts`:
- Line 220: The token-retrieval shell command currently embeds the literal
placeholder "<sandbox>" making it non-runnable; replace that placeholder with
the actual sandbox name variable (e.g., _sandboxName) using string interpolation
or template literals wherever the command is constructed (references around
_sandboxName at the spots flagged, including the second occurrence near line
239) so the emitted command contains the real sandbox name and is copy/paste
runnable.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro Plus
Run ID: 45d36868-22ac-480b-8f64-dedca3a2b6f3
📒 Files selected for processing (2)
src/lib/agent-onboard.tssrc/lib/dashboard.ts
Signed-off-by: ColinM-sys <cmcdonough@50words.com>
|
✨ Thanks for submitting this PR that proposes a fix to redact the gateway auth token from console output, which could help improve the security of NemoClaw and prevent sensitive information from being exposed. |
Use dashboard label from main while keeping token-redaction behavior from this branch. The 'save it now' hint is no longer needed since the gateway token is now redacted from console output.
The test from main expected the 'save-now' warning for tokenized URLs. Since this PR redacts the gateway token from console output, update the test to verify: - Token is redacted in displayed URLs - Retrieval hint is printed instead of save-now warning - buildUrlsLoopback mock respects the forDisplay parameter
There was a problem hiding this comment.
🧹 Nitpick comments (1)
src/lib/agent-onboard.test.ts (1)
93-93: Consider asserting the full token-retrieval command text.At Line 93, the assertion only checks the
nemoclaw sandbox-y connectprefix. You may want to assert the full jq hint string too, so regressions in the retrieval instructions are caught.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/lib/agent-onboard.test.ts` at line 93, The test currently uses expect(output).toContain with only the "To get the full token: nemoclaw sandbox-y connect" prefix which is too weak; update the assertion on the output variable (the expect(output).toContain call) to assert the entire token-retrieval hint string including the pipe to jq and the '.token' field (i.e., the full command users should run) so the test fails if the retrieval instructions change.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@src/lib/agent-onboard.test.ts`:
- Line 93: The test currently uses expect(output).toContain with only the "To
get the full token: nemoclaw sandbox-y connect" prefix which is too weak; update
the assertion on the output variable (the expect(output).toContain call) to
assert the entire token-retrieval hint string including the pipe to jq and the
'.token' field (i.e., the full command users should run) so the test fails if
the retrieval instructions change.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Enterprise
Run ID: 0e61c52c-a886-43e5-884a-1e98dac1a4d3
📒 Files selected for processing (1)
src/lib/agent-onboard.test.ts
Address CodeRabbit review: tokens <=4 chars were returned unredacted, and longer tokens only masked up to 20 trailing characters. Now short tokens are fully masked and longer tokens mask all characters after the first four. Added test coverage for forDisplay redaction behavior.
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/lib/dashboard.ts`:
- Around line 65-68: getDashboardAccessInfo is calling buildControlUiUrls(token,
dashboardPort) which leaves forDisplay=false and embeds the raw token in URLs;
update the call in getDashboardAccessInfo to pass forDisplay=true (i.e., call
buildControlUiUrls(token, dashboardPort, true)) so the token is redacted before
those URLs are mapped/printed, ensuring you reference the same token and
dashboardPort variables used in the existing call.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Enterprise
Run ID: 499ae399-f242-47ff-9cad-5d4efddc7dd2
📒 Files selected for processing (2)
src/lib/dashboard.test.tssrc/lib/dashboard.ts
✅ Files skipped from review due to trivial changes (1)
- src/lib/dashboard.test.ts
| forDisplay: boolean = false, | ||
| ): string[] { | ||
| const hash = token ? `#token=${token}` : ""; | ||
| const displayToken = token ? redactToken(token, forDisplay) : ""; | ||
| const hash = token ? `#token=${displayToken}` : ""; |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Find call sites still using the 2-arg form (defaults to forDisplay=false)
ast-grep --lang ts --pattern 'buildControlUiUrls($TOKEN, $PORT)'
# Inspect display/logging flows around dashboard guidance and URL printing
rg -n -C4 'getDashboardAccessInfo|getDashboardGuidanceLines|console\.log|buildControlUiUrls\(' src/lib/onboard.ts src/lib/agent-onboard.tsRepository: NVIDIA/NemoClaw
Length of output: 50371
🏁 Script executed:
# Get context around line 6193 in onboard.ts
head -n 6210 src/lib/onboard.ts | tail -n 30Repository: NVIDIA/NemoClaw
Length of output: 1096
🏁 Script executed:
# Get context around line 6263 in onboard.ts
head -n 6280 src/lib/onboard.ts | tail -n 30Repository: NVIDIA/NemoClaw
Length of output: 1208
🏁 Script executed:
# Check the buildControlUiUrls function signature
rg -A 10 'function buildControlUiUrls|export.*buildControlUiUrls' src/lib/dashboard.tsRepository: NVIDIA/NemoClaw
Length of output: 574
🏁 Script executed:
# Find all calls to buildControlUiUrls and check if results are used in console.log/print contexts
rg -B 3 -A 3 'buildControlUiUrls' src/lib/onboard.ts | head -100Repository: NVIDIA/NemoClaw
Length of output: 1298
🏁 Script executed:
# Find where getDashboardAccessInfo is called and used
rg -B 5 -A 5 'getDashboardAccessInfo' src/lib/onboard.ts src/lib/agent-onboard.tsRepository: NVIDIA/NemoClaw
Length of output: 1957
🏁 Script executed:
# Check what buildAuthenticatedDashboardUrl does - is it redacting tokens?
rg -A 15 'function buildAuthenticatedDashboardUrl|export.*buildAuthenticatedDashboardUrl' src/lib/dashboard.tsRepository: NVIDIA/NemoClaw
Length of output: 41
🏁 Script executed:
# Search for any other 2-arg calls to buildControlUiUrls in display contexts
rg -B 5 'buildControlUiUrls\([^,]+,[^,)]+\)' src/lib/onboard.ts | grep -A 5 -B 5 'console\|log\|print'Repository: NVIDIA/NemoClaw
Length of output: 548
🏁 Script executed:
# Check buildAuthenticatedDashboardUrl implementation
rg -A 20 'buildAuthenticatedDashboardUrl' src/lib/dashboard.ts | head -50Repository: NVIDIA/NemoClaw
Length of output: 41
🏁 Script executed:
# Check getDashboardGuidanceLines - does it print the URLs?
rg -A 15 'getDashboardGuidanceLines' src/lib/dashboard.tsRepository: NVIDIA/NemoClaw
Length of output: 41
🏁 Script executed:
# See where getDashboardGuidanceLines output is used
rg -B 3 -A 3 'getDashboardGuidanceLines' src/lib/onboard.tsRepository: NVIDIA/NemoClaw
Length of output: 760
🏁 Script executed:
# Search for buildAuthenticatedDashboardUrl in dashboard.ts
cat src/lib/dashboard.ts | grep -A 20 'buildAuthenticatedDashboardUrl'Repository: NVIDIA/NemoClaw
Length of output: 41
🏁 Script executed:
# Get the full getDashboardGuidanceLines implementation
sed -n '/^function getDashboardGuidanceLines/,/^}/p' src/lib/dashboard.ts | head -100Repository: NVIDIA/NemoClaw
Length of output: 41
🏁 Script executed:
# Trace: show what's in guidanceLines that gets printed
rg -A 10 'const guidanceLines = getDashboardGuidanceLines' src/lib/onboard.tsRepository: NVIDIA/NemoClaw
Length of output: 606
🏁 Script executed:
# Find where guidanceLines is printed
rg -A 20 'guidanceLines' src/lib/onboard.ts | grep -A 20 'console.log'Repository: NVIDIA/NemoClaw
Length of output: 2112
🏁 Script executed:
# More thorough search for guidanceLines usage
rg 'guidanceLines' src/lib/onboard.tsRepository: NVIDIA/NemoClaw
Length of output: 206
🏁 Script executed:
# Get the full buildAuthenticatedDashboardUrl function
rg -B 2 'export.*buildAuthenticatedDashboardUrl|function buildAuthenticatedDashboardUrl' src/lib/dashboard.ts -A 15Repository: NVIDIA/NemoClaw
Length of output: 41
🏁 Script executed:
# Also check if it's in a different file
find src -name "*.ts" | xargs grep -l "buildAuthenticatedDashboardUrl"Repository: NVIDIA/NemoClaw
Length of output: 77
🏁 Script executed:
# Let's look at what buildAuthenticatedDashboardUrl actually does with the token
rg 'buildAuthenticatedDashboardUrl' -B 5 -A 20 src/lib/dashboard.tsRepository: NVIDIA/NemoClaw
Length of output: 41
🏁 Script executed:
# Search all TypeScript files for buildAuthenticatedDashboardUrl definition
rg 'buildAuthenticatedDashboardUrl' src/lib/ -B 3 -A 20Repository: NVIDIA/NemoClaw
Length of output: 5054
🏁 Script executed:
# Check dashboard.ts file for all exports
grep -E 'export (function|const)' src/lib/dashboard.tsRepository: NVIDIA/NemoClaw
Length of output: 141
🏁 Script executed:
# Look for the actual implementation by searching patterns
ast-grep --pattern 'function buildAuthenticatedDashboardUrl' src/lib/Repository: NVIDIA/NemoClaw
Length of output: 309
🏁 Script executed:
# Verify: check what redactToken does
rg -A 10 'function redactToken|export.*redactToken' src/lib/dashboard.tsRepository: NVIDIA/NemoClaw
Length of output: 411
🏁 Script executed:
# Double-check the flow: getDashboardAccessInfo returns what?
sed -n '6189,6215p' src/lib/onboard.tsRepository: NVIDIA/NemoClaw
Length of output: 1088
🏁 Script executed:
# Confirm that dashboardAccess entries are indeed printed with full tokens
sed -n '6270,6280p' src/lib/onboard.tsRepository: NVIDIA/NemoClaw
Length of output: 344
Pass forDisplay=true to buildControlUiUrls() in getDashboardAccessInfo to prevent token leakage.
At line 6193, getDashboardAccessInfo calls buildControlUiUrls(token, dashboardPort) without the forDisplay argument, which defaults to false. This causes unredacted tokens to be embedded in URLs that are subsequently printed to console at lines 6278–6280. The full token then appears in terminal scrollback and CI/CD logs.
Change line 6193 to:
const dashboardAccess = buildControlUiUrls(token, dashboardPort, true).map((url, index) => ({This aligns with the pattern already correctly implemented in agent-onboard.ts:261 where dashboard URLs are printed with forDisplay=true.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/lib/dashboard.ts` around lines 65 - 68, getDashboardAccessInfo is calling
buildControlUiUrls(token, dashboardPort) which leaves forDisplay=false and
embeds the raw token in URLs; update the call in getDashboardAccessInfo to pass
forDisplay=true (i.e., call buildControlUiUrls(token, dashboardPort, true)) so
the token is redacted before those URLs are mapped/printed, ensuring you
reference the same token and dashboardPort variables used in the existing call.
|
Closing this PR — thank you @ColinM-sys for identifying the token leakage problem. The security concern is real and appreciated. Since this was opened, three major refactors have landed that restructure the code this PR targets:
The unified Filed #2467 to track the remaining work. Thanks again for flagging this. |
Summary
forDisplayparameter tobuildControlUiUrls()—trueredacts the token (safe for logs),falsepreserves full token (backward compatible).Why
The gateway auth token controls full access to the OpenClaw dashboard — agent commands, workspace read/write, and action execution. During onboard, the full 64-character hex token is printed to stdout as part of the dashboard URL:
In CI/CD environments (
--non-interactive), this token persists in build logs that are often world-readable (public GitHub Actions repos, shared GitLab CI). Terminal scrollback and piped output also retain the plaintext token. CWE-532 (Insertion of Sensitive Information into Log File).What changed
src/lib/dashboard.ts—buildControlUiUrls()gains optionalforDisplayparameter; newredactToken()helpersrc/lib/agent-onboard.ts—printDashboardUi()callsbuildControlUiUrls(token, port, true)and prints a hint for retrieving the full token viajqTest plan
npm run build:cli— compiles cleanlynemoclaw onboardshows redacted token in dashboard URL (e.g.#token=a1b2********************)jq '.gateway.auth.token' /sandbox/.openclaw/openclaw.jsonSigned-off-by: ColinM-sys cmcdonough@50words.com
Summary by CodeRabbit
New Features
Bug Fixes / UX
Tests