diff --git a/packages/core/src/flag/flag.ts b/packages/core/src/flag/flag.ts index 3ed67bb785..54f2445e00 100644 --- a/packages/core/src/flag/flag.ts +++ b/packages/core/src/flag/flag.ts @@ -21,7 +21,6 @@ export const Flag = { OPENCODE_DISABLE_PRUNE: truthy("OPENCODE_DISABLE_PRUNE"), OPENCODE_DISABLE_TERMINAL_TITLE: truthy("OPENCODE_DISABLE_TERMINAL_TITLE"), OPENCODE_SHOW_TTFD: truthy("OPENCODE_SHOW_TTFD"), - OPENCODE_PERMISSION: process.env["OPENCODE_PERMISSION"], OPENCODE_DISABLE_AUTOCOMPACT: truthy("OPENCODE_DISABLE_AUTOCOMPACT"), OPENCODE_DISABLE_MODELS_FETCH: truthy("OPENCODE_DISABLE_MODELS_FETCH"), OPENCODE_DISABLE_MOUSE: truthy("OPENCODE_DISABLE_MOUSE"), @@ -59,6 +58,9 @@ export const Flag = { get OPENCODE_PURE() { return truthy("OPENCODE_PURE") }, + get OPENCODE_PERMISSION() { + return process.env["OPENCODE_PERMISSION"] + }, get OPENCODE_PLUGIN_META_FILE() { return process.env["OPENCODE_PLUGIN_META_FILE"] }, diff --git a/packages/opencode/src/config/config.ts b/packages/opencode/src/config/config.ts index b13d3a8c81..cdc32b971d 100644 --- a/packages/opencode/src/config/config.ts +++ b/packages/opencode/src/config/config.ts @@ -703,7 +703,11 @@ export const layer = Layer.effect( } if (Flag.OPENCODE_PERMISSION) { - result.permission = mergeDeep(result.permission ?? {}, JSON.parse(Flag.OPENCODE_PERMISSION)) + try { + result.permission = mergeDeep(result.permission ?? {}, JSON.parse(Flag.OPENCODE_PERMISSION)) + } catch (err) { + log.warn("OPENCODE_PERMISSION contains invalid JSON, skipping", { err }) + } } if (result.tools) { diff --git a/packages/opencode/test/config/config.test.ts b/packages/opencode/test/config/config.test.ts index 9a88fcf4b9..12fb96dc67 100644 --- a/packages/opencode/test/config/config.test.ts +++ b/packages/opencode/test/config/config.test.ts @@ -2079,6 +2079,34 @@ describe("OPENCODE_DISABLE_PROJECT_CONFIG", () => { }) }) +// Regression for #28206: malformed OPENCODE_PERMISSION JSON used to crash +// the app on startup with an unhandled SyntaxError. Loading the config with +// an invalid JSON value in this env var should not throw. +describe("OPENCODE_PERMISSION env var", () => { + test("does not crash when OPENCODE_PERMISSION contains invalid JSON", async () => { + const original = process.env["OPENCODE_PERMISSION"] + process.env["OPENCODE_PERMISSION"] = "{invalid" + try { + await using tmp = await tmpdir() + await withTestInstance({ + directory: tmp.path, + fn: async (ctx) => { + const config = await load(ctx) + // We don't assert on permission shape; the regression is that + // load() throws before returning anything. + expect(config).toBeDefined() + }, + }) + } finally { + if (original !== undefined) { + process.env["OPENCODE_PERMISSION"] = original + } else { + delete process.env["OPENCODE_PERMISSION"] + } + } + }) +}) + describe("OPENCODE_CONFIG_CONTENT token substitution", () => { test("substitutes {env:} tokens in OPENCODE_CONFIG_CONTENT", async () => { const originalEnv = process.env["OPENCODE_CONFIG_CONTENT"]