mirror of
https://github.com/OpenRouterTeam/spawn.git
synced 2026-05-20 18:00:23 +00:00
fix(security): add cmd validation to Sprite runSprite() and runSpriteSilent() (#2904)
Mirrors the guard already in interactiveSession() and all other clouds. Null bytes in cmd could truncate commands at the C level. Fixes #2903 Agent: security-auditor Co-authored-by: B <6723574+louisgv@users.noreply.github.com> Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
5392ff2d7a
commit
97b6424ebe
2 changed files with 20 additions and 0 deletions
|
|
@ -488,6 +488,20 @@ describe("sprite/destroyServer", () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// ─── runSprite validation ────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
describe("sprite/runSprite validation", () => {
|
||||||
|
it("rejects empty command", async () => {
|
||||||
|
const { runSprite } = await import("../sprite/sprite");
|
||||||
|
await expect(runSprite("")).rejects.toThrow("Invalid command");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("rejects null byte in command", async () => {
|
||||||
|
const { runSprite } = await import("../sprite/sprite");
|
||||||
|
await expect(runSprite("echo\x00hello")).rejects.toThrow("Invalid command");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
// ─── runSprite ───────────────────────────────────────────────────────────────
|
// ─── runSprite ───────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
describe("sprite/runSprite", () => {
|
describe("sprite/runSprite", () => {
|
||||||
|
|
|
||||||
|
|
@ -478,6 +478,9 @@ export function getVmConnection(): VMConnection {
|
||||||
* Run a command on the remote sprite. Retries on transient errors.
|
* Run a command on the remote sprite. Retries on transient errors.
|
||||||
*/
|
*/
|
||||||
export async function runSprite(cmd: string, timeoutSecs?: number): Promise<void> {
|
export async function runSprite(cmd: string, timeoutSecs?: number): Promise<void> {
|
||||||
|
if (!cmd || /\0/.test(cmd)) {
|
||||||
|
throw new Error("Invalid command: must be non-empty and must not contain null bytes");
|
||||||
|
}
|
||||||
const spriteCmd = getSpriteCmd()!;
|
const spriteCmd = getSpriteCmd()!;
|
||||||
await spriteRetry("sprite exec", async () => {
|
await spriteRetry("sprite exec", async () => {
|
||||||
const proc = Bun.spawn(
|
const proc = Bun.spawn(
|
||||||
|
|
@ -515,6 +518,9 @@ export async function runSprite(cmd: string, timeoutSecs?: number): Promise<void
|
||||||
|
|
||||||
/** Run a command silently (no stdout/stderr). Throws on failure. */
|
/** Run a command silently (no stdout/stderr). Throws on failure. */
|
||||||
async function runSpriteSilent(cmd: string): Promise<void> {
|
async function runSpriteSilent(cmd: string): Promise<void> {
|
||||||
|
if (!cmd || /\0/.test(cmd)) {
|
||||||
|
throw new Error("Invalid command: must be non-empty and must not contain null bytes");
|
||||||
|
}
|
||||||
const spriteCmd = getSpriteCmd()!;
|
const spriteCmd = getSpriteCmd()!;
|
||||||
const proc = Bun.spawn(
|
const proc = Bun.spawn(
|
||||||
[
|
[
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue