openclaw - ✅(Solved) Fix [Feature]: Per-agent exec security mode (tools.exec.security per agent entry) [2 pull requests, 1 comments, 2 participants]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
openclaw/openclaw#52845Fetched 2026-04-08 01:18:35
View on GitHub
Comments
1
Participants
2
Timeline
2
Reactions
0
Author
Participants
Timeline (top)
commented ×1cross-referenced ×1

Add per-agent tools.exec.security configuration so individual agents can be locked to allowlist mode with their own binary allowlists, without requiring a single global security=full setting for all agents.

Root Cause

  • Global security=allowlist: Would require a unified allowlist covering all agents. Impractical because general-purpose agents (e.g., main, forge) call a wide and variable set of binaries depending on the task. Maintaining one shared allowlist is fragile and too broad to provide meaningful restriction.
  • Separate agent processes / sandbox mode: Sandbox is not available on all host setups (e.g., bare-metal Linux without container support). Per-agent exec security achieves meaningful hardening without requiring sandbox infrastructure.

PR fix notes

PR #55053: feat(guardrails): pluggable GuardrailProvider interface for tool authorization

Description (problem / solution / changelog)

Summary

  • Problem: OpenClaw has exec approvals for shell commands, but no general-purpose authorization for other tools -- file writes, browser actions, messaging, MCP tools, git operations. An agent can write to ~/.ssh/authorized_keys or send messages to arbitrary recipients with no policy check.
  • Why it matters: this has been requested across 10+ issues spanning the past few months (listed below). Security-conscious deployments have no standard way to enforce tool-level authorization. Every solution is ad-hoc and incompatible.
  • What changed: added a pluggable GuardrailProvider type as a core service (not a plugin). Ships with a built-in AllowlistProvider. Runs before plugin hooks -- cannot be bypassed. Zero impact when not configured.
  • What did NOT change (scope boundary): no changes to the steerable agent loop, exec approvals, plugin system, or any existing tool. No bundled external providers.

Change Type (select all)

  • Bug fix
  • Feature
  • Refactor required for the fix
  • Docs
  • Security hardening
  • Chore/infra

Scope (select all touched areas)

  • Gateway / orchestration
  • Skills / tool execution
  • Auth / tokens
  • Memory / storage
  • Integrations
  • API / contracts
  • UI / DX
  • CI/CD / infra

Linked Issue/PR

  • Closes #46441
  • Related #22068 (tool:before/tool:after internal hooks -- this PR builds on that infrastructure)
  • This PR fixes a bug or regression

Community issues this unblocks:

  • #513 -- Path-based access control rules
  • #1546 -- Per-group tool policies
  • #6823 -- Execution guardrails (agent deleted OAuth credentials)
  • #8081 -- Multi-user RBAC
  • #12202 -- Per-agent file path access control
  • #12617 -- Model output context to before_tool_call
  • #23900 -- Self-preservation guardrails for agents
  • #30504 -- Middleware hooks for agent protocol enforcement
  • #48304 -- Tool-level authorization policy
  • #48503 -- Enrich before_tool_call event
  • #49183 -- Per-session tool policies
  • #49342 -- Pre-tool execution hooks / middleware API
  • #52845 -- Per-agent exec security mode

Root Cause / Regression History (if applicable)

N/A

Regression Test Plan (if applicable)

  • Coverage level that should have caught this:
    • Unit test
    • Seam / integration test
    • End-to-end test
    • Existing coverage already sufficient
  • Target test or file: test/guardrails/builtin.test.ts, test/guardrails/index.test.ts
  • Scenario the test should lock in: AllowlistProvider allow/deny logic, evaluateGuardrail fail-closed/fail-open behavior, loadGuardrailProvider builtin and error paths.
  • Why this is the smallest reliable guardrail: the interface is a new subsystem with no existing callers, so unit tests at the provider and evaluation boundary cover the contract without broader integration setup.
  • Existing test that already covers this (if any): none -- this is a new subsystem.
  • If no new test is added, why not: N/A -- new tests are included.

User-visible / Behavior Changes

When guardrails.enabled is true in config.yaml, every tool call is evaluated against the configured provider before execution. If the provider denies, the tool is blocked and the agent sees a denial reason. When not configured, behavior is identical to before this PR.

Security Impact (required)

  • New permissions/capabilities? Yes
  • Secrets/tokens handling changed? No
  • New/changed network calls? No (AllowlistProvider is local-only; external providers may make calls but that is provider-specific)
  • Command/tool execution surface changed? Yes
  • Data access scope changed? No
  • If any Yes, explain risk and mitigation: this adds a pre-execution authorization gate for all tool calls. The gate is opt-in (disabled by default). When enabled, it runs before plugin hooks as a core service, so it cannot be bypassed by disabling a plugin. failClosed defaults to true, meaning provider errors block tool calls rather than allowing them. The AllowlistProvider has zero external dependencies.

Repro and Verification

Environment

  • OS: macOS
  • Runtime/container: Node 22 + pnpm workspace

Steps to verify

  1. Add to config.yaml:
guardrails:
  enabled: true
  provider:
    use: "builtin:allowlist"
    config:
      deniedTools:
        - "exec"
  1. Start OpenClaw
  2. Ask agent: "Run echo hello using exec"
  3. Expected: agent sees "Guardrail (allowlist): 'exec' is in the denied list"
  4. Remove "exec" from deniedTools, restart
  5. Same request now executes normally

Tests

pnpm test test/guardrails/

Changed files

  • docs/docs.json (modified, +1/-0)
  • docs/tools/guardrails.md (added, +307/-0)
  • src/agents/pi-tools.before-tool-call.ts (modified, +25/-0)
  • src/config/types.openclaw.ts (modified, +2/-0)
  • src/config/zod-schema.guardrails.ts (added, +21/-0)
  • src/config/zod-schema.ts (modified, +2/-0)
  • src/gateway/server-plugin-bootstrap.ts (modified, +2/-0)
  • src/guardrails/builtin.ts (added, +39/-0)
  • src/guardrails/index.ts (added, +88/-0)
  • src/guardrails/init.ts (added, +53/-0)
  • src/guardrails/types.ts (added, +59/-0)
  • test/guardrails/builtin.test.ts (added, +59/-0)
  • test/guardrails/index.test.ts (added, +105/-0)

PR #64868: feat(guardrails): add provider-based pre-tool-call authorization

Description (problem / solution / changelog)

Summary

  • Problem: OpenClaw had exec approvals for shell commands, but no general-purpose pre-tool-call authorization surface for other tools such as file writes, browser actions, messaging, MCP tools, and git operations.
  • Why it matters: policy-based authorization should run before tool execution and before plugin hooks, with a neutral interface that does not hard-code a specific vendor into core. This also creates a standard seam for several related community requests around tool-level authorization, pre-tool middleware, richer hook context, and session-scoped policy.
  • What changed: this adds a core GuardrailProvider seam, a minimal built-in allowlist provider, config-driven provider loading, fail-open/fail-closed behavior, and docs for built-in, Open Agent Passport (OAP), and custom adapter-based integrations.
  • What did NOT change (scope boundary): this PR does not bundle APort or Microsoft tooling into OpenClaw core, does not add human approval UX, and does not change existing exec approvals behavior.

Change Type (select all)

  • Bug fix
  • Feature
  • Refactor required for the fix
  • Docs
  • Security hardening
  • Chore/infra

Scope (select all touched areas)

  • Gateway / orchestration
  • Skills / tool execution
  • Auth / tokens
  • Memory / storage
  • Integrations
  • API / contracts
  • UI / DX
  • CI/CD / infra

Linked Issue/PR

  • Closes openclaw/openclaw#46441
  • Related openclaw/openclaw#48532
  • Supersedes closed PR openclaw/openclaw#55053
  • Related community requests this builds toward:
    • openclaw/openclaw#48304 [Feature]: Tool-level authorization policy — per-action, per-sender validation for message tool
    • openclaw/openclaw#49342 Feature Request: Pre-tool execution hooks / middleware API
    • openclaw/openclaw#49183 RFC: Per-session tool policies — bridging operator scopes and agent tool access
    • openclaw/openclaw#48503 [Feature]: Enrich before_tool_call event with action classification and input provenance
    • openclaw/openclaw#52845 [Feature]: Per-agent exec security mode (tools.exec.security per agent entry)
  • This PR fixes a bug or regression

Root Cause (if applicable)

  • Root cause: N/A
  • Missing detection / guardrail: N/A
  • Contributing context (if known): N/A

Regression Test Plan (if applicable)

  • Coverage level that should have caught this:
    • Unit test
    • Seam / integration test
    • End-to-end test
    • Existing coverage already sufficient
  • Target test or file: N/A
  • Scenario the test should lock in: N/A
  • Why this is the smallest reliable guardrail: N/A
  • Existing test that already covers this (if any): N/A
  • If no new test is added, why not: N/A

User-visible / Behavior Changes

  • New optional guardrails: config surface
  • New built-in builtin:allowlist provider
  • Guardrail evaluation now runs before plugin before_tool_call hooks when enabled
  • New docs for OpenClaw guardrails, including:
    • built-in allowlist
    • APort as the reference OAP provider
    • Microsoft Agent Governance Toolkit as an adapter/sidecar example
    • custom providers through the same interface

Diagram (if applicable)

Before:
agent tool call -> plugin before_tool_call hooks -> tool executes

After:
agent tool call -> GuardrailProvider -> plugin before_tool_call hooks -> tool executes
                   | allow/deny |

Security Impact (required)

  • New permissions/capabilities? Yes
  • Secrets/tokens handling changed? No
  • New/changed network calls? No in core; provider-dependent for external providers
  • Command/tool execution surface changed? Yes
  • Data access scope changed? No
  • If any Yes, explain risk + mitigation:

This adds a new authorization interception layer for tool execution. Risk is primarily around provider initialization failures, provider exceptions, or accidental bypass in alternate entry points. Mitigations in this PR:

  • evaluate before plugin hooks
  • require guardrails.provider.use when enabled
  • initialize from both gateway bootstrap and agent tool creation paths
  • support explicit fail-closed behavior
  • dedupe initialization and retry after failed load
  • avoid surfacing raw provider exception text back to the agent on fail-closed paths

Repro + Verification

Environment

  • OS: macOS
  • Runtime/container: local Node/pnpm dev environment
  • Model/provider: N/A
  • Integration/channel (if any): N/A
  • Relevant config (redacted): local guardrails: configs for built-in allowlist and provider-loading scenarios

Steps

  1. Configure guardrails.enabled: true with either builtin:allowlist or a provider package/file
  2. Start OpenClaw and issue a tool call such as exec or write
  3. Verify the provider evaluates before plugin hooks and blocks or allows as configured

Expected

  • guardrails are initialized once per config
  • allowed tool calls proceed normally
  • denied tool calls are blocked before execution
  • fail-closed providers block with a generic user-facing reason and log raw provider errors separately

Actual

  • matched expected behavior in the targeted scenarios below

Evidence

Attach at least one:

  • Failing test/log before + passing after
  • Trace/log snippets
  • Screenshot/recording
  • Perf numbers (if relevant)

Human Verification (required)

  • Verified scenarios:
    • built-in allowlist evaluation
    • provider decision handling
    • fail-closed generic error behavior
    • initialization dedupe and retry behavior
    • docs/examples alignment for APort and Microsoft based on their current repos
  • Edge cases checked:
    • missing provider config when enabled
    • provider load failure retry
    • duplicate init across entry points
    • raw provider error text not returned to the agent on fail-closed path
  • What you did not verify:
    • full CI matrix on Windows/macOS runners
    • full pnpm test and pnpm test:channels suites
    • external APort or Microsoft runtime execution end-to-end inside OpenClaw

Review Conversations

  • I replied to or resolved every bot review conversation I addressed in this PR.
  • I left unresolved only the conversations that still need reviewer or maintainer judgment.

Compatibility / Migration

  • Backward compatible? Yes
  • Config/env changes? No
  • Migration needed? No
  • If yes, exact upgrade steps:

Risks and Mitigations

  • Risk: an enabled provider may not initialize on every tool execution entry point
    • Mitigation: guardrails now initialize from both gateway bootstrap and agent tool creation paths
  • Risk: provider errors could accidentally fail open or leak raw internals to the agent
    • Mitigation: explicit fail-closed behavior plus generic user-facing block reason and logged raw provider error
  • Risk: docs could overstate provider support in core
    • Mitigation: docs now keep OpenClaw core neutral, have APort as the reference Open Agent Passport provider, and position Microsoft as an adapter/sidecar example rather than a native core provider

Changed files

  • docs/.generated/config-baseline.sha256 (modified, +2/-2)
  • docs/.i18n/glossary.zh-CN.json (modified, +4/-0)
  • docs/docs.json (modified, +1/-0)
  • docs/tools/guardrails.md (added, +423/-0)
  • src/agents/pi-tool-definition-adapter.ts (modified, +2/-34)
  • src/agents/pi-tools.before-tool-call.e2e.test.ts (modified, +114/-0)
  • src/agents/pi-tools.before-tool-call.ts (modified, +83/-5)
  • src/agents/pi-tools.ts (modified, +9/-1)
  • src/agents/tool-params.ts (added, +28/-0)
  • src/config/schema.base.generated.ts (modified, +30/-0)
  • src/config/types.openclaw.ts (modified, +2/-0)
  • src/config/zod-schema.guardrails.ts (added, +21/-0)
  • src/config/zod-schema.ts (modified, +2/-0)
  • src/gateway/server-plugin-bootstrap.ts (modified, +2/-0)
  • src/guardrails/builtin.ts (added, +45/-0)
  • src/guardrails/index.ts (added, +136/-0)
  • src/guardrails/init.ts (added, +125/-0)
  • src/guardrails/runtime.ts (added, +22/-0)
  • src/guardrails/types.ts (added, +59/-0)
  • src/mcp/plugin-tools-serve.test.ts (modified, +47/-0)
  • src/mcp/plugin-tools-serve.ts (modified, +7/-0)
  • test/guardrails/builtin.test.ts (added, +59/-0)
  • test/guardrails/index.test.ts (added, +248/-0)
  • test/guardrails/init.test.ts (added, +293/-0)

Code Example

{
  "agents": {
    "list": [
      {
        "id": "monitor",
        "tools": {
          "exec": {
            "security": "allowlist",
            "allowlist": [
              "/usr/bin/python3",
              "/usr/bin/curl",
              "/usr/bin/ssh",
              "/usr/bin/sshpass",
              "/usr/bin/age",
              "/usr/bin/bash",
              "/usr/bin/free",
              "/usr/bin/df",
              "/usr/bin/systemctl"
            ]
          }
        }
      },
      {
        "id": "main",
        "tools": {
          "exec": {
            "security": "full"
          }
        }
      }
    ]
  }
}
RAW_BUFFERClick to expand / collapse

Summary

Add per-agent tools.exec.security configuration so individual agents can be locked to allowlist mode with their own binary allowlists, without requiring a single global security=full setting for all agents.

Problem to solve

Currently tools.exec.security is a global setting under tools.exec and applies identically to all agents. Operators who want to harden a narrow-scope agent (e.g., a monitoring agent that only calls a small, known set of binaries) cannot do so without also restricting all other agents — including general-purpose ones that legitimately need broader exec access.

The consequence is an all-or-nothing tradeoff: either every agent runs security=full (broad trust for all), or every agent is constrained to the same allowlist (breaks general-purpose workflows). There is no middle ground.

Proposed solution

Allow tools.exec.security (and tools.exec.allowlist) to be specified per agent entry inside agents.list[], following the same override pattern already used for other per-agent settings (e.g., tools.profile, tools.deny):

{
  "agents": {
    "list": [
      {
        "id": "monitor",
        "tools": {
          "exec": {
            "security": "allowlist",
            "allowlist": [
              "/usr/bin/python3",
              "/usr/bin/curl",
              "/usr/bin/ssh",
              "/usr/bin/sshpass",
              "/usr/bin/age",
              "/usr/bin/bash",
              "/usr/bin/free",
              "/usr/bin/df",
              "/usr/bin/systemctl"
            ]
          }
        }
      },
      {
        "id": "main",
        "tools": {
          "exec": {
            "security": "full"
          }
        }
      }
    ]
  }
}

The global tools.exec.security would remain as the default/fallback for agents that do not override it.

Alternatives considered

  • Global security=allowlist: Would require a unified allowlist covering all agents. Impractical because general-purpose agents (e.g., main, forge) call a wide and variable set of binaries depending on the task. Maintaining one shared allowlist is fragile and too broad to provide meaningful restriction.
  • Separate agent processes / sandbox mode: Sandbox is not available on all host setups (e.g., bare-metal Linux without container support). Per-agent exec security achieves meaningful hardening without requiring sandbox infrastructure.

Impact

  • Affected: Operators running multiple agents with different trust requirements (e.g., a narrow monitoring agent alongside a general-purpose assistant agent)
  • Severity: Workflow blocker for security-conscious operators — the only current option is security=full for all agents
  • Frequency: Constant — applies to every exec call across all agent sessions
  • Consequence: Operators cannot enforce least-privilege on individual agents; any agent could theoretically execute arbitrary binaries if the agent were manipulated (e.g., via prompt injection through external data). Scoped allowlists per agent would contain this risk.

Evidence/examples

The pattern of per-agent tool overrides already exists in the config (e.g., tools.deny per agent). Extending the same pattern to tools.exec is consistent with the existing architecture. Common monitoring/automation agents have a small, well-defined binary footprint that is trivially expressible as an allowlist.

Additional information

  • The global tools.exec.ask and tools.exec.strictInlineEval settings should follow the same per-agent override pattern for consistency.
  • Backward compatibility: if no per-agent tools.exec override is set, behavior falls back to the global tools.exec config unchanged.

extent analysis

Fix Plan

To implement per-agent tools.exec.security configuration, follow these steps:

  • Update the configuration file to include tools.exec.security and tools.exec.allowlist settings per agent in the agents.list[] section.
  • Set the security mode to allowlist for agents that require restricted execution.
  • Specify the allowed binaries in the allowlist array for each agent.

Example configuration:

{
  "agents": {
    "list": [
      {
        "id": "monitor",
        "tools": {
          "exec": {
            "security": "allowlist",
            "allowlist": [
              "/usr/bin/python3",
              "/usr/bin/curl",
              "/usr/bin/ssh",
              "/usr/bin/sshpass",
              "/usr/bin/age",
              "/usr/bin/bash",
              "/usr/bin/free",
              "/usr/bin/df",
              "/usr/bin/systemctl"
            ]
          }
        }
      },
      {
        "id": "main",
        "tools": {
          "exec": {
            "security": "full"
          }
        }
      }
    ]
  }
}
  • Ensure backward compatibility by maintaining the global tools.exec configuration as the default for agents without per-agent overrides.

Verification

To verify the fix:

  • Check that agents with security=allowlist can only execute binaries specified in their allowlist.
  • Verify that agents with security=full can execute any binary.
  • Test that agents without per-agent tools.exec overrides fall back to the global tools.exec configuration.

Extra Tips

  • When specifying allowlists, ensure that all required binaries are included to avoid workflow disruptions.
  • Consider implementing a mechanism to review and update allowlists regularly to maintain security and functionality.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING

openclaw - ✅(Solved) Fix [Feature]: Per-agent exec security mode (tools.exec.security per agent entry) [2 pull requests, 1 comments, 2 participants]