mirror of
https://github.com/stablyai/orca.git
synced 2026-10-30 16:47:34 +00:00
* refactor(shell): one shell-argument-quoting module for the duplicated quote copies Each former copy now calls the canonical function with its exact spelling; a table test pins byte-identical output for every copy over hostile inputs. Distinct spellings stay distinct functions: '\'' vs '"'"', the two plain-word sets, and the cmd.exe doubled quote. Co-Authored-By: Claude <noreply@anthropic.com> * style: keep the quoting import at the top of two import-less files Co-Authored-By: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com>
142 lines
4.9 KiB
TypeScript
142 lines
4.9 KiB
TypeScript
import { describe, expect, it } from 'vitest'
|
|
import {
|
|
quoteCmdDoubleQuoted,
|
|
quotePosixShell,
|
|
quotePosixShellDoubleQuotedApostrophe,
|
|
quotePosixShellUnlessPlain,
|
|
quotePosixShellUnlessPlainNoAt
|
|
} from './shell-argument-quoting'
|
|
|
|
// Verbatim bodies of the copies this module replaced, keyed by where each one lived. Every
|
|
// former copy must produce byte-identical output to the function that now serves its callers.
|
|
const sq = (value: string): string => `'${value.replace(/'/g, "'\\''")}'`
|
|
const sqAll = (value: string): string => `'${value.replaceAll("'", "'\\''")}'`
|
|
const plainAt = (value: string): string => (/^[a-zA-Z0-9_./@:-]+$/.test(value) ? value : sq(value))
|
|
const plainNoAt = (value: string): string => (/^[A-Za-z0-9_./:-]+$/.test(value) ? value : sq(value))
|
|
const dq = (value: string): string => `'${value.replaceAll("'", `'"'"'`)}'`
|
|
|
|
const FORMER_COPIES: [string, (value: string) => string, (value: string) => string][] = [
|
|
['main/ssh/ssh-connection-utils shellEscape', sq, quotePosixShell],
|
|
['shared/wsl-login-shell-command quotePosixShell', sq, quotePosixShell],
|
|
['main/agent-hooks/posix-hook-command quotePosixShellString', sqAll, quotePosixShell],
|
|
['main/codex/codex-hook-definition inline', sqAll, quotePosixShell],
|
|
['main/ipc/preflight-command-exec shellQuote', sq, quotePosixShell],
|
|
['main/ipc/preflight-wsl-agent-detection shellQuote', sq, quotePosixShell],
|
|
['main/rate-limits/hidden-rate-limit-shell', sq, quotePosixShell],
|
|
['main/runtime/orchestration/setup-completion-signal quotePosixArg', sq, quotePosixShell],
|
|
['main/ssh/ssh-remote-directory-browse shellEscapeRaw', sq, quotePosixShell],
|
|
['shared/posix-command-path-lookup shellQuote', sq, quotePosixShell],
|
|
['renderer ipynb-kernel-setup-commands POSIX branch', sqAll, quotePosixShell],
|
|
['shared/ephemeral-vm-recipe-process POSIX branch', sq, quotePosixShell],
|
|
['cli/shell-command-quote POSIX tail', sqAll, quotePosixShell],
|
|
['main/external-editor-launch escapePosixPathForShell', plainAt, quotePosixShellUnlessPlain],
|
|
['renderer pane-helpers shellEscapePath POSIX', plainAt, quotePosixShellUnlessPlain],
|
|
['shared/setup-agent-sequencing quotePosixArg', plainNoAt, quotePosixShellUnlessPlainNoAt],
|
|
['shared/setup-runner-command quotePosixArg', plainNoAt, quotePosixShellUnlessPlainNoAt],
|
|
['main/cli/cli-install-path-format quoteShell', dq, quotePosixShellDoubleQuotedApostrophe],
|
|
['main/cli/wsl-cli-scripts quoteShell', dq, quotePosixShellDoubleQuotedApostrophe],
|
|
['main/antigravity/antigravity-browser-helper quote', dq, quotePosixShellDoubleQuotedApostrophe],
|
|
[
|
|
'cli/orchestration-mutation-recovery shellQuote tail',
|
|
dq,
|
|
quotePosixShellDoubleQuotedApostrophe
|
|
],
|
|
[
|
|
'main/linux-package-install-command quoteForPosixShell',
|
|
(value) => `'${value.split("'").join(`'"'"'`)}'`,
|
|
quotePosixShellDoubleQuotedApostrophe
|
|
],
|
|
[
|
|
'shared/ephemeral-vm-recipe-process win32 branch',
|
|
(value) => `"${value.replace(/"/g, '""')}"`,
|
|
quoteCmdDoubleQuoted
|
|
],
|
|
[
|
|
'shared/setup-runner-command quoteWindowsArg',
|
|
(value) => `"${value.replace(/"/g, '""')}"`,
|
|
quoteCmdDoubleQuoted
|
|
],
|
|
[
|
|
'shared/ai-vault-resume-command quoteWindowsCmdArg',
|
|
(value) => `"${value.replace(/"/g, '""')}"`,
|
|
quoteCmdDoubleQuoted
|
|
]
|
|
]
|
|
|
|
const HOSTILE_INPUTS = [
|
|
'',
|
|
"'",
|
|
"''",
|
|
"it's",
|
|
"'; rm -rf /; '",
|
|
'plain',
|
|
'a@b',
|
|
'user@host:/path',
|
|
'/tmp/Orca Setup.deb',
|
|
'$HOME',
|
|
'${PATH}',
|
|
'`id`',
|
|
'$(id)',
|
|
'"quoted"',
|
|
'\\',
|
|
'trailing\\',
|
|
'C:\\Program Files\\Orca\\orca.exe',
|
|
'%PATH%',
|
|
'!x!',
|
|
'^&|<>()',
|
|
'~',
|
|
'~/dir',
|
|
'-n',
|
|
'--flag=value',
|
|
'a b\tc',
|
|
'line1\nline2',
|
|
'crlf\r\n',
|
|
'nul\u0000byte',
|
|
'é☃😀',
|
|
'*?[a]{b,c}',
|
|
'#comment',
|
|
'a;b&&c||d'
|
|
]
|
|
|
|
describe('shell-argument-quoting matches every former copy byte for byte', () => {
|
|
describe.each(FORMER_COPIES)('%s', (_site, former, current) => {
|
|
it.each(HOSTILE_INPUTS)('%j', (input) => {
|
|
expect(current(input)).toBe(former(input))
|
|
})
|
|
})
|
|
})
|
|
|
|
describe('quotePosixShell', () => {
|
|
it('wraps string in single quotes', () => {
|
|
expect(quotePosixShell('hello')).toBe("'hello'")
|
|
})
|
|
|
|
it('escapes embedded single quotes', () => {
|
|
expect(quotePosixShell("it's")).toBe("'it'\\''s'")
|
|
})
|
|
|
|
it('handles empty string', () => {
|
|
expect(quotePosixShell('')).toBe("''")
|
|
})
|
|
|
|
it('handles special characters', () => {
|
|
expect(quotePosixShell('foo bar; rm -rf /')).toBe("'foo bar; rm -rf /'")
|
|
})
|
|
})
|
|
|
|
describe('the spellings stay distinct', () => {
|
|
it('spells an apostrophe two different ways', () => {
|
|
expect(quotePosixShell("it's")).toBe(`'it'\\''s'`)
|
|
expect(quotePosixShellDoubleQuotedApostrophe("it's")).toBe(`'it'"'"'s'`)
|
|
})
|
|
|
|
it('leaves @ bare only in the plain-with-@ variant', () => {
|
|
expect(quotePosixShellUnlessPlain('a@b')).toBe('a@b')
|
|
expect(quotePosixShellUnlessPlainNoAt('a@b')).toBe("'a@b'")
|
|
})
|
|
|
|
it('doubles embedded quotes for cmd.exe', () => {
|
|
expect(quoteCmdDoubleQuoted('say "hi"')).toBe('"say ""hi"""')
|
|
})
|
|
})
|