Skip to content

Fix Windows ipc tests: accept self-owned pipe in test binary only#24

Open
korjwl1 wants to merge 1 commit into
mainfrom
fix/windows-ipc-tests
Open

Fix Windows ipc tests: accept self-owned pipe in test binary only#24
korjwl1 wants to merge 1 commit into
mainfrom
fix/windows-ipc-tests

Conversation

@korjwl1

@korjwl1 korjwl1 commented Jul 15, 2026

Copy link
Copy Markdown
Owner

Problem

go test ./internal/ipc has been broken on Windows (on main and every branch):

  • TestClientServerRPC, TestEventBroadcast — fail with pipe ownership verification failed: pipe owned by unexpected SID: S-1-5-21-...
  • TestMethodNotFoundpanics (nil deref in client.Close()) because it discarded NewClient's error

Root cause: verifyPipeOwner accepts only Local System / Built-in Administrators as the pipe owner. The tests create their own listener in-process, and an unelevated go test owns that pipe as the user SID → every dial rejected.

Fix

  • allowSelfOwnedPipes package var in transport_windows.go, set only by a _test.go init(). Production builds never set it — the SY/BA requirement is byte-for-byte unchanged for real clients (this is deliberately NOT a loosening of the check that Windows: privileged helper pipe is writable by every interactive user #20 concerns).
  • TestMethodNotFound now checks NewClient's error instead of dereferencing nil.

Why not skip when unelevated?

There is no CI job that runs go test (release.yml is the only workflow — filed separately), so t.Skip would leave the Windows ipc transport with zero coverage anywhere, forever. With this fix the tests exercise the real named-pipe transport unelevated.

Empirical note

Measured with a throwaway probe using the helper's exact SDDL: an elevated process owns its pipe as BUILTIN\Administrators (S-1-5-32-544), so the real helper pipe passes the strict production check. This also disproves the earlier "helper must run as SYSTEM to own objects as Administrators" theory from the 2026-05-23 debugging notes — the UAPI protected-namespace failure has some other cause.

Verification (Windows 11, real run)

  • go test -count=1 ./internal/ipc — all pass, via the real transport (not skipped)
  • go vet ./..., go build ./..., go build -tags production ./... — clean
  • GOOS=darwin / GOOS=linux builds of the package — unaffected (file is //go:build windows)
  • grep -rn allowSelfOwnedPipes — only assignment is in transport_windows_test.go

🤖 Generated with Claude Code

go test ./internal/ipc was broken on Windows: verifyPipeOwner accepts
only SY/BA pipe owners, but an unelevated test binary owns its
in-process listener pipe as the user SID, so every Dial was rejected.
TestClientServerRPC and TestEventBroadcast failed, and
TestMethodNotFound panicked on a nil client because it discarded
NewClient's error.

- transport_windows.go: add allowSelfOwnedPipes escape hatch, settable
  only from this package's tests (init in transport_windows_test.go).
  Production builds never set it, so the SY/BA requirement is
  unchanged. Verified empirically: an elevated process owns its pipe
  as BUILTIN\Administrators, so the real helper pipe passes the strict
  check — the SYSTEM-service theory from earlier debugging notes was
  wrong.
- ipc_test.go: check NewClient's error in TestMethodNotFound instead
  of dereferencing a nil client on failure.

Skipping when unelevated was rejected: no CI job runs go test at all,
so a skip would leave the Windows ipc transport with zero coverage
anywhere.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant