From 9c4325bcf8070d84a6911ae78b898c116ebad2ac Mon Sep 17 00:00:00 2001 From: Adam <2363879+adamdotdevin@users.noreply.github.com> Date: Tue, 10 Mar 2026 11:32:05 -0500 Subject: [PATCH] fix(core): don't permit access to system directories (#16891) --- packages/opencode/src/file/index.ts | 6 +-- packages/opencode/src/file/protected.ts | 59 +++++++++++++++++++++++++ packages/opencode/src/file/watcher.ts | 3 +- 3 files changed, 63 insertions(+), 5 deletions(-) create mode 100644 packages/opencode/src/file/protected.ts diff --git a/packages/opencode/src/file/index.ts b/packages/opencode/src/file/index.ts index 7a51ca36c..e03fc8a9f 100644 --- a/packages/opencode/src/file/index.ts +++ b/packages/opencode/src/file/index.ts @@ -11,6 +11,7 @@ import { Ripgrep } from "./ripgrep" import fuzzysort from "fuzzysort" import { Global } from "../global" import { git } from "@/util/git" +import { Protected } from "./protected" export namespace File { const log = Log.create({ service: "file" }) @@ -345,10 +346,7 @@ export namespace File { if (isGlobalHome) { const dirs = new Set() - const ignore = new Set() - - if (process.platform === "darwin") ignore.add("Library") - if (process.platform === "win32") ignore.add("AppData") + const ignore = Protected.names() const ignoreNested = new Set(["node_modules", "dist", "build", "target", "vendor"]) const shouldIgnore = (name: string) => name.startsWith(".") || ignore.has(name) diff --git a/packages/opencode/src/file/protected.ts b/packages/opencode/src/file/protected.ts new file mode 100644 index 000000000..d51974619 --- /dev/null +++ b/packages/opencode/src/file/protected.ts @@ -0,0 +1,59 @@ +import path from "path" +import os from "os" + +const home = os.homedir() + +// macOS directories that trigger TCC (Transparency, Consent, and Control) +// permission prompts when accessed by a non-sandboxed process. +const DARWIN_HOME = [ + // Media + "Music", + "Pictures", + "Movies", + // User-managed folders synced via iCloud / subject to TCC + "Downloads", + "Desktop", + "Documents", + // Other system-managed + "Public", + "Applications", + "Library", +] + +const DARWIN_LIBRARY = [ + "Application Support/AddressBook", + "Calendars", + "Mail", + "Messages", + "Safari", + "Cookies", + "Application Support/com.apple.TCC", + "PersonalizationPortrait", + "Metadata/CoreSpotlight", + "Suggestions", +] + +const DARWIN_ROOT = ["/.DocumentRevisions-V100", "/.Spotlight-V100", "/.Trashes", "/.fseventsd"] + +const WIN32_HOME = ["AppData", "Downloads", "Desktop", "Documents", "Pictures", "Music", "Videos", "OneDrive"] + +export namespace Protected { + /** Directory basenames to skip when scanning the home directory. */ + export function names(): ReadonlySet { + if (process.platform === "darwin") return new Set(DARWIN_HOME) + if (process.platform === "win32") return new Set(WIN32_HOME) + return new Set() + } + + /** Absolute paths that should never be watched, stated, or scanned. */ + export function paths(): string[] { + if (process.platform === "darwin") + return [ + ...DARWIN_HOME.map((n) => path.join(home, n)), + ...DARWIN_LIBRARY.map((n) => path.join(home, "Library", n)), + ...DARWIN_ROOT, + ] + if (process.platform === "win32") return WIN32_HOME.map((n) => path.join(home, n)) + return [] + } +} diff --git a/packages/opencode/src/file/watcher.ts b/packages/opencode/src/file/watcher.ts index 537f52646..3797c1627 100644 --- a/packages/opencode/src/file/watcher.ts +++ b/packages/opencode/src/file/watcher.ts @@ -14,6 +14,7 @@ import type ParcelWatcher from "@parcel/watcher" import { Flag } from "@/flag/flag" import { readdir } from "fs/promises" import { git } from "@/util/git" +import { Protected } from "./protected" const SUBSCRIBE_TIMEOUT_MS = 10_000 @@ -76,7 +77,7 @@ export namespace FileWatcher { if (Flag.OPENCODE_EXPERIMENTAL_FILEWATCHER) { const pending = w.subscribe(Instance.directory, subscribe, { - ignore: [...FileIgnore.PATTERNS, ...cfgIgnores], + ignore: [...FileIgnore.PATTERNS, ...cfgIgnores, ...Protected.paths()], backend, }) const sub = await withTimeout(pending, SUBSCRIBE_TIMEOUT_MS).catch((err) => {