Commit Graph

5 Commits

Author SHA1 Message Date
Jiang Bohan
9a43fcf87e fix(daemon): migrate existing .local daemon_ids and normalize on register
PR #1070 stripped `.local` from the daemon-side hostname so CLI and
desktop daemons stop registering as separate devices on macOS, but it
left two gaps:

1. Existing rows in `agent_runtime` for every macOS user still carry
   the `.local` suffix. After upgrade the daemon registers under the
   new canonical `daemon_id`, hits the `(workspace_id, daemon_id,
   provider)` unique key as a miss, and INSERTs a fresh row. The old
   `agent.runtime_id` FK keeps pointing at the orphaned `.local` row,
   which never receives a heartbeat again — the user's agent appears
   offline until they manually rebind it.
2. Older or non-CLI clients (anyone calling /api/daemon/register
   directly) can still send the suffixed form and create the same
   orphan condition going forward.

Fixes:

- Migration 048 walks `agent_runtime` and, for every (workspace_id,
  provider) where both `X` and `X.local` rows exist, redirects the
  `agent` and `agent_task_queue` FK references from the `.local` row
  to the canonical row inside a single statement (so the RESTRICT
  constraint passes), then deletes the duplicate. Orphaned `.local`
  rows with no canonical counterpart are renamed in place.
- Handler-side `normalizeDaemonID` strips the suffix on every
  /api/daemon/register call before the upsert, so stale clients can't
  re-create the orphan.

Tests cover the normalization helper directly and exercise the
register endpoint twice — once with `.local` and once canonical — to
prove both forms upsert into the same row.

Refs: MUL-971
2026-04-17 00:32:24 +08:00
Bohan Jiang
f8c6dd505f fix(security): bind URL issueId to workspace on four issue-scoped daemon.go handlers (#1145)
GetActiveTaskForIssue, CancelTask, ListTasksByIssue, and GetIssueUsage
accepted the issueId URL parameter and queried by it without verifying
that the issue belonged to the caller's X-Workspace-ID workspace. The
RequireWorkspaceMember middleware only proves membership in the header
workspace; it does not bind the path-parameter issue to it. A member of
workspace A could therefore enumerate tasks, cancel tasks, and read
usage metadata for any issue UUID in workspace B.

Route every issueId through loadIssueForUser (matching GetIssue and the
existing comment/subscriber handlers). For CancelTask additionally
verify that the task's IssueID matches the loaded issue — the task must
not only belong to the caller's workspace but also to the specific
issue named in the URL, and the access check must run before any
mutation.

Follow-up to MUL-899 / #1112.
2026-04-16 15:08:27 +08:00
Bohan Jiang
ce52374d5d test(daemon): add cross-workspace regression for GetIssueGCCheck (#1143)
Adds TestGetIssueGCCheck_WithDaemonToken_CrossWorkspace alongside the
existing TestGetTaskStatus_WithDaemonToken_CrossWorkspace, covering:

- daemon token scoped to a different workspace → 404 (matches the
  "issue not found" status, so no UUID enumeration oracle)
- daemon token scoped to the issue's workspace → 200 with status and
  updated_at fields populated

Follow-up to #1121, which fixed the underlying IDOR reported in #1112
but did not ship a regression test. This gates the class of bug at CI
so the next handler to forget requireDaemonWorkspaceAccess will be
caught before merge.
2026-04-16 13:49:54 +08:00
Black
0427fd8cc7 fix(daemon): refresh workspace repos on checkout miss (#1085)
Co-authored-by: black-fe <black-fe@gate.me>
2026-04-15 19:10:54 +08:00
Jiayuan Zhang
82bbce98fd fix(security): add workspace ownership checks to daemon API routes (#684)
* fix(security): add workspace ownership checks to all daemon API routes

Switch daemon routes from middleware.Auth to middleware.DaemonAuth and
add per-handler workspace ownership verification. This prevents
cross-workspace access to runtimes, tasks, usage, and daemon lifecycle
endpoints (HIGH-1/2/3 + CHAIN-1/2/3).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(security): support mdt_ daemon tokens in DaemonRegister + add regression tests

DaemonRegister now handles both auth paths:
- mdt_ daemon tokens: verify workspace match, skip member check, zero OwnerID
  (SQL COALESCE preserves existing owner on upsert)
- PAT/JWT: existing member check + OwnerID from member

Also adds WithDaemonContext helper and regression tests covering:
- Successful register with daemon token
- Workspace mismatch rejection
- Cross-workspace heartbeat rejection
- Cross-workspace task status rejection

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-11 12:49:23 +08:00