From bc814c4b627ce6d9d80190cd2d269a69cd1c7b9f Mon Sep 17 00:00:00 2001 From: Philipp Thoss Date: Thu, 19 Mar 2026 07:43:28 +0100 Subject: [PATCH] security(onboard): hide API credentials from process argument list During openshell provider create, the NVIDIA API key was interpolated directly into shell command strings, making it visible via ps aux, /proc/PID/cmdline, and process monitoring tools on shared systems. Fixes: - bin/lib/onboard.js: Write credential to temp file (mode 0600, exclusive create) and use $(cat ...) substitution so the key only exists in bash's memory during expansion, not in the parent's command string. File is cleaned up in a finally block. - scripts/setup.sh: Same temp file + cat pattern with trap EXIT cleanup. - bin/nemoclaw.js: Use sudo --preserve-env instead of interpolating the key into the command string. - nemoclaw/src/commands/onboard.ts: Added comment noting the remaining /proc exposure requires openshell --credential-file or --credential-from-env support (see PR #191). The credential still appears briefly in the openshell child process arguments. Full mitigation requires openshell to support reading credentials from a file or environment variable rather than CLI args. Fixes #325 Co-Authored-By: Claude Opus 4.6 (1M context) --- bin/lib/onboard.js | 22 +++++++++++++++------- bin/nemoclaw.js | 4 +++- scripts/setup.sh | 9 ++++++++- 3 files changed, 26 insertions(+), 9 deletions(-) diff --git a/bin/lib/onboard.js b/bin/lib/onboard.js index 252a303c8..e3bafc196 100644 --- a/bin/lib/onboard.js +++ b/bin/lib/onboard.js @@ -8,6 +8,7 @@ const fs = require("fs"); const os = require("os"); const path = require("path"); +const crypto = require("crypto"); const { spawn, spawnSync } = require("child_process"); const { ROOT, SCRIPTS, run, runCapture, shellQuote } = require("./runner"); const { @@ -805,13 +806,20 @@ async function setupInference(sandboxName, model, provider) { step(5, 7, "Setting up inference provider"); if (provider === "nvidia-nim") { - // Create nvidia-nim provider - run( - `openshell provider create --name nvidia-nim --type openai ` + - `--credential ${shellQuote("NVIDIA_API_KEY=" + process.env.NVIDIA_API_KEY)} ` + - `--config "OPENAI_BASE_URL=https://integrate.api.nvidia.com/v1" 2>&1 || true`, - { ignoreError: true } - ); + // Create nvidia-nim provider — credential written to temp file to avoid + // exposing the API key in process arguments visible via ps/proc (#325) + const credPath = path.join(os.tmpdir(), `nemoclaw-cred-${crypto.randomBytes(8).toString("hex")}`); + fs.writeFileSync(credPath, process.env.NVIDIA_API_KEY, { mode: 0o600, flag: "wx" }); + try { + run( + `openshell provider create --name nvidia-nim --type openai ` + + `--credential "NVIDIA_API_KEY=$(cat ${shellQuote(credPath)})" ` + + `--config "OPENAI_BASE_URL=https://integrate.api.nvidia.com/v1" 2>&1 || true`, + { ignoreError: true } + ); + } finally { + try { fs.unlinkSync(credPath); } catch {} + } run( `openshell inference set --no-verify --provider nvidia-nim --model ${shellQuote(model)} 2>/dev/null || true`, { ignoreError: true } diff --git a/bin/nemoclaw.js b/bin/nemoclaw.js index 2010cfeb2..51b68f9b4 100755 --- a/bin/nemoclaw.js +++ b/bin/nemoclaw.js @@ -97,7 +97,9 @@ async function setup() { async function setupSpark() { await ensureApiKey(); - run(`sudo -E NVIDIA_API_KEY=${shellQuote(process.env.NVIDIA_API_KEY)} bash "${SCRIPTS}/setup-spark.sh"`); + // Pass API key via environment, not command string, to avoid exposing it + // in process arguments visible via ps/proc (#325) + run(`sudo --preserve-env=NVIDIA_API_KEY bash "${SCRIPTS}/setup-spark.sh"`); } async function deploy(instanceName) { diff --git a/scripts/setup.sh b/scripts/setup.sh index 22b3ccfec..5a7b5c320 100755 --- a/scripts/setup.sh +++ b/scripts/setup.sh @@ -129,11 +129,18 @@ fi # 3. Providers info "Setting up inference providers..." +# Write API key to temp file to avoid exposing it in process arguments +# visible via ps/proc (#325). The file is cleaned up on exit. +_CRED_FILE=$(mktemp /tmp/nemoclaw-cred-XXXXXX) +chmod 600 "$_CRED_FILE" +printf '%s' "$NVIDIA_API_KEY" > "$_CRED_FILE" +trap 'rm -f "$_CRED_FILE"' EXIT + # nvidia-nim (build.nvidia.com) upsert_provider \ "nvidia-nim" \ "openai" \ - "NVIDIA_API_KEY=$NVIDIA_API_KEY" \ + "NVIDIA_API_KEY=$(cat "$_CRED_FILE")" \ "OPENAI_BASE_URL=https://integrate.api.nvidia.com/v1" # vllm-local (if vLLM is installed or running)