diff --git a/bin/lib/onboard.js b/bin/lib/onboard.js index 2070408b50..b2b75797fd 100644 --- a/bin/lib/onboard.js +++ b/bin/lib/onboard.js @@ -5,7 +5,7 @@ const fs = require("fs"); const path = require("path"); -const { ROOT, SCRIPTS, run, runCapture } = require("./runner"); +const { ROOT, SCRIPTS, run, runCapture, validateName } = require("./runner"); const { prompt, ensureApiKey, getCredential } = require("./credentials"); const registry = require("./registry"); const nim = require("./nim"); @@ -125,6 +125,7 @@ async function createSandbox(gpu) { const nameAnswer = await prompt(" Sandbox name [my-assistant]: "); const sandboxName = nameAnswer || "my-assistant"; + validateName(sandboxName); // Check if sandbox already exists in registry const existing = registry.getSandbox(sandboxName); diff --git a/bin/lib/runner.js b/bin/lib/runner.js index 57bccf6a6a..77302b6c20 100644 --- a/bin/lib/runner.js +++ b/bin/lib/runner.js @@ -45,4 +45,17 @@ function runCapture(cmd, opts = {}) { } } -module.exports = { ROOT, SCRIPTS, run, runCapture }; +/** + * Validate a sandbox or instance name to prevent shell injection. + * Names must be 1–63 chars, alphanumeric with hyphens and underscores, + * and must start with a letter or digit. + */ +function validateName(name) { + if (!name || !/^[a-zA-Z0-9][a-zA-Z0-9._-]{0,62}$/.test(name)) { + console.error(` Invalid name: '${String(name).slice(0, 40)}'`); + console.error(" Names must be 1–63 characters: letters, digits, hyphens, underscores, dots."); + process.exit(1); + } +} + +module.exports = { ROOT, SCRIPTS, run, runCapture, validateName }; diff --git a/bin/nemoclaw.js b/bin/nemoclaw.js index a8a31188a1..1b14ec2194 100755 --- a/bin/nemoclaw.js +++ b/bin/nemoclaw.js @@ -7,7 +7,7 @@ const path = require("path"); const fs = require("fs"); const os = require("os"); -const { ROOT, SCRIPTS, run, runCapture } = require("./lib/runner"); +const { ROOT, SCRIPTS, run, runCapture, validateName } = require("./lib/runner"); const { ensureApiKey, ensureGithubToken, @@ -57,6 +57,7 @@ async function deploy(instanceName) { console.error(" nemoclaw deploy nemoclaw-test"); process.exit(1); } + validateName(instanceName); await ensureApiKey(); if (isRepoPrivate("NVIDIA/OpenShell")) { await ensureGithubToken(); @@ -329,6 +330,7 @@ const [cmd, ...args] = process.argv.slice(2); } // Sandbox-scoped commands: nemoclaw + validateName(cmd); const sandbox = registry.getSandbox(cmd); if (sandbox) { const action = args[0] || "connect";