[PR #2036] [CLOSED] fix(process): name CS_RESTRICT environment withholding in ownership diagnostics #4977

Closed
opened 2026-10-31 00:57:37 +00:00 by bsakaguchi · 0 comments
Owner

📋 Pull Request Information

Original PR: https://github.com/morluto/rea/pull/2036
Author: @Ghost-Kang
Created: 10/12/2026
Status: ❌ Closed

Base: main ← Head: fix/process-restricted-env-diagnostic


📝 Commits (1)

  • 39df0e7 fix(process): name CS_RESTRICT environment withholding in ownership diagnostics

📊 Changes

3 files changed (+120 additions, -96 deletions)

View changed files

📝 bridge/process/ProcessRunTokenReader.swift (+19 -2)
📝 src/process/ProcessOwnership.test.ts (+99 -94)
📝 src/process/ProcessOwnership.ts (+2 -0)

📄 Description

Summary

Refs #2035. On macOS, process captures intermittently fail with cleanup_incomplete / environment_unavailable when an unrelated CS_RESTRICT process (Chrome or Electron helpers, etc.) starts during the capture. This PR is diagnostic only: the run-token reader now names that case restricted_process_environment_withheld instead of the generic environment_unavailable. Containment is unchanged — the candidate still fails closed. Whether to exempt such processes (as #1057 did for platform binaries) is left to the maintainer decision in #2035.

Problem and expected behavior

XNU omits the environment of cs_restricted processes from KERN_PROCARGS2. bridge/process/ProcessRunTokenReader.swift only recognised CS_PLATFORM_BINARY in its environmentUnavailable path, so restricted processes were indistinguishable from a genuinely transient read failure. Expected: the cleanup reason says why the token was unreadable.

Change and scope

  • Swift reader: codeSigningStatus(pid:) is factored out of isPlatformBinary and adds isRestrictedProcess (CS_RESTRICT = 0x800). In the environmentUnavailable catch, the order is platform binary → restricted → environment_unavailable. If csops fails, the result stays environment_unavailable.

  • ProcessOwnership.ts: sanitizedTokenReadFailure passes the new reason through, so the reason names it instead of other_unavailable.

  • Intentionally not changed:

    • environmentWithheldFromInspection, so there is no new exemption.
    • The reader's runId == nil path, where changing classification would alter ownership outcomes.

    Both are discussed in #2035.

Contract and boundary impact

  • Semantic owner and earliest changed stage: Darwin run-token reader (cleanup diagnostics)
  • CLI and MCP/tool-catalog contract: the cleanup_incomplete reason text can now contain restricted_process_environment_withheld; the error code, category and shape are unchanged
  • Provider, bridge, target-format, or platform compatibility: macOS only; the Swift bridge gains one more csops call on an already-failing path
  • Evidence, artifact, provenance, or reconstruction contract: none
  • Process execution, authorization, cleanup, or containment impact: none; restricted candidates remain fail-closed whether related or unrelated (covered by tests)
  • Generated metadata (docs/public/product-catalog.json), package, or installation impact: none

Evidence and regression coverage

  • Tests added or updated: src/process/ProcessOwnership.test.ts. The unrelated-candidate fail-closed test and the related-candidate fail-closed test are parametrised over restricted_process_environment_withheld.
  • Base reproduction or other evidence: reproduction steps are in #2035. With an unrelated CS_RESTRICT process spawned every 0.3 s, the "accepts NUL" acceptance test fails on main with environment_unavailable=N. With this PR, after npm run compile, it still fails (4/4, containment unchanged), but the reason reads restricted_process_environment_withheld=N.
  • User-visible CLI/MCP output:
    owned_process_group: process ownership token could not be read for 1 live process(es): restricted_process_environment_withheld=1; live candidates <pid>=restricted_process_environment_withheld
    
  • Remaining proof gaps: there is no automated native test that launches a real CS_RESTRICT image, because no stable restricted binary is available on CI. The Swift path was verified manually as above.

Validation performed

  • npx vitest run src/process tests/boundary/process/darwinProcessRunTokenReader.test.ts: 24 files, 229 passed, 1 skipped
  • npm run check: passed
  • xcrun swiftc -typecheck bridge/process/ProcessRunTokenReader.swift: passed
  • The manual restricted-noise repro described above
  • pre-commit and pre-push hooks: passed
  • Environment: macOS 26.6.2 arm64, Node 24.21.0, Xcode 27.0

Compatibility, safety, and release

  • Breaking changes or migration steps: none
  • Real Hopper/Ghidra, browser, or OS coverage: macOS 26.6.2 only
  • Package or release metadata impact: none
  • Security, privacy, process, or containment review: no relaxation; the new reason is fail-closed everywhere

Review checklist

  • This PR addresses a concrete problem or an agreed enhancement.
  • The PR has one focused outcome and the title follows type(scope): outcome.
  • Related issue is linked, or the reason for not linking one is stated above.
  • Tests cover changed observable behavior and meaningful failure paths.
  • Owning docs, contracts, and generated metadata are updated where needed.
  • User-visible CLI/MCP changes include representative output.
  • I checked the final diff for secrets, unrelated cleanup, and unsupported claims.

🤖 Generated with Claude Code


🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.

## 📋 Pull Request Information **Original PR:** https://github.com/morluto/rea/pull/2036 **Author:** [@Ghost-Kang](https://github.com/Ghost-Kang) **Created:** 10/12/2026 **Status:** ❌ Closed **Base:** `main` ← **Head:** `fix/process-restricted-env-diagnostic` --- ### 📝 Commits (1) - [`39df0e7`](https://github.com/morluto/rea/commit/39df0e7584ec871faf7c39bd036321ff0b77f873) fix(process): name CS_RESTRICT environment withholding in ownership diagnostics ### 📊 Changes **3 files changed** (+120 additions, -96 deletions) <details> <summary>View changed files</summary> 📝 `bridge/process/ProcessRunTokenReader.swift` (+19 -2) 📝 `src/process/ProcessOwnership.test.ts` (+99 -94) 📝 `src/process/ProcessOwnership.ts` (+2 -0) </details> ### 📄 Description ## Summary Refs #2035. On macOS, process captures intermittently fail with `cleanup_incomplete` / `environment_unavailable` when an unrelated `CS_RESTRICT` process (Chrome or Electron helpers, etc.) starts during the capture. This PR is **diagnostic only**: the run-token reader now names that case `restricted_process_environment_withheld` instead of the generic `environment_unavailable`. Containment is unchanged — the candidate still fails closed. Whether to exempt such processes (as #1057 did for platform binaries) is left to the maintainer decision in #2035. ## Problem and expected behavior XNU omits the environment of `cs_restricted` processes from `KERN_PROCARGS2`. `bridge/process/ProcessRunTokenReader.swift` only recognised `CS_PLATFORM_BINARY` in its `environmentUnavailable` path, so restricted processes were indistinguishable from a genuinely transient read failure. Expected: the cleanup reason says why the token was unreadable. ## Change and scope - **Swift reader:** `codeSigningStatus(pid:)` is factored out of `isPlatformBinary` and adds `isRestrictedProcess` (`CS_RESTRICT` = `0x800`). In the `environmentUnavailable` catch, the order is platform binary → restricted → `environment_unavailable`. If `csops` fails, the result stays `environment_unavailable`. - **`ProcessOwnership.ts`:** `sanitizedTokenReadFailure` passes the new reason through, so the reason names it instead of `other_unavailable`. - **Intentionally not changed:** - `environmentWithheldFromInspection`, so there is no new exemption. - The reader's `runId == nil` path, where changing classification would alter ownership outcomes. Both are discussed in #2035. ## Contract and boundary impact - Semantic owner and earliest changed stage: Darwin run-token reader (cleanup diagnostics) - CLI and MCP/tool-catalog contract: the `cleanup_incomplete` reason text can now contain `restricted_process_environment_withheld`; the error code, category and shape are unchanged - Provider, bridge, target-format, or platform compatibility: macOS only; the Swift bridge gains one more `csops` call on an already-failing path - Evidence, artifact, provenance, or reconstruction contract: none - Process execution, authorization, cleanup, or containment impact: none; restricted candidates remain fail-closed whether related or unrelated (covered by tests) - Generated metadata (`docs/public/product-catalog.json`), package, or installation impact: none ## Evidence and regression coverage - Tests added or updated: `src/process/ProcessOwnership.test.ts`. The unrelated-candidate fail-closed test and the related-candidate fail-closed test are parametrised over `restricted_process_environment_withheld`. - Base reproduction or other evidence: reproduction steps are in #2035. With an unrelated `CS_RESTRICT` process spawned every 0.3 s, the "accepts NUL" acceptance test fails on `main` with `environment_unavailable=N`. With this PR, after `npm run compile`, it still fails (4/4, containment unchanged), but the reason reads `restricted_process_environment_withheld=N`. - User-visible CLI/MCP output: ``` owned_process_group: process ownership token could not be read for 1 live process(es): restricted_process_environment_withheld=1; live candidates <pid>=restricted_process_environment_withheld ``` - Remaining proof gaps: there is no automated native test that launches a real `CS_RESTRICT` image, because no stable restricted binary is available on CI. The Swift path was verified manually as above. ## Validation performed - `npx vitest run src/process tests/boundary/process/darwinProcessRunTokenReader.test.ts`: 24 files, 229 passed, 1 skipped - `npm run check`: passed - `xcrun swiftc -typecheck bridge/process/ProcessRunTokenReader.swift`: passed - The manual restricted-noise repro described above - pre-commit and pre-push hooks: passed - Environment: macOS 26.6.2 arm64, Node 24.21.0, Xcode 27.0 ## Compatibility, safety, and release - Breaking changes or migration steps: none - Real Hopper/Ghidra, browser, or OS coverage: macOS 26.6.2 only - Package or release metadata impact: none - Security, privacy, process, or containment review: no relaxation; the new reason is fail-closed everywhere ## Review checklist - [x] This PR addresses a concrete problem or an agreed enhancement. - [x] The PR has one focused outcome and the title follows `type(scope): outcome`. - [x] Related issue is linked, or the reason for not linking one is stated above. - [x] Tests cover changed observable behavior and meaningful failure paths. - [x] Owning docs, contracts, and generated metadata are updated where needed. - [x] User-visible CLI/MCP changes include representative output. - [x] I checked the final diff for secrets, unrelated cleanup, and unsupported claims. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
bsakaguchi 2026-10-31 00:57:37 +00:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
morluto/rea#4977
No description provided.