Skip to content

Consolidated audit handoff for maintainer triage #1122

Description

@calebeden

Before wrapping up my internship, I wanted to preserve a set of revalidated findings without opening a burst of individual issues. These began as agent-identified candidates and were individually investigated against current main and existing GitHub work under my guidance. None requires private disclosure.

I worked on fixes for some of the original findings but was unable to complete everything. I am sharing the remaining list as a handoff for maintainers or external contributors who may find it useful. This is a record of my local notes, not a request to prioritize or fix everything. Please close, defer, combine, or split items however best fits the roadmap.

How to read this report

  • The list contains 21 current findings. Older findings that were fixed or not substantiated are omitted.
  • Impact and likelihood are intentionally separated. Some items have meaningful worst-case impact but narrow prerequisites or low expected frequency.
  • Nothing below is presented as a release blocker.
  • The security-adjacent items were reviewed against SECURITY.md. They are suitable for public handling and do not require a private advisory based on the supported threat model currently documented.
  • File locations were checked against main on August 7, 2026, but line numbers may drift.
  • Related open work is called out to reduce duplication. Some pull requests address only part of a finding.

Summary

ID Finding Impact if triggered Trigger or scope Existing work Suggested disposition
NODE-1 Recording contracts disagree with implementation Truncated or mislabeled recordings and high process memory pressure An authorized screen or camera recording call PR #1007 fixed cancellation only Align the contract, validate formats, encode incrementally, and bound concurrency
NODE-2 MXC timeout cleanup can hang indefinitely One invocation slot can remain stuck; repeated hangs can exhaust all node slots An approved MXC command plus failed termination or inherited output handles PR #816 bounds the probe path only Bound termination and output-drain grace periods; add an MXC regression test
NODE-3 Fragmented WebSocket UTF-8 is decoded per receive chunk Silent Unicode corruption in operator or node JSON strings A multibyte code point split across receive-buffer boundaries PRs #1095 and #1120 do not fix decoding Accumulate bytes through end-of-message or use a stateful decoder
NODE-4 Full command text enters persistent logs Arbitrary positional secrets can persist locally and survive diagnostics sanitization system.run approval logs or system.run.prepare PR #1051 replaced legacy ownership but retained canonical logging Log executable, count, outcome, duration, and correlation metadata without argument values
NODE-5 Align WSL setup configuration with the safer script-delivery path Setup configuration passes through more process layers than necessary Clean local gateway setup inside the trusted appliance PR #659 established the preferred script-delivery pattern elsewhere Reuse the stdin-based path and add focused regression coverage
CONN-1 Scope repository-only CLI identity state to the selected gateway CLI behavior can differ from explicit endpoint and override expectations Narrow manual use of unshipped source tooling with persisted profile state CLI is not shipped; repository validation calls are stale Use gateway-scoped identity resolution and consistent override precedence
CONN-2 Legacy migration drops one of two valid credentials Shared-token-dependent features and later recovery can fail after upgrade Legacy profile contains both shared and bootstrap tokens No direct fix found Migrate both fields independently and add a combined-credential test
CONN-3 Gateway registry recovery is unsafe Saved gateways can disappear for a run; later writes may overwrite the only corrupt copy; startup can remain partial Malformed JSON or read-side I/O failure PR #948 hardened writes, not recovery reads Quarantine/preserve input, expose degraded state, and continue startup safely
CONN-4 Shared-token replacement reports success before authentication A healthy connection can be severed and invalid state persisted Trusted local UI or authenticated local MCP replacement with a bad token PR #1076 partially addresses WinUI flows, not the manager/MCP path Validate or await terminal state, then commit; otherwise roll back
CONN-5 Runtime settings writes are non-atomic Local preferences and capability posture can be lost after interruption Crash, power loss, disk-full, or I/O failure during save SetupEngine has a separate atomic writer Use same-directory temp write, durable replace, backup, and recovery
CHAT-1 Image preview cache collides and grows indefinitely Older rows can show newer images; memory remains retained until process exit Reused filenames or repeated local image sends PR #1119 fixes identity/isolation but not eviction Use immutable keys plus bounded eviction and lifecycle cleanup
CHAT-2 Chat Stop can render a queued final assistant frame Post-cancel content can render, complete the turn, and reach TTS Abort response is processed before a late runless final PR #1096 preserves the early-clear behavior Retain suppression until a new turn or carry run identity on chat frames
UI-1 Channels linking and recovery copy bypasses localization Significant setup and recovery flows remain English-only Non-English locale using channel linking/recovery PR #988 improved recovery but did not localize it Add runtime resource keys and replace English code-behind literals
UI-2 Skills mutation failures are success-shaped, and the icon action lacks an accessible name UI can show a change that was rejected; screen readers may get an unnamed action Skill enable/disable plus dynamic skill cards Existing accessibility scan does not render the card action Split into response-aware mutation handling and accessibility fixes
UI-3 Cron mutations discard failures Create/edit input can disappear, and remove/toggle state can be misleading Gateway rejection, disconnect, or send failure cron.run is response-aware; other mutations are not Add response-aware mutation APIs and update UI only after acceptance
UI-4 Settings-only hotkey registration can survive Unregister() Disabled hotkeys can continue reserving and invoking the Settings chord Voice registration fails while Settings registration succeeds PR #1088 preserves the service behavior Track registration state per hotkey and test partial registration
TOOL-1 SetupEngine tests are omitted from the root solution Root dotnet test misses an active suite despite documentation Local developer uses the documented root command CI runs the suite explicitly Add the project to the solution or correct the documentation
TOOL-2 Installer validator searches obsolete artifact names Default validation explicitly skips and provides no install/uninstall proof Running the legacy packaging validator without an explicit path Draft PR #1116 does not fix the locator Update or replace the legacy validator and decide whether to add a runtime lane
TOOL-3 GitVersion remains split between local/build and CI Future version-engine divergence can fail builds or releases A graph or tag on which 6.4.x and 6.8.2 differ Documentation now records the split; checks fail closed Align CI when practical; retain the cross-version guards until then
TOOL-4 OpenClaw.Cli lacks a dedicated CI lane Parser and credential regressions can escape focused coverage Changes to repository-only CLI source PR #1065 added indirect compilation and two process tests Add direct parser/credential tests or formally retire the CLI
TOOL-5 Piper extraction waits before draining stderr Optional voice installation can hang indefinitely Verbose extraction failure caused by abnormal destination state No direct fix found Drain stderr concurrently and add a high-volume child-process regression test

Detailed notes

NODE-1: Recording contracts disagree with implementation

Current behavior

  • MCP and winnode documentation advertise screen.record WebM, maxWidth, a monitor alias, a 30 FPS default, and up to 300 seconds.
  • The capability rejects non-MP4 formats, ignores maxWidth and the monitor alias, defaults to 10 FPS, and the backend silently reclamps duration to 60 seconds.
  • ScreenRecordingService retains raw BGRA frames before encoding. At 1080p it can retain about 506 MiB within seconds, then truncate early.
  • camera.clip always creates MP4 but can report the arbitrary requested format and ignores advertised maxWidth.
  • The earlier broad claim that camera still capture ignores width/quality is not current. camera.snap applies those values.

Current locations

  • src/OpenClaw.Shared/Mcp/McpToolBridge.cs:245-268,327-338
  • src/OpenClaw.Shared/Capabilities/ScreenCapability.cs:137-171,195,233-240
  • src/OpenClaw.Shared/Capabilities/CameraCapability.cs:135-162,208-214
  • src/OpenClaw.Tray.WinUI/Services/ScreenRecordingService.cs:27-215
  • src/OpenClaw.Tray.WinUI/Services/CameraCaptureService.cs:192-331,685-763

Possible direction

Align the advertised contract with supported codecs and defaults, validate unsupported values, apply scaling consistently, encode incrementally rather than retaining raw frames, and serialize or cap concurrent recording memory.

NODE-2: MXC timeout cleanup can hang indefinitely

Current behavior

After cancellation, MxcExecutor attempts process-tree termination and then performs unbounded exit and output-drain waits. A failed kill or descendant retaining redirected handles can defeat the host timeout indefinitely. Each stuck call retains one of eight node invocation slots.

Current locations

  • src/OpenClaw.Shared/Mxc/DirectAppContainerExecutor.cs:103-129
  • src/OpenClaw.Shared/Mxc/MxcExecutor.cs:132-168
  • src/OpenClaw.Shared/Mxc/MxcAvailability.cs:367-405
  • src/OpenClaw.Shared/LocalCommandRunner.cs:92-165

Related evidence

Issue #28 and PR #38 fixed the analogous inherited-output-handle problem in the host runner. PR #816 applied a bounded drain to the MXC availability probe but not execution.

Possible direction

Use a bounded termination and drain grace period, abandon remaining handles safely, and add an MXC-specific timeout test. This is public availability hardening; a reliable slot-exhaustion harness should not be necessary in the issue.

NODE-3: Fragmented WebSocket UTF-8 is decoded per chunk

Current behavior

WebSocketClientBase calls Encoding.UTF8.GetChars independently for each receive chunk. UTF-8 permits a code point to span frame or receive-buffer boundaries, so valid text can become replacement characters before JSON parsing. Both operator and Windows node clients use this base.

Current location

  • src/OpenClaw.Shared/WebSocketClientBase.cs:256-312

Possible direction

Accumulate bounded raw bytes through EndOfMessage and decode once, or maintain a per-message Decoder. Add a test that splits a multibyte character across receives.

NODE-4: Full command text enters persistent logs

Current behavior

V2 exec approval logs include the canonical full argv, and system.run.prepare logs raw or fully formatted command text. Pattern sanitization cannot identify arbitrary positional secrets. Logs persist locally and may be included in an explicitly generated diagnostics bundle after another pass through the same sanitizer.

Current locations

  • src/OpenClaw.Shared/ExecApprovals/ExecApprovalV2NormalizationStep.cs:42-44
  • src/OpenClaw.Shared/ExecApprovals/ExecApprovalsCoordinator.cs:142-145,282-285,472-503
  • src/OpenClaw.Shared/Capabilities/SystemCapability.cs:193-225
  • src/OpenClaw.Tray.WinUI/Services/Logger.cs:23-59,90-158

Possible direction

Persist executable identity, argument count, decision, outcome, duration, and correlation identifiers without argument values. Keep any full-command diagnostic channel explicit and nonpersistent.

NODE-5: Align WSL setup configuration with the safer script-delivery path

Current behavior and scope

One gateway-configuration step still uses the direct process-argument path rather than the repository's stdin-based WSL script-delivery mode.

Revalidation found no supported untrusted observer during clean setup. The managed distro is a single-purpose trusted appliance at this point: the gateway is not yet running, no operator/node is paired, no agent or MCP service is active, and supported setup creates no second untrusted executable principal. This is defense-in-depth, not a supported-boundary vulnerability.

Current area

  • SetupEngine gateway configuration and WSL command-runner paths

Possible direction

Use the existing stdin-based script transport and, where practical, a downstream configuration interface that does not place configuration values in command arguments. Add regression tests using synthetic values.

CONN-1: Scope repository-only CLI identity state to the selected gateway

Current behavior and practical scope

The source-only gateway validation CLI combines endpoint overrides with profile-level identity state. Under narrow persisted-state conditions, inherited identity precedence can differ from the override behavior described by CLI help and output.

The CLI remains current source tooling but is not included in installers, portable ZIPs, or release assets. The repository validation script's CLI calls are stale, fail argument parsing, omit the now-required token, and normally use isolated profile state. Practical exposure therefore requires narrow manual contributor use.

Current area

  • OpenClaw.Cli endpoint, credential, and identity selection
  • Shared gateway-client credential precedence

Possible direction

Resolve known endpoints through GatewayRegistry and use only the selected gateway's identity directory. Give unknown endpoints isolated identity state, make explicit overrides authoritative unless the user opts into stored identity, and repair or retire the stale validation script.

CONN-2: Dual-token legacy migration loses the shared token

Current behavior

When both legacy Token and BootstrapToken are present, migration stores bootstrap but drops the shared token. URL deduplication then prevents later backfill. This conflicts with the documented independent migration contract.

Current locations

  • src/OpenClaw.Tray.WinUI/Services/SettingsManager.cs:361-370
  • src/OpenClaw.Tray.WinUI/App.xaml.cs:2047-2075
  • src/OpenClaw.Connection/GatewayRegistry.cs:332-410

Possible direction

Persist both fields independently and add a migration test with both nonempty values.

CONN-3: Gateway registry recovery is unsafe

Current behavior

Malformed JSON produces an empty startup registry without a quarantine copy or explicit degraded state. The original file remains initially, but later migration or registry writes can replace it. Read-side I/O exceptions abort the remaining guarded startup sequence, leaving a partially initialized tray.

Current locations

  • src/OpenClaw.Connection/GatewayRegistry.cs:144-206,384-408
  • src/OpenClaw.Tray.WinUI/App.xaml.cs:516-522,712-727,1691-1717
  • src/OpenClaw.Shared/AsyncEventHandlerGuard.cs:19-37

Possible direction

Preserve or quarantine unreadable input, return a typed load/degraded result, prevent overwrite until recovery is acknowledged, and allow unrelated startup services to continue safely.

CONN-4: Shared-token replacement reports success before authentication

Current behavior

Shared-token-only and new records skip pre-mutation handshake validation. The manager persists and activates the supplied token, disconnects the old client, starts connection asynchronously, and returns success before authentication. Later failure does not restore the prior record or connection.

The affected manager-backed capability is local-MCP-only and authenticated. This is transactional correctness, not an authorization bypass.

Current locations

  • src/OpenClaw.Connection/GatewayConnectionManager.cs:555-573,1125-1219,1363-1416
  • src/OpenClaw.Tray.WinUI/App.CapabilityHandlers.cs:285-297
  • src/OpenClaw.Shared/Capabilities/AppConnectionCapability.cs:92-103

Related work

Open PR #1076 introduces terminal-state waiting and rollback for direct WinUI connection transactions, but the manager/MCP path remains success-shaped.

CONN-5: Runtime settings writes are non-atomic

Current behavior

SettingsManager.Save() directly overwrites settings.json. Interruption can leave empty or partial JSON, and load silently retains defaults with no last-known-good recovery. Active gateway credentials now live elsewhere, reducing the blast radius, but general settings and capability preferences can still be lost.

Current locations

  • src/OpenClaw.Tray.WinUI/Services/SettingsManager.cs:198-223,434-462
  • src/OpenClaw.Shared/SettingsData.cs:225-236

Possible direction

Use a same-directory temporary file, flush and atomic replace, preserve a backup, and recover explicitly from invalid primary content.

CHAT-1: Image preview cache collides and grows indefinitely

Current behavior

A process-wide filename-keyed cache lets a later local image with the same basename replace the bytes shown by older timeline rows. Raw bytes and decoded bitmap resources remain retained without a capacity or lifecycle policy.

Current locations

  • src/OpenClaw.Tray.WinUI/Chat/OpenClawChatDataProvider.cs:80-88,362-385,2201-2237
  • src/OpenClaw.Tray.WinUI/Chat/ReactorChatTimeline.cs:610-719,1026-1029

Related work

Open PR #1119 adds opaque per-image keys and origin isolation, fixing collision and historical mutation. It does not add eviction or cleanup, so lifetime growth remains.

CHAT-2: Chat Stop can render a queued final frame

Current behavior

After a successful abort acknowledgement, the provider immediately clears thread suppression. Chat frames carry no run ID, so a later final frame can pass, render, complete the turn, and reach automatic TTS. This is a local cancellation and transcript-integrity race, not a server-side abort or command-approval bypass.

Current locations

  • src/OpenClaw.Tray.WinUI/Chat/OpenClawChatDataProvider.cs:403-420,911-1000,2574-3053
  • src/OpenClaw.Shared/OpenClawGatewayClient.cs:426-438,3526-3727

Possible direction

Keep thread suppression until the next user-turn boundary, or carry run identity through chat events and filter aborted runs.

UI-1: Channels linking and recovery copy bypasses localization

Current behavior

QR linking, gateway/provider recovery, progress and error states, QR decode messages, plugin installation guidance, and unlink dialogs are still built from English literals in code-behind despite existing runtime localization infrastructure.

Current location

  • src/OpenClaw.Tray.WinUI/Pages/ChannelsPage.xaml.cs:1650-2055,2098-2225

Possible direction

Add format-capable ChannelsPage_* resources, route runtime text through LocalizationHelper, and update source contract tests to assert resource use rather than fixed English sentences.

UI-2: Skills mutation failures and accessibility

Current behavior

The enable/disable UI treats a transport-oriented Boolean as gateway acceptance, optimistically changes its cached state, and provides no user-visible rollback for later rejection. The dynamic icon-only action has a tooltip but no explicit automation name.

Current locations

  • src/OpenClaw.Tray.WinUI/Pages/SkillsPage.xaml.cs:97-126,283-294
  • src/OpenClaw.Shared/OpenClawGatewayClient.cs:1207-1210,1881-1909,2140-2145,2341-2348

Possible direction

Add a response-aware mutation result and surface rejection without committing local state. Give the button a localized automation name and add deterministic dynamic-card accessibility coverage.

UI-3: Cron mutations discard failures

Current behavior

Create/edit closes and clears the form unconditionally. Remove and enable/disable update local UI without rollback. The existing Task<bool> reports local send behavior, not gateway acceptance, so merely awaiting it would not handle server rejection.

Current locations

  • src/OpenClaw.Tray.WinUI/Pages/CronPage.xaml.cs:129-168,266-290,378-512
  • src/OpenClaw.Shared/OpenClawGatewayClient.cs:1168-1182,1881-1909,2140-2144,2290-2337

Possible direction

Model add/update/remove responses the way detailed cron.run is modeled, preserve the form on failure, and reconcile remove/toggle state only after acceptance.

UI-4: Settings-only hotkey registration can survive Unregister

Current behavior

Registration state is stored only from the Voice hotkey result. If Voice registration fails while the Settings chord succeeds, _registered remains false and public Unregister() returns before posting native unregister work.

Current locations

  • src/OpenClaw.Tray.WinUI/Services/GlobalHotkeyService.cs:108-120,227-355
  • src/OpenClaw.Tray.WinUI/App.xaml.cs:3469-3553,4321-4324

Possible direction

Track each hotkey independently and test all partial-registration combinations.

TOOL-1: SetupEngine tests are omitted from the root solution

Current behavior

OpenClaw.SetupEngine.Tests remains absent from openclaw-windows-node.slnx, while docs/TEST_COVERAGE.md says root dotnet test runs all local-development tests except E2E. CI explicitly builds and runs the suite, so this is developer test discovery and documentation correctness, not a release coverage gap.

Current locations

  • openclaw-windows-node.slnx:31-45
  • docs/TEST_COVERAGE.md:20-32,84-99
  • .github/workflows/ci.yml:139-149,258-269
TOOL-2: Installer validation searches obsolete artifact names

Current behavior

The Inno installer emits OpenClawCompanion-Setup-*, while the legacy uninstall-ordering test auto-discovers only OpenClawTray-Setup-*. Default invocation explicitly skips with exit code 2. The script also describes obsolete [UninstallRun] behavior, while current cleanup runs through [Code]. CI does not invoke this runtime validator.

Current locations

  • installer.iss:43-57
  • tests/PackagingTests/Test-InnoUninstallOrdering.ps1:89-114,230-256,384-397
  • tests/OpenClaw.Tray.Tests/InstallerIssAssertionTests.cs:43-52,69-122

Possible direction

Either modernize the locator and assertions or retire the legacy script in favor of an installed-app smoke lane. Avoid treating the current skip as a silent pass or a security issue.

TOOL-3: GitVersion remains split between local/build and CI

Current behavior

The repository-local tool and GitVersion.MsBuild are pinned to 6.8.2, while CI computes workflow outputs with 6.4.x. Documentation now records the split. Tag and assembly comparisons fail closed, so this is latent build/release reliability debt rather than silent artifact corruption.

Current locations

  • .config/dotnet-tools.json:5-10
  • src/Directory.Build.props:17-20
  • .github/workflows/ci.yml:100-117,512-523,751-812
  • docs/VERSIONING.md:18-24
TOOL-4: OpenClaw.Cli lacks a dedicated CI lane

Current behavior

CI has no explicit OpenClaw.Cli step, but Shared tests now reference and compile it and run two corrupt-identity process cases. Parser matrices, credential precedence, unknown URL behavior, and profile/per-gateway identity selection remain uncovered.

Current locations

  • .github/workflows/ci.yml:123-199
  • tests/OpenClaw.Shared.Tests/OpenClaw.Shared.Tests.csproj:3-10
  • tests/OpenClaw.Shared.Tests/DeviceIdentityFailClosedTests.cs:381-423,472-495

Possible direction

Add a small dedicated test project/lane if the CLI remains supported, or retire the stale CLI and validation-script surface explicitly.

TOOL-5: Piper extraction can deadlock on redirected stderr

Current behavior

ExtractTarBz2 redirects stderr, waits synchronously for process exit, and only then reads stderr. The normal pinned archive is quiet, but sufficiently verbose extraction failures can fill the pipe and block both processes. This was reproduced with a synthetic abnormal destination state.

Current locations

  • src/OpenClaw.Shared/Audio/PiperVoiceManager.cs:161-238,338-371
  • src/OpenClaw.Tray.WinUI/Pages/VoiceSettingsPage.xaml.cs:646-720

Possible direction

Drain stderr concurrently before awaiting exit and add a regression child process that emits more than the pipe capacity. Keep reproduction isolated from real profile data.

Suggested maintainer handling

I deliberately did not open one issue per row. If any item is useful, please feel free to:

  • split it into a focused issue;
  • associate it with existing work;
  • mark it help-wanted for an external contributor;
  • defer it to the backlog; or
  • close it if the current behavior is intentional or the cost is not justified.

I am happy for this consolidated report itself to remain only as an audit record rather than an active roadmap checklist.

Metadata

Metadata

Assignees

No one assigned

    Labels

    P2Normal priority bug or improvement with limited blast radius.clawsweeper:needs-live-reproClawSweeper needs live local, crabbox, or manual validation to confirm this issue.clawsweeper:needs-maintainer-reviewClawSweeper marked this issue as needing maintainer review before automation.clawsweeper:needs-product-decisionClawSweeper marked this issue as needing a product or behavior decision.clawsweeper:needs-security-reviewClawSweeper marked this issue as needing security-sensitive review.clawsweeper:no-new-fix-prClawSweeper does not recommend queueing a new automated fix PR for this issue.impact:otherThis issue has meaningful maintainer-visible impact outside the owned taxonomy.impact:securityThis issue is about security boundaries, credentials, authz, sandboxing, or sensitive data.issue-rating: 🐚 platinum hermitGood issue quality with a plausible reproduction path needing some confirmation.

    Type

    No type

    Fields

    Priority

    None yet

    Projects

    Status
    No status

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions