Skip to content

fix: Limit Unix IPC endpoints to the current OS user#1793

Merged
hatayama merged 4 commits into
security/cli-distribution-hardening-integrationfrom
fix/unix-ipc-user-isolation
Jul 15, 2026
Merged

fix: Limit Unix IPC endpoints to the current OS user#1793
hatayama merged 4 commits into
security/cli-distribution-hardening-integrationfrom
fix/unix-ipc-user-isolation

Conversation

@hatayama

@hatayama hatayama commented Jul 15, 2026

Copy link
Copy Markdown
Owner

Summary

  • Give each OS user a private Unix IPC socket directory.
  • Validate the endpoint only at the single client dial boundary, so project-only dispatcher commands do not require Unity to be running.

Previous behavior and root cause

All Unix clients used a shared /tmp/uloop directory. Another OS user could precreate or replace endpoint paths before the intended Unity process used them. The initial fix validated while resolving a project, but resolution also serves pin, cache, help, and version-forwarding commands that never dial Unity.

Fix and attack conditions

C# and Go derive the same /tmp/uloop-UID endpoint. They validate the sticky root-owned /tmp target, require a real owner-only 0700 endpoint directory, and refuse symlinks, wrong ownership, special bits, and unsafe stale paths. Socket permissions are verified as 0600 after bind.

The Go client now has one dial choke point. On Unix it validates the same endpoint policy immediately before net.Dial; a missing private directory is typed as Unity not running and never attempts a dial. Windows enters the same choke point with a no-op validator, preserving named-pipe behavior.

The Unix stat ABI is selected explicitly for macOS x64, macOS arm64, and glibc Linux x64/arm64. Unknown ABI layouts fail closed.

Verification

  • Focused Go tests: go test ./project ./unityipc ./ipcendpoint ./errors
  • Dispatcher Go tests: go test ./...
  • scripts/check-go-cli.sh
  • Unity compile: 0 errors, 0 warnings
  • Unix endpoint/listener EditMode tests: 31 passed
  • Windows owner-only named-pipe tests: 3 skipped on macOS as expected
  • TestProtocolVersionMatchesUnityPackage
  • Release trigger guard
  • Windows and Linux Go project cross-compilation
  • Exact-head CI, d89b9f25d0bba6f48a92d9505baeb83d59dd9878:

Protocol release ordering

This PR changes the IPC rendezvous contract and advances protocolVersion from 3 to 4. The PR-diff-equivalent local invocation of check-protocol-minimum-version intentionally fails until a project runner release advertising protocol 4 is published. The manually dispatched exact-head CI has no pull-request diff and therefore skips that diff-dependent reminder; it is not evidence of a passing minimum-version check.

After the runner release, release-please must advance the package pin to the published runner version before the Unity package release proceeds.

Residual risk

The design protects against other OS users in the shared temporary parent. A process running as the same OS user can still manipulate that user's private directory, which is outside this multi-user boundary.

hatayama added 2 commits July 15, 2026 20:50
Use an owner-only per-user socket directory and validate every existing path before use. This prevents another OS user from precreating or replacing the endpoint while preserving the not-running recovery path when Unity has not created it yet.
The user-owned Unix endpoint changes the rendezvous contract between the Unity package and project runner. Advance both protocol declarations together while preserving release-managed version pins.
@coderabbitai

coderabbitai Bot commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

Unix IPC endpoints now use UID-scoped paths, explicit Unix filesystem security validation, restricted socket permissions, protected listener cleanup, CLI pre-dial checks, updated endpoint contracts, and expanded Unity and Go test coverage.

Changes

Unix IPC endpoint security

Layer / File(s) Summary
Contract and per-user endpoint paths
tests/contracts/endpoint_contract.json, cli/common/project/..., Packages/src/Editor/Infrastructure/..., Assets/Tests/Editor/BridgeTransportEndpointTests.cs
Endpoint paths now use /tmp/uloop-<UID>/...; the contract defines parent, directory, socket, symlink, and mode requirements.
CLI endpoint-directory validation
cli/common/ipcendpoint/*, cli/common/unityipc/*, cli/common/errors/*, cli/common/project/*, cli/dispatcher/...
Unix dialing validates endpoint metadata before connection, reports missing directories with a typed error, and classifies that error as a retryable Unity-not-reachable CLI error.
Native Unix security policy
Packages/src/Editor/Infrastructure/UnixEndpointSecurity.cs, Packages/src/Editor/Infrastructure/UnixNativeFileSystem.cs, Assets/Tests/Editor/UnixEndpointSecurityTests.cs
Native stat, lstat, mkdir, chmod, effective-UID, ABI-layout, directory, stale-socket, and socket-mode handling are added with deterministic and platform-specific tests.
Secured listener lifecycle
Packages/src/Editor/Infrastructure/BridgeTransportListener.cs, Assets/Tests/Editor/BridgeTransportListenerTests.cs
Listener startup validates directories and stale paths before binding, restricts the socket afterward, and preserves untrusted replacement files during shutdown.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant CLI
  participant UnityIPC
  participant IPCEndpoint
  participant UnixSocket
  CLI->>UnityIPC: Dial endpoint
  UnityIPC->>IPCEndpoint: Validate parent and endpoint directory
  IPCEndpoint-->>UnityIPC: Return validation result
  UnityIPC->>UnixSocket: Dial only after successful validation
  UnixSocket-->>UnityIPC: Return connection
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 37.50% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title accurately summarizes the main change: restricting Unix IPC endpoints to the current OS user.
Description check ✅ Passed The description is directly related to the changeset and covers the endpoint security, validation, and protocol update work.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/unix-ipc-user-isolation

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@Packages/src/Editor/Infrastructure/BridgeTransportListener.cs`:
- Around line 120-125: Update the restriction-failure branch in the bridge
transport listener setup to invoke the existing secured shutdown/cleanup path
that removes the bound socket, rather than only closing and nulling `_listener`;
preserve throwing the IOException with socketModeResult.ErrorMessage afterward.
🪄 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

Run ID: 1e6321df-2255-4c0a-8beb-aba9543f3a1c

📥 Commits

Reviewing files that changed from the base of the PR and between bc8be01 and b179187.

⛔ Files ignored due to path filters (3)
  • Assets/Tests/Editor/UnixEndpointSecurityTests.cs.meta is excluded by none and included by none
  • Packages/src/Editor/Infrastructure/UnixEndpointSecurity.cs.meta is excluded by none and included by none
  • Packages/src/Editor/Infrastructure/UnixNativeFileSystem.cs.meta is excluded by none and included by none
📒 Files selected for processing (23)
  • Assets/Tests/Editor/BridgeTransportEndpointTests.cs
  • Assets/Tests/Editor/BridgeTransportListenerTests.cs
  • Assets/Tests/Editor/UnixEndpointSecurityTests.cs
  • Packages/src/Editor/Domain/CliConstants.cs
  • Packages/src/Editor/Infrastructure/BridgeTransportEndpoint.cs
  • Packages/src/Editor/Infrastructure/BridgeTransportListener.cs
  • Packages/src/Editor/Infrastructure/UnixEndpointSecurity.cs
  • Packages/src/Editor/Infrastructure/UnixNativeFileSystem.cs
  • cli/common/clicontract/contract.json
  • cli/common/errors/unity_endpoint_not_created.go
  • cli/common/errors/unity_endpoint_not_created_test.go
  • cli/common/project/endpoint_contract_test.go
  • cli/common/project/project.go
  • cli/common/project/project_test.go
  • cli/common/project/unix_endpoint_security.go
  • cli/common/project/unix_endpoint_security_test.go
  • cli/common/project/unix_endpoint_security_unix.go
  • cli/common/project/unix_endpoint_security_unix_test.go
  • cli/common/project/unix_endpoint_security_windows.go
  • cli/dispatcher/internal/dispatcher/dispatcher_test.go
  • cli/dispatcher/shared-inputs-stamp.json
  • cli/project-runner/shared-inputs-stamp.json
  • tests/contracts/endpoint_contract.json

Comment on lines +120 to +125
UnixEndpointSecurityResult socketModeResult = _securityPolicy.RestrictSocket(Endpoint.Path);
if (!socketModeResult.Success)
{
_listener.Close();
_listener = null;
throw new IOException(socketModeResult.ErrorMessage);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Remove the bound socket when restriction fails.

Closing and nulling _listener leaves Endpoint.Path behind, and subsequent Stop() returns without cleanup. Use the secured shutdown path before throwing.

Proposed fix
 if (!socketModeResult.Success)
 {
-    _listener.Close();
-    _listener = null;
+    Stop();
     throw new IOException(socketModeResult.ErrorMessage);
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
UnixEndpointSecurityResult socketModeResult = _securityPolicy.RestrictSocket(Endpoint.Path);
if (!socketModeResult.Success)
{
_listener.Close();
_listener = null;
throw new IOException(socketModeResult.ErrorMessage);
UnixEndpointSecurityResult socketModeResult = _securityPolicy.RestrictSocket(Endpoint.Path);
if (!socketModeResult.Success)
{
Stop();
throw new IOException(socketModeResult.ErrorMessage);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Packages/src/Editor/Infrastructure/BridgeTransportListener.cs` around lines
120 - 125, Update the restriction-failure branch in the bridge transport
listener setup to invoke the existing secured shutdown/cleanup path that removes
the bound socket, rather than only closing and nulling `_listener`; preserve
throwing the IOException with socketModeResult.ErrorMessage afterward.

@hatayama
hatayama merged commit e278c5a into security/cli-distribution-hardening-integration Jul 15, 2026
8 checks passed
@hatayama
hatayama deleted the fix/unix-ipc-user-isolation branch July 15, 2026 12:23
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