Skip to content

Fix Windows discovery dispatch on managed devices - #287

Open
zeus-12 wants to merge 1 commit into
stagingfrom
vv/fix-windows-discovery-acl
Open

zeus-12 wants to merge 1 commit into
stagingfrom
vv/fix-windows-discovery-acl

Conversation

@zeus-12

@zeus-12 zeus-12 commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

What

  • stores Windows discovery debounce and dispatch state in the user-owned %TEMP%\unbound directory
  • keeps reading API configuration from ~/.unbound/config.json
  • still respects fresh discovery locks in both the managed home state and user temp state
  • adds a stable hook revision and executing-file SHA-256 to error reports
  • applies the same behavior to Claude Code, Cursor, Copilot, Codex, and Augment

Gateway support for the new Sentry tags: https://github.com/websentry-ai/ai-gateway/pull/899

Root cause

PR #281 catches PermissionError but retries by deleting the same SYSTEM-owned dispatch marker. On affected Windows devices that delete is also denied, so discovery still never launches. The old regression test mocked only the first os.open; its marker remained deletable.

Current production evidence for Xome: the new PR #281 error signature has reached 24 events across 13 Copilot hook identities, so old-hook adoption does not explain all failures.

Testing

  • regression test denies both opening and deleting the managed home marker
  • tests/test_discovery_dispatch.py: 35 passed
  • discovery plus frozen contract: 50 passed
  • full suite: 2452 passed, 39 skipped, 175 subtests; four unrelated Claude setup tests fail identically on untouched origin/staging because this workstation has enterprise hooks installed
  • python3 -m py_compile on all five changed hooks
  • git diff --check

Greptile Summary

The PR moves Windows discovery debounce and dispatch state into the invoking user’s temporary directory while retaining checks for active legacy home-directory locks. It also adds hook revision and executing-file SHA-256 metadata to error reports across all five integrations.

  • Applies consistent Windows discovery-state selection to Claude Code, Cursor, Copilot, Codex, and Augment.
  • Continues reading API configuration from the shared home-directory configuration.
  • Adds regression coverage for inaccessible managed-home dispatch markers, legacy discovery locks, and error-report metadata.

Confidence Score: 5/5

The PR appears safe to merge, with no concrete changed-code failure identified.

The new Windows path is used consistently for cache, dispatch claim, cleanup, and successful-launch timestamping, while the legacy home lock remains checked and API configuration remains sourced from the home directory.

Important Files Changed

Filename Overview
augment/hooks/unbound.py Moves Windows discovery coordination state to user temp storage and adds executing-hook metadata to error reports.
claude-code/hooks/unbound.py Applies the shared Windows state-path behavior while preserving managed-home lock checks and configuration lookup.
codex/hooks/unbound.py Mirrors the discovery-state and error-report metadata changes without altering non-Windows behavior.
copilot/hooks/unbound.py Redirects Windows dispatch and debounce files to user temp storage while retaining legacy active-lock coordination.
cursor/unbound.py Implements the same user-owned Windows discovery state and hook identity reporting for Cursor.
tests/test_discovery_dispatch.py Adds cross-integration regression coverage for inaccessible managed markers, active legacy locks, and hook identity fields.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
  A[Session start] --> B{Windows?}
  B -->|No| C[Use home discovery state]
  B -->|Yes| D[Use user TEMP discovery state]
  D --> E{Fresh home or TEMP discovery lock?}
  C --> F{Fresh discovery lock?}
  E -->|Yes| G[Skip dispatch]
  F -->|Yes| G
  E -->|No| H[Atomically claim dispatch marker]
  F -->|No| H
  H --> I[Read API configuration from home]
  I --> J[Launch discovery]
  J --> K[Write successful-run debounce timestamp]
  K --> L[Remove dispatch marker]
Loading

Reviews (1): Last reviewed commit: "fix(hooks): isolate Windows discovery st..." | Re-trigger Greptile

Context used (3)

@zeus-12
zeus-12 requested a review from a team September 2, 2026 09:25

@vigneshsubbiah16 vigneshsubbiah16 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

🛡️ Automated Security Review (consensus)

2 findings — 2 high-confidence, 0 to triage. Reviewers: Cursor, Claude, Semgrep, Gitleaks.


🔴 HIGH — Shared Windows temp discovery state lacks ownership/reparse validation

claude-code/hooks/unbound.py:85-88 (same pattern: augment/hooks/unbound.py:85, codex/hooks/unbound.py:57, copilot/hooks/unbound.py:44, cursor/unbound.py:38; consumed in _dispatch_discovery e.g. claude-code/hooks/unbound.py:5653-5654)

Impact: DISCOVERY_WINDOWS_STATE_DIR = Path(tempfile.gettempdir()) / "unbound" is predictable; mkdir(..., exist_ok=True) adopts pre-existing paths with no owner or reparse-point check — in shared-TEMP contexts (e.g. C:\Windows\Temp) a local user can pre-create a fresh discovery.lock to silently suppress discovery, or a junction to redirect cache I/O.

Fix: Use a per-user base (%LOCALAPPDATA%\unbound or SID-scoped path), create with a user-only DACL, and before use verify the path is a real directory owned by the current user (reject reparse points/junctions).

Flagged by: Claude, Lead (Cursor)


🔴 HIGH — 0o600 on dispatch marker is ineffective on Windows

claude-code/hooks/unbound.py:5686 (mirrored in augment/hooks/unbound.py:3536, codex/hooks/unbound.py:3224, copilot/hooks/unbound.py:4033, cursor/unbound.py:3163)

Impact: POSIX 0o600 is ignored on Windows; new lock/cache/dispatch files inherit the parent temp directory's ACL, so they may be readable/writable by any principal with access to that shared directory.

Fix: Do not rely on os.open(..., 0o600) on Windows — place state under a user-private directory (see above) or set an explicit DACL at directory/file creation.

Flagged by: Claude, Lead (Cursor)


Notes (non-findings): Gitleaks reported no secrets. Semgrep hits on unrelated 0o755/$BITS permissions and a SQLAlchemy raw-query warning in cursor/unbound.py:542 are outside this diff and were not raised. hook_revision / hook_sha256 telemetry in error reports is low-sensitivity diagnostic metadata.


🤖 consensus review · reviewers: Cursor,Claude,Semgrep,Gitleaks · head 8a9d8a12 · 2026-09-02T09:32Z

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.

2 participants