Skip to content

fix(security): harden temporary file creation - #5517

Merged
cv merged 1 commit into
mainfrom
fix/secure-tmp-file-creation
Jun 16, 2026
Merged

fix(security): harden temporary file creation#5517
cv merged 1 commit into
mainfrom
fix/secure-tmp-file-creation

Conversation

@cv

@cv cv commented Jun 16, 2026

Copy link
Copy Markdown
Collaborator

Summary

This PR hardens a focused set of insecure temporary file creation paths without changing their runtime contracts. Predictable host SSH config temp files now live in mkdtemp-backed private directories, and the listed /tmp log, marker, and PID creation paths in nemoclaw-start.sh use same-directory temp files plus atomic rename while preserving final paths, ownership/modes, and best-effort semantics.

Changes

  • Added a shared createTempSshConfig() helper that writes SSH configs as ssh_config inside private mkdtemp directories and cleans the directory in finally paths.
  • Replaced predictable pid/time SSH config paths in version.ts, process-recovery.ts, state/sandbox.ts, and both skill-install.ts call sites.
  • Hardened nemoclaw-start.sh startup log, gateway/auto-pair log creation, health marker, and gateway PID writes with safe temp+rename creation while keeping the stable /tmp final paths.
  • Added regression coverage for temp SSH config cleanup, process recovery SSH execution, skill install/remove cleanup, safe start-script temp creation modes, marker/PID behavior, and early start-log capture.
  • Split new start-script coverage into a focused test file and ratcheted the legacy test/nemoclaw-start.test.ts size budget downward.

Type of Change

  • Code change (feature, bug fix, or refactor)
  • Code change with doc updates
  • Doc only (prose changes, no code sample modifications)
  • Doc only (includes code sample changes)

Verification

  • Git hooks passed during commit and push, or npx prek run --from-ref main --to-ref HEAD passes
  • Targeted tests pass for changed behavior
  • Full npm test passes (broad runtime changes only)
  • Tests added or updated for new or changed behavior
  • No secrets, API keys, or credentials committed
  • Docs updated for user-facing behavior changes
  • npm run docs builds without warnings (doc changes only)
  • Doc pages follow the style guide (doc changes only)
  • New doc pages include SPDX header and frontmatter (new pages only)

Commands run:

  • npx vitest run --project cli src/lib/sandbox/temp-ssh-config.test.ts src/lib/sandbox/version.test.ts src/lib/actions/sandbox/process-recovery-temp-ssh.test.ts src/lib/actions/sandbox/skill-install.test.ts test/nemoclaw-start.test.ts test/nemoclaw-start-safe-tmp.test.ts test/nemoclaw-start-gateway-marker.test.ts test/gateway-pid-recording.test.ts test/seccomp-guard.test.ts — passed, 9 files / 164 tests.
  • npm run typecheck:cli — passed.
  • bash -n scripts/nemoclaw-start.sh — passed.
  • npm run source-shape:check — passed.
  • npm run test-size:check — passed.
  • npx biome check --write ... on changed TS/test files — passed with no fixes.
  • git diff --cached --check — passed.
  • Commit and push hooks passed, including shfmt, ShellCheck, Test (CLI), source-shape/test-size ratchets, commitlint, and pre-push TypeScript (CLI).

Docs assessment: no user-facing behavior or documented command/runtime path changed; stable /tmp paths remain the same, so no docs update was needed.


Signed-off-by: Carlos Villela cvillela@nvidia.com

Summary by CodeRabbit

  • Bug Fixes

    • Improved temporary file creation reliability through atomic operations and enhanced permission handling to prevent partially-initialized files during failures.
    • Enhanced SSH configuration management with safer temporary file operations and centralized cleanup logic.
  • Tests

    • Added comprehensive test coverage for temporary SSH configuration handling, recovery flows, and atomic file creation operations.

Signed-off-by: Carlos Villela <cvillela@nvidia.com>
@cv cv added security area: security Security controls, permissions, secrets, or hardening labels Jun 16, 2026
@cv cv self-assigned this Jun 16, 2026
@coderabbitai

coderabbitai Bot commented Jun 16, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

Two parallel safe temp-file abstractions are introduced: a TypeScript createTempSshConfig helper (mkdtemp-backed, 0o600 permissions, cleanup callback) and Bash _nemoclaw_safe_create/replace_tmp_file helpers (atomic rename, controlled ownership/mode). All existing callers in TypeScript (version.ts, skill-install.ts, process-recovery.ts, sandbox.ts) and shell (nemoclaw-start.sh) are migrated to these helpers, with tests added for each.

Changes

TypeScript createTempSshConfig helper and callers

Layer / File(s) Summary
TempSshConfig module: type, implementation, and unit tests
src/lib/sandbox/temp-ssh-config.ts, src/lib/sandbox/temp-ssh-config.test.ts
Defines TempSshConfig type, implements createTempSshConfig with mkdtemp-backed dir, ssh_config written at 0o600, error-path cleanup, and cleanup() callback. Tests cover happy path and write-failure propagation.
probeAgentVersion migration
src/lib/sandbox/version.ts, src/lib/sandbox/version.test.ts
Removes manual fs/os/path temp-file code; imports and uses createTempSshConfig for ssh -F arg and finally cleanup. Test extended to assert config dir naming pattern and post-probe removal.
skill-install.ts migration and cleanup assertions
src/lib/actions/sandbox/skill-install.ts, src/lib/actions/sandbox/skill-install.test.ts
removeSandboxSkill and installSandboxSkill replaced with createTempSshConfig-based temp config and cleanup(). Test adds expectTempSshConfigCleanedUp helper used in remove and install assertions.
executeSandboxCommand migration
src/lib/actions/sandbox/process-recovery.ts, src/lib/actions/sandbox/process-recovery-temp-ssh.test.ts
Manual temp-file creation replaced with createTempSshConfig; finally uses cleanup(). New test file mocks SSH config capture and spawnSync, asserts temp dir creation/cleanup and null return on capture failure.
sandbox.ts backup/restore migration
src/lib/state/sandbox.ts
Removes local writeTempSshConfig helper; both backupSandboxState and restoreSandboxState use createTempSshConfig with cleanup() in finally.

Shell _nemoclaw_safe_create/replace_tmp_file helpers and tests

Layer / File(s) Summary
Shell helper functions and call-site migrations
scripts/nemoclaw-start.sh
Adds _nemoclaw_safe_replace_tmp_file and _nemoclaw_safe_create_tmp_file. Updates _START_LOG setup, mark_in_container_gateway, record_gateway_pid, and both non-root and root log-file initialization to use these helpers.
Shell helper tests and harness stubs
test/nemoclaw-start-safe-tmp.test.ts, test/gateway-pid-recording.test.ts, test/nemoclaw-start-gateway-marker.test.ts, test/nemoclaw-start.test.ts, ci/test-file-size-budget.json
New nemoclaw-start-safe-tmp.test.ts verifies permission bits, file content, and .tmp. cleanup. Existing gateway-pid and marker tests inject extracted safeTmpHelpers and assert 600 modes. nemoclaw-start.test.ts replaces dynamic extraction with inline stubs. Budget updated.

Sequence Diagram(s)

sequenceDiagram
  participant Caller as Caller (version/skill-install/process-recovery/sandbox)
  participant createTempSshConfig
  participant fs as node:fs
  participant ssh as spawnSync("ssh")

  Caller->>createTempSshConfig: createTempSshConfig(contents, prefix)
  createTempSshConfig->>fs: mkdtempSync(tmpdir/prefix)
  createTempSshConfig->>fs: writeFileSync(dir/ssh_config, contents, mode 0o600)
  createTempSshConfig-->>Caller: {dir, file, cleanup()}
  Caller->>ssh: spawnSync("ssh", ["-F", file, ...])
  ssh-->>Caller: result
  Caller->>createTempSshConfig: cleanup()
  createTempSshConfig->>fs: rmSync(dir, {recursive, force})
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • NVIDIA/NemoClaw#5116: Directly related — that PR introduced mark_in_container_gateway behavior and healthcheck marker tests that this PR now upgrades to use the new safe temp-file helpers.
  • NVIDIA/NemoClaw#5432: Overlaps in sandbox state flows — that PR modified src/lib/actions/sandbox/snapshot.ts restore/backup paths that share the same SSH config temp-file pattern being refactored here.

Suggested labels

bug-fix, v0.0.66

Suggested reviewers

  • ericksoa

Poem

🐇 Hop, hop, no more scattered chmod and chown,
A safe little helper now wears the crown.
mkdtemp burrows deep, permissions just right,
cleanup() sweeps the warren tidy and tight.
Shell and TypeScript both thumping in sync —
No leftover .tmp. files, not even a blink! 🌿

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 52.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 clearly describes the main security improvement: hardening temporary file creation. This accurately reflects the core changes across SSH config helpers and shell script utilities.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/secure-tmp-file-creation

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

@github-code-quality

github-code-quality Bot commented Jun 16, 2026

Copy link
Copy Markdown
Contributor

Code Coverage Overview

Languages: TypeScript

TypeScript / code-coverage/plugin

The overall coverage in the fix/secure-tmp-file-... branch is 96%. Coverage data for the main branch is not yet available.

Show a code coverage summary of the most covered files.
File main fix/secure-tmp-file-... 3cca854 +/-
nemoclaw/src/se...cret-scanner.ts 100%
nemoclaw/src/commands/slash.ts 100%
nemoclaw/src/li...bprocess-env.ts 100%
nemoclaw/src/bl...eprint/state.ts 98%
nemoclaw/src/onboard/config.ts 98%
nemoclaw/src/bl...int/snapshot.ts 97%
nemoclaw/src/bl...print/runner.ts 95%
nemoclaw/src/co...ration-state.ts 94%
nemoclaw/src/bl...ate-networks.ts 94%
nemoclaw/src/index.ts 94%

TypeScript / code-coverage/cli

The overall coverage in the fix/secure-tmp-file-... branch is 46%. Coverage data for the main branch is not yet available.

Show a code coverage summary of the most covered files.
File main fix/secure-tmp-file-... 3cca854 +/-
src/lib/state/o...oard-session.ts 90%
src/lib/inference/local.ts 76%
src/lib/sandbox/config.ts 72%
src/lib/actions...dbox/rebuild.ts 67%
src/lib/onboard/preflight.ts 64%
src/lib/actions...licy-channel.ts 56%
src/lib/state/sandbox.ts 55%
src/lib/policy/index.ts 49%
src/lib/onboard...er-gpu-patch.ts 44%
src/lib/onboard.ts 18%

Updated June 16, 2026 16:27 UTC
Code Coverage is in Public Preview. Learn more and provide us with your feedback.

@cv cv added the v0.0.66 label Jun 16, 2026
Comment thread test/nemoclaw-start-safe-tmp.test.ts Dismissed
@github-actions

Copy link
Copy Markdown
Contributor

PR Review Advisor

Findings: 0 needs attention, 0 worth checking, 0 nice ideas

Consider writing more tests for
  • **Runtime validation** — backupSandboxState removes the mkdtemp SSH config directory when the remote directory existence probe returns non-zero. Focused unit and shell-behavior tests cover the changed helper and most call sites. Because this PR touches bootstrap and sandbox runtime paths, targeted runtime/integration validation would further reduce risk, but no blocker was found.
  • **Runtime validation** — restoreSandboxState removes the mkdtemp SSH config directory when pre-restore cleanup fails. Focused unit and shell-behavior tests cover the changed helper and most call sites. Because this PR touches bootstrap and sandbox runtime paths, targeted runtime/integration validation would further reduce risk, but no blocker was found.
  • **Runtime validation** — createTempSshConfig creates its private directory with owner-only permissions. Focused unit and shell-behavior tests cover the changed helper and most call sites. Because this PR touches bootstrap and sandbox runtime paths, targeted runtime/integration validation would further reduce risk, but no blocker was found.
  • **Runtime validation** — nemoclaw-start safe tmp helper replaces a pre-existing symlink path without writing through the symlink. Focused unit and shell-behavior tests cover the changed helper and most call sites. Because this PR touches bootstrap and sandbox runtime paths, targeted runtime/integration validation would further reduce risk, but no blocker was found.

Workflow run details

This is an automated advisory review. A human maintainer must make the final merge decision.

@github-actions

Copy link
Copy Markdown
Contributor

E2E Advisor Recommendation

Required E2E: issue-2478-crash-loop-recovery-e2e, snapshot-commands-e2e, upgrade-stale-sandbox-e2e
Optional E2E: sandbox-operations-e2e, state-backup-restore-e2e, openclaw-skill-cli-e2e

Dispatch hint: issue-2478-crash-loop-recovery-e2e,snapshot-commands-e2e,upgrade-stale-sandbox-e2e

Auto-dispatched E2E: issue-2478-crash-loop-recovery-e2e via nightly-e2e.yaml at 3cca854ae1dc486bd05fac68b1d34b13da681336nightly run

Workflow run

Full advisor summary

E2E Recommendation Advisor

Base: origin/main
Head: HEAD
Confidence: high

Required E2E

  • issue-2478-crash-loop-recovery-e2e (high): Required because the PR changes sandbox entrypoint log/PID/marker file creation and the process-recovery SSH execution path. This E2E kills and recovers the OpenClaw gateway through the real nemoclaw <name> connect --probe-only path and inspects /tmp/gateway.log and recovery behavior inside a live sandbox.
  • snapshot-commands-e2e (medium): Required because src/lib/state/sandbox.ts changed the SSH config lifecycle used by state backup/restore. This job exercises nemoclaw <name> snapshot create/list/restore against a live OpenClaw sandbox and verifies restored state and credential-safe snapshot behavior.
  • upgrade-stale-sandbox-e2e (high): Required because src/lib/sandbox/version.ts changed live version probing via SSH, which drives stale sandbox detection and rebuild upgrade flows. This job validates stale detection, rebuild, and post-upgrade version checks in a real sandbox.

Optional E2E

  • sandbox-operations-e2e (high): Useful broad confidence for adjacent sandbox lifecycle behavior: status, logs, multi-sandbox operation, SSH execution, and gateway recovery. It overlaps with the required crash-loop recovery test but gives wider operator-flow coverage.
  • state-backup-restore-e2e (high): Optional adjacent coverage for backup/restore behavior with SSH-based sandbox file transfer. Snapshot commands are more directly tied to src/lib/state/sandbox.ts, but this catches broader workspace backup/restore regressions.
  • openclaw-skill-cli-e2e (medium): Adjacent skill confidence only: it verifies OpenClaw skill installation/listing inside a live sandbox, but it does not directly exercise the changed nemoclaw <sandbox> skill install/remove orchestration path.

New E2E recommendations

  • sandbox skill deployment (high): The changed src/lib/actions/sandbox/skill-install.ts path appears to lack a direct live E2E that runs nemoclaw <sandbox> skill install <path> and nemoclaw <sandbox> skill remove <name> against a real sandbox, verifies files/mirrors/session refresh, and confirms the temporary SSH config directory is cleaned up.
    • Suggested test: Add a live E2E for nemoclaw <sandbox> skill install/remove using a minimal SKILL.md fixture in an onboarded OpenClaw sandbox.
  • sandbox entrypoint safe temporary file replacement (medium): Unit tests cover extracted helpers, but there is no focused live E2E that starts a real sandbox and asserts /tmp/nemoclaw-start.log, /tmp/gateway.log, /tmp/auto-pair.log, /tmp/nemoclaw-gateway-local, and /tmp/nemoclaw-gateway.pid permissions/ownership after entrypoint startup in root and non-root modes.
    • Suggested test: Add an entrypoint health artifact E2E that inspects the real sandbox container's startup log, gateway marker, gateway PID, and log file permissions after onboard.

Dispatch hint

  • Workflow: .github/workflows/nightly-e2e.yaml
  • jobs input: issue-2478-crash-loop-recovery-e2e,snapshot-commands-e2e,upgrade-stale-sandbox-e2e

@github-actions

Copy link
Copy Markdown
Contributor

Vitest E2E Scenario Recommendation

Required Vitest E2E scenarios: ubuntu-repo-cloud-openclaw, issue-2478-crash-loop-recovery-vitest, sandbox-rebuild-vitest
Optional Vitest E2E scenarios: sandbox-survival-vitest, skill-agent-vitest

Dispatch required Vitest E2E scenarios:

  • gh workflow run e2e-vitest-scenarios.yaml --ref <pr-head-ref> --field scenarios=ubuntu-repo-cloud-openclaw
  • gh workflow run e2e-vitest-scenarios.yaml --ref <pr-head-ref> --field jobs=issue-2478-crash-loop-recovery-vitest
  • gh workflow run e2e-vitest-scenarios.yaml --ref <pr-head-ref> --field jobs=sandbox-rebuild-vitest

Workflow run

Full Vitest E2E advisor summary

Vitest E2E Scenario Advisor

Base: origin/main
Head: HEAD
Confidence: medium

Required Vitest E2E scenarios

  • ubuntu-repo-cloud-openclaw: Baseline live OpenClaw Docker onboarding exercises the changed sandbox entrypoint path, gateway startup/log setup, marker/PID file creation, and steady-state gateway/sandbox validation.
    • Dispatch: gh workflow run e2e-vitest-scenarios.yaml --ref <pr-head-ref> --field scenarios=ubuntu-repo-cloud-openclaw
  • issue-2478-crash-loop-recovery-vitest: The PR changes gateway PID/marker temp-file writes in nemoclaw-start and the sandbox process-recovery SSH execution helper; this free-standing live job exercises real gateway crash/recovery via the production connect/probe path.
    • Dispatch: gh workflow run e2e-vitest-scenarios.yaml --ref <pr-head-ref> --field jobs=issue-2478-crash-loop-recovery-vitest
  • sandbox-rebuild-vitest: The PR changes rebuild state backup/restore SSH config handling and sandbox version probing; this live job runs a real nemoclaw <sandbox> rebuild --yes, verifies state preservation, registry version refresh, and rebuild backup hygiene.
    • Dispatch: gh workflow run e2e-vitest-scenarios.yaml --ref <pr-head-ref> --field jobs=sandbox-rebuild-vitest

Optional Vitest E2E scenarios

  • sandbox-survival-vitest: Adjacent coverage for gateway restart, registry/list/status, sandbox exec/SSH access, durable state markers, and live inference after restart; useful extra signal for the entrypoint and temp SSH config changes.
    • Dispatch: gh workflow run e2e-vitest-scenarios.yaml --ref <pr-head-ref> --field jobs=sandbox-survival-vitest
  • skill-agent-vitest: Adjacent skill-surface coverage for injecting a skill into a real OpenClaw sandbox and verifying agent consumption. The changed NemoClaw skill action temp SSH helper is not directly covered by a typed live scenario, so this is supporting signal rather than primary proof.
    • Dispatch: gh workflow run e2e-vitest-scenarios.yaml --ref <pr-head-ref> --field jobs=skill-agent-vitest

Relevant changed files

  • scripts/nemoclaw-start.sh
  • src/lib/actions/sandbox/process-recovery.ts
  • src/lib/actions/sandbox/skill-install.ts
  • src/lib/sandbox/temp-ssh-config.ts
  • src/lib/sandbox/version.ts
  • src/lib/state/sandbox.ts

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
src/lib/state/sandbox.ts (1)

1133-1135: Run the state lifecycle E2Es for this migration path before merge.

Because both backup and restore now rely on the shared temp SSH config helper, validate end-to-end persistence behavior with:
gh workflow run nightly-e2e.yaml --ref <branch> -f jobs=state-backup-restore-e2e,snapshot-commands-e2e,rebuild-openclaw-e2e

As per coding guidelines, src/lib/state/sandbox.ts changes affect sandbox persistence lifecycle operations and should run those targeted E2E jobs.

Also applies to: 1463-1465

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/lib/state/sandbox.ts` around lines 1133 - 1135, Before merging this PR,
you must validate the sandbox persistence lifecycle by running the specified E2E
test jobs. Execute the GitHub workflow command with the provided parameters: gh
workflow run nightly-e2e.yaml --ref <your-branch-name> -f
jobs=state-backup-restore-e2e,snapshot-commands-e2e,rebuild-openclaw-e2e. This
validates the end-to-end behavior of the backup and restore operations that now
share the createTempSshConfig helper function used at lines 1133-1135 and the
locations noted at lines 1463-1465. Ensure all three E2E job runs complete
successfully before proceeding with the merge.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/lib/sandbox/version.ts`:
- Around line 66-67: The createTempSshConfig call in the probeAgentVersion
function is positioned outside the try block, which means any failures during
temp SSH config creation will throw an error instead of being caught and
returning null as the function's contract specifies. Move the
createTempSshConfig invocation inside the try block so that any failures during
temporary directory or file creation are properly caught and handled by the
existing catch block that returns null.

---

Nitpick comments:
In `@src/lib/state/sandbox.ts`:
- Around line 1133-1135: Before merging this PR, you must validate the sandbox
persistence lifecycle by running the specified E2E test jobs. Execute the GitHub
workflow command with the provided parameters: gh workflow run nightly-e2e.yaml
--ref <your-branch-name> -f
jobs=state-backup-restore-e2e,snapshot-commands-e2e,rebuild-openclaw-e2e. This
validates the end-to-end behavior of the backup and restore operations that now
share the createTempSshConfig helper function used at lines 1133-1135 and the
locations noted at lines 1463-1465. Ensure all three E2E job runs complete
successfully before proceeding with the merge.
🪄 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: Enterprise

Run ID: 864df673-2a5d-486f-9967-8ee44a8320fb

📥 Commits

Reviewing files that changed from the base of the PR and between 6c0fb04 and 3cca854.

📒 Files selected for processing (15)
  • ci/test-file-size-budget.json
  • scripts/nemoclaw-start.sh
  • src/lib/actions/sandbox/process-recovery-temp-ssh.test.ts
  • src/lib/actions/sandbox/process-recovery.ts
  • src/lib/actions/sandbox/skill-install.test.ts
  • src/lib/actions/sandbox/skill-install.ts
  • src/lib/sandbox/temp-ssh-config.test.ts
  • src/lib/sandbox/temp-ssh-config.ts
  • src/lib/sandbox/version.test.ts
  • src/lib/sandbox/version.ts
  • src/lib/state/sandbox.ts
  • test/gateway-pid-recording.test.ts
  • test/nemoclaw-start-gateway-marker.test.ts
  • test/nemoclaw-start-safe-tmp.test.ts
  • test/nemoclaw-start.test.ts

Comment on lines +66 to 67
const tmpSshConfig = createTempSshConfig(sshConfigResult.output, "nemoclaw-ver-");
try {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Preserve probeAgentVersion failure contract for temp-config creation errors.

Line 66 creates the temp SSH config outside the try. If temp dir/file creation fails, this path throws instead of returning null, which breaks the function’s “null on failure” behavior.

Suggested fix
 export function probeAgentVersion(sandboxName: string): string | null {
@@
-  const tmpSshConfig = createTempSshConfig(sshConfigResult.output, "nemoclaw-ver-");
+  let tmpSshConfig: ReturnType<typeof createTempSshConfig> | null = null;
   try {
+    tmpSshConfig = createTempSshConfig(sshConfigResult.output, "nemoclaw-ver-");
     const result = spawnSync(
       "ssh",
@@
   } catch {
     return null;
   } finally {
-    tmpSshConfig.cleanup();
+    tmpSshConfig?.cleanup();
   }
 }

Also applies to: 88-92

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/lib/sandbox/version.ts` around lines 66 - 67, The createTempSshConfig
call in the probeAgentVersion function is positioned outside the try block,
which means any failures during temp SSH config creation will throw an error
instead of being caught and returning null as the function's contract specifies.
Move the createTempSshConfig invocation inside the try block so that any
failures during temporary directory or file creation are properly caught and
handled by the existing catch block that returns null.

@github-actions

Copy link
Copy Markdown
Contributor

Selective E2E Results — ✅ All requested jobs passed

Run: 27632502585
Target ref: 3cca854ae1dc486bd05fac68b1d34b13da681336
Workflow ref: main
Requested jobs: issue-2478-crash-loop-recovery-e2e
Summary: 1 passed, 0 failed, 0 cancelled, 0 skipped

Job Result
issue-2478-crash-loop-recovery-e2e ✅ success

@cv
cv merged commit ca43cb0 into main Jun 16, 2026
47 checks passed
@cv
cv deleted the fix/secure-tmp-file-creation branch June 16, 2026 17:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area: security Security controls, permissions, secrets, or hardening

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants