mirror of
https://github.com/stablyai/orca.git
synced 2026-10-12 08:02:37 +00:00
Three fixes, all on paths this PR could not exercise locally. The managed hook command was stored as a bare path. jcode tokenizes that string shell-style before exec'ing it directly (parse_hook_command, crates/jcode-terminal-launch/src/lib.rs): unquoted whitespace splits, and every unquoted backslash is consumed as an escape. So on Windows `C:\Users\me\.orca\agent-hooks\jcode-hook.cmd` reached exec as `C:Usersme.orcaagent-hooksjcode-hook.cmd` and no hook fired at all, and a POSIX home with a space split into two arguments. Store the path single-quoted (verbatim, backslashes included), falling back to double quotes for a path containing a single quote. Existing bare entries are already repointed by the stale-key path, and getStatus accepts both forms so the repair is not reported as a user-owned hook. The quoting helper was previously dead code that only tests called; the three production sites now use it. isJcodeManagedCommand also normalizes separators, since a `/`-only needle never matched a Windows entry. Commit-message generation feeds a staged patch to `jcode run` as the prompt — attacker-influenced text — while jcode's default profile exposes shell, read, write, and MCP. Pass `--tool-profile none`, which resolves to an empty allowed-tool set in jcode's config (base_allowed_tools), matching the read-only posture claude (plan) and codex (read-only) already take. docs/reference/jcode-hook-events.md was never actually in this PR: the repo ignores docs/** and tracks reference docs by allow-list only, so the captured-payload evidence four source comments point at was silently dropped. Allow-list it. Co-authored-by: czzczz <chanzrz_zbf@foxmail.com>
82 lines
2.6 KiB
TypeScript
82 lines
2.6 KiB
TypeScript
import { describe, expect, it } from 'vitest'
|
|
import { getJcodeManagedCommand, isJcodeManagedCommand } from './hook-settings'
|
|
|
|
/**
|
|
* jcode tokenizes a configured hook command shell-style before executing it directly
|
|
* (`parse_hook_command`, crates/jcode-terminal-launch/src/lib.rs). These cases mirror
|
|
* that tokenizer: unquoted whitespace splits, unquoted `\` is an escape, single quotes
|
|
* are verbatim, and inside double quotes only `\` and `"` are escapes.
|
|
*/
|
|
function tokenizeLikeJcode(raw: string): string[] {
|
|
const parts: string[] = []
|
|
let current = ''
|
|
let quote: string | null = null
|
|
let escaped = false
|
|
let started = false
|
|
for (const char of raw) {
|
|
if (escaped) {
|
|
current += char
|
|
started = true
|
|
escaped = false
|
|
} else if (quote) {
|
|
if (char === quote) {
|
|
quote = null
|
|
} else if (char === '\\' && quote === '"') {
|
|
escaped = true
|
|
} else {
|
|
current += char
|
|
started = true
|
|
}
|
|
} else if (char === '\\') {
|
|
escaped = true
|
|
started = true
|
|
} else if (char === "'" || char === '"') {
|
|
quote = char
|
|
started = true
|
|
} else if (/\s/.test(char)) {
|
|
if (started) {
|
|
parts.push(current)
|
|
current = ''
|
|
started = false
|
|
}
|
|
} else {
|
|
current += char
|
|
started = true
|
|
}
|
|
}
|
|
if (started) {
|
|
parts.push(current)
|
|
}
|
|
return parts
|
|
}
|
|
|
|
describe('jcode managed hook command quoting', () => {
|
|
const paths = [
|
|
'C:\\Users\\me\\.orca\\agent-hooks\\jcode-hook.cmd',
|
|
'C:\\Users\\First Last\\.orca\\agent-hooks\\jcode-hook.cmd',
|
|
'/home/me/.orca/agent-hooks/jcode-hook.sh',
|
|
'/Users/First Last/.orca/agent-hooks/jcode-hook.sh',
|
|
"/Users/o'brien/.orca/agent-hooks/jcode-hook.sh",
|
|
'/Users/quote"odd/.orca/agent-hooks/jcode-hook.sh'
|
|
]
|
|
|
|
it.each(paths)('survives jcode\u2019s tokenizer as one argument: %s', (scriptPath) => {
|
|
expect(tokenizeLikeJcode(getJcodeManagedCommand(scriptPath))).toEqual([scriptPath])
|
|
})
|
|
|
|
it('is what a bare path fails to do, which is why the quoting exists', () => {
|
|
// Regression anchor: the unquoted Windows path loses every separator, so jcode
|
|
// execs `C:Usersme.orcaagent-hooksjcode-hook.cmd` and no hook ever fires.
|
|
expect(tokenizeLikeJcode('C:\\Users\\me\\.orca\\agent-hooks\\jcode-hook.cmd')).toEqual([
|
|
'C:Usersme.orcaagent-hooksjcode-hook.cmd'
|
|
])
|
|
})
|
|
|
|
it('still recognizes its own managed entry on either separator', () => {
|
|
for (const scriptPath of paths) {
|
|
expect(isJcodeManagedCommand(getJcodeManagedCommand(scriptPath))).toBe(true)
|
|
}
|
|
expect(isJcodeManagedCommand("'/home/me/.config/my-own-hook.sh'")).toBe(false)
|
|
})
|
|
})
|