Skip to content

Stabilize NeuroRift↔OpenClaw runtime: sandboxing, approval forwarding, and deterministic compose#26

Open
Neuro-Rift wants to merge 2 commits into
mainfrom
codex/integrate-neurorift-with-openclaw-gateway-ej0i9r
Open

Stabilize NeuroRift↔OpenClaw runtime: sandboxing, approval forwarding, and deterministic compose#26
Neuro-Rift wants to merge 2 commits into
mainfrom
codex/integrate-neurorift-with-openclaw-gateway-ej0i9r

Conversation

@Neuro-Rift
Copy link
Copy Markdown
Owner

@Neuro-Rift Neuro-Rift commented Feb 27, 2026

User description

Motivation

  • Harden and stabilize the NeuroRift <-> OpenClaw integration by enforcing sandboxing, approval controls, deterministic Docker runtime, and strict environment normalization.
  • Prevent accidental destructive operations and prototype leakage by adding allow/deny tool policies and approval-forwarder behavior for high-risk commands.
  • Improve operational visibility and heartbeat discipline with structured logging and lifecycle events to make diagnostics deterministic and auditable.
  • Normalize runtime service names and compose topology to make local development and production parity predictable.

Description

  • Revamped the boot/runbook in BOOT.md to require normalized runtime env, run scripts/openclaw_doctor.py for preflight checks, and use docker compose up -d --build gateway neurorift-core rust-engine web-ui ollama sandbox-runner for deterministic startup.
  • Reworked docker-compose.yml and docker-compose.dev.yml to rename services (neurorift -> neurorift-core, openclaw -> rust-engine), add gateway and sandbox-runner services, tighten healthchecks, consolidate volumes, and update service env/ports for consistent wiring.
  • Rewrote integrations/openclaw/openclaw_gateway_adapter.py to add strict environment normalization, structured logging, session context resolution, terminal-only execution policy, tool allow/deny lists, high-risk pattern detection, approval forwarding with event emissions, heartbeat emission, lifecycle handling, and safer RPC frame construction.
  • Extended openclaw.json5 with a visible systemPrompt, operator and sandbox policies, approvalForwarder configuration, heartbeat and scheduling guards, observability event emission, evolution controls, and runtime mode isolation flags.
  • Added scripts/openclaw_doctor.py as a preflight checker for required env variables and runtime port collisions and added docs/OPENCLAW_INTEGRATION_AUDIT.md documenting the stabilization scope and residual risks.

Testing

  • Ran python3 scripts/openclaw_doctor.py as a preflight check to validate required environment variables and port availability and observed successful completion (exit 0) in local verification.
  • Validated Docker composition syntactically with docker compose config and confirmed the updated docker-compose.yml loads without configuration errors.

Codex Task


CodeAnt-AI Description

Stabilize NeuroRift↔OpenClaw runtime with sandboxed execution, approval forwarding, and startup validation

What Changed

  • New preflight check script verifies required environment variables and warns if expected runtime ports are in use, failing startup on missing critical env.
  • Adapter now enforces terminal-only execution, a tool allow/deny policy, and auto-denies high-risk commands unless approved; approval requests are forwarded (Discord/Telegram) and emit structured approval events.
  • Adapter emits structured JSON diagnostics (including heartbeat, execution finished, approval requested/result, lifecycle updates) and preserves channel/group routing and mention policy in responses.
  • Docker composition and boot instructions updated for deterministic startup: services renamed (neurorift-core, rust-engine, gateway, sandbox-runner), added sandbox runner, and clear healthchecks and startup order in compose.
  • Runtime configuration updated to require session isolation, sandbox defaults (/workspace), heartbeat discipline, scheduling guards, and observability hooks so responses and alerts include latency, token/cost hints, and are trackable.

Impact

✅ Fewer startup failures due to missing env or port collisions
✅ Safer command execution with explicit allow/deny and human approval
✅ Clearer operational events for audits and troubleshooting

💡 Usage Guide

Checking Your Pull Request

Every time you make a pull request, our system automatically looks through it. We check for security issues, mistakes in how you're setting up your infrastructure, and common code problems. We do this to make sure your changes are solid and won't cause any trouble later.

Talking to CodeAnt AI

Got a question or need a hand with something in your pull request? You can easily get in touch with CodeAnt AI right here. Just type the following in a comment on your pull request, and replace "Your question here" with whatever you want to ask:

@codeant-ai ask: Your question here

This lets you have a chat with CodeAnt AI about your pull request, making it easier to understand and improve your code.

Example

@codeant-ai ask: Can you suggest a safer alternative to storing this secret?

Preserve Org Learnings with CodeAnt

You can record team preferences so CodeAnt AI applies them in future reviews. Reply directly to the specific CodeAnt AI suggestion (in the same thread) and replace "Your feedback here" with your input:

@codeant-ai: Your feedback here

This helps CodeAnt AI learn and adapt to your team's coding style and standards.

Example

@codeant-ai: Do not flag unused imports.

Retrigger review

Ask CodeAnt AI to review the PR again, by typing:

@codeant-ai: review

Check Your Repository Health

To analyze the health of your code repository, visit our dashboard at https://app.codeant.ai. This tool helps you identify potential issues and areas for improvement in your codebase, ensuring your repository maintains high standards of code health.

Summary by CodeRabbit

  • Documentation

    • Added stabilization audit documentation
    • Updated boot procedures with new diagnostic workflows
  • New Features

    • Enhanced execution security policies and sandbox controls
    • Added structured approval and logging system
    • Introduced heartbeat monitoring discipline
    • Added preflight health check diagnostics
  • Chores

    • Service infrastructure reorganization for improved stability
    • Configuration version update and environment normalization

@codeant-ai
Copy link
Copy Markdown

codeant-ai Bot commented Feb 27, 2026

CodeAnt AI is reviewing your PR.

@chatgpt-codex-connector
Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

This commit fixes the style issues introduced in f05e77b according to the output
from Black.

Details: #26
Copy link
Copy Markdown

@greptile-apps greptile-apps Bot left a comment

Choose a reason for hiding this comment

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

Your free trial has ended. If you'd like to continue receiving code reviews, you can add a payment method here.

@codeant-ai codeant-ai Bot added the size:XL This PR changes 500-999 lines, ignoring generated files label Feb 27, 2026
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Feb 27, 2026

📝 Walkthrough

Walkthrough

This pull request stabilizes the NeuroRift and OpenClaw integration by introducing production-hardened runtime controls, structured logging, approval workflows, heartbeat discipline, and deterministic Docker orchestration. Configuration, deployment definitions, documentation, and core adapter logic are updated to enforce security policies, sandbox isolation, and lifecycle management across the unified system.

Changes

Cohort / File(s) Summary
Boot & Runtime Configuration
BOOT.md, openclaw.json5, scripts/openclaw_doctor.py
Updated boot documentation with new "Preflight (mandatory)" section, deterministic Docker startup commands, and expanded adapter security guarantees. Config version bumped to 1.1 with new systemPrompt, operatorPolicy, sessionIsolation, heartbeat block, approval forwarder controls, channel routing policies, scheduling enhancements, evolution block, and runtime.modeIsolation. Added preflight doctor script to validate required environment variables (OPENCLAW_CONFIG_PATH, OPENCLAW_STATE_DIR, OLLAMA_HOST, NEURORIFT_BRIDGE_URL) and port availability.
Docker Composition
docker-compose.dev.yml, docker-compose.yml
Service renames: neurorift→neurorift-core, openclaw→rust-engine. Added new gateway (port 127.0.0.1:18789:18789) and sandbox-runner services. Updated environment variable formatting (removed quotes from LOG_LEVEL, NODE_ENV), new volumes for gateway state and session audit tracking, adjusted network exposure from explicit host ports to network-scoped binding, updated service dependencies and healthcheck formats.
Core Integration & Adapter
integrations/openclaw/openclaw_gateway_adapter.py
Introduced StructuredLogger class for sanitized logging. Enhanced ExecutionApprovalForwarder with logger dependency and correlation_id tracing. Significantly expanded NeuroRiftOpenClawAdapter with session context isolation, terminal-only exec policy enforcement, sandbox allow/deny lists, structured approval request logging, heartbeat lifecycle loop, event-driven frame handling (rpc.request, event.signal, lifecycle.update), and graceful shutdown signaling. Added helper methods for session resolution, command preview extraction, policy validation, and heartbeat emission.
Documentation & Audit
docs/OPENCLAW_INTEGRATION_AUDIT.md
New audit documentation detailing stabilization scope, compliance matrix covering 12 implemented requirements (gateway integration, configuration, policy, diagnostics, environment normalization, deterministic services, preflight checks), and residual risk notes on secure defaults and runtime considerations.

Sequence Diagram(s)

sequenceDiagram
    participant Client as WebSocket Client
    participant Adapter as NeuroRiftOpenClawAdapter
    participant Policy as Policy Validator
    participant Approver as ExecutionApprovalForwarder
    participant Logger as StructuredLogger
    participant Gateway as OpenClaw Gateway

    Client->>Adapter: rpc.request event
    Adapter->>Adapter: resolve session context
    Adapter->>Policy: validate exec policy<br/>(allow/deny, terminal-only)
    Policy-->>Adapter: policy status
    
    alt High-Risk Command
        Adapter->>Logger: log approval request
        Adapter->>Approver: evaluate(command, session_id,<br/>correlation_id)
        Approver->>Logger: log approval decision
        Approver-->>Adapter: ApprovalResult
        
        alt Approved
            Adapter->>Adapter: build RPC frame
            Adapter->>Logger: log execution
            Adapter->>Gateway: send frame with sandbox params
        else Not Approved
            Adapter->>Logger: log approval rejection
            Adapter->>Gateway: send approval-required response
        end
    else Standard Command
        Adapter->>Adapter: build RPC frame
        Adapter->>Logger: log execution
        Adapter->>Gateway: send frame
    end
    
    par Concurrent Heartbeat Loop
        Adapter->>Adapter: heartbeat interval elapsed
        Adapter->>Logger: emit heartbeat event
        Adapter->>Gateway: send heartbeat frame
    end
    
    Gateway-->>Client: response/heartbeat
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~30 minutes

Poem

🐰 Hops with glee through stabilized gates,
Approvals flow and sessions isolate,
Heartbeats tick in rhythm true,
Docker composes the sandbox anew—
Safety and structure, bundled just right!

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title 'Stabilize NeuroRift↔OpenClaw runtime: sandboxing, approval forwarding, and deterministic compose' directly reflects the main objectives: hardening the integration with sandboxing, approval controls, and deterministic Docker runtime composition.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

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

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch codex/integrate-neurorift-with-openclaw-gateway-ej0i9r

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

@deepsource-io
Copy link
Copy Markdown
Contributor

deepsource-io Bot commented Feb 27, 2026

DeepSource Code Review

We reviewed changes in 564df95...0c1d575 on this pull request. Below is the summary for the review, and you can see the individual issues we found as inline review comments.

See full review on DeepSource ↗

PR Report Card

Overall Grade  

Focus Area: Complexity
Security  

Reliability  

Complexity  

Hygiene  

Code Review Summary

Analyzer Status Updated (UTC) Details
Python Feb 27, 2026 12:52p.m. Review ↗

@codeant-ai
Copy link
Copy Markdown

codeant-ai Bot commented Feb 27, 2026

Nitpicks 🔍

🔒 No security issues identified
⚡ Recommended areas for review

  • Policy bypass risk
    _validate_exec_policy derives the base command using a naive split on whitespace. Commands with full paths or shell wrappers (e.g. /bin/rm, sh -c 'rm -rf /') or quoted arguments may bypass allow/deny checks. Use a safer parsing/normalisation (shlex + os.path.basename) and consider checking the resolved binary via whitelist/deny.

  • Sensitive exposure
    The approval message includes raw command content and is forwarded directly to external channels (Discord/Telegram). Commands may contain secrets or PII; forwarding without sanitization risks secret exfiltration to third-party services.

  • Port check edge-cases
    _port_in_use uses a short TCP connect on 127.0.0.1 only; services bound to 0.0.0.0, IPv6, or using different interfaces may not be detected consistently. This can produce false negatives/positives in preflight checks.

@codeant-ai
Copy link
Copy Markdown

codeant-ai Bot commented Feb 27, 2026

CodeAnt AI finished reviewing your PR.

Copy link
Copy Markdown

@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: 8

🧹 Nitpick comments (1)
integrations/openclaw/openclaw_gateway_adapter.py (1)

30-60: Policy is duplicated in code instead of sourced from runtime config.

These hardcoded allow/deny/risk patterns can drift from openclaw.json5 and weaken policy governance over time.

Load these values from OPENCLAW_CONFIG_PATH (with safe defaults), then enforce using the loaded policy set.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@integrations/openclaw/openclaw_gateway_adapter.py` around lines 30 - 60,
Replace the hardcoded policy constants DEFAULT_TOOL_ALLOW, DEFAULT_TOOL_DENY,
and HIGH_RISK_PATTERNS with values loaded at runtime from the
OPENCLAW_CONFIG_PATH JSON5 config (falling back to the existing defaults if the
file/members are missing or invalid); implement a loader function (e.g.,
load_openclaw_policy) that reads OPENCLAW_CONFIG_PATH, parses json5, validates
keys (allow/deny/high_risk_patterns), compiles regex strings into regex objects
for HIGH_RISK_PATTERNS, and exposes a policy object used everywhere the code
currently references DEFAULT_TOOL_ALLOW/DEFAULT_TOOL_DENY/HIGH_RISK_PATTERNS
(keep TOOL_METHOD_MAP as-is unless specified in config), and ensure any failures
to read/parse the config log a warning and use the safe defaults.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@BOOT.md`:
- Around line 26-55: The runbook currently starts core services twice; update
BOOT.md to present two mutually exclusive modes — e.g., "all-docker" and
"local-adapter" — and move commands into mode-specific sections so each service
is started only once: put the `docker compose up -d --build gateway
neurorift-core rust-engine web-ui ollama sandbox-runner` and `docker compose ps`
health check under the "all-docker" mode, and under a separate "local-adapter"
mode include `python3 modules/web/bridge_server.py`, `curl -s
http://127.0.0.1:8766/health`, `openclaw gateway --config ./openclaw.json5`, and
`python3 integrations/openclaw/openclaw_gateway_adapter.py`; ensure you add a
short note at the top instructing users to choose one mode and remove duplicated
startup steps for `gateway`/`neurorift-core` between modes.

In `@docker-compose.yml`:
- Line 86: The gateway service currently uses a mutable image tag
("neurorift/openclaw:latest"); update the gateway image reference to an
immutable identifier by replacing the :latest tag with a concrete version tag or
an image digest (e.g., neurorift/openclaw:vX.Y.Z or
neurorift/openclaw@sha256:<digest>) so deployments are deterministic and do not
drift.
- Line 79: The healthcheck in docker-compose.yml currently uses a CMD-SHELL that
depends on /dev/tcp (see the healthcheck test entry around the existing "test:
[\"CMD-SHELL\", \"curl -sf http://localhost:8765/health 2>/dev/null || (echo
>/dev/tcp/localhost/8765) 2>/dev/null || exit 1\"]"), which is shell-dependent
and can false-fail; fix by removing /dev/tcp usage and making the probe
portable: either (A) ensure the container images guarantee a bash-compatible
shell by adding an explicit SHELL or bash installation in the related
Dockerfile(s) (so /bin/bash is available), or (B) replace the CMD-SHELL
healthcheck with a pure binary probe that exists in the image (e.g., use
curl/wget/nc in the healthcheck command), and apply the same change for the
other healthcheck instance (the similar entry referenced at line 105). Ensure
the chosen probe (curl/wget/nc) is installed in the image or the Dockerfile is
updated accordingly so the healthcheck will not rely on /dev/tcp.

In `@integrations/openclaw/openclaw_gateway_adapter.py`:
- Around line 409-415: The receive loop around ws.recv/json.loads and the rpc
handling (calls to json.loads, event.get, _build_rpc_frame, and ws.send) is
unprotected and a malformed frame or policy exception can unwind the loop; wrap
the json.loads + event parsing and the await self._build_rpc_frame(...) / await
ws.send(...) calls in a try/except that catches broad exceptions (at least
Exception), logs the error (include the raw incoming string and exception) and
continues the loop instead of letting the exception propagate, and optionally
send a safe error-response frame when appropriate; apply the same protection to
the other receive branch around the code at lines ~430-440 so both places use
identical try/except resilience around parsing and frame-building/sending.
- Around line 183-187: The approval flow currently auto-denies immediately
because the code uses await asyncio.sleep(0) before creating the ApprovalResult;
change this to actually wait for the configured timeout (e.g., await
asyncio.sleep(timeout_seconds)) or use an awaitable that respects
timeout_seconds (for example asyncio.wait_for or waiting on the human approval
event with timeout_seconds) so the ApprovalResult(approved=False, reason=...) is
only created after the timeout elapses; update the block around result =
ApprovalResult(...) in openclaw_gateway_adapter.py (the approval routine that
references timeout_seconds and ApprovalResult) to wait the configured duration
rather than sleeping 0 seconds.
- Around line 274-281: The _validate_exec_policy function currently only
inspects the first token allowing command chaining to bypass policy; update
_validate_exec_policy to reject or safely parse commands containing shell
metacharacters (e.g., ;, &&, ||, |, ``, $(), &, >, <, \n) or any
subshell/redirect constructs and raise PermissionError when such operators are
present, or alternatively split the command into discrete tokens and validate
every executable token against DEFAULT_TOOL_DENY and DEFAULT_TOOL_ALLOW; target
the _validate_exec_policy function and the constants DEFAULT_TOOL_DENY /
DEFAULT_TOOL_ALLOW to ensure every potential executed program is checked rather
than only the initial token.

In `@openclaw.json5`:
- Line 202: The absoluteTimes entry for id "daily-summary" uses a past timestamp
(at: "2026-01-01T00:00:00Z") which can trigger missed or immediate catch-up
runs; update the absoluteTimes configuration (the object with id "daily-summary"
and its at field) to a future ISO8601 timestamp or replace it with a
relative/recurring schedule instead so the scheduler won't treat it as a past
event.

In `@scripts/openclaw_doctor.py`:
- Around line 44-47: The preflight currently only warns when required ports are
occupied; change the logic so that after computing collisions = [port for port
in PORTS if _port_in_use(port)] the script prints an error (not just a warn) and
exits with a non-zero status (e.g., sys.exit(1)) when collisions is non-empty;
update the branch that handles collisions to use PORTS, _port_in_use and
collisions to emit a clear error message listing the ports and then terminate
the process with a failing exit code.

---

Nitpick comments:
In `@integrations/openclaw/openclaw_gateway_adapter.py`:
- Around line 30-60: Replace the hardcoded policy constants DEFAULT_TOOL_ALLOW,
DEFAULT_TOOL_DENY, and HIGH_RISK_PATTERNS with values loaded at runtime from the
OPENCLAW_CONFIG_PATH JSON5 config (falling back to the existing defaults if the
file/members are missing or invalid); implement a loader function (e.g.,
load_openclaw_policy) that reads OPENCLAW_CONFIG_PATH, parses json5, validates
keys (allow/deny/high_risk_patterns), compiles regex strings into regex objects
for HIGH_RISK_PATTERNS, and exposes a policy object used everywhere the code
currently references DEFAULT_TOOL_ALLOW/DEFAULT_TOOL_DENY/HIGH_RISK_PATTERNS
(keep TOOL_METHOD_MAP as-is unless specified in config), and ensure any failures
to read/parse the config log a warning and use the safe defaults.

ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 564df95 and 0c1d575.

📒 Files selected for processing (7)
  • BOOT.md
  • docker-compose.dev.yml
  • docker-compose.yml
  • docs/OPENCLAW_INTEGRATION_AUDIT.md
  • integrations/openclaw/openclaw_gateway_adapter.py
  • openclaw.json5
  • scripts/openclaw_doctor.py

Comment thread BOOT.md
Comment on lines +26 to 55
## 2) Start deterministic Docker runtime

```bash
curl -s http://127.0.0.1:8766/health
docker compose up -d --build gateway neurorift-core rust-engine web-ui ollama sandbox-runner
```

## 3) Start OpenClaw gateway
Verify service health:

```bash
docker compose ps
```

Use your OpenClaw runtime with the unified config:
## 3) Start NeuroRift FastAPI bridge

```bash
openclaw gateway --config ./openclaw.json5
python3 modules/web/bridge_server.py
```

## 4) Start the adapter bridge
Health check:

```bash
curl -s http://127.0.0.1:8766/health
```

## 4) Start OpenClaw gateway and adapter

```bash
openclaw gateway --config ./openclaw.json5
python3 integrations/openclaw/openclaw_gateway_adapter.py
```
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Runbook currently starts core components twice (container + host).

Step 2 already starts gateway/neurorift-core; Step 3/4 then starts bridge/gateway again manually. This creates conflicting runtime paths and ambiguous troubleshooting.

Please split the guide into explicit mutually-exclusive modes (e.g., all-docker vs local-adapter) and keep only one startup path per mode.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@BOOT.md` around lines 26 - 55, The runbook currently starts core services
twice; update BOOT.md to present two mutually exclusive modes — e.g.,
"all-docker" and "local-adapter" — and move commands into mode-specific sections
so each service is started only once: put the `docker compose up -d --build
gateway neurorift-core rust-engine web-ui ollama sandbox-runner` and `docker
compose ps` health check under the "all-docker" mode, and under a separate
"local-adapter" mode include `python3 modules/web/bridge_server.py`, `curl -s
http://127.0.0.1:8766/health`, `openclaw gateway --config ./openclaw.json5`, and
`python3 integrations/openclaw/openclaw_gateway_adapter.py`; ensure you add a
short note at the top instructing users to choose one mode and remove duplicated
startup steps for `gateway`/`neurorift-core` between modes.

Comment thread docker-compose.yml
healthcheck:
# TCP-level check: try to open port 8765
test: [ "CMD-SHELL", "curl -sf http://localhost:8765/health 2>/dev/null || (echo >/dev/tcp/localhost/8765) 2>/dev/null || exit 1" ]
test: ["CMD-SHELL", "curl -sf http://localhost:8765/health 2>/dev/null || (echo >/dev/tcp/localhost/8765) 2>/dev/null || exit 1"]
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Healthchecks rely on /dev/tcp, which is shell-dependent and can false-fail.

Line 79 and Line 105 depend on /dev/tcp; this is not portable across all /bin/sh implementations and can keep healthy services marked unhealthy.

Run this read-only verification to confirm whether the image/tooling guarantees support for these probes.
Expected result: either explicit bash/SHELL support or a portable probe binary (curl/wget/nc) used in healthchecks.

#!/bin/bash
set -euo pipefail

echo "== Compose healthchecks using /dev/tcp =="
rg -n 'dev/tcp|healthcheck' docker-compose.yml -C2

echo
echo "== Dockerfile shell/probe capabilities =="
for f in $(fd -a 'Dockerfile' docker | sort); do
  echo "--- $f"
  rg -n '^(FROM|SHELL|RUN .*bash|RUN .*curl|RUN .*wget|RUN .*nc|RUN .*netcat)' "$f" || true
done

Also applies to: 105-105

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docker-compose.yml` at line 79, The healthcheck in docker-compose.yml
currently uses a CMD-SHELL that depends on /dev/tcp (see the healthcheck test
entry around the existing "test: [\"CMD-SHELL\", \"curl -sf
http://localhost:8765/health 2>/dev/null || (echo >/dev/tcp/localhost/8765)
2>/dev/null || exit 1\"]"), which is shell-dependent and can false-fail; fix by
removing /dev/tcp usage and making the probe portable: either (A) ensure the
container images guarantee a bash-compatible shell by adding an explicit SHELL
or bash installation in the related Dockerfile(s) (so /bin/bash is available),
or (B) replace the CMD-SHELL healthcheck with a pure binary probe that exists in
the image (e.g., use curl/wget/nc in the healthcheck command), and apply the
same change for the other healthcheck instance (the similar entry referenced at
line 105). Ensure the chosen probe (curl/wget/nc) is installed in the image or
the Dockerfile is updated accordingly so the healthcheck will not rely on
/dev/tcp.

Comment thread docker-compose.yml
start_period: 45s

gateway:
image: neurorift/openclaw:latest
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Pin gateway image to an immutable version (not latest).

Line 86 currently uses a mutable tag, which conflicts with deterministic runtime guarantees and can drift between deployments.

Suggested patch
-    image: neurorift/openclaw:latest
+    image: "neurorift/openclaw:${OPENCLAW_IMAGE_TAG:?set OPENCLAW_IMAGE_TAG}"
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
image: neurorift/openclaw:latest
image: "neurorift/openclaw:${OPENCLAW_IMAGE_TAG:?set OPENCLAW_IMAGE_TAG}"
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docker-compose.yml` at line 86, The gateway service currently uses a mutable
image tag ("neurorift/openclaw:latest"); update the gateway image reference to
an immutable identifier by replacing the :latest tag with a concrete version tag
or an image digest (e.g., neurorift/openclaw:vX.Y.Z or
neurorift/openclaw@sha256:<digest>) so deployments are deterministic and do not
drift.

Comment on lines +183 to +187
# Human callback hook should flip result in external controller.
await asyncio.sleep(0)
return ApprovalResult(approved=False, reason="approval pending/timeout -> deny")
result = ApprovalResult(
approved=False, reason="approval pending/timeout -> deny"
)
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Approval flow ignores configured timeout and auto-denies immediately.

Lines 183–187 never wait for timeout_seconds; high-risk commands are denied right away, not on timeout.

Minimal behavior-alignment patch
-        await asyncio.sleep(0)
+        await asyncio.sleep(self.timeout_seconds)
         result = ApprovalResult(
-            approved=False, reason="approval pending/timeout -> deny"
+            approved=False, reason="approval timeout -> deny"
         )
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
# Human callback hook should flip result in external controller.
await asyncio.sleep(0)
return ApprovalResult(approved=False, reason="approval pending/timeout -> deny")
result = ApprovalResult(
approved=False, reason="approval pending/timeout -> deny"
)
# Human callback hook should flip result in external controller.
await asyncio.sleep(self.timeout_seconds)
result = ApprovalResult(
approved=False, reason="approval timeout -> deny"
)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@integrations/openclaw/openclaw_gateway_adapter.py` around lines 183 - 187,
The approval flow currently auto-denies immediately because the code uses await
asyncio.sleep(0) before creating the ApprovalResult; change this to actually
wait for the configured timeout (e.g., await asyncio.sleep(timeout_seconds)) or
use an awaitable that respects timeout_seconds (for example asyncio.wait_for or
waiting on the human approval event with timeout_seconds) so the
ApprovalResult(approved=False, reason=...) is only created after the timeout
elapses; update the block around result = ApprovalResult(...) in
openclaw_gateway_adapter.py (the approval routine that references
timeout_seconds and ApprovalResult) to wait the configured duration rather than
sleeping 0 seconds.

Comment on lines +274 to +281
def _validate_exec_policy(self, command: str) -> None:
base_cmd = command.strip().split()[0] if command.strip() else ""
if not base_cmd:
raise ValueError("Empty exec command")
if base_cmd in DEFAULT_TOOL_DENY:
raise PermissionError(f"Tool denied by policy: {base_cmd}")
if base_cmd not in DEFAULT_TOOL_ALLOW:
raise PermissionError(f"Tool not in allow-list: {base_cmd}")
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Allow/deny policy is bypassable via command chaining.

Line 275 only checks the first token. A payload like nmap ...; shutdown now passes base-command allow checks while still executing forbidden operations.

Suggested hardening patch
 import os
 import re
+import shlex
 import signal
@@
     def _validate_exec_policy(self, command: str) -> None:
-        base_cmd = command.strip().split()[0] if command.strip() else ""
-        if not base_cmd:
+        raw = command.strip()
+        if not raw:
             raise ValueError("Empty exec command")
+        if any(op in raw for op in (";", "&&", "||", "|", "`", "$(", ">", "<")):
+            raise PermissionError("Shell operators are not allowed in exec commands")
+        try:
+            parts = shlex.split(raw, posix=True)
+        except ValueError as exc:
+            raise PermissionError("Malformed exec command") from exc
+        base_cmd = os.path.basename(parts[0]) if parts else ""
         if base_cmd in DEFAULT_TOOL_DENY:
             raise PermissionError(f"Tool denied by policy: {base_cmd}")
         if base_cmd not in DEFAULT_TOOL_ALLOW:
             raise PermissionError(f"Tool not in allow-list: {base_cmd}")
🧰 Tools
🪛 Ruff (0.15.2)

[warning] 277-277: Avoid specifying long messages outside the exception class

(TRY003)


[warning] 279-279: Avoid specifying long messages outside the exception class

(TRY003)


[warning] 281-281: Avoid specifying long messages outside the exception class

(TRY003)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@integrations/openclaw/openclaw_gateway_adapter.py` around lines 274 - 281,
The _validate_exec_policy function currently only inspects the first token
allowing command chaining to bypass policy; update _validate_exec_policy to
reject or safely parse commands containing shell metacharacters (e.g., ;, &&,
||, |, ``, $(), &, >, <, \n) or any subshell/redirect constructs and raise
PermissionError when such operators are present, or alternatively split the
command into discrete tokens and validate every executable token against
DEFAULT_TOOL_DENY and DEFAULT_TOOL_ALLOW; target the _validate_exec_policy
function and the constants DEFAULT_TOOL_DENY / DEFAULT_TOOL_ALLOW to ensure
every potential executed program is checked rather than only the initial token.

Comment on lines +409 to +415
incoming = await ws.recv()
event = json.loads(incoming)
event_type = event.get("type")

if event_type == "rpc.request":
frame = await self._build_rpc_frame(event)
await ws.send(json.dumps(frame))
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Malformed or policy-failing frames can crash the main receive loop.

Line 410 and Line 414 are not protected. A bad frame or policy exception can unwind the loop and prematurely stop the adapter.

Suggested resilience patch
 import asyncio
+import contextlib
 import json
@@
                 while not self._stop_event.is_set():
                     incoming = await ws.recv()
-                    event = json.loads(incoming)
+                    try:
+                        event = json.loads(incoming)
+                    except json.JSONDecodeError:
+                        self.logger.emit("gateway.invalid_json", raw=str(incoming)[:256])
+                        continue
                     event_type = event.get("type")
@@
                     if event_type == "rpc.request":
-                        frame = await self._build_rpc_frame(event)
+                        try:
+                            frame = await self._build_rpc_frame(event)
+                        except (ValueError, PermissionError) as exc:
+                            frame = {
+                                "type": "rpc.response",
+                                "id": str(event.get("id") or uuid.uuid4()),
+                                "error": {"code": "policy_error", "message": str(exc)},
+                            }
                         await ws.send(json.dumps(frame))
@@
             finally:
                 lifecycle_task.cancel()
-                await ws.send(
+                with contextlib.suppress(asyncio.CancelledError):
+                    await lifecycle_task
+                if not ws.closed:
+                    await ws.send(
                     json.dumps(
                         {
                             "type": "lifecycle.update",
                             "state": "stopped",
                             "session": {"id": self.session_id, "mode": "isolated"},
                         }
                     )
                 )

Also applies to: 430-440

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@integrations/openclaw/openclaw_gateway_adapter.py` around lines 409 - 415,
The receive loop around ws.recv/json.loads and the rpc handling (calls to
json.loads, event.get, _build_rpc_frame, and ws.send) is unprotected and a
malformed frame or policy exception can unwind the loop; wrap the json.loads +
event parsing and the await self._build_rpc_frame(...) / await ws.send(...)
calls in a try/except that catches broad exceptions (at least Exception), logs
the error (include the raw incoming string and exception) and continues the loop
instead of letting the exception propagate, and optionally send a safe
error-response frame when appropriate; apply the same protection to the other
receive branch around the code at lines ~430-440 so both places use identical
try/except resilience around parsing and frame-building/sending.

Comment thread openclaw.json5
},
],
relativeIntervals: [{ id: "heartbeat-cycle", every: "15m", mode: "isolated" }],
absoluteTimes: [{ id: "daily-summary", at: "2026-01-01T00:00:00Z", mode: "isolated" }],
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

absoluteTimes is configured to a past timestamp.

Line 202 uses 2026-01-01T00:00:00Z, which is already in the past as of February 27, 2026. This can cause missed schedules or immediate catch-up behavior.

Suggested patch
-      absoluteTimes: [{ id: "daily-summary", at: "2026-01-01T00:00:00Z", mode: "isolated" }],
+      absoluteTimes: [{ id: "daily-summary", at: "2026-03-01T00:00:00Z", mode: "isolated" }],
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
absoluteTimes: [{ id: "daily-summary", at: "2026-01-01T00:00:00Z", mode: "isolated" }],
absoluteTimes: [{ id: "daily-summary", at: "2026-03-01T00:00:00Z", mode: "isolated" }],
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@openclaw.json5` at line 202, The absoluteTimes entry for id "daily-summary"
uses a past timestamp (at: "2026-01-01T00:00:00Z") which can trigger missed or
immediate catch-up runs; update the absoluteTimes configuration (the object with
id "daily-summary" and its at field) to a future ISO8601 timestamp or replace it
with a relative/recurring schedule instead so the scheduler won't treat it as a
past event.

Comment on lines +44 to +47
collisions = [port for port in PORTS if _port_in_use(port)]
if collisions:
print(f"[WARN] Ports currently in use: {', '.join(map(str, collisions))}")
else:
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Port collisions should fail mandatory preflight, not just warn.

Lines 44–47 currently keep exit code 0 when required ports are occupied, which weakens the preflight gate.

Suggested patch
     collisions = [port for port in PORTS if _port_in_use(port)]
     if collisions:
-        print(f"[WARN] Ports currently in use: {', '.join(map(str, collisions))}")
+        print(f"[FAIL] Ports currently in use: {', '.join(map(str, collisions))}")
+        return 1
     else:
         print("[OK] No expected runtime ports in use")
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
collisions = [port for port in PORTS if _port_in_use(port)]
if collisions:
print(f"[WARN] Ports currently in use: {', '.join(map(str, collisions))}")
else:
collisions = [port for port in PORTS if _port_in_use(port)]
if collisions:
print(f"[FAIL] Ports currently in use: {', '.join(map(str, collisions))}")
return 1
else:
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@scripts/openclaw_doctor.py` around lines 44 - 47, The preflight currently
only warns when required ports are occupied; change the logic so that after
computing collisions = [port for port in PORTS if _port_in_use(port)] the script
prints an error (not just a warn) and exits with a non-zero status (e.g.,
sys.exit(1)) when collisions is non-empty; update the branch that handles
collisions to use PORTS, _port_in_use and collisions to emit a clear error
message listing the ports and then terminate the process with a failing exit
code.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

codex size:XL This PR changes 500-999 lines, ignoring generated files

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant