mirror of
https://github.com/anthropic-experimental/sandbox-runtime.git
synced 2026-05-06 13:40:59 +08:00
fix(cli): shell-quote positional args instead of join(" ") (#239)
In positional mode (`srt cmd arg1 arg2`), the argv is reassembled into a
string and run via `bash -c`. Reassembling with `commandArgs.join(" ")`
discards argument boundaries, so any arg containing whitespace or shell
metacharacters is re-tokenised by bash:
srt printf "%s\n" "hello world"
# before: prints "hello\nworld\n" (two args)
# after: prints "hello world\n"
shell-quote is already a dependency (used by the macOS/Linux wrappers).
The -c path is unchanged — it receives a single string and is meant to
be parsed by the shell.
Fixes #157
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
#!/usr/bin/env node
|
||||
import shellquote from 'shell-quote'
|
||||
import { Command } from 'commander'
|
||||
import { SandboxManager } from './index.js'
|
||||
import type { SandboxRuntimeConfig } from './sandbox/sandbox-config.js'
|
||||
@@ -147,8 +148,11 @@ async function main(): Promise<void> {
|
||||
command = options.c
|
||||
logForDebugging(`Command string mode (-c): ${command}`)
|
||||
} else if (commandArgs.length > 0) {
|
||||
// Default mode: simple join
|
||||
command = commandArgs.join(' ')
|
||||
// Default mode: argv-style invocation. The result is later
|
||||
// executed via `bash -c <command>`, so each arg must be
|
||||
// shell-quoted to survive that re-parse — a plain join(' ')
|
||||
// splits arguments containing whitespace (#157).
|
||||
command = shellquote.quote(commandArgs)
|
||||
logForDebugging(`Original command: ${command}`)
|
||||
} else {
|
||||
console.error(
|
||||
|
||||
@@ -98,6 +98,24 @@ describe('CLI', () => {
|
||||
expect(result.stdout).toBe('no newline')
|
||||
expect(result.status).toBe(0)
|
||||
})
|
||||
|
||||
test('preserves argument boundaries when args contain spaces', () => {
|
||||
// Regression for #157: positional args are later run via `bash -c`,
|
||||
// so they must be shell-quoted. printf '%s\n' emits one line per
|
||||
// arg, which exposes whether "hello world" arrived as one token
|
||||
// (correct) or was re-split into "hello" and "world".
|
||||
const result = runCli(['printf', '%s\n', 'hello world', 'a b'])
|
||||
expect(result.stdout).toBe('hello world\na b\n')
|
||||
expect(result.status).toBe(0)
|
||||
})
|
||||
|
||||
test('preserves shell metacharacters in positional args', () => {
|
||||
// Positional mode is argv-style; metacharacters in an arg are data,
|
||||
// not shell syntax (use -c for shell semantics).
|
||||
const result = runCli(['printf', '%s', '$HOME;|&'])
|
||||
expect(result.stdout).toBe('$HOME;|&')
|
||||
expect(result.status).toBe(0)
|
||||
})
|
||||
})
|
||||
|
||||
describe('error handling', () => {
|
||||
|
||||
Reference in New Issue
Block a user