Skip to content

fix(exec): gate versioned inline interpreters#96216

Merged
eleqtrizit merged 3 commits into
openclaw:mainfrom
eleqtrizit:772
Jun 24, 2026
Merged

fix(exec): gate versioned inline interpreters#96216
eleqtrizit merged 3 commits into
openclaw:mainfrom
eleqtrizit:772

Conversation

@eleqtrizit

@eleqtrizit eleqtrizit commented Jun 23, 2026

Copy link
Copy Markdown
Contributor

Summary

Tightens strict inline-eval approval detection so additional interpreter forms cannot bypass the existing approval guard when interpreter executables are otherwise allowlisted.

Changes

  • Matches version-suffixed Python and PyPy executable names when resolving inline-eval interpreter specs.
  • Adds PHP inline-code flags -B, -E, and -R, and R/Rscript -e to the detector table.
  • Applies the same interpreter matching to allowlist-pattern recognition so allow-always persistence remains suppressed for these interpreter targets.
  • Adds focused detector and handler-level regression coverage.
  • Removes the unsupported R --exec detector case after upstream/R CLI contract review.

Validation

  • node scripts/run-vitest.mjs src/infra/command-analysis/inline-eval.test.ts src/node-host/invoke-system-run.test.ts — 2 files, 65 tests passed.
  • ./node_modules/.bin/oxfmt --check --threads=1 src/infra/command-analysis/inline-eval.ts src/infra/command-analysis/inline-eval.test.ts src/node-host/invoke-system-run.test.ts
  • git diff --check
  • .agents/skills/autoreview/scripts/autoreview --mode local — clean, no accepted/actionable findings.
  • Terminal production-handler proof: direct handleSystemRunInvoke run with strictInlineEval=true showed python3.13 -c denied before execution (runCommandCalls: 0, SYSTEM_RUN_DENIED: approval required ... strictInlineEval mode) and an explicit allow-always approval ran once without persisting an allowlist entry (persistedAllowlistEntries: 0).

Notes

Security-sensitive report details are tracked privately. CHANGELOG.md was not updated.

@openclaw-barnacle openclaw-barnacle Bot added size: S maintainer Maintainer-authored PR labels Jun 23, 2026
@eleqtrizit

eleqtrizit commented Jun 23, 2026

Copy link
Copy Markdown
Contributor Author

Superseded by the current behavioral proof comment below. The latest proof removes the stale R --exec references and records the fresh node-host/system.run validation from commit f420505b85ba838b3912bab1e5ab6caef6f35fb9.

@clawsweeper

clawsweeper Bot commented Jun 23, 2026

Copy link
Copy Markdown
Contributor

🦞🧹
ClawSweeper re-review requested.

I asked ClawSweeper to review this item again.
Action: item re-review queued (workflow sweep.yml, event repository_dispatch).
Result: the existing ClawSweeper review comment will be edited in place when the review finishes.

@clawsweeper

clawsweeper Bot commented Jun 24, 2026

Copy link
Copy Markdown
Contributor

Codex review: needs maintainer review before merge. Reviewed June 24, 2026, 2:58 PM ET / 18:58 UTC.

Summary
The branch broadens strict inline-eval detection to versioned interpreter names, PHP/R inline flags, matching allow-always suppression, and focused detector/node-host tests.

PR surface: Source +35, Tests +19. Total +54 across 3 files.

Reproducibility: yes. Current main exact-matches interpreter names while strictInlineEval consumes that detector, and the contributor's redacted node-host proof shows the fixed head denying python3.13 -c before execution and keeping allow-always one-shot.

Review metrics: 1 noteworthy metric.

  • Strict inline-eval coverage: 6 positives added, 4 negatives added, 2 handler checks touched. These cases change which already-configured strictInlineEval commands require explicit approval and how allow-always persists.

Stored data model
Persistent data-model change detected: persistent cache schema: src/infra/command-analysis/inline-eval.test.ts. Confirm migration or upgrade compatibility proof before merge.

Merge readiness
Overall: 🦞 diamond lobster
Proof: 🦞 diamond lobster
Patch quality: 🦞 diamond lobster
Result: ready for maintainer review.

Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch.

Risk before merge

  • [P1] StrictInlineEval users with existing allowlisted versioned Python/PyPy, PHP -B/-E/-R, or R/Rscript -e workflows will now receive explicit approval prompts and allow-always will stay one-shot for those inline carriers.
  • [P1] The PR has a protected maintainer label and private security-sensitive context, so explicit maintainer/security acceptance is still required before merge even though the code review is clean.

Maintainer options:

  1. Accept stricter strictInlineEval prompts (recommended)
    A maintainer/security reviewer can merge once they accept that these additional inline interpreter forms no longer reuse stored allow-always trust.
  2. Narrow matching before merge
    If the compatibility blast radius is too broad, restrict version-suffix normalization to the interpreter families maintainers explicitly want covered and keep the collision tests.
  3. Pause for private security review
    Keep the PR paused if the private report or upgrade impact needs owner review before changing the approval surface.

Next step before merge

  • [P2] The branch now has sufficient real proof and no code findings, but protected maintainer/private security context plus compatibility impact need explicit human acceptance.

Security
Cleared: The diff hardens exec approval detection and changes only detector/test code, with no new dependency, workflow, secret, or supply-chain surface.

Review details

Best possible solution:

Land the shared detector hardening after explicit maintainer/security acceptance of the stricter approval behavior, without adding a parallel runtime-specific detector.

Do we have a high-confidence way to reproduce the issue?

Yes. Current main exact-matches interpreter names while strictInlineEval consumes that detector, and the contributor's redacted node-host proof shows the fixed head denying python3.13 -c before execution and keeping allow-always one-shot.

Is this the best way to solve the issue?

Yes. The shared command-analysis detector is the best boundary because node-host, gateway/bash approval flows, audit checks, and allow-always persistence all consume it; duplicating checks in one runtime would be narrower and easier to drift.

AGENTS.md: found and applied where relevant.

Codex review notes: model internal, reasoning high; reviewed against b6bc3ed0dbd8.

Label changes

Label changes:

  • add proof: sufficient: Contributor real behavior proof is sufficient. The updated PR comment includes redacted terminal/log proof from a node-host/system.run setup showing denial, one approved run, and no persisted allow-always entry.
  • add rating: 🦞 diamond lobster: Overall readiness is 🦞 diamond lobster; proof is 🦞 diamond lobster and patch quality is 🦞 diamond lobster.
  • add status: 👀 ready for maintainer look: ClawSweeper has no concrete contributor-facing blocker left for this PR. Sufficient (terminal): The updated PR comment includes redacted terminal/log proof from a node-host/system.run setup showing denial, one approved run, and no persisted allow-always entry.
  • remove rating: 🧂 unranked krab: Current PR rating is rating: 🦞 diamond lobster, so this older rating label is no longer current.
  • remove status: 📣 needs proof: Current PR status label is status: 👀 ready for maintainer look.

Label justifications:

  • P1: The PR addresses a security-sensitive exec approval guard bypass affecting real command-execution workflows for strictInlineEval users.
  • merge-risk: 🚨 compatibility: The diff intentionally makes additional previously allowlisted interpreter command forms require explicit approval during upgrade.
  • rating: 🦞 diamond lobster: Overall readiness is 🦞 diamond lobster; proof is 🦞 diamond lobster and patch quality is 🦞 diamond lobster.
  • status: 👀 ready for maintainer look: ClawSweeper has no concrete contributor-facing blocker left for this PR. Sufficient (terminal): The updated PR comment includes redacted terminal/log proof from a node-host/system.run setup showing denial, one approved run, and no persisted allow-always entry.
  • proof: sufficient: Contributor real behavior proof is sufficient. The updated PR comment includes redacted terminal/log proof from a node-host/system.run setup showing denial, one approved run, and no persisted allow-always entry.
Evidence reviewed

PR surface:

Source +35, Tests +19. Total +54 across 3 files.

View PR surface stats
Area Files Added Removed Net
Source 1 41 6 +35
Tests 2 20 1 +19
Docs 0 0 0 0
Config 0 0 0 0
Generated 0 0 0 0
Other 0 0 0 0
Total 3 61 7 +54

What I checked:

Likely related people:

  • jacobtomlinson: Authored merged strict inline-eval and interpreter allow-always persistence work that established the invariant extended by this PR. (role: feature-history owner; confidence: high; commits: 1a75906a6fe5, 7bd2761b92ee; files: src/infra/exec-approvals-allowlist.ts, src/node-host/invoke-system-run.test.ts)
  • steipete: Introduced the shared command-analysis inline-eval detector and has recent merged work in exec approval trust paths. (role: shared detector introducer and recent area contributor; confidence: high; commits: bd0e10a2f68f, 524185a68ea2, 3e452f267139; files: src/infra/command-analysis/inline-eval.ts, src/infra/exec-approvals-allowlist.ts, src/node-host/invoke-system-run.ts)
  • jesse-merhi: Recently rebuilt the command authorization planner across the allowlist, persistence, and node-host system.run path touched by this behavior. (role: recent authorization planner refactor owner; confidence: medium; commits: c9707ab635b9; files: src/infra/exec-approvals-allowlist.ts, src/infra/exec-approvals.ts, src/node-host/invoke-system-run.ts)
  • luoyanglang: Authored adjacent work keeping strict inline-eval interpreter approvals reusable and is credited in the follow-up boundary preservation commit. (role: adjacent strict-inline-eval contributor; confidence: medium; commits: 7c83cae42542, 3e452f267139; files: src/infra/exec-approvals-allowlist.ts, src/infra/exec-approvals-allow-always.test.ts)
What the crustacean ranks mean
  • 🦀 challenger crab: rare, exceptional readiness with strong proof, clean implementation, and convincing validation.
  • 🦞 diamond lobster: very strong readiness with only minor maintainer review expected.
  • 🐚 platinum hermit: good normal PR, likely mergeable with ordinary maintainer review.
  • 🦐 gold shrimp: useful signal, but proof or patch confidence is still limited.
  • 🦪 silver shellfish: thin signal; proof, validation, or implementation needs work.
  • 🧂 unranked krab: not merge-ready because proof is missing/unusable or there are serious correctness or safety concerns.
  • 🌊 off-meta tidepool: rating does not apply to this item.

Shiny media proof means a screenshot, video, or linked artifact directly shows the changed behavior. Runtime, network, CSP, and security claims still need visible diagnostics.

How this review workflow works
  • ClawSweeper keeps one durable marker-backed review comment per issue or PR.
  • Re-runs edit this comment so the latest verdict, findings, and automation markers stay together instead of adding duplicate bot comments.
  • A fresh review can be triggered by eligible @clawsweeper re-review comments, exact-item GitHub events, scheduled/background review runs, or manual workflow dispatch.
  • PR/issue authors and users with repository write access can comment @clawsweeper re-review or @clawsweeper re-run on an open PR or issue to request a fresh review only.
  • Maintainers can also comment @clawsweeper review to request a fresh review only.
  • Fresh-review commands do not start repair, autofix, rebase, CI repair, or automerge.
  • Maintainer-only repair and merge flows require explicit commands such as @clawsweeper autofix, @clawsweeper automerge, @clawsweeper fix ci, or @clawsweeper address review.
  • Maintainers can comment @clawsweeper explain to ask for more context, or @clawsweeper stop to stop active automation.

@clawsweeper clawsweeper Bot added rating: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. P1 High-priority user-facing bug, regression, or broken workflow. merge-risk: 🚨 compatibility 🚨 May break existing users, config, migrations, defaults, or upgrade paths. labels Jun 24, 2026
@eleqtrizit

eleqtrizit commented Jun 24, 2026

Copy link
Copy Markdown
Contributor Author

Behavioral proof

This addresses the current ClawSweeper ask for redacted real node-host/system.run proof, not only mocked handler tests.

Head checked: 3a087080b03526cfc45c0c90cb2aa0ca2fa80034.

What is now proven:

  • The unsupported R --exec detector case is gone. Current R coverage is R/Rscript -e only.
  • The r2 -e ... collision is guarded: one-letter stripped names are not synthesized, and r2 has a negative detector/allowlist test.
  • A real node-host system.run invocation with strictInlineEval=true, security=allowlist, ask=on-miss, and an allowlisted versioned interpreter name is denied before execution.
  • The same node-host path, when explicitly approved with allow-always, runs once and does not persist any source: "allow-always" entry for the inline carrier.

Code points checked:

  • src/infra/command-analysis/inline-eval.ts:37 defines version-suffix stripping.
  • src/infra/command-analysis/inline-eval.ts:52-61 covers PHP -B/-E/-R and R/Rscript -e; no --exec entry exists.
  • src/infra/command-analysis/inline-eval.ts:173-177 prevents one-letter stripped variants such as r2 becoming r.
  • src/infra/command-analysis/inline-eval.test.ts:21-28 covers the positive detector cases.
  • src/infra/command-analysis/inline-eval.test.ts:67-73 covers normal script/file negatives and the r2 -e collision negative.
  • src/node-host/invoke-system-run.test.ts:1596-1634 proves strict inline-eval denial at the handler boundary.
  • src/node-host/invoke-system-run.test.ts:1662-1699 proves explicit allow-always approval is one-shot for strict inline-eval carriers.

Redacted live node-host/system.run proof:

$ node --import tsx .artifacts/openclaw-local-checks/strict-inline-eval-live-proof.ts
{
  "setup": {
    "root": "<redacted-temp-dir>",
    "executable": "<redacted-temp-dir>/bin/python3.13",
    "strictInlineEval": true,
    "security": "allowlist",
    "ask": "on-miss"
  },
  "denied": {
    "command": [
      "<redacted-temp-dir>/bin/python3.13",
      "-c",
      "DENIED_SHOULD_NOT_RUN"
    ],
    "markerExists": false,
    "logs": [
      "{\"method\":\"node.event\",\"params\":{\"event\":\"exec.denied\",\"payloadJSON\":\"{\\\"sessionKey\\\":\\\"agent:main:main\\\",\\\"runId\\\":\\\"3d211009-f0c0-4836-9a00-966fc65e8218\\\",\\\"host\\\":\\\"node\\\",\\\"command\\\":\\\"<redacted-temp-dir>/bin/python3.13 -c DENIED_SHOULD_NOT_RUN\\\",\\\"reason\\\":\\\"approval-required\\\",\\\"suppressNotifyOnExit\\\":false}\"}}",
      "{\"method\":\"node.invoke.result\",\"params\":{\"id\":\"deny-versioned-inline\",\"nodeId\":\"proof-node\",\"ok\":false,\"error\":{\"code\":\"UNAVAILABLE\",\"message\":\"SYSTEM_RUN_DENIED: approval required (python3.13 -c requires explicit approval in strictInlineEval mode)\"}}}"
    ]
  },
  "approved": {
    "command": [
      "<redacted-temp-dir>/bin/python3.13",
      "-c",
      "APPROVED_RAN_ONCE"
    ],
    "markerExists": true,
    "markerText": "APPROVED_RAN_ONCE",
    "logs": [
      "{\"method\":\"node.event\",\"params\":{\"event\":\"exec.finished\",\"payloadJSON\":\"{\\\"sessionKey\\\":\\\"agent:main:main\\\",\\\"runId\\\":\\\"1827c854-a43e-42f5-a267-527b5b7d39b9\\\",\\\"host\\\":\\\"node\\\",\\\"command\\\":\\\"<redacted-temp-dir>/bin/python3.13 -c APPROVED_RAN_ONCE\\\",\\\"exitCode\\\":0,\\\"timedOut\\\":false,\\\"success\\\":true,\\\"output\\\":\\\"LIVE_RUN_OK:-c\\\"}\"}}",
      "{\"method\":\"node.invoke.result\",\"params\":{\"id\":\"approved-versioned-inline\",\"nodeId\":\"proof-node\",\"ok\":true,\"payloadJSON\":\"{\\\"exitCode\\\":0,\\\"timedOut\\\":false,\\\"success\\\":true,\\\"stdout\\\":\\\"LIVE_RUN_OK:-c\\\\n\\\",\\\"stderr\\\":\\\"\\\",\\\"error\\\":null}\"}}"
    ]
  },
  "persistedAllowAlwaysEntries": []
}

Interpretation:

  • Denial path: exec.denied + ok:false with the strict inline-eval message; markerExists:false proves the temp executable was not spawned.
  • Approved path: exec.finished + ok:true; markerExists:true and markerText:"APPROVED_RAN_ONCE" prove the node-host spawned the temp executable once after explicit approval.
  • Persistence path: persistedAllowAlwaysEntries: [] proves the allow-always approval did not become a durable allowlist entry for the inline carrier.

Focused validation:

$ OPENCLAW_HEAVY_CHECK_LOCK_SCOPE=worktree node scripts/run-vitest.mjs src/infra/command-analysis/inline-eval.test.ts src/node-host/invoke-system-run.test.ts
[test] starting test/vitest/vitest.unit-fast.config.ts

 RUN  v4.1.8 /app

 Test Files  2 passed (2)
      Tests  65 passed (65)
   Start at  18:49:14
   Duration  274ms (transform 56ms, setup 0ms, import 124ms, tests 117ms, environment 0ms)

[test] passed 1 Vitest shard in 3.80s
$ OPENCLAW_HEAVY_CHECK_LOCK_SCOPE=worktree node scripts/run-vitest.mjs src/node-host/invoke-system-run.test.ts -- --testNamePattern "requires explicit approval for strict inline-eval carriers|does not persist allow-always approvals for strict inline-eval carriers" --reporter verbose
[test] starting test/vitest/vitest.unit-fast.config.ts

 RUN  v4.1.8 /app

 ✓ |unit-fast| src/node-host/invoke-system-run.test.ts > handleSystemRunInvoke mac app exec host routing > requires explicit approval for strict inline-eval carriers 7ms
 ✓ |unit-fast| src/node-host/invoke-system-run.test.ts > handleSystemRunInvoke mac app exec host routing > does not persist allow-always approvals for strict inline-eval carriers 3ms

 Test Files  1 passed (1)
      Tests  2 passed | 31 skipped (33)
   Start at  18:49:24
   Duration  193ms (transform 48ms, setup 0ms, import 104ms, tests 22ms, environment 0ms)

[test] passed 1 Vitest shard in 3.61s
$ ./node_modules/.bin/oxfmt --check --threads=1 src/infra/command-analysis/inline-eval.ts src/infra/command-analysis/inline-eval.test.ts src/node-host/invoke-system-run.test.ts .artifacts/openclaw-local-checks/strict-inline-eval-live-proof.ts
Checking formatting...

All matched files use the correct format.
Finished in 9ms on 4 files using 1 threads.

@clawsweeper

clawsweeper Bot commented Jun 24, 2026

Copy link
Copy Markdown
Contributor

🦞🧹
ClawSweeper re-review requested.

I asked ClawSweeper to review this item again.
Action: item re-review queued (workflow sweep.yml, event repository_dispatch).
Result: the existing ClawSweeper review comment will be edited in place when the review finishes.

@clawsweeper clawsweeper Bot added rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. and removed rating: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. labels Jun 24, 2026
@clawsweeper

clawsweeper Bot commented Jun 24, 2026

Copy link
Copy Markdown
Contributor

🦞🧹
ClawSweeper re-review requested.

I asked ClawSweeper to review this item again.
Action: item re-review queued (workflow sweep.yml, event repository_dispatch).
Result: the existing ClawSweeper review comment will be edited in place when the review finishes.

@clawsweeper

clawsweeper Bot commented Jun 24, 2026

Copy link
Copy Markdown
Contributor

🦞👀
ClawSweeper picked this up.

Command router queued. I will update this comment with the next step.

@eleqtrizit

Copy link
Copy Markdown
Contributor Author

@clawsweeper re-review

@clawsweeper clawsweeper Bot added proof: sufficient ClawSweeper judged the real behavior proof convincing. rating: 🦞 diamond lobster Very strong PR readiness with only minor maintainer review expected. status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR. and removed rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. labels Jun 24, 2026
@eleqtrizit
eleqtrizit merged commit 3168987 into openclaw:main Jun 24, 2026
118 of 126 checks passed
github-actions Bot pushed a commit to Desicool/openclaw that referenced this pull request Jun 25, 2026
* fix(exec): gate versioned inline interpreters

* fix(exec): trim unsupported R inline flag

* fix(exec): avoid r2 inline eval collision
@eleqtrizit
eleqtrizit deleted the 772 branch June 30, 2026 19:27
QiuYuang pushed a commit to QiuYuang/openclaw that referenced this pull request Jul 1, 2026
* fix(exec): gate versioned inline interpreters

* fix(exec): trim unsupported R inline flag

* fix(exec): avoid r2 inline eval collision
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

maintainer Maintainer-authored PR merge-risk: 🚨 compatibility 🚨 May break existing users, config, migrations, defaults, or upgrade paths. P1 High-priority user-facing bug, regression, or broken workflow. proof: sufficient ClawSweeper judged the real behavior proof convincing. rating: 🦞 diamond lobster Very strong PR readiness with only minor maintainer review expected. size: S status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant