Skip to content

fix(security): make .openclaw symlinks immutable after validation#1137

Merged
cv merged 1 commit intoNVIDIA:mainfrom
latenighthackathon:fix/symlink-immutable-after-validation
Mar 31, 2026
Merged

fix(security): make .openclaw symlinks immutable after validation#1137
cv merged 1 commit intoNVIDIA:mainfrom
latenighthackathon:fix/symlink-immutable-after-validation

Conversation

@latenighthackathon
Copy link
Copy Markdown
Contributor

@latenighthackathon latenighthackathon commented Mar 31, 2026

Summary

The symlink validation loop in nemoclaw-start.sh verifies that all symlinks in /sandbox/.openclaw/ point to their expected /sandbox/.openclaw-data/ targets, but this check runs only once at boot. After validation completes (line 360), the gateway starts on the next line (line 366), leaving a TOCTOU window where symlinks could theoretically be swapped.

This adds defense-in-depth by setting the immutable flag (chattr +i) on both the /sandbox/.openclaw directory and its validated symlinks immediately after the validation loop passes.

Changes

  • Added chattr +i on /sandbox/.openclaw directory and all symlinks after the validation loop
  • Guarded behind command -v chattr check — degrades gracefully if not available
  • All chattr calls use || true to avoid failures on filesystems that don't support immutable flags

Security Analysis

Layer Protection Status
DAC (chmod/chown) Directory owned by root:root, mode 755 Already in place (Dockerfile)
Landlock /sandbox/.openclaw in read-only policy best_effort — not enforced on older kernels
Immutable flag chattr +i prevents modification even by root Added by this PR

The immutable flag cannot be removed by the sandbox user (requires CAP_LINUX_IMMUTABLE), closing the TOCTOU window even if DAC or Landlock are bypassed.

Test Plan

  • Full test suite: 650/653 passed (3 pre-existing failures in service-env.test.js)
  • Verified graceful degradation when chattr is unavailable
  • Confirmed chattr guard (command -v) prevents errors in minimal containers

Closes #1019

Summary by CodeRabbit

  • Chores
    • Added additional sandbox hardening: verification of internal links followed by an optional immutability step to protect those links from runtime modification, improving integrity and reducing risk of unauthorized changes.

Signed-off-by: latenighthackathon latenighthackathon@users.noreply.github.com

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Mar 31, 2026

📝 Walkthrough

Walkthrough

After validating symlinks under /sandbox/.openclaw/ point to /sandbox/.openclaw-data/*, the script optionally runs chattr +i (if available) on /sandbox/.openclaw and each symlink to make them immutable, suppressing chattr errors; this runs only after the validation loop completes and as root.

Changes

Cohort / File(s) Summary
Symlink immutability hardening
scripts/nemoclaw-start.sh
After the existing one-shot symlink validation, add an optional post-validation step: if chattr exists, set immutable attribute (chattr +i) on /sandbox/.openclaw and each symlink inside it, with failures suppressed (`2>/dev/null

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Poem

🐇 I checked each link, then locked the door,
No sly swaps now can change the core.
Immutable hops keep secrets tight,
Gateway sleeps easy through the night. ✨

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main change: making .openclaw symlinks immutable after validation, which directly addresses the TOCTOU vulnerability in issue #1019.
Linked Issues check ✅ Passed The PR fully addresses #1019's requirements by implementing immutable flag protection via chattr +i on validated symlinks, with graceful degradation when chattr is unavailable, closing the TOCTOU window.
Out of Scope Changes check ✅ Passed All changes are narrowly scoped to the stated objective: adding post-validation immutable hardening to symlinks in scripts/nemoclaw-start.sh with no unrelated modifications.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ 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
Contributor

@cv cv left a comment

Choose a reason for hiding this comment

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

Nice catch!

@13ernkastel
Copy link
Copy Markdown
Contributor

I opened a small follow-up PR on top of this branch to tighten the hardening path and make it easier to review operationally: latenighthackathon#1

It adds two things on top of the current fix:

  • explicit security logging when immutable hardening is unavailable, partial, or successful
  • a gateway-isolation E2E check that fails if \ is missing from the image

The intent is to keep the current defense-in-depth approach while making silent regressions and silent no-op hardening easier to catch.

The symlink validation loop in nemoclaw-start.sh verifies that all
symlinks in /sandbox/.openclaw/ point to their expected
/sandbox/.openclaw-data/ targets, but this check runs only once at
boot. After validation, the symlinks could theoretically be swapped
before the gateway starts on the next line (TOCTOU).

While DAC already prevents the sandbox user from modifying the
root-owned /sandbox/.openclaw directory, this adds defense-in-depth
by setting the immutable flag (chattr +i) on both the directory and
its symlinks after validation passes. The immutable flag cannot be
removed by the sandbox user, closing the TOCTOU window even if DAC
or Landlock are bypassed.

The fix degrades gracefully: if chattr is not available or the
filesystem does not support immutable flags, the existing DAC
protections remain in effect.

Closes NVIDIA#1019

Signed-off-by: latenighthackathon <latenighthackathon@users.noreply.github.com>
@latenighthackathon latenighthackathon force-pushed the fix/symlink-immutable-after-validation branch from 99ef53b to 5ab4386 Compare March 31, 2026 13:14
@cv cv merged commit 5326e37 into NVIDIA:main Mar 31, 2026
1 check passed
laitingsheng pushed a commit that referenced this pull request Apr 2, 2026
)

## Summary

The symlink validation loop in `nemoclaw-start.sh` verifies that all
symlinks in `/sandbox/.openclaw/` point to their expected
`/sandbox/.openclaw-data/` targets, but this check runs only once at
boot. After validation completes (line 360), the gateway starts on the
next line (line 366), leaving a TOCTOU window where symlinks could
theoretically be swapped.

This adds defense-in-depth by setting the immutable flag (`chattr +i`)
on both the `/sandbox/.openclaw` directory and its validated symlinks
immediately after the validation loop passes.

## Changes

- Added `chattr +i` on `/sandbox/.openclaw` directory and all symlinks
after the validation loop
- Guarded behind `command -v chattr` check — degrades gracefully if not
available
- All `chattr` calls use `|| true` to avoid failures on filesystems that
don't support immutable flags

## Security Analysis

| Layer | Protection | Status |
|-------|-----------|--------|
| DAC (chmod/chown) | Directory owned by root:root, mode 755 | Already
in place (Dockerfile) |
| Landlock | `/sandbox/.openclaw` in read-only policy | `best_effort` —
not enforced on older kernels |
| **Immutable flag** | `chattr +i` prevents modification even by root |
**Added by this PR** |

The immutable flag cannot be removed by the sandbox user (requires
`CAP_LINUX_IMMUTABLE`), closing the TOCTOU window even if DAC or
Landlock are bypassed.

## Test Plan

- [x] Full test suite: 650/653 passed (3 pre-existing failures in
`service-env.test.js`)
- [x] Verified graceful degradation when `chattr` is unavailable
- [x] Confirmed `chattr` guard (`command -v`) prevents errors in minimal
containers

Closes #1019

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **Chores**
* Added additional sandbox hardening: verification of internal links
followed by an optional immutability step to protect those links from
runtime modification, improving integrity and reducing risk of
unauthorized changes.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

Signed-off-by: latenighthackathon
<latenighthackathon@users.noreply.github.com>

Signed-off-by: latenighthackathon <latenighthackathon@users.noreply.github.com>
Co-authored-by: latenighthackathon <latenighthackathon@users.noreply.github.com>
lakamsani pushed a commit to lakamsani/NemoClaw that referenced this pull request Apr 4, 2026
…IDIA#1137)

## Summary

The symlink validation loop in `nemoclaw-start.sh` verifies that all
symlinks in `/sandbox/.openclaw/` point to their expected
`/sandbox/.openclaw-data/` targets, but this check runs only once at
boot. After validation completes (line 360), the gateway starts on the
next line (line 366), leaving a TOCTOU window where symlinks could
theoretically be swapped.

This adds defense-in-depth by setting the immutable flag (`chattr +i`)
on both the `/sandbox/.openclaw` directory and its validated symlinks
immediately after the validation loop passes.

## Changes

- Added `chattr +i` on `/sandbox/.openclaw` directory and all symlinks
after the validation loop
- Guarded behind `command -v chattr` check — degrades gracefully if not
available
- All `chattr` calls use `|| true` to avoid failures on filesystems that
don't support immutable flags

## Security Analysis

| Layer | Protection | Status |
|-------|-----------|--------|
| DAC (chmod/chown) | Directory owned by root:root, mode 755 | Already
in place (Dockerfile) |
| Landlock | `/sandbox/.openclaw` in read-only policy | `best_effort` —
not enforced on older kernels |
| **Immutable flag** | `chattr +i` prevents modification even by root |
**Added by this PR** |

The immutable flag cannot be removed by the sandbox user (requires
`CAP_LINUX_IMMUTABLE`), closing the TOCTOU window even if DAC or
Landlock are bypassed.

## Test Plan

- [x] Full test suite: 650/653 passed (3 pre-existing failures in
`service-env.test.js`)
- [x] Verified graceful degradation when `chattr` is unavailable
- [x] Confirmed `chattr` guard (`command -v`) prevents errors in minimal
containers

Closes NVIDIA#1019

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **Chores**
* Added additional sandbox hardening: verification of internal links
followed by an optional immutability step to protect those links from
runtime modification, improving integrity and reducing risk of
unauthorized changes.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

Signed-off-by: latenighthackathon
<latenighthackathon@users.noreply.github.com>

Signed-off-by: latenighthackathon <latenighthackathon@users.noreply.github.com>
Co-authored-by: latenighthackathon <latenighthackathon@users.noreply.github.com>
cv added a commit that referenced this pull request Apr 6, 2026
## Summary

This follow-up builds on #1137 and improves the observability around
immutable symlink hardening without changing the underlying
defense-in-depth approach.

## What Changed

- factors `.openclaw` symlink validation into a reusable helper so both
startup paths use the same validation logic
- adds explicit security logging when immutable hardening succeeds, is
partial, or is skipped because `chattr` is unavailable
- extends the gateway-isolation E2E to fail if `chattr` is missing from
the image, so the mitigation cannot silently disappear

## Why

The original immutable-hardening fix is directionally strong, but the
`chattr` path is intentionally best-effort and currently silent. That
makes the mitigation harder to trust and harder to debug because:

- a missing `chattr` binary looks the same as successful hardening
- partial `chattr +i` failures are suppressed with no visibility
- the image can regress and stop shipping `chattr` without CI catching
it

These changes make the mitigation easier to audit while staying
compatible with the current layered hardening model.

## Validation

- `bash -n scripts/nemoclaw-start.sh`
- `bash -n test/e2e-gateway-isolation.sh`
- `git diff --check`
- not run: `test/e2e-gateway-isolation.sh` (`docker` is not installed in
this environment)

## Relationship To #1137

This is a repost of the follow-up originally opened as
`latenighthackathon#1`, now targeted at `NVIDIA/NemoClaw` as
requested.

## Note

This replaces `#1467`, which GitHub auto-closed because the repository's
contributor open-PR limit was hit at the time.

Signed-off-by: 13ernkastel <LennonCMJ@live.com>


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **Chores**
* Enhanced startup process validation to ensure system integrity and
correct configuration
* Improved security hardening mechanisms with comprehensive logging and
graceful fallback handling when system features are unavailable

* **Tests**
* Updated end-to-end integration tests to verify system hardening
capabilities and feature availability

<!-- end of auto-generated comment: release notes by coderabbit.ai -->

Co-authored-by: Carlos Villela <cvillela@nvidia.com>
tranzmatt pushed a commit to tranzmatt/NemoClaw that referenced this pull request Apr 6, 2026
## Summary

This follow-up builds on NVIDIA#1137 and improves the observability around
immutable symlink hardening without changing the underlying
defense-in-depth approach.

## What Changed

- factors `.openclaw` symlink validation into a reusable helper so both
startup paths use the same validation logic
- adds explicit security logging when immutable hardening succeeds, is
partial, or is skipped because `chattr` is unavailable
- extends the gateway-isolation E2E to fail if `chattr` is missing from
the image, so the mitigation cannot silently disappear

## Why

The original immutable-hardening fix is directionally strong, but the
`chattr` path is intentionally best-effort and currently silent. That
makes the mitigation harder to trust and harder to debug because:

- a missing `chattr` binary looks the same as successful hardening
- partial `chattr +i` failures are suppressed with no visibility
- the image can regress and stop shipping `chattr` without CI catching
it

These changes make the mitigation easier to audit while staying
compatible with the current layered hardening model.

## Validation

- `bash -n scripts/nemoclaw-start.sh`
- `bash -n test/e2e-gateway-isolation.sh`
- `git diff --check`
- not run: `test/e2e-gateway-isolation.sh` (`docker` is not installed in
this environment)

## Relationship To NVIDIA#1137

This is a repost of the follow-up originally opened as
`latenighthackathon#1`, now targeted at `NVIDIA/NemoClaw` as
requested.

## Note

This replaces `NVIDIA#1467`, which GitHub auto-closed because the repository's
contributor open-PR limit was hit at the time.

Signed-off-by: 13ernkastel <LennonCMJ@live.com>


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **Chores**
* Enhanced startup process validation to ensure system integrity and
correct configuration
* Improved security hardening mechanisms with comprehensive logging and
graceful fallback handling when system features are unavailable

* **Tests**
* Updated end-to-end integration tests to verify system hardening
capabilities and feature availability

<!-- end of auto-generated comment: release notes by coderabbit.ai -->

Co-authored-by: Carlos Villela <cvillela@nvidia.com>
gemini2026 pushed a commit to gemini2026/NemoClaw that referenced this pull request Apr 14, 2026
…IDIA#1137)

## Summary

The symlink validation loop in `nemoclaw-start.sh` verifies that all
symlinks in `/sandbox/.openclaw/` point to their expected
`/sandbox/.openclaw-data/` targets, but this check runs only once at
boot. After validation completes (line 360), the gateway starts on the
next line (line 366), leaving a TOCTOU window where symlinks could
theoretically be swapped.

This adds defense-in-depth by setting the immutable flag (`chattr +i`)
on both the `/sandbox/.openclaw` directory and its validated symlinks
immediately after the validation loop passes.

## Changes

- Added `chattr +i` on `/sandbox/.openclaw` directory and all symlinks
after the validation loop
- Guarded behind `command -v chattr` check — degrades gracefully if not
available
- All `chattr` calls use `|| true` to avoid failures on filesystems that
don't support immutable flags

## Security Analysis

| Layer | Protection | Status |
|-------|-----------|--------|
| DAC (chmod/chown) | Directory owned by root:root, mode 755 | Already
in place (Dockerfile) |
| Landlock | `/sandbox/.openclaw` in read-only policy | `best_effort` —
not enforced on older kernels |
| **Immutable flag** | `chattr +i` prevents modification even by root |
**Added by this PR** |

The immutable flag cannot be removed by the sandbox user (requires
`CAP_LINUX_IMMUTABLE`), closing the TOCTOU window even if DAC or
Landlock are bypassed.

## Test Plan

- [x] Full test suite: 650/653 passed (3 pre-existing failures in
`service-env.test.js`)
- [x] Verified graceful degradation when `chattr` is unavailable
- [x] Confirmed `chattr` guard (`command -v`) prevents errors in minimal
containers

Closes NVIDIA#1019

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **Chores**
* Added additional sandbox hardening: verification of internal links
followed by an optional immutability step to protect those links from
runtime modification, improving integrity and reducing risk of
unauthorized changes.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

Signed-off-by: latenighthackathon
<latenighthackathon@users.noreply.github.com>

Signed-off-by: latenighthackathon <latenighthackathon@users.noreply.github.com>
Co-authored-by: latenighthackathon <latenighthackathon@users.noreply.github.com>
gemini2026 pushed a commit to gemini2026/NemoClaw that referenced this pull request Apr 14, 2026
## Summary

This follow-up builds on NVIDIA#1137 and improves the observability around
immutable symlink hardening without changing the underlying
defense-in-depth approach.

## What Changed

- factors `.openclaw` symlink validation into a reusable helper so both
startup paths use the same validation logic
- adds explicit security logging when immutable hardening succeeds, is
partial, or is skipped because `chattr` is unavailable
- extends the gateway-isolation E2E to fail if `chattr` is missing from
the image, so the mitigation cannot silently disappear

## Why

The original immutable-hardening fix is directionally strong, but the
`chattr` path is intentionally best-effort and currently silent. That
makes the mitigation harder to trust and harder to debug because:

- a missing `chattr` binary looks the same as successful hardening
- partial `chattr +i` failures are suppressed with no visibility
- the image can regress and stop shipping `chattr` without CI catching
it

These changes make the mitigation easier to audit while staying
compatible with the current layered hardening model.

## Validation

- `bash -n scripts/nemoclaw-start.sh`
- `bash -n test/e2e-gateway-isolation.sh`
- `git diff --check`
- not run: `test/e2e-gateway-isolation.sh` (`docker` is not installed in
this environment)

## Relationship To NVIDIA#1137

This is a repost of the follow-up originally opened as
`latenighthackathon#1`, now targeted at `NVIDIA/NemoClaw` as
requested.

## Note

This replaces `NVIDIA#1467`, which GitHub auto-closed because the repository's
contributor open-PR limit was hit at the time.

Signed-off-by: 13ernkastel <LennonCMJ@live.com>


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **Chores**
* Enhanced startup process validation to ensure system integrity and
correct configuration
* Improved security hardening mechanisms with comprehensive logging and
graceful fallback handling when system features are unavailable

* **Tests**
* Updated end-to-end integration tests to verify system hardening
capabilities and feature availability

<!-- end of auto-generated comment: release notes by coderabbit.ai -->

Co-authored-by: Carlos Villela <cvillela@nvidia.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

nemoclaw-start.sh: symlink validation is one-shot at boot — no runtime monitoring (TOCTOU)

3 participants