Skip to content

fix: harden Dockerfile replaceConfigFile patch#2876

Merged
ericksoa merged 2 commits into
mainfrom
replay-2696-rcf-patch
May 2, 2026
Merged

fix: harden Dockerfile replaceConfigFile patch#2876
ericksoa merged 2 commits into
mainfrom
replay-2696-rcf-patch

Conversation

@ericksoa

@ericksoa ericksoa commented May 2, 2026

Copy link
Copy Markdown
Contributor

Summary

  • replay fix: make Dockerfile replaceConfigFile patch robust for 2026.4.24 #2696 by @BenediktSchackenberg: extract the OpenClaw replaceConfigFile patch into scripts/rcf_patch.py and stage that helper into the sandbox image/build context
  • keep the helper scoped to replaceConfigFile, tolerant of whitespace drift, and tolerant of snapshot / nextConfig property-order drift
  • keep the hadolint ignore attached to the long Dockerfile patching RUN block so the basic checks gate can pass

Fixes #2689.
Closes #2875.

Credit

This is based on #2696 by @BenediktSchackenberg. The commit includes Benedikt as co-author:

Co-authored-by: Benedikt Schackenberg <6381261+BenediktSchackenberg@users.noreply.github.com>

Validation

  • python3 fixture harness for scripts/rcf_patch.py: current order, reversed property order, unrelated-block scoping, and missing-target fail-close behavior
  • hadolint Dockerfile
  • npm run build:cli
  • npm run typecheck:cli
  • npm run validate:configs
  • cd nemoclaw && npm install && npm run build
  • npx prek run hadolint --files Dockerfile --stage pre-push
  • npx vitest run test/local-inference-setup.test.ts --testNamePattern "vLLM startup uses trusted loopback serving and waits for exact model readiness"

Note: npx prek run --all-files --stage pre-push passed through lint/build/config and then hit one local temp-file failure in test/local-inference-setup.test.ts; the exact failing test passed when rerun by itself.

Signed-off-by: Aaron Erickson aerickson@nvidia.com

Summary by CodeRabbit

  • Refactor

    • Moved Docker build patching into a dedicated helper script for clearer build steps and maintainability.
  • Bug Fix

    • Prevents build failures inside the read-only sandbox by catching and logging EACCES when the sandbox marker is present, allowing builds to proceed.
  • Chores

    • Ensured the helper script is included in the staged build context for Docker.
  • Tests

    • Added tests to validate the patching behavior and sandbox safety checks.

Replay the intent of #2696 by moving the OpenClaw replaceConfigFile patch into a staged helper script, then keep the Dockerfile patch path available in optimized rebuild contexts.

Also keep the matcher tolerant of snapshot/nextConfig property order drift and keep the hadolint suppression attached to the patching RUN block.

Co-authored-by: Benedikt Schackenberg <6381261+BenediktSchackenberg@users.noreply.github.com>
Signed-off-by: Aaron Erickson <aerickson@nvidia.com>
@coderabbitai

coderabbitai Bot commented May 2, 2026

Copy link
Copy Markdown
Contributor

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: a3bf0079-1b2a-4a8e-9373-915421ee6424

📥 Commits

Reviewing files that changed from the base of the PR and between 9c77c7a and fbdd625.

📒 Files selected for processing (2)
  • scripts/rcf_patch.py
  • test/rcf-patch.test.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • scripts/rcf_patch.py

📝 Walkthrough

Walkthrough

Replaces the inline Dockerfile Python patch with a staged helper script (scripts/rcf_patch.py) that extracts and patches OpenClaw's replaceConfigFile body to wrap the config-write block with EACCES-handling logic; the script is staged into the Docker build context and invoked during the image build.

Changes

OpenClaw EACCES Patch Refactoring

Layer / File(s) Summary
Data Shape / New Artifact
scripts/rcf_patch.py
Adds a standalone Python patcher that locates async function replaceConfigFile(...) and extracts its {...} body using brace-matching that skips quotes and comments.
Core Implementation
scripts/rcf_patch.py
Within the extracted body, applies a DOTALL regex to find the tryWriteSingleTopLevelIncludeMutation(...) + await writeConfigFile(...) sequence (whitespace/property-order tolerant), and builds a replacement that wraps the matched block in try { ... } catch(_rcfErr) { if (process.env.OPENSHELL_SANDBOX==="1") console.error(...); else throw _rcfErr }. Writes the patched file back and logs completion.
Build Context Staging
src/lib/sandbox-build-context.ts
Stages scripts/rcf_patch.py into the optimized sandbox build context at buildCtx/scripts/rcf_patch.py so it is available to the Docker build.
Docker Build Integration
Dockerfile
Copies staged scripts/rcf_patch.py into the image at /usr/local/lib/nemoclaw/rcf_patch.py and replaces the prior inline python3 -c ... step by invoking python3 /usr/local/lib/nemoclaw/rcf_patch.py "$rcf_file".
Tests
test/rcf-patch.test.ts
Adds tests that run the script against synthetic replaceConfigFile sources exercising property-order variants, verifies successful patch application and presence of OPENSHELL_SANDBOX and the try { if (!await tryWriteSingleTopLevelIncludeMutation pattern, tests brace-matching that ignores braces in strings/comments, and a negative test for the missing-pattern failure case.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

I nibble at code with careful paws,
Moved brittle ink into a steadier cause.
A patch that hops in sandbox light,
Now gentle errors sleep at night —
🐇✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.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 accurately captures the main change: extracting and hardening the Dockerfile's OpenClaw replaceConfigFile patch into a dedicated, robust helper script.
Linked Issues check ✅ Passed The PR addresses both linked issues: #2689 (robust patching across OpenClaw versions) and #2875 (property-order independence via brace-matching and regex tolerance).
Out of Scope Changes check ✅ Passed All changes are directly scoped to the stated objectives: the new rcf_patch.py script, Dockerfile integration, build-context staging, and regression tests. No unrelated modifications.

✏️ 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 replay-2696-rcf-patch

Review rate limit: 9/10 reviews remaining, refill in 6 minutes.

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

@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

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

Inline comments:
In `@scripts/rcf_patch.py`:
- Around line 20-46: The current brace-walking that computes
fn_body_start/fn_src using depth is unsafe because it treats braces inside
strings/comments as code and can terminate early; replace it with a
string/comment-aware scanner or use Python's AST to locate the function body:
implement scanning that advances character-by-character from fn_body_start and
skips over single-quoted, double-quoted and triple-quoted strings (honoring
escapes), skips // line comments and /* ... */ block comments, and only
increments/decrements depth for braces encountered outside those contexts (or
alternatively parse src with ast.parse and map the target function node to its
source slice), then set fn_src from the real closing brace; keep the existing
regex match (pat) afterwards unchanged so subsequent logic still finds
tryWriteSingleTopLevelIncludeMutation/writeConfigFile.
🪄 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: 1fdc4d38-35c9-4f94-995b-dea63e999b9f

📥 Commits

Reviewing files that changed from the base of the PR and between 19b1b4f and 9c77c7a.

📒 Files selected for processing (3)
  • Dockerfile
  • scripts/rcf_patch.py
  • src/lib/sandbox-build-context.ts

Comment thread scripts/rcf_patch.py Outdated
Skip quoted strings and comments when locating the replaceConfigFile body so braces in string/comment text cannot terminate the scan early.

Add focused regression coverage for property-order drift, comment/string braces, and fail-close behavior.

Signed-off-by: Aaron Erickson <aerickson@nvidia.com>
@ericksoa

ericksoa commented May 2, 2026

Copy link
Copy Markdown
Contributor Author

Addressed the CodeRabbit body-scanner finding in fbdd625: rcf_patch.py now skips quoted strings plus // and /* */ comments while locating the replaceConfigFile body, and test/rcf-patch.test.ts covers string/comment braces, property-order drift, and fail-close behavior.

@ericksoa ericksoa self-assigned this May 2, 2026
@ericksoa ericksoa requested a review from cv May 2, 2026 02:05
@ericksoa ericksoa enabled auto-merge (squash) May 2, 2026 02:18
@ericksoa ericksoa merged commit e0290e1 into main May 2, 2026
17 of 18 checks passed
@ericksoa ericksoa deleted the replay-2696-rcf-patch branch May 2, 2026 02:19
laitingsheng added a commit that referenced this pull request May 14, 2026
Patch 4 is a regex-based monkey-patch over OpenClaw's compiled JS that
suppresses EACCES inside replaceConfigFile. Its source-shape coupling
has broken three times in eight days (#2377, #2484, #2876) chasing
upstream refactors; #2686 and #3497 report the latest casualty, where
the regex no longer finds the function in 2026.4.24 and the image build
fails.

Patch 4 is also unnecessary by design:

* In mutable-default mode, openclaw.json is chmod 660 sandbox:sandbox
  and the gateway UID is in the sandbox group (#2681), so plugin
  install writes through without ever hitting EACCES.
* In shields-up mode, the entire config tree (file + parent dir + the
  plugin/extensions state dirs in HIGH_RISK_STATE_DIRS) is locked to
  root:root by design — refusing runtime mutations is the whole point
  of shields-up. Suppressing the EACCES masked that refusal and made
  the install appear to succeed silently while only the auto-discovery
  half landed.

The expected flow is configure-in-mutable-mode → shields up → run.
Plugin install attempted while shielded should fail cleanly, which is
what happens without Patch 4.

Reverts the rcf-shim replacement attempt; the require-hook approach
does not catch OpenClaw's ESM named imports anyway (capture-at-import-
time semantics).

Resolves #2686
Resolves #3497

Signed-off-by: Tinson Lai <tinsonl@nvidia.com>
cv pushed a commit that referenced this pull request May 15, 2026
## Summary
Patch 4 in the sandbox `Dockerfile` is a regex-based monkey-patch over
OpenClaw's compiled JS that wraps `replaceConfigFile` in an EACCES
try/catch suppression. It is source-shape-coupled and has been rewritten
three times in eight days chasing OpenClaw refactors:

- `fefd69fa2` (#2377) — original literal-string match against
[openclaw/openclaw@v2026.4.9](https://github.com/openclaw/openclaw/releases/tag/v2026.4.9).
- `5dcb0a9b9` (#2484) — updated the literal string for the restructured
write block in
[openclaw/openclaw@v2026.4.24](https://github.com/openclaw/openclaw/releases/tag/v2026.4.24).
- `e0290e153` (#2876) — hardened to a tolerant
whitespace/property-order-aware regex against
[openclaw/openclaw@v2026.4.24](https://github.com/openclaw/openclaw/releases/tag/v2026.4.24).

#2686 and #3497 are the latest break: in current OpenClaw, the regex no
longer finds the function shape and the image build aborts at Step
17/56.

Patch 4 is also unnecessary by design. The EACCES it was suppressing
does not happen in the supported flows:

- **Mutable-default mode** (fresh sandbox, before `nemoclaw shields
up`): `openclaw.json` is `chmod 660 sandbox:sandbox` and the gateway UID
is in the sandbox group, courtesy of #2851 (closing #2681; superseding
the EACCES-swallow attempt in #2693). `openclaw plugins install` writes
through normally; no EACCES.
- **Shields-up mode** (locked): the entire config tree — file, parent
directory, and the `extensions`/`plugins` state dirs from
[HIGH_RISK_STATE_DIRS](src/lib/shields/index.ts#L292-L306) — is locked
to `root:root` by design. Shields-up exists *to refuse* runtime config
and plugin mutations. Suppressing the EACCES masked that refusal and
made `openclaw plugins install` appear to succeed silently while only
the auto-discovery half landed.

The expected lifecycle is **configure-in-mutable-mode → `shields up` →
run**. Plugin install attempted while shielded should fail cleanly; that
is exactly what happens without Patch 4.

This PR therefore deletes Patch 4 entirely.

## Related Issue
Resolves #2686
Resolves #3497

Related context:

- #2681 — original "make `.openclaw` group-writable" issue, closed by
#2851.
- #2851 — PR that made mutable-mode plugin install work without an
EACCES swallow.
- #2693 — closed earlier EACCES-swallow attempt, superseded once #2851
landed.
- #2544 — NemoClaw issue tracking the broader "plugin config requires
multi-minute rebuild" problem.
- openclaw/openclaw#72950 — upstream defect (no env-var or
writable-overlay path for `plugins.entries.<id>.config`); the real fix
has to land there.

## Changes
- `Dockerfile`: drop the Patch 4 block (the `COPY scripts/rcf_patch.py`
line and the inline Python invocation + grep guard).
- `scripts/rcf_patch.py`: deleted.
- `src/lib/sandbox/build-context.ts`: stop staging
`scripts/rcf_patch.py` into the sandbox build context.
- `test/rcf-patch.test.ts`: deleted.

## Type of Change

- [x] 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

- [x] `npx prek run --all-files` passes
- [x] `npm test` passes
- [x] Tests added or updated for new or changed behavior
- [x] No secrets, API keys, or credentials committed
- [ ] Docs updated for user-facing behavior changes
- [ ] `make docs` builds without warnings (doc changes only)
- [ ] Doc pages follow the [style
guide](https://github.com/NVIDIA/NemoClaw/blob/main/docs/CONTRIBUTING.md)
(doc changes only)
- [ ] New doc pages include SPDX header and frontmatter (new pages only)

Signed-off-by: Tinson Lai <tinsonl@nvidia.com>


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

## Summary by CodeRabbit

* **Chores**
* Removed OpenClaw patching logic from Docker build process and related
artifact copies
  * Updated build context script staging behavior

* **Tests**
* Enhanced sandbox configuration test suite with environment variable
passthrough support
* Added version-based conditional patching validation and warning
behavior tests

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

---------

Signed-off-by: Tinson Lai <tinsonl@nvidia.com>
@wscurran wscurran added the bug-fix PR fixes a bug or regression label Jun 8, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug-fix PR fixes a bug or regression

Projects

None yet

2 participants