Skip to content

fix: replace predictable temp filenames with mkdtempSync#1105

Open
BenediktSchackenberg wants to merge 3 commits intoNVIDIA:mainfrom
BenediktSchackenberg:fix/predictable-temp-filenames
Open

fix: replace predictable temp filenames with mkdtempSync#1105
BenediktSchackenberg wants to merge 3 commits intoNVIDIA:mainfrom
BenediktSchackenberg:fix/predictable-temp-filenames

Conversation

@BenediktSchackenberg
Copy link
Copy Markdown
Contributor

@BenediktSchackenberg BenediktSchackenberg commented Mar 30, 2026

Summary

Six functions in bin/lib/onboard.js create temporary files using Date.now() and Math.random().toString(36), both of which are predictable. A local attacker can win a TOCTOU race to pre-create a symlink at the predicted path in /tmp, enabling:

  1. Data exfiltration — redirect curl output (API responses with model data) to an attacker-controlled location
  2. Script injection — via writeSandboxConfigSyncFile, inject a malicious script that gets piped into openshell sandbox connect

Changes

bin/lib/onboard.js:

  • Added secureTempFile(prefix, ext) helper that uses fs.mkdtempSync() (OS-level mkdtemp syscall with cryptographically random suffix)
  • Replaced all 6 predictable temp file constructions:
    • probeOpenAiLikeEndpoint (line ~667)
    • probeAnthropicEndpoint (line ~711)
    • fetchNvidiaEndpointModels (line ~857)
    • fetchOpenAiLikeModels (line ~911)
    • fetchAnthropicModels (line ~947)
    • writeSandboxConfigSyncFile (line ~527)

test/onboard.test.js:

  • Updated the sync file test to validate the new mkdtemp-based path structure instead of the old predictable pattern

Why this approach

The file already uses fs.mkdtempSync() securely in two other places (lines 1781 and 2710), making this fix consistent with existing code patterns rather than introducing new dependencies.

The remaining Date.now() usage in patchStagedDockerfile is a build identifier written into the Dockerfile, not a temp filename — left unchanged.

Fixes #1093

Summary by CodeRabbit

  • Refactor

    • Improved temporary-file handling: sandbox sync scripts and model request payloads are now written to uniquely named secure temp files inside isolated temp subdirectories, with safer directory-level cleanup to reduce leftover artifacts.
  • Tests

    • Tests updated to validate new temp-file placement and naming pattern, exact script contents, and tightened directory cleanup checks.

Copilot AI review requested due to automatic review settings March 30, 2026 17:04
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Mar 30, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 3c2e4a8c-3775-495a-8d0f-637551eac0f7

📥 Commits

Reviewing files that changed from the base of the PR and between 9b7898d and f54ad44.

📒 Files selected for processing (2)
  • bin/lib/onboard.js
  • test/onboard.test.js
🚧 Files skipped from review as they are similar to previous changes (1)
  • bin/lib/onboard.js

📝 Walkthrough

Walkthrough

Replaced predictable temp filenames with a secure helper that creates an mkdtemp-scoped temp dir and returns a unique temp file path. Updated probe/model download helpers and sandbox sync writer to use the helper and to clean up only the mkdtemp-created parent directory. Removed caller-controlled tmpDir from sandbox writer.

Changes

Cohort / File(s) Summary
Onboard library
bin/lib/onboard.js
Added secureTempFile(prefix, ext) and cleanupTempDir(filePath, expectedPrefix); replaced Date.now/Math.random temp filenames across probeOpenAiLikeEndpoint, probeAnthropicEndpoint, fetchNvidiaEndpointModels, fetchOpenAiLikeModels, fetchAnthropicModels, and writeSandboxConfigSyncFile to use secureTempFile(...); changed cleanup to remove the mkdtemp-created parent dir; changed writeSandboxConfigSyncFile(script, tmpDir = os.tmpdir())writeSandboxConfigSyncFile(script).
Tests
test/onboard.test.js
Updated test to call writeSandboxConfigSyncFile("echo test") without passing tmpDir; assert filename matches nemoclaw-sync.*\.sh, verify exact file contents, check parent dir is mkdtemp-created (not os.tmpdir()), and conditionally clean up the mkdtemp parent directory.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Poem

🐇 I hid a script in a burrow, snug and deep,
No stamp of time or random seed to keep.
With mkdtemp I nibble, neat and quick,
No symlink traps, no sneaky trick.
Hooray — safe tunnels for code to sleep. 🥕

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 25.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'fix: replace predictable temp filenames with mkdtempSync' accurately describes the main change: replacing Date.now()+Math.random temp filename construction with mkdtempSync-based secure temp paths.
Linked Issues check ✅ Passed The PR fully addresses #1093 by replacing all six predictable temp filename constructions with secureTempFile() using mkdtempSync, adding cleanupTempDir() guardrails, and updating tests to validate the new path structure.
Out of Scope Changes check ✅ Passed All changes are directly scoped to #1093: secureTempFile() and cleanupTempDir() helpers support the six targeted functions, test updates validate the mkdtemp-based structure, and Date.now() in patchStagedDockerfile is intentionally left unchanged.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR addresses a local symlink/TOCTOU risk caused by predictable temporary filenames in onboarding probe/model-fetch helpers by switching to fs.mkdtempSync()-backed temp paths.

Changes:

  • Added a secureTempFile(prefix, ext) helper to generate temp file paths inside mkdtemp-created directories.
  • Replaced 6 predictable /tmp/...Date.now()+Math.random() temp filename constructions with secureTempFile(...).
  • Updated the sandbox sync script test to assert the new mkdtemp-based path shape and to clean up the created temp directory.

Reviewed changes

Copilot reviewed 1 out of 2 changed files in this pull request and generated no comments.

File Description
bin/lib/onboard.js Introduces secureTempFile and migrates probe/model-fetch/sync-script temp paths away from predictable names.
test/onboard.test.js Adjusts the sandbox sync temp-file test for mkdtemp-based paths and cleans up the temp directory.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@bin/lib/onboard.js`:
- Around line 539-543: The temp directories created by secureTempFile() are
never removed; modify writeSandboxConfigSyncFile (and all places that delete the
returned temp file) to remove the temp file's parent directory instead of only
unlinking the file. Specifically: when cleaning up values returned by
secureTempFile() (e.g., the path produced by writeSandboxConfigSyncFile and
other secureTempFile callers), compute the parent directory
(path.dirname(returnedPath)) and remove that directory (recursively/forcibly)
rather than just fs.unlink/fs.unlinkSync on the file; update all cleanup sites
that currently unlink the file to remove the parent dir to avoid leaving empty
temp dirs.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: ed611c39-c8ec-4bbc-a2aa-ea9a3e893649

📥 Commits

Reviewing files that changed from the base of the PR and between dae494f and 3ef6fbc.

📒 Files selected for processing (2)
  • bin/lib/onboard.js
  • test/onboard.test.js

@wscurran wscurran added security Something isn't secure priority: high Important issue that should be resolved in the next release fix labels Mar 30, 2026
@wscurran
Copy link
Copy Markdown
Contributor

✨ Thanks for submitting this PR with a detailed summary, it addresses a security bug with predictable temp filenames and proposes a fix to improve the security of NemoClaw, which could prevent potential vulnerabilities.

Six functions in onboard.js created temporary files using Date.now() and
Math.random().toString(36), both of which are predictable. A local
attacker could win a race to pre-create a symlink at the predicted path
in /tmp, redirecting curl output (API responses, model data) or
injecting a malicious script via writeSandboxConfigSyncFile.

Replace all six call sites with a secureTempFile() helper that uses
fs.mkdtempSync() — the same approach already used securely at lines
1781 and 2710. Each temp file now lives inside its own directory with
an OS-level cryptographically random suffix.

The remaining Date.now() usage in patchStagedDockerfile is a build
identifier written into the Dockerfile, not a temp filename — left
as-is.

Fixes: NVIDIA#1093
@BenediktSchackenberg BenediktSchackenberg force-pushed the fix/predictable-temp-filenames branch from 4884e10 to 9b7898d Compare March 30, 2026 20:01
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@test/onboard.test.js`:
- Line 442: The cleanup call unconditionally calls
fs.rmSync(path.dirname(scriptFile), { recursive: true, force: true }) which can
delete the system temp root if writeSandboxConfigSyncFile regresses; before
calling fs.rmSync, add a strict guard that ensures the target dir is a
subdirectory of os.tmpdir() (e.g., compute tempRoot = os.tmpdir(), targetDir =
path.resolve(path.dirname(scriptFile)), then verify targetDir !== tempRoot and
path.relative(tempRoot, targetDir) does not start with '..' and does start with
the path.sep or a non-empty relative path) and only then perform fs.rmSync;
reference path.dirname(scriptFile), writeSandboxConfigSyncFile, fs.rmSync and
os.tmpdir() when locating the code to change.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: dc7963be-a3e1-484e-81dc-0ee47bbe65a4

📥 Commits

Reviewing files that changed from the base of the PR and between 4884e10 and 9b7898d.

📒 Files selected for processing (2)
  • bin/lib/onboard.js
  • test/onboard.test.js
🚧 Files skipped from review as they are similar to previous changes (1)
  • bin/lib/onboard.js

BenediktSchackenberg and others added 2 commits March 30, 2026 20:07
Add cleanupTempDir() helper that verifies the parent directory was
actually created by secureTempFile() (checks prefix match and is not
os.tmpdir() itself) before calling fs.rmSync recursive. This prevents
a regression in secureTempFile from accidentally wiping the system
temp root.

Addresses CodeRabbit review on test and production cleanup sites.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

fix priority: high Important issue that should be resolved in the next release security Something isn't secure

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[NemoClaw] Predictable temp filenames in onboard probe functions allow symlink attacks

3 participants