From 08137e37edcc54a9149c8735af52b0aab920bfe4 Mon Sep 17 00:00:00 2001 From: Devin Rousso Date: Tue, 21 Jul 2026 09:43:44 -0600 Subject: [PATCH] fix(mcp): report malformed JSON config instead of falling back to INI `loadConfig` fell back to INI parsing on any JSON error, so a typo'd JSON config (e.g. a trailing comma) was silently accepted as INI: the intended settings were dropped for the defaults and the raw JSON kept as an unused top-level key surface the original `SyntaxError` when the input is a JSON object (`{`) while keeping the INI fallback (including files whose first line is a `[section]` header) and BOM handling --- .../playwright-core/src/tools/mcp/config.ts | 12 +++++++++--- tests/mcp/config-resolve.spec.ts | 19 +++++++++++++++++++ 2 files changed, 28 insertions(+), 3 deletions(-) diff --git a/packages/playwright-core/src/tools/mcp/config.ts b/packages/playwright-core/src/tools/mcp/config.ts index 47c8c40335f5e..fd03dc661c201 100644 --- a/packages/playwright-core/src/tools/mcp/config.ts +++ b/packages/playwright-core/src/tools/mcp/config.ts @@ -447,10 +447,16 @@ export async function loadConfig(configFile: string | undefined): Promise { expect(config.browser.launchOptions.channel).toBeUndefined(); }); + test('malformed JSON config throws instead of falling back to INI', { + annotation: { type: 'issue', description: 'https://github.com/microsoft/playwright/issues/41893' }, + }, async ({}, testInfo) => { + const configFile = testInfo.outputPath('config.json'); + // Trailing comma makes this invalid JSON; it must not be silently parsed as INI. + await fs.promises.writeFile(configFile, '{ "browser": { "browserName": "firefox", } }'); + await expect(resolveCLIConfigForMCP({ config: configFile }, emptyEnv)).rejects.toThrow(); + }); + + test('INI config starting with a section header still parses', { + annotation: { type: 'issue', description: 'https://github.com/microsoft/playwright/issues/41893' }, + }, async ({}, testInfo) => { + const configFile = testInfo.outputPath('config.cfg'); + // A leading `[section]` is INI, not JSON — it must not be treated as malformed JSON. + await fs.promises.writeFile(configFile, '[browser]\nbrowserName = firefox\n'); + const config = await resolveCLIConfigForMCP({ config: configFile }, emptyEnv); + expect(config.browser.browserName).toBe('firefox'); + }); + test('config file browserName + channel are both preserved', async ({}, testInfo) => { const configFile = testInfo.outputPath('config.json'); const fileConfig: Config = {