Skip to content
5 changes: 3 additions & 2 deletions server/container-computer.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ import {
perBotLocalVmTarget,
podmanSecurityIsHardened,
setupCommands,
shellWord,
workspaceSources,
type CommandRunner,
type LocalVmTarget,
Expand Down Expand Up @@ -1450,14 +1451,14 @@ describe("setupCommands", () => {
expect(command).toContain(`--label ${WORKSPACE_LABEL}=1`);
expect(command).toContain(`--hostname ${CONTAINER}`);
expect(command).toContain(
`--mount type=bind,source=${VM_WORKSPACE_DIR},target=${VM_WORKSPACE_GUEST}`,
`--mount ${shellWord(`type=bind,source=${VM_WORKSPACE_DIR},target=${VM_WORKSPACE_GUEST}`)}`,
);
});

it("asks rootless Podman to map and privately relabel the durable workspace", () => {
const command = setupCommands("podman", "linux").run!;
expect(command).toContain(
`--mount type=bind,source=${VM_WORKSPACE_DIR},target=${VM_WORKSPACE_GUEST},relabel=private,U=true`,
`--mount ${shellWord(`type=bind,source=${VM_WORKSPACE_DIR},target=${VM_WORKSPACE_GUEST},relabel=private,U=true`)}`,
);
Comment thread
kody-ai[bot] marked this conversation as resolved.
Resolved
});

Expand Down
17 changes: 10 additions & 7 deletions server/container-computer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1995,6 +1995,16 @@ export function containerComputerMcp(
};
}

/** Display text a person may paste into a shell. Any word that is not a bare
* shell token (letters, digits, and a small punctuation set) is single-quoted
* so whitespace, `!`, or a Windows path separator cannot be split or
* history-expanded when the string is pasted into cmd or bash. */
export function shellWord(word: string): string {
return /^[A-Za-z0-9_@%+=:,./-]+$/.test(word)
? word
: `'${word.replace(/'/g, `'\\''`)}'`;
Comment thread
kody-ai[bot] marked this conversation as resolved.
Resolved
Comment thread
swu-gb-deployer[bot] marked this conversation as resolved.
Resolved
}

/** Commands shown as a transparent fallback. Normal setup builds the pinned
* derivative through the API, so users do not need to author a Dockerfile. */
export function setupCommands(
Expand Down Expand Up @@ -2031,13 +2041,6 @@ export function setupCommands(
view: target.viewerPort ? `http://127.0.0.1:${target.viewerPort}/vnc.html` : "",
};
}
// Display text a person may paste into a shell, so an argument with whitespace
// or a shell metacharacter (the git credential helper, `!gh auth git-credential`)
// is single-quoted rather than left to be split or history-expanded. A
// backslash or `~` alone is left bare: those are ordinary in a Windows path,
// which has always been printed as-is here.
const shellWord = (word: string) =>
/[\s!"'`$&;|<>()*?{}#]/.test(word) ? `'${word.replace(/'/g, `'\\''`)}'` : word;
const command = (args: string[]) => [runtime, ...args].map(shellWord).join(" ");
return {
install,
Expand Down
Loading