mirror of
https://github.com/stablyai/orca.git
synced 2026-09-30 16:02:56 +00:00
fix(linux): strip injected Chromium switches from CLI args
This commit is contained in:
@@ -55,6 +55,48 @@ describe('CLI launch redirect: entry-path form', () => {
|
||||
).toEqual(['status'])
|
||||
})
|
||||
|
||||
it('strips injected Chromium switches before node-mode CLI arguments', () => {
|
||||
expect(
|
||||
getCliLaunchArgs(
|
||||
[
|
||||
linux.execPath,
|
||||
linux.cliEntryPath,
|
||||
'--no-sandbox',
|
||||
'--disable-gpu',
|
||||
'--disable-features=Vulkan',
|
||||
'status',
|
||||
'--json'
|
||||
],
|
||||
linux.cliEntryPath,
|
||||
linuxOptions
|
||||
)
|
||||
).toEqual(['status', '--json'])
|
||||
expect(
|
||||
getCliLaunchArgs(
|
||||
[linux.execPath, linux.cliEntryPath, '--disable-features', 'Vulkan', 'skills', 'get'],
|
||||
linux.cliEntryPath,
|
||||
linuxOptions
|
||||
)
|
||||
).toEqual(['skills', 'get'])
|
||||
})
|
||||
|
||||
it('keeps user flags after the command and malformed boolean assignments', () => {
|
||||
expect(
|
||||
getCliLaunchArgs(
|
||||
[linux.execPath, linux.cliEntryPath, 'status', '--disable-features=Vulkan'],
|
||||
linux.cliEntryPath,
|
||||
linuxOptions
|
||||
)
|
||||
).toEqual(['status', '--disable-features=Vulkan'])
|
||||
expect(
|
||||
getCliLaunchArgs(
|
||||
[linux.execPath, linux.cliEntryPath, '--no-sandbox=true', 'status'],
|
||||
linux.cliEntryPath,
|
||||
linuxOptions
|
||||
)
|
||||
).toEqual(['--no-sandbox=true', 'status'])
|
||||
})
|
||||
|
||||
it('does not treat a later positional entrypoint path as the launcher', () => {
|
||||
expect(
|
||||
getCliLaunchArgs(
|
||||
@@ -112,6 +154,13 @@ describe('CLI launch redirect: command form', () => {
|
||||
linuxOptions
|
||||
)
|
||||
).toEqual(['serve', '--help'])
|
||||
expect(
|
||||
getCliLaunchArgs(
|
||||
[linux.execPath, '--disable-features', 'Vulkan', 'serve', '--help'],
|
||||
linux.cliEntryPath,
|
||||
linuxOptions
|
||||
)
|
||||
).toEqual(['serve', '--help'])
|
||||
})
|
||||
|
||||
it('treats help as a CLI launch even without a command', () => {
|
||||
|
||||
@@ -21,6 +21,7 @@ export type CliLaunchRedirectOptions = {
|
||||
|
||||
const CLI_EARLY_EXIT_FLAGS = new Set(['--help', '-h', 'help', '--version', '-v'])
|
||||
const DESKTOP_FLAGS = new Set(['--no-sandbox', '--disable-gpu'])
|
||||
const DESKTOP_VALUE_FLAGS = new Set(['--disable-features'])
|
||||
const CLI_LAUNCH_VALUE_FLAG_NAMES = [...VALUE_TAKING_FLAGS].map((flag) => flag.slice(2))
|
||||
|
||||
// Fence recursion if a wrapper drops ELECTRON_RUN_AS_NODE again.
|
||||
@@ -85,23 +86,22 @@ export function getCliLaunchArgs(
|
||||
if (!options.isPackaged) {
|
||||
return null
|
||||
}
|
||||
return (
|
||||
getEntryPathLaunchArgs(argv, cliEntryPath, options.platform) ??
|
||||
getCommandLaunchArgs(argv, options)
|
||||
)
|
||||
return getEntryPathLaunchArgs(argv, cliEntryPath, options) ?? getCommandLaunchArgs(argv, options)
|
||||
}
|
||||
|
||||
function getEntryPathLaunchArgs(
|
||||
argv: string[],
|
||||
cliEntryPath: string,
|
||||
platform: NodeJS.Platform
|
||||
options: { platform: NodeJS.Platform; commandNames: readonly string[] }
|
||||
): string[] | null {
|
||||
const expectedCliPath = normalizePathForPlatform(cliEntryPath, platform)
|
||||
const expectedCliPath = normalizePathForPlatform(cliEntryPath, options.platform)
|
||||
// The packaged launcher always passes the entrypoint as Electron's first argument.
|
||||
// Matching later positional arguments can mistake a normal desktop launch for the CLI.
|
||||
return argv[1] && normalizePathForPlatform(argv[1], platform) === expectedCliPath
|
||||
? argv.slice(2)
|
||||
: null
|
||||
if (!argv[1] || normalizePathForPlatform(argv[1], options.platform) !== expectedCliPath) {
|
||||
return null
|
||||
}
|
||||
const args = argv.slice(2)
|
||||
return stripDesktopFlags(args, findCommandIndex(args, options.commandNames))
|
||||
}
|
||||
|
||||
function getCommandLaunchArgs(
|
||||
@@ -115,11 +115,8 @@ function getCommandLaunchArgs(
|
||||
if (args.length === 0) {
|
||||
return null
|
||||
}
|
||||
const commandPaths = options.commandNames.map((name) => [name])
|
||||
const commandIndex = findCliCommandIndex(args, commandPaths, CLI_LAUNCH_VALUE_FLAG_NAMES)
|
||||
const cliArgs = args.filter(
|
||||
(arg, index) => (commandIndex !== -1 && index > commandIndex) || !DESKTOP_FLAGS.has(arg)
|
||||
)
|
||||
const commandIndex = findCommandIndex(args, options.commandNames)
|
||||
const cliArgs = stripDesktopFlags(args, commandIndex)
|
||||
const command = commandIndex === -1 ? null : args[commandIndex]
|
||||
// Keep direct serve in-process so signals reach its full child tree.
|
||||
if (command && command !== 'serve') {
|
||||
@@ -128,6 +125,50 @@ function getCommandLaunchArgs(
|
||||
return hasCliEarlyExitArg(args, commandIndex) ? cliArgs : null
|
||||
}
|
||||
|
||||
function findCommandIndex(args: readonly string[], commandNames: readonly string[]): number {
|
||||
return findCliCommandIndex(
|
||||
args,
|
||||
commandNames.map((name) => [name]),
|
||||
CLI_LAUNCH_VALUE_FLAG_NAMES
|
||||
)
|
||||
}
|
||||
|
||||
function stripDesktopFlags(args: readonly string[], commandIndex: number): string[] {
|
||||
const boundary = commandIndex === -1 ? findLeadingFlagBoundary(args) : commandIndex
|
||||
const cliArgs: string[] = []
|
||||
for (let index = 0; index < args.length; index += 1) {
|
||||
const arg = args[index]!
|
||||
if (index < boundary) {
|
||||
if (DESKTOP_FLAGS.has(arg)) {
|
||||
continue
|
||||
}
|
||||
if (DESKTOP_VALUE_FLAGS.has(flagName(arg))) {
|
||||
if (!arg.includes('=') && args[index + 1] && !args[index + 1]!.startsWith('-')) {
|
||||
index += 1
|
||||
}
|
||||
continue
|
||||
}
|
||||
}
|
||||
cliArgs.push(arg)
|
||||
}
|
||||
return cliArgs
|
||||
}
|
||||
|
||||
function findLeadingFlagBoundary(args: readonly string[]): number {
|
||||
let index = 0
|
||||
while (index < args.length) {
|
||||
const token = args[index]!
|
||||
if (token === '--' || !token.startsWith('-')) {
|
||||
return index
|
||||
}
|
||||
index += 1
|
||||
if (takesLaunchValue(token, args[index])) {
|
||||
index += 1
|
||||
}
|
||||
}
|
||||
return index
|
||||
}
|
||||
|
||||
function hasCliEarlyExitArg(args: readonly string[], commandIndex: number): boolean {
|
||||
let index = 0
|
||||
let positionalCount = 0
|
||||
@@ -167,8 +208,13 @@ function takesLaunchValue(token: string, next: string | undefined): boolean {
|
||||
) {
|
||||
return false
|
||||
}
|
||||
const flagName = token.startsWith('--') ? token.slice(2) : token.replace(/^-+/, '')
|
||||
return !CLI_BOOLEAN_FLAGS.has(flagName)
|
||||
const name = flagName(token)
|
||||
return DESKTOP_VALUE_FLAGS.has(name) || !CLI_BOOLEAN_FLAGS.has(name.replace(/^-+/, ''))
|
||||
}
|
||||
|
||||
function flagName(arg: string): string {
|
||||
const equalsIndex = arg.indexOf('=')
|
||||
return equalsIndex === -1 ? arg : arg.slice(0, equalsIndex)
|
||||
}
|
||||
|
||||
function buildPackagedCliEntryPath(platform: NodeJS.Platform, resourcesPath: string): string {
|
||||
|
||||
@@ -25,6 +25,19 @@ describe('serve-mode-argv', () => {
|
||||
expect(findServeSubcommandIndex(['app', '--user-data-dir', '/tmp/x', 'serve'])).toBe(3)
|
||||
})
|
||||
|
||||
it('skips a space-separated Chromium switch value while locating serve', () => {
|
||||
const argv = ['/AppRun', '--disable-features', 'Vulkan', 'serve', '--port', '6768']
|
||||
expect(findServeSubcommandIndex(argv)).toBe(3)
|
||||
expect(normalizeServeModeArgv(argv)).toEqual([
|
||||
'/AppRun',
|
||||
'--disable-features',
|
||||
'Vulkan',
|
||||
'--serve',
|
||||
'--serve-port',
|
||||
'6768'
|
||||
])
|
||||
})
|
||||
|
||||
it('refuses a help launch instead of binding a server', () => {
|
||||
// Why: `--help` is not a serve flag, so it used to be swallowed and the launch bound a
|
||||
// network-exposed runtime server with pairing on. The AppImage redirect routes help to the CLI.
|
||||
|
||||
@@ -24,13 +24,14 @@ const CLI_TO_SERVE_VALUE_FLAG = new Map([
|
||||
/**
|
||||
* Flags that consume the next argv token as a value (CLI-form + Electron passthrough).
|
||||
* Residual class: a flag outside this list whose space-separated value is literally `serve` would
|
||||
* read as the subcommand. Chromium switches are `--flag=value` only, so no real launch does that.
|
||||
* read as the subcommand. Include switches that may arrive in either argv shape.
|
||||
*/
|
||||
export const VALUE_TAKING_FLAGS = new Set([
|
||||
...CLI_TO_SERVE_VALUE_FLAG.keys(),
|
||||
'--serve-port',
|
||||
'--serve-pairing-address',
|
||||
'--serve-project-root',
|
||||
'--disable-features',
|
||||
'--user-data-dir',
|
||||
'--proxy-server',
|
||||
'--environment',
|
||||
|
||||
Reference in New Issue
Block a user