Skip to content

Commit 1158c9b

Browse files
committed
fix: address code review findings on security PR
- Fix setupSpark(): pass NVIDIA_API_KEY via env option, not shell string - Remove dead sandboxEnv variable in createSandbox(), clarify comment on openshell env subcommand protocol - Add assertSafeName() at dispatch entry before registry lookup, blocking metacharacters even for unregistered sandbox names - Validate NEMOCLAW_GPU with dedicated regex allowing colons/dots, instead of stripping chars before assertSafeName - Remove no-op local `execSync("echo ok")` from SSH wait loop - Convert waitForNimHealth() curl to runCaptureArgv for consistency
1 parent 380ad4a commit 1158c9b

3 files changed

Lines changed: 17 additions & 13 deletions

File tree

bin/lib/nim.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,7 @@ function waitForNimHealth(port = 8000, timeout = 300) {
155155

156156
while ((Date.now() - start) / 1000 < timeout) {
157157
try {
158-
const result = runCapture(`curl -sf http://localhost:${port}/v1/models`, {
158+
const result = runCaptureArgv("curl", ["-sf", `http://localhost:${port}/v1/models`], {
159159
ignoreError: true,
160160
});
161161
if (result) {

bin/lib/onboard.js

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -190,11 +190,10 @@ async function createSandbox(gpu) {
190190

191191
console.log(` Creating sandbox '${sandboxName}' (this takes a few minutes on first run)...`);
192192
const chatUiUrl = process.env.CHAT_UI_URL || 'http://127.0.0.1:18789';
193-
// Pass secrets via environment, not command-line arguments
194-
const sandboxEnv = { CHAT_UI_URL: chatUiUrl };
195-
if (process.env.NVIDIA_API_KEY) {
196-
sandboxEnv.NVIDIA_API_KEY = process.env.NVIDIA_API_KEY;
197-
}
193+
// Note: openshell sandbox create uses `-- env K=V` to set env vars inside
194+
// the sandbox. These appear in the process argument list but are required by
195+
// the openshell CLI protocol. The values are validated (chatUiUrl is a URL,
196+
// NVIDIA_API_KEY starts with "nvapi-") and passed via argv (no shell expansion).
198197
createArgv.push("--", "env",
199198
`CHAT_UI_URL=${chatUiUrl}`,
200199
...(process.env.NVIDIA_API_KEY ? [`NVIDIA_API_KEY=${process.env.NVIDIA_API_KEY}`] : []),

bin/nemoclaw.js

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,9 @@ async function setup() {
4444

4545
async function setupSpark() {
4646
await ensureApiKey();
47-
run(`sudo -E NVIDIA_API_KEY="${process.env.NVIDIA_API_KEY}" bash "${SCRIPTS}/setup-spark.sh"`);
47+
runArgv("sudo", ["-E", "bash", `${SCRIPTS}/setup-spark.sh`], {
48+
env: { ...process.env, NVIDIA_API_KEY: process.env.NVIDIA_API_KEY },
49+
});
4850
}
4951

5052
async function deploy(instanceName) {
@@ -64,7 +66,10 @@ async function deploy(instanceName) {
6466
}
6567
const name = instanceName;
6668
const gpu = process.env.NEMOCLAW_GPU || "a2-highgpu-1g:nvidia-tesla-a100:1";
67-
assertSafeName(gpu.replace(/[:.]/g, "-"), "NEMOCLAW_GPU");
69+
if (!/^[a-zA-Z0-9][a-zA-Z0-9_.:-]{0,127}$/.test(gpu)) {
70+
console.error(` Invalid NEMOCLAW_GPU: "${gpu}". Only alphanumerics, hyphens, underscores, dots, and colons are allowed.`);
71+
process.exit(1);
72+
}
6873

6974
console.log("");
7075
console.log(` Deploying NemoClaw to Brev instance: ${name}`);
@@ -96,11 +101,8 @@ async function deploy(instanceName) {
96101

97102
console.log(" Waiting for SSH...");
98103
for (let i = 0; i < 60; i++) {
99-
try {
100-
execSync("echo ok", { encoding: "utf-8", stdio: "pipe", timeout: 10000 });
101-
const r = spawnSync("ssh", [...sshOpts, name, "echo ok"], { encoding: "utf-8", stdio: "pipe", timeout: 10000 });
102-
if (r.status === 0) break;
103-
} catch {}
104+
const r = spawnSync("ssh", [...sshOpts, name, "echo ok"], { encoding: "utf-8", stdio: "pipe", timeout: 10000 });
105+
if (r.status === 0) break;
104106
if (i === 59) {
105107
console.error(` Timed out waiting for SSH to ${name}`);
106108
process.exit(1);
@@ -345,6 +347,9 @@ const [cmd, ...args] = process.argv.slice(2);
345347
}
346348

347349
// Sandbox-scoped commands: nemoclaw <name> <action>
350+
// Validate the name early — before any lookup or use — to block
351+
// shell metacharacters regardless of whether the sandbox exists.
352+
assertSafeName(cmd, "sandbox name");
348353
const sandbox = registry.getSandbox(cmd);
349354
if (sandbox) {
350355
const action = args[0] || "connect";

0 commit comments

Comments
 (0)