fix(config): refuse partial JSONC parses that wipe opencode.jsonc

jsonc-parser was returning truncated trees (often only `$schema`) when
configs contained JSON5-style unquoted keys. Config mutations then backed
up and overwrote the full file with that stub. Check parse errors on read
and refuse to overwrite unparseable files on write in web and VS Code.

Fixes #2923

Co-authored-by: serkraser <serkraser@gmail.com>
This commit is contained in:
Cursor Agent
2026-08-15 04:25:40 +00:00
co-authored by serkraser
parent 6b1e677aaf
commit 4cc090130c
8 changed files with 312 additions and 6 deletions
@@ -0,0 +1,96 @@
import { afterEach, beforeEach, describe, test } from 'node:test';
import assert from 'node:assert/strict';
import fs from 'node:fs';
import os from 'node:os';
import path from 'node:path';
import { updateMcpConfig } from './opencodeConfig';
const PARTIAL_PARSE_CONFIG = [
'{',
' "$schema": "https://opencode.ai/config.json",',
' plugin: ["opencode-see-image"],',
' mcp: {',
' openproject: {',
' type: "remote",',
' url: "https://openproject.example.com/mcp",',
' enabled: true',
' }',
' },',
' provider: {',
' "ollama-cloud": {',
' npm: "@ai-sdk/openai-compatible",',
' name: "Ollama Cloud"',
' }',
' }',
'}',
'',
].join('\n');
const VALID_CONFIG = [
'{',
' "$schema": "https://opencode.ai/config.json",',
' "plugin": ["opencode-see-image"],',
' "mcp": {',
' "openproject": {',
' "type": "remote",',
' "url": "https://openproject.example.com/mcp",',
' "enabled": true',
' }',
' },',
' "provider": {',
' "ollama-cloud": {',
' "npm": "@ai-sdk/openai-compatible",',
' "name": "Ollama Cloud"',
' }',
' }',
'}',
'',
].join('\n');
describe('opencodeConfig JSONC parse safety (issue #2923)', () => {
let tempDir: string;
let previousOpenCodeConfig: string | undefined;
beforeEach(() => {
tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'openchamber-vscode-config-parse-'));
previousOpenCodeConfig = process.env.OPENCODE_CONFIG;
});
afterEach(() => {
if (previousOpenCodeConfig === undefined) delete process.env.OPENCODE_CONFIG;
else process.env.OPENCODE_CONFIG = previousOpenCodeConfig;
fs.rmSync(tempDir, { recursive: true, force: true });
});
test('refuses MCP updates that would overwrite a partial-parse config', () => {
const configPath = path.join(tempDir, 'opencode.jsonc');
fs.writeFileSync(configPath, PARTIAL_PARSE_CONFIG, 'utf8');
process.env.OPENCODE_CONFIG = configPath;
assert.throws(
() => updateMcpConfig('openproject', { enabled: true }),
(error: unknown) => (
error instanceof Error
&& /cannot be loaded safely/.test(error.message)
&& (error as Error & { code?: string }).code === 'INVALID_JSONC'
),
);
assert.equal(fs.readFileSync(configPath, 'utf8'), PARTIAL_PARSE_CONFIG);
assert.equal(fs.existsSync(`${configPath}.openchamber.backup`), false);
});
test('preserves unrelated keys when updating a valid MCP config', () => {
const configPath = path.join(tempDir, 'opencode.jsonc');
fs.writeFileSync(configPath, VALID_CONFIG, 'utf8');
process.env.OPENCODE_CONFIG = configPath;
updateMcpConfig('openproject', { enabled: false });
const rewritten = JSON.parse(fs.readFileSync(configPath, 'utf8'));
assert.deepEqual(rewritten.plugin, ['opencode-see-image']);
assert.equal(rewritten.provider['ollama-cloud'].name, 'Ollama Cloud');
assert.equal(rewritten.mcp.openproject.enabled, false);
assert.equal(fs.readFileSync(`${configPath}.openchamber.backup`, 'utf8'), VALID_CONFIG);
});
});