-
Notifications
You must be signed in to change notification settings - Fork 2.5k
fix(cli): avoid headless browser open crashes #4716
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 1 commit
890bfbf
84fdcca
357000c
d148e8a
72b35ef
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -4,13 +4,13 @@ | |||||||||||||||||||||||||||
| * SPDX-License-Identifier: Apache-2.0 | ||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| import open from 'open'; | ||||||||||||||||||||||||||||
| import process from 'node:process'; | ||||||||||||||||||||||||||||
| import { | ||||||||||||||||||||||||||||
| type CommandContext, | ||||||||||||||||||||||||||||
| type SlashCommand, | ||||||||||||||||||||||||||||
| CommandKind, | ||||||||||||||||||||||||||||
| } from './types.js'; | ||||||||||||||||||||||||||||
| import { openBrowserSecurely } from '@qwen-code/qwen-code-core'; | ||||||||||||||||||||||||||||
| import { MessageType } from '../types.js'; | ||||||||||||||||||||||||||||
| import { t, getCurrentLanguage } from '../../i18n/index.js'; | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
|
|
@@ -57,7 +57,7 @@ export const docsCommand: SlashCommand = { | |||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||
| Date.now(), | ||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||
| await open(docsUrl); | ||||||||||||||||||||||||||||
| await openBrowserSecurely(docsUrl); | ||||||||||||||||||||||||||||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [Suggestion] Unlike
Suggested change
— qwen3.7-max via Qwen Code /review |
||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
| return; | ||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -10,14 +10,15 @@ import { MessageType } from '../types.js'; | |
| import type { HistoryItemInsightProgress } from '../types.js'; | ||
| import { t } from '../../i18n/index.js'; | ||
| import { join } from 'path'; | ||
| import { pathToFileURL } from 'node:url'; | ||
| import { StaticInsightGenerator } from '../../services/insight/generators/StaticInsightGenerator.js'; | ||
| import { | ||
| createDebugLogger, | ||
| encodeInsightProgressMessage, | ||
| encodeInsightReadyMessage, | ||
| openBrowserSecurely, | ||
| Storage, | ||
| } from '@qwen-code/qwen-code-core'; | ||
| import open from 'open'; | ||
|
|
||
| const logger = createDebugLogger('DataProcessor'); | ||
|
|
||
|
|
@@ -215,18 +216,20 @@ export const insightCommand: SlashCommand = { | |
| Date.now(), | ||
| ); | ||
|
|
||
| try { | ||
| await open(outputPath); | ||
| context.ui.addItem( | ||
| { | ||
| type: MessageType.INFO, | ||
| text: t('Opening insights in your browser: {{path}}', { | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [Suggestion] Moving the "Opening insights" message before the Consider either:
— qwen3.7-max via Qwen Code /review |
||
| path: outputPath, | ||
| }), | ||
| }, | ||
| Date.now(), | ||
| ); | ||
|
|
||
| context.ui.addItem( | ||
| { | ||
| type: MessageType.INFO, | ||
| text: t('Opening insights in your browser: {{path}}', { | ||
| path: outputPath, | ||
| }), | ||
| }, | ||
| Date.now(), | ||
| ); | ||
| try { | ||
| await openBrowserSecurely(pathToFileURL(outputPath).href, { | ||
| allowFile: true, | ||
| }); | ||
| } catch (browserError) { | ||
| logger.error('Failed to open browser automatically:', browserError); | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -23,12 +23,14 @@ describe('secure-browser-launcher', () => { | |
| vi.clearAllMocks(); | ||
| mockExecFile.mockResolvedValue({ stdout: '', stderr: '' }); | ||
| originalPlatform = Object.getOwnPropertyDescriptor(process, 'platform'); | ||
| vi.stubEnv('BROWSER', ''); | ||
| }); | ||
|
|
||
| afterEach(() => { | ||
| if (originalPlatform) { | ||
| Object.defineProperty(process, 'platform', originalPlatform); | ||
| } | ||
| vi.unstubAllEnvs(); | ||
| }); | ||
|
|
||
| function setPlatform(platform: string) { | ||
|
|
@@ -71,6 +73,24 @@ describe('secure-browser-launcher', () => { | |
| ); | ||
| }); | ||
|
|
||
| it('should allow file URLs only when explicitly requested', async () => { | ||
| setPlatform('linux'); | ||
|
|
||
| await expect(openBrowserSecurely('file:///tmp/report.html')).rejects.toThrow( | ||
| 'Unsafe protocol', | ||
| ); | ||
|
|
||
| await openBrowserSecurely('file:///tmp/report.html', { | ||
| allowFile: true, | ||
| }); | ||
|
|
||
| expect(mockExecFile).toHaveBeenCalledWith( | ||
| 'xdg-open', | ||
| ['file:///tmp/report.html'], | ||
| expect.any(Object), | ||
| ); | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [Suggestion] Several test scenarios for the new
Adding these would strengthen coverage of the new code paths. — qwen3.7-max via Qwen Code /review |
||
| }); | ||
|
|
||
| it('should reject invalid URLs', async () => { | ||
| await expect(openBrowserSecurely('not-a-url')).rejects.toThrow( | ||
| 'Invalid URL', | ||
|
|
@@ -198,6 +218,19 @@ describe('secure-browser-launcher', () => { | |
| 'Unsupported platform', | ||
| ); | ||
| }); | ||
|
|
||
| it('should prefer BROWSER when it is configured', async () => { | ||
| setPlatform('linux'); | ||
| vi.stubEnv('BROWSER', 'firefox --new-tab'); | ||
|
|
||
| await openBrowserSecurely('https://example.com'); | ||
|
|
||
| expect(mockExecFile).toHaveBeenCalledWith( | ||
| 'firefox', | ||
| ['--new-tab', 'https://example.com'], | ||
| expect.any(Object), | ||
| ); | ||
| }); | ||
| }); | ||
|
|
||
| describe('Error handling', () => { | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[Suggestion]
docsCommandandextensionsCommandboth have tests for whenopenBrowserSecurelythrows (e.g.,mockOpenBrowserSecurely.mockRejectedValue(...)), butbugCommandonly tests the success path. The catch block inbugCommand.ts(lines 60–68) that adds an ERROR-type history item is dead code from a testing perspective.Consider adding:
— qwen3.7-max via Qwen Code /review