security: restrict desktop sidecar CORS to funnel URL (env-driven) - #1
Open
neohiro wants to merge 3 commits into
Open
security: restrict desktop sidecar CORS to funnel URL (env-driven)#1neohiro wants to merge 3 commits into
neohiro wants to merge 3 commits into
Conversation
Follow-up to the previous CORS-restriction commit. Three improvements: 1. Fix PowerShell double-quote escape in the JS patch marker. The previous version used backticks (\`) inside a double-quoted PS string, which produces a string containing literal backslashes when evaluated. The asar would receive "cors: JSON.parse(... || '[\\\"*\\\"']')" with backslashes that JS interprets as string escapes (still valid JS, but harder to grep and audit). Switch to the standard PowerShell "" escape inside single-quoted JS content: "cors: JSON.parse(... || '[\"\"*\"\"]')" which evaluates to "cors: JSON.parse(... || '[\"*\"\"]')" — wait, no. Final form: '["*"]' as JS bytes, written cleanly without literal backslashes. Easier to audit and matches the local install. Affects: patch-opencode-desktop.ps1, watch-opencode-desktop.ps1 2. Add URL validation in start-opencode-desktop.ps1 before exporting OPENCODE_SERVER_CORS. Previously the script trusted whatever was in ~/.opencode-funnel-url; if the file was hand-edited or partially written, the CORS allowlist could contain garbage. The new check uses the same regex the JS plugin uses (readCorsAllowlist) so malformed URLs are rejected with a warning, and the allowlist falls back to ["oc://renderer"] only. 3. Add 12 unit tests for readCorsAllowlist in test/mobile-sync.test.mjs covering: missing file, empty file, whitespace-only, valid URL, URL with path, http rejected, wildcard rejected, hyphen-prefix rejected, single-char hostname, trailing-slash rejected, JSON output parseable, permission error. 62/62 tests pass.
neohiro
force-pushed
the
cve/cors-allowlist
branch
from
August 28, 2026 20:27
b0fd453 to
b960236
Compare
neohiro
marked this pull request as ready for review
August 28, 2026 21:39
Owner
Author
CI StatusThis PR has been verified locally against the mobile-sync test suite: \ ✓ readCorsAllowlist tests: 12 passing All tests pass on this branch (b960236). CI workflow on opencode-config is passing: manifest integrity ✓, sync self-tests ✓, PSScriptAnalyzer lint ✓. |
added 2 commits
August 28, 2026 23:47
…pdate temp dir, plugin backup, restore) [oc]
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
security: restrict desktop sidecar CORS to funnel URL (env-driven) [oc]
The desktop sidecar (app.asar) currently uses
cors: ["*"]afterpatching, which exposes it to the public internet when reachable via
Tailscale Funnel. The funnel is intended to limit access to mobile
clients, but wildcard CORS makes the CORS layer moot once an
attacker can reach the endpoint.
This change moves the CORS allowlist to an env var
(OPENCODE_SERVER_CORS) and sets it from a single source of truth
(~/.opencode-funnel-url). The allowlist is always ["oc://renderer"]
plus the funnel URL when present. The asar falls back to ["*"] only
when the env var is unset (e.g. before the plugin has loaded once
or in CI/test).
Changes:
mobile-sync.js:
URL with /^https://a-z0-9?$/i (defense
against origin-injection), and returns ["oc://renderer",].
command, so manually-launched opencode processes (and the sidecar
when started outside start-opencode-desktop.ps1) also get the
restricted allowlist.
scripts/patch-opencode-desktop.ps1:
cors: JSON.parse(process.env.OPENCODE_SERVER_CORS || '["*"]')
unset, to keep first-run compatibility.
scripts/watch-opencode-desktop.ps1:
detection check stays in sync.
scripts/start-opencode-desktop.ps1:
["oc://renderer",] (empty funnel = just oc://renderer).
README.md: