Skip to content

fix(server): reject foreign directory hints before instance lookup#34256

Open
romanilyin wants to merge 4 commits into
anomalyco:devfrom
romanilyin:directory-boundary
Open

fix(server): reject foreign directory hints before instance lookup#34256
romanilyin wants to merge 4 commits into
anomalyco:devfrom
romanilyin:directory-boundary

Conversation

@romanilyin

Copy link
Copy Markdown

Issue for this PR

Closes #34255

Part of #33107

Related: #30895, #19473

Type of change

  • Bug fix
  • New feature
  • Refactor / code improvement
  • Documentation

What does this PR do?

Validates request directory hints before InstanceStore.load.

Query values are consumed once, encoded SDK headers are decoded once, and Windows drive/UNC paths sent to a POSIX/WSL server return a structured 400 instead of being resolved under the server cwd.

The path logic stays behind one small wrapper. This PR does not migrate SQLite/App state, change session matching, resolve symlink identity, or translate Windows paths to WSL paths.

How did you verify your code works?

  • Added parser tests for native POSIX/Windows paths and foreign rooted syntax.
  • Added HTTP tests for query/header decoding and URL-sensitive directory names.
  • Verified /mnt/c/... succeeds on WSL while C:\... and UNC directory hints return 400.
  • Verified existing relative-directory and raw-header behavior remains compatible.

Screenshots / recordings

N/A — server/API change.

Checklist

  • I have tested my changes locally
  • I have not included unrelated changes in this PR

@andrei-hasna

Copy link
Copy Markdown

Merge blocker found during review: the PR validates top-level directory query/header hints before InstanceStore.load, but the v2 ServerApi path still has an equivalent directory-hint bypass. packages/server/src/location.ts reads location[directory] and x-opencode-directory, decodes the header, and calls LocationServiceMap.get(...) without ServerDirectory.parse/native-path rejection. Those routes include location-scoped /api/* endpoints such as packages/protocol/src/groups/fs.ts, so a POSIX server can still accept /api/fs/find?location[directory]=C%3A%5CWork%5CRepo without the intended 400. Please apply the same server-native parsing/rejection to LocationMiddleware or a shared parser and add focused tests for location[directory] plus encoded x-opencode-directory on /api/* routes. Local evidence from a clean worktree: bun install --frozen-lockfile passed, focused server tests passed 49/49, and packages/opencode typecheck passed. Note: this account could not submit a formal request-changes review because GitHub reports it lacks explicit repository access.

@romanilyin

Copy link
Copy Markdown
Author

Addressed in 255e714.

Changes:

  • Moved ServerDirectory into @opencode-ai/server/server-directory so both v1 instance routing and v2 LocationMiddleware share the same parser.
  • packages/server/src/location.ts now validates location[directory] query hints and decoded x-opencode-directory header hints before LocationServiceMap.get(...).
  • Invalid v2 hints now return InvalidRequestError with kind: "Query" or kind: "Header" and field: "location[directory]".
  • Added focused /api/fs/find coverage for both location[directory]=C%3A%5CWork%5CRepo and encoded x-opencode-directory.

Verification:

  • bun test --timeout 60000 test/server/server-directory.test.ts test/server/httpapi-v2-location.test.ts test/server/httpapi-instance-context.test.ts test/server/httpapi-workspace-routing.test.ts test/server/httpapi-sdk.test.ts -> 54 pass
  • bun typecheck in packages/server
  • bun typecheck in packages/opencode
  • bun run generate from packages/client -> no generated diff
  • git diff --check
  • pre-push bun turbo typecheck -> 29/29 packages passed

@andrei-hasna

Copy link
Copy Markdown

Follow-up safe-merge review on current head 255e714295885819f1a94d069cde8a556ff44bd1:

Validation in an isolated worktree merged into current origin/dev (61a7f6db35e1b8284258de247b889edf14748234) passed:

  • bun install --frozen-lockfile
  • bun test --timeout 60000 test/server/server-directory.test.ts test/server/httpapi-v2-location.test.ts test/server/httpapi-instance-context.test.ts test/server/httpapi-workspace-routing.test.ts test/server/httpapi-sdk.test.ts -> 54 pass
  • bun typecheck in packages/server
  • bun typecheck in packages/opencode
  • bun run generate in packages/client with no generated diff
  • bun turbo typecheck -> 29/29 successful
  • git diff --check

Current state: the PR is open/non-draft, checks pass, local merge into latest dev is conflict-free. I could not merge from this account because GitHub reports viewerPermission: READ; mergeStateStatus is still BLOCKED even though mergeable is MERGEABLE and no branch protection rule was visible through the API.

Residual review notes before a maintainer merges: the original v2 LocationMiddleware blocker is addressed in this head. The remaining risk is narrower: POSIX/WSL parsing still intentionally accepts non-rooted Windows-like strings such as C:Work\\Repo and POSIX //server/share style paths, and the fix adds the new single-maintainer dependency @romanilyin/canonicalpath@2026.6.19-1 in request-boundary validation code. Those may be acceptable for this narrow issue, but should be an explicit maintainer decision.

@andrei-hasna

Copy link
Copy Markdown

Thanks for the focused fix. I locally validated the PR's merge result against current dev in a clean worktree and the new/changed server tests pass, and full bun typecheck passes.

I don't think this is merge-ready yet:

  1. ServerDirectory.parse() still accepts slash-form UNC-like paths on POSIX/WSL. C:\Work\Repo, C:/Work/Repo, and \\server\share\repo are rejected, but //server/share/repo currently flows through as a native POSIX path before instance/location lookup. Given the PR says Windows drive/UNC hints are rejected, please either reject this form too or add an explicit test/comment showing that POSIX //... compatibility is intentional.

    Local repro against the PR merge:

    //server/share/repo => "//server/share/repo"
    \\server\share\repo => ServerDirectoryParseError foreign
    C:/Work/Repo => ServerDirectoryParseError foreign
    

    Relevant area: packages/server/src/server-directory.ts (parse, hasWindowsUncRoot) and the new server directory/v2 location tests.

  2. Required branch ruleset checks have not run successfully. The required contexts are unit (linux), unit (windows), e2e (linux), e2e (windows), and typecheck; the PR head currently only has successful check-standards/check-compliance, while the pull_request workflows show action_required.

Other observed state: PR is open, non-draft, mergeable without content conflicts, but stale by 3 commits from current dev.

@romanilyin romanilyin force-pushed the directory-boundary branch from 255e714 to 86b4435 Compare June 28, 2026 07:37
@romanilyin

Copy link
Copy Markdown
Author

Addressed the latest review note in 86b443550.

Changes:

  • Rebased the branch onto current origin/dev (dfeb1b5051a05b359bd4af711b204d2c0342c5f4), so the PR is no longer behind dev.
  • ServerDirectory.parse() now rejects slash-form UNC-like hints such as //server/share/repo on POSIX/WSL, in addition to \\server\share\repo, C:\..., and C:/....
  • Added coverage for //server/share/repo in the parser test, the v1/top-level directory middleware path, and v2 /api/fs/find query/header paths.

Verification:

  • bun test --timeout 60000 test/server/server-directory.test.ts test/server/httpapi-v2-location.test.ts test/server/httpapi-instance-context.test.ts test/server/httpapi-workspace-routing.test.ts test/server/httpapi-sdk.test.ts -> 54 pass
  • bun typecheck in packages/server
  • bun typecheck in packages/opencode
  • bun run generate from packages/client -> no generated diff
  • git diff --check
  • pre-push bun turbo typecheck -> 29/29 packages passed

Current GitHub state after push: the PR is mergeable and check-standards/check-compliance are passing. The required unit/e2e/typecheck workflow contexts still have not been started by GitHub on this fork PR, so I cannot clear those from my side beyond keeping the branch current and locally verified.

@andrei-hasna

Copy link
Copy Markdown

Blocking merge on one remaining cross-platform parser issue found by independent review and reproduced locally. I cannot submit this as a formal request-changes review because GitHub says this account lacks explicit repository access.

On the current PR head 86b443550783c5eed4f4a21e708f52d9f8cd18be, ServerDirectory.parse() rejects Windows-rooted hints on POSIX/WSL, but Windows-profile parsing still accepts POSIX-rooted hints and silently converts them into Windows paths. In packages/server/src/server-directory.ts, parseWindows() calls normalize(raw) without constraining the target profile, then toWin32(...) converts /tmp/repo to \tmp\repo. That means a request like ?directory=/tmp/repo to a Windows OpenCode server is not rejected as foreign syntax and can route to a different directory than the client intended.

Local reproduction on the PR merge tree:

/tmp/repo => \tmp\repo
file:///tmp/repo => ServerDirectoryParseError invalid
C:/Work/Repo => C:\Work\Repo
C:\Work\Repo => C:\Work\Repo
C:Work\Repo => ServerDirectoryParseError drive-relative
\\server\share\repo => \\server\share\repo
relative\repo => relative\repo

Required before merge:

  • Reject POSIX-rooted paths in the Windows profile unless there is an explicit documented reason to treat them as native Windows current-drive paths.
  • Add platform-independent unit coverage using ServerDirectory.parse(input, { kind: "win32" }) for /tmp/repo, file:///tmp/repo, drive-root paths, drive-relative paths, UNC paths, and relative paths.

Validation already run on the local merge result:

  • bun install --frozen-lockfile passed.
  • From packages/opencode: bun test --timeout 30000 test/server/server-directory.test.ts test/server/httpapi-instance-context.test.ts test/server/httpapi-workspace-routing.test.ts test/server/httpapi-v2-location.test.ts test/server/httpapi-sdk.test.ts -> 54 pass, 0 fail.
  • From packages/server: bun typecheck passed.
  • From packages/opencode: bun typecheck passed.

GitHub state is still mergeable=MERGEABLE, mergeStateStatus=BLOCKED, no review decision, and only check-standards/check-compliance are passing. I am not merging this head.

@romanilyin romanilyin force-pushed the directory-boundary branch from 86b4435 to ee7abd5 Compare June 28, 2026 17:34
@romanilyin

Copy link
Copy Markdown
Author

Addressed the Windows-profile parser blocker in ee7abd5ab.

Changes:

  • Rebased the branch onto current origin/dev (58ba99e505a8c898f79e1438bddea35c197bc0af), so the PR is no longer behind dev.
  • ServerDirectory.parse(input, { kind: "win32" }) now rejects POSIX-rooted single-slash hints such as /tmp/repo as foreign before normalize(...) can convert them to \tmp\repo.
  • Added platform-independent Windows-profile unit coverage for /tmp/repo, file:///tmp/repo, drive-root paths, drive-relative paths, UNC paths, and relative paths.
  • No canonicalpath change was needed; this is the OpenCode request-boundary wrapper policy.

Verification:

  • bun test --timeout 60000 test/server/server-directory.test.ts -> 7 pass
  • bun test --timeout 60000 test/server/server-directory.test.ts test/server/httpapi-v2-location.test.ts test/server/httpapi-instance-context.test.ts test/server/httpapi-workspace-routing.test.ts test/server/httpapi-sdk.test.ts -> 56 pass
  • bun typecheck in packages/server
  • bun typecheck in packages/opencode
  • bun run generate from packages/client -> no generated diff
  • git diff --check
  • pre-push bun turbo typecheck -> 29/29 packages passed

Current GitHub state after push: PR is mergeable, branch is current with dev, and check-standards/check-compliance are passing. The upstream pull_request workflows are still gated by GitHub fork approval/action_required, which requires maintainer/admin approval.

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.

[BUG]: POSIX server resolves foreign Windows directory hints against its cwd

2 participants