Skip to content

feat: add /trust and /untrust commands for temporary exec security elevation#805

Open
BingqingLyu wants to merge 16 commits into
mainfrom
fork-pr-47696-feature-trust-command
Open

feat: add /trust and /untrust commands for temporary exec security elevation#805
BingqingLyu wants to merge 16 commits into
mainfrom
fork-pr-47696-feature-trust-command

Conversation

@BingqingLyu

@BingqingLyu BingqingLyu commented Apr 27, 2026

Copy link
Copy Markdown
Owner

Summary

  • Problem: Existing exec approval workflows are binary and permanent. Users get inundated with confirmation requests and likely disable approvals entirely, defeating the security model.
  • Why it matters: This feature increases usable security surface by introducing temporary time-bound escalation, a pattern common in sudo timeouts and auth tokens. Users can grant temporary full access as needed instead of permanently disabling protections.
  • What changed: Added /trust [minutes] and /untrust slash commands with in-memory-only state, auto-expiry, anti-stacking, and a single enforcement point for all surfaces. Doesn't touch the security model, just acts as a wrapper for existing exec_security setting.
  • What did NOT change (scope boundary): No changes to existing /exec behavior, config schema, persistence layer, or approval flows. No per-surface wiring — uses existing command registry

Change Type (select all)

  • Bug fix
  • Feature
  • Refactor
  • 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

User-visible / Behavior Changes

  • New /trust [minutes] command: temporarily sets exec.security=full for the current session (1-480 minutes, default 15)
  • New /untrust command: immediately revokes temporary trust
  • Anti-stacking: /trust while already trusted is refused with remaining time shown
  • Trust is in-memory only — lost on process restart (fail-closed by design)
  • Available on all surfaces (CLI, TUI, Discord, Telegram, Slack, WhatsApp, web) via existing command registry

Security Impact (required)

  • New permissions/capabilities? Yes — temporary exec escalation to security=full
  • Secrets/tokens handling changed? No
  • New/changed network calls? No
  • Command/tool execution surface changed? Yes — two new slash commands that affect exec security resolution
  • Data access scope changed? No

Risk + mitigation:

  • Risk: Unauthorized user could escalate exec permissions
    • Mitigation: Gated by existing rejectUnauthorizedCommand — same auth check as /exec
  • Risk: Trust could persist beyond intended window
    • Mitigation: In-memory only (Map with setTimeout), never written to disk, lost on restart (fail-closed). Timer uses .unref() matching codebase convention
  • Risk: Trust could leak across sessions
    • Mitigation: Keyed by sessionKey, same scoping as all other session state
  • Risk: Anti-stacking bypass via /untrust then /trust
    • Mitigation: This is intentional and strictly less powerful than existing /exec security=full which is permanent. Cycling requires explicit authorized user action each time
  • Design: Trust sits in the exec resolution chain at inline directive > trust > session entry, matching existing /exec precedence. Does not bypass agent-level exec ceilings in exec-approvals.json

Repro + Verification

Environment

  • OS: Fedora Linux
  • Runtime/container: OpenClaw v3.14 from keylimesoda/openclaw branch
  • Model/provider: Any
  • Integration/channel (if any): All surfaces (tested CLI)
  • Relevant config (redacted): Default exec config

Steps

  1. Start a session
  2. Send /trust 15
  3. Observe trust grant message with duration
  4. Send /trust 30 — observe anti-stacking refusal
  5. Send /untrust — observe revocation
  6. Send /trust 5 — observe re-grant works after untrust
  7. Wait for expiry — observe trust auto-revokes

Expected

  • Trust granted with clear feedback, refused when already active, revocable, auto-expires

Actual

  • All behaviors match expected. All user-facing strings reviewed and refined

Evidence

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

7/7 unit tests pass covering: default window, anti-stacking, auto-expiry, /untrust revocation, bounds validation, unauthorized rejection, numerical input scrubbing (0, -5, 15.5, 481, abc, 1e5).

Build (pnpm build), typecheck (pnpm tsgo), and format all pass.

Human Verification (required)

  • Verified scenarios: I verified all trust/untrust command paths tested on OpenClaw v3.14, Fedora Linux. Trust grant, anti-stacking refusal, untrust revocation, re-trust after untrust, and input bounds checking. All user-facing strings reviewed and updated across multiple iterations
  • Edge cases checked: Zero minutes, negative minutes, decimal minutes, scientific notation, above-max minutes, untrust with no active window, untrust with extra args
  • What you did not verify: Cross-surface testing on Discord/Telegram/Slack native commands (verified via command registry integration which serves all surfaces identically). Did not test 480-minute real-time expiry.

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 — no existing behavior changed
  • Config/env changes? No
  • Migration needed? No

Failure Recovery (if this breaks)

  • How to disable/revert this change quickly: Designed to have as little impact to existing codebase as possible. Single git revert of this commit removes the entire feature. Alternatively, delete 2 new files and revert 3 surgical edits (import line, 2 handler entries, 1 resolution chain call)
  • Files/config to restore: commands-registry.data.ts, commands-core.ts, get-reply-directives.ts (remove added lines)
  • Known bad symptoms reviewers should watch for: Trust persisting beyond expected window (check timer cleanup), trust applying to wrong session (check sessionKey scoping), process hanging on exit (check .unref() on timer)

Risks and Mitigations

  • Risk: Module-level Map state grows unbounded
    • Mitigation: Entries auto-expire via setTimeout and are bounded by authorized sessions (not user-controllable). Each entry is one Map key + timer
  • Risk: Timer keeps Node process alive
    • Mitigation: .unref() called on timer, matching 15+ codebase precedents for background timers

Ric Lewis and others added 16 commits March 15, 2026 18:14
…evation

Add time-limited exec.security=full via /trust [minutes] (1-480, default 15).
Anti-stacking prevents extension while active. /untrust for early revocation.
In-memory only (fail-closed on restart). Single decision point for all surfaces.
Trust scoped by sessionId so /new and /reset revoke trust automatically.
Includes oxfmt formatting fixes for CI compliance.

Closes openclaw#47695

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Fixes slash-commands-doc.test.ts failure — the test checks that all
registered command aliases appear in the docs.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature]: Temporary time-bound exec security escalation via /trust and /untrust

2 participants