Skip to content

feat(exec): add Trust Windows for time-bounded exec approvals#46657

Closed
keylimesoda wants to merge 12 commits intoopenclaw:mainfrom
keylimesoda:upstream/trust-windows
Closed

feat(exec): add Trust Windows for time-bounded exec approvals#46657
keylimesoda wants to merge 12 commits intoopenclaw:mainfrom
keylimesoda:upstream/trust-windows

Conversation

@keylimesoda
Copy link
Copy Markdown

Summary

Add Trust Windows — time-bounded sessions that temporarily set exec to permissive mode (security=full, ask=off), then automatically expire. This strikes a usability middle ground between constant approval prompts and permanent broad allowlists.

What's included

Core infra (\src/infra/exec-approvals.ts)

  • In-memory trust window cache keyed by normalized agent ID
  • \grantTrustWindow\ /
    evokeTrustWindow\ / \getTrustWindow\ / \isTrustWindowActive\ APIs
  • Default cap (60m), absolute cap (480m), anti-stacking, agent ID validation
  • Trust override applied in
    esolveExecApprovalsFromFile\ defaults resolution
  • Cache cleared on gateway startup (\initTrustWindowCache)

Exec host enforcement (\�ash-tools.exec-host-shared.ts, \�ash-tools.exec.ts, \�ash-tools.exec-host-node.ts)

  • Trust override in shared approval context resolution (gateway + node paths)
  • Audit logging of commands executed during active trust windows

Trust audit module (\src/infra/trust-audit.ts) — NEW

  • JSONL audit trail with command redaction/truncation
  • Load, summarize (with time-window filtering), and cleanup utilities

Gateway RPC surface (\src/gateway/server-methods/exec-approvals.ts)

  • \�xec.approvals.trust\ — grant a trust window
  • \�xec.approvals.untrust\ — revoke with optional audit summary
  • \�xec.approvals.trust.status\ — query active trust state

Protocol schemas (\src/gateway/protocol/schema/exec-approvals.ts)

  • Typebox schemas and AJV validators for all three trust methods

CLI commands (\src/cli/exec-approvals-cli.ts)

  • \openclaw approvals trust --minutes \ — interactive-only, blocks agent self-escalation
  • \openclaw approvals untrust\ — revoke with audit cleanup prompt
  • \openclaw approvals trust-status\ — show remaining time

Tests (23 new/extended)

  • \src/infra/exec-approvals.test.ts\ — grant/revoke/expiry/anti-stacking/caps/normalization/resolver override
  • \src/infra/trust-audit.test.ts\ — write/load/format/summarize/cleanup
  • \src/cli/exec-approvals-cli.test.ts\ — trust/untrust/trust-status CLI routing + TTY gating

Design decisions

  • Memory-authoritative: trust state lives in an in-memory Map, not persisted to exec-approvals.json. Cache clears on gateway restart — no stale trust survives a restart.
  • Single enforcement layer: trust override is applied in the shared
    esolveExecApprovalsFromFile\ path, so behavior is identical across gateway and node exec hosts.
  • No agent self-escalation: CLI trust grant requires interactive TTY and blocks when \OPENCLAW_SESSION_KEY\ or \OPENCLAW_AGENT_ID\ env vars are set.
  • Anti-stacking: cannot extend an active window — must revoke first.

Validation

  • \pnpm build\ ✅
  • \pnpm tsgo\ ✅
  • \pnpm lint\ ✅
  • \pnpm vitest run\ on trust-related tests ✅ (23/23)
  • \pnpm check\ ⚠️ fails on pre-existing formatting issue in \src/gateway/server/ws-connection/message-handler.ts\ (unchanged by this PR)

Files changed (16 files, +994 / -22)

File Change
\src/infra/exec-approvals.ts\ Trust types, cache, constants, grant/revoke/status APIs, resolver override
\src/infra/trust-audit.ts\ New — JSONL audit module
\src/infra/trust-audit.test.ts\ New — audit tests
\src/infra/exec-approvals.test.ts\ Trust behavior tests
\src/agents/bash-tools.exec-host-shared.ts\ Trust override in shared approval context
\src/agents/bash-tools.exec.ts\ Trust audit append for gateway exec
\src/agents/bash-tools.exec-host-node.ts\ Trust audit append for node exec
\src/gateway/server.impl.ts\ \initTrustWindowCache()\ on startup
\src/gateway/server-methods/exec-approvals.ts\ Trust/untrust/status RPC handlers
\src/gateway/server-methods-list.ts\ Register trust method names
\src/gateway/protocol/schema/exec-approvals.ts\ Trust param schemas
\src/gateway/protocol/schema/protocol-schemas.ts\ Schema registry entries
\src/gateway/protocol/schema/types.ts\ Type exports
\src/gateway/protocol/index.ts\ AJV validators + re-exports
\src/cli/exec-approvals-cli.ts\ Trust/untrust/trust-status commands
\src/cli/exec-approvals-cli.test.ts\ CLI trust command tests

@openclaw-barnacle openclaw-barnacle Bot added app: web-ui App: web-ui gateway Gateway runtime cli CLI command changes agents Agent runtime and tooling size: XL labels Mar 14, 2026
@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented Mar 14, 2026

Greptile Summary

This PR introduces Trust Windows — time-bounded, in-memory exec permission grants that temporarily set security=full / ask=off for a specific agent, then auto-expire. It is a well-scoped feature with good test coverage (23 new/extended tests) and addresses all of the concerns raised in the previous review round (server-side self-escalation guards on all three RPC endpoints, audit write failures wrapped in try-catch, expired-window handling, dead parameter removal, and the summarizeTrustAudit null-return fix).

Key findings from this round:

  • Dead "No active trust window" error check (src/cli/exec-approvals-cli.ts) — The untrust catch block still checks for this specific error string, but the server now uses allowMissing: true which makes the gateway always return success for a missing window. The branch is unreachable and should be removed to avoid misleading future maintainers.
  • trust-status CLI missing agent session guard (src/cli/exec-approvals-cli.ts) — Unlike trust and untrust, the trust-status command has no OPENCLAW_SESSION_KEY/OPENCLAW_AGENT_ID env-var check. An agent process with CLI binary access and without those env vars can call trust-status to learn whether a trust window is active, which is inconsistent with the stated anti-self-escalation design.
  • Temp directory leak in trust-audit.test.ts — Each test creates a temp home directory via makeTempDir() in beforeEach but afterEach only restores the env var without deleting the directory. Other tests in the suite use fs.rmSync in a finally block.

Confidence Score: 4/5

  • Safe to merge with minor follow-up; functional issues are limited to a dead code branch and a minor CLI guard inconsistency.
  • The core trust window semantics, server-side enforcement, and audit resilience are all well-implemented and tested. All critical concerns from the prior review round (self-escalation server guards, audit write fault tolerance, expired-window and dead-parameter bugs) have been resolved. The remaining issues are a dead error-message check in the CLI, a missing env-var guard on the trust-status command, and a test directory cleanup omission — none of which affect production correctness of the trust enforcement path.
  • src/cli/exec-approvals-cli.ts — dead catch branch and missing trust-status session guard. src/infra/trust-audit.test.ts — temp dir cleanup.
Prompt To Fix All With AI
This is a comment left during a code review.
Path: src/cli/exec-approvals-cli.ts
Line: 604-608

Comment:
**Dead "No active trust window" error path in `untrust` catch block**

This error branch can no longer be triggered. Prior to this PR the server returned a "No active trust window" error when `untrust` was called with no active window; the CLI swallowed that particular error and treated it as success. The updated server handler now passes `allowMissing: true` to `revokeTrustWindow`, so the gateway always responds with `ok: true` (never an error) when the window is simply absent. The string-match check is dead code that could mislead future maintainers into thinking this error path is still live.

```suggestion
      } catch (err) {
        defaultRuntime.error(formatCliError(err));
        defaultRuntime.exit(1);
      }
```

How can I resolve this? If you propose a fix, please make it concise.

---

This is a comment left during a code review.
Path: src/cli/exec-approvals-cli.ts
Line: 629-656

Comment:
**`trust-status` CLI command missing agent session guard**

`trust` has both a TTY check and an `OPENCLAW_SESSION_KEY`/`OPENCLAW_AGENT_ID` env-var check. `untrust` has the env-var check. `trust-status` has neither. An agent process that has access to the CLI binary (and whose env vars are not set) can run `openclaw approvals trust-status` from within a session and learn whether a trust window is active for its agent ID, because the server's `isTrustGrantCallerAllowed` will see a legitimate CLI connection and allow it through.

While knowing the trust status doesn't directly enable further escalation (the agent's exec calls are already permitted or denied by the approval layer), it is inconsistent with the "no agent self-escalation" design and lets an agent adapt its behavior based on observed trust state. Consider adding at minimum the env-var guard:

```typescript
.action(async (opts: TrustStatusOpts) => {
  try {
    if (process.env.OPENCLAW_SESSION_KEY || process.env.OPENCLAW_AGENT_ID) {
      exitWithError(
        "Trust status queries are blocked from agent sessions. Use a direct terminal session.",
      );
    }
    // ... rest of handler
```

How can I resolve this? If you propose a fix, please make it concise.

---

This is a comment left during a code review.
Path: src/infra/trust-audit.test.ts
Line: 22-35

Comment:
**Temp directory not cleaned up in `afterEach`**

`homeDir` is populated by `makeTempDir()` in `beforeEach` but `afterEach` only restores the `OPENCLAW_HOME` env var — it never removes the temp directory itself. Compare with the existing cleanup pattern used in `exec-approvals.test.ts`:

```typescript
} finally {
  process.env.OPENCLAW_HOME = previousHome;
  fs.rmSync(homeDir, { recursive: true, force: true });
}
```

Each test run leaks a temp directory. Add `fs.rmSync(homeDir, { recursive: true, force: true })` to the `afterEach` hook:

```suggestion
  afterEach(() => {
    process.env.OPENCLAW_HOME = previousHome;
    if (homeDir) {
      fs.rmSync(homeDir, { recursive: true, force: true });
    }
  });
```

How can I resolve this? If you propose a fix, please make it concise.

Last reviewed commit: 06f4c7c

Comment thread src/gateway/server-methods/exec-approvals.ts Outdated
Comment thread src/agents/bash-tools.exec.ts
Comment thread src/agents/bash-tools.exec-host-node.ts
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 76243c7966

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread src/cli/exec-approvals-cli.ts
Comment thread src/agents/bash-tools.exec.ts Outdated
@keylimesoda keylimesoda force-pushed the upstream/trust-windows branch from 76243c7 to 1e56d52 Compare March 14, 2026 23:50
@openclaw-barnacle openclaw-barnacle Bot added the app: macos App: macos label Mar 14, 2026
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 1e56d52403

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread src/agents/bash-tools.exec-host-node.ts Outdated
@keylimesoda keylimesoda force-pushed the upstream/trust-windows branch from 1e56d52 to 3fc693c Compare March 15, 2026 00:24
Ric Lewis and others added 2 commits March 14, 2026 17:24
Apply server-side trust grant caller guard, derive grantedBy from authenticated gateway connection metadata, and harden trust audit writes so I/O errors never alter execution results or mask original failures.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Add server-side caller validation for exec.approvals.trust, derive grantedBy from connection metadata, and wrap trust audit writes to prevent masking command outcomes or original errors; include targeted tests.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@keylimesoda keylimesoda force-pushed the upstream/trust-windows branch from 3fc693c to fc51b81 Compare March 15, 2026 00:24
@keylimesoda
Copy link
Copy Markdown
Author

@greptile-apps please re-review latest commit fc51b81 (security feedback addressed).

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: d44d877891

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread src/infra/exec-approvals.ts Outdated
Comment thread src/infra/exec-approvals.ts
Comment thread src/infra/exec-approvals.ts
Comment thread src/infra/trust-audit.ts
Treat expired windows as inactive during revoke, remove dead revoke parameters, and return null summaries when no audited commands exist. Add targeted tests for expired revoke and empty-summary behavior.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@keylimesoda keylimesoda force-pushed the upstream/trust-windows branch from d44d877 to 86043a7 Compare March 15, 2026 00:52
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 86043a7748

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread src/infra/exec-approvals.ts Outdated
Comment on lines +497 to +499
if (!isTrustWindowActive(trustWindow, now)) {
return { ok: false, error: `No active trust window for agent "${validated.agentId}"` };
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Let untrust handle expired trust windows

This early return makes revokeTrustWindow fail as soon as a window has naturally expired, so exec.approvals.untrust never reaches its audit cleanup path (cleanupTrustAudit(...) in src/gateway/server-methods/exec-approvals.ts). In practice, if an operator runs untrust after expiry, the CLI just reports “No active trust window” and the trust audit file cannot be removed via the supported untrust workflow, while expired cache entries remain until restart; expired windows should still be treated as revocable for cleanup purposes.

Useful? React with 👍 / 👎.

@keylimesoda
Copy link
Copy Markdown
Author

@greptile-apps please re-review latest commit 86043a7 (expired-window revoke + dead params + null empty-summary follow-ups addressed).

Comment thread src/gateway/server-methods/exec-approvals.ts Outdated
Ensure revokeTrustWindow removes active trust before attempting summary generation so unreadable audit files cannot block de-escalation. Add regression test for directory-at-audit-path failure mode.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: c6de4655a9

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread src/gateway/server-methods/exec-approvals.ts Outdated
@keylimesoda keylimesoda force-pushed the upstream/trust-windows branch from c6de465 to 7b16d1a Compare March 15, 2026 01:43
@keylimesoda
Copy link
Copy Markdown
Author

@greptile-apps please re-review latest commit 7b16d1a (revoke remains safe even if audit summary generation fails).

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 7b16d1aa8d

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread src/cli/exec-approvals-cli.ts Outdated
Require trusted trust/untrust callers to be CLI-identified clients with authenticated device identity, and add the same caller gate to exec.approvals.untrust. Add regression tests for cli-mode spoof without device identity and non-CLI untrust rejection.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@keylimesoda
Copy link
Copy Markdown
Author

@greptile-apps please re-review latest commit a7c386a (trust/untrust caller gate now requires CLI identity + authenticated device identity).

Comment thread src/gateway/server-methods/exec-approvals.ts Outdated
Comment thread src/infra/exec-approvals.ts
Comment thread src/agents/bash-tools.exec-host-shared.ts Outdated
Validate approvals trust --minutes as a strict decimal integer string before numeric conversion so values like 10foo, 1.5, and 0x20 are rejected instead of coerced.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@keylimesoda keylimesoda force-pushed the upstream/trust-windows branch from 6d01dc0 to fe2c92a Compare March 15, 2026 02:03
Gate exec.approvals.trust.status behind the same trusted interactive CLI caller checks used by trust/untrust to prevent non-CLI clients from probing trust window metadata. Add regression test for non-CLI rejection.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@keylimesoda
Copy link
Copy Markdown
Author

@greptile-apps please re-review latest commit aa1629d (trust.status now restricted to trusted interactive CLI callers).

Expose trustWindowActive from resolveExecApprovalsFromFile and consume it in resolveExecHostApprovalContext instead of re-reading trust cache. This removes dual independent trust-state checks while preserving authoritative host enforcement behavior. Update typed test fixtures accordingly.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@keylimesoda keylimesoda force-pushed the upstream/trust-windows branch from 46ffdf4 to 99dda03 Compare March 15, 2026 02:10
@keylimesoda
Copy link
Copy Markdown
Author

@greptile-apps please re-review latest commit 99dda03 (trust state now sourced once from resolveExecApprovalsFromFile; host context consumes trustWindowActive instead of a second trust-cache read).

Comment thread src/cli/exec-approvals-cli.ts
Comment thread src/infra/trust-audit.ts
Comment thread src/infra/trust-audit.ts
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 99dda037d9

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread src/gateway/server-methods/exec-approvals.ts Outdated
Allow untrust to clear expired/missing trust-window state for cleanup workflows while keeping revoke strict by default. Add CLI agent-session guard to approvals untrust and extend trust/untrust regression coverage.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@keylimesoda
Copy link
Copy Markdown
Author

@greptile-apps please re-review latest commit a694f17 (untrust now handles expired/missing windows for cleanup, plus untrust CLI now has the same agent-session guard as trust).

Permit trusted CLI trust/untrust/status calls without device identity when the caller is loopback and authenticated via token/password, matching callGateway local auth behavior. Preserve stricter checks for non-loopback/no-auth callers and add regression coverage.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@keylimesoda keylimesoda force-pushed the upstream/trust-windows branch from 4b2980f to dd6ba0e Compare March 15, 2026 04:05
@keylimesoda
Copy link
Copy Markdown
Author

@greptile-apps please re-review latest commit dd6ba0e (trust caller gate now allows localhost CLI token/password auth without device identity, while still rejecting non-loopback no-device callers).

Comment on lines +604 to +608
const message = formatCliError(err);
if (message.includes("No active trust window")) {
defaultRuntime.log(message);
return;
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Dead "No active trust window" error path in untrust catch block

This error branch can no longer be triggered. Prior to this PR the server returned a "No active trust window" error when untrust was called with no active window; the CLI swallowed that particular error and treated it as success. The updated server handler now passes allowMissing: true to revokeTrustWindow, so the gateway always responds with ok: true (never an error) when the window is simply absent. The string-match check is dead code that could mislead future maintainers into thinking this error path is still live.

Suggested change
const message = formatCliError(err);
if (message.includes("No active trust window")) {
defaultRuntime.log(message);
return;
}
} catch (err) {
defaultRuntime.error(formatCliError(err));
defaultRuntime.exit(1);
}
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/cli/exec-approvals-cli.ts
Line: 604-608

Comment:
**Dead "No active trust window" error path in `untrust` catch block**

This error branch can no longer be triggered. Prior to this PR the server returned a "No active trust window" error when `untrust` was called with no active window; the CLI swallowed that particular error and treated it as success. The updated server handler now passes `allowMissing: true` to `revokeTrustWindow`, so the gateway always responds with `ok: true` (never an error) when the window is simply absent. The string-match check is dead code that could mislead future maintainers into thinking this error path is still live.

```suggestion
      } catch (err) {
        defaultRuntime.error(formatCliError(err));
        defaultRuntime.exit(1);
      }
```

How can I resolve this? If you propose a fix, please make it concise.

Comment on lines +629 to +656
expiresAt: number;
grantedAt: number;
grantedBy?: string;
remainingMs?: number;
};
};

if (opts.json) {
defaultRuntime.log(JSON.stringify(result));
return;
}

if (!result.trustWindow) {
defaultRuntime.log(`Trust status: inactive (agent "${result.agentId}")`);
return;
}
const remainingMs =
typeof result.trustWindow.remainingMs === "number"
? result.trustWindow.remainingMs
: Math.max(0, result.trustWindow.expiresAt - Date.now());
const remainingMinutes = Math.ceil(remainingMs / 60_000);
defaultRuntime.log(`Trust status: active (agent "${result.agentId}")`);
defaultRuntime.log(`Remaining: ${remainingMinutes}m`);
} catch (err) {
defaultRuntime.error(formatCliError(err));
defaultRuntime.exit(1);
}
});
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

trust-status CLI command missing agent session guard

trust has both a TTY check and an OPENCLAW_SESSION_KEY/OPENCLAW_AGENT_ID env-var check. untrust has the env-var check. trust-status has neither. An agent process that has access to the CLI binary (and whose env vars are not set) can run openclaw approvals trust-status from within a session and learn whether a trust window is active for its agent ID, because the server's isTrustGrantCallerAllowed will see a legitimate CLI connection and allow it through.

While knowing the trust status doesn't directly enable further escalation (the agent's exec calls are already permitted or denied by the approval layer), it is inconsistent with the "no agent self-escalation" design and lets an agent adapt its behavior based on observed trust state. Consider adding at minimum the env-var guard:

.action(async (opts: TrustStatusOpts) => {
  try {
    if (process.env.OPENCLAW_SESSION_KEY || process.env.OPENCLAW_AGENT_ID) {
      exitWithError(
        "Trust status queries are blocked from agent sessions. Use a direct terminal session.",
      );
    }
    // ... rest of handler
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/cli/exec-approvals-cli.ts
Line: 629-656

Comment:
**`trust-status` CLI command missing agent session guard**

`trust` has both a TTY check and an `OPENCLAW_SESSION_KEY`/`OPENCLAW_AGENT_ID` env-var check. `untrust` has the env-var check. `trust-status` has neither. An agent process that has access to the CLI binary (and whose env vars are not set) can run `openclaw approvals trust-status` from within a session and learn whether a trust window is active for its agent ID, because the server's `isTrustGrantCallerAllowed` will see a legitimate CLI connection and allow it through.

While knowing the trust status doesn't directly enable further escalation (the agent's exec calls are already permitted or denied by the approval layer), it is inconsistent with the "no agent self-escalation" design and lets an agent adapt its behavior based on observed trust state. Consider adding at minimum the env-var guard:

```typescript
.action(async (opts: TrustStatusOpts) => {
  try {
    if (process.env.OPENCLAW_SESSION_KEY || process.env.OPENCLAW_AGENT_ID) {
      exitWithError(
        "Trust status queries are blocked from agent sessions. Use a direct terminal session.",
      );
    }
    // ... rest of handler
```

How can I resolve this? If you propose a fix, please make it concise.

Comment on lines +22 to +35
});

afterEach(() => {
process.env.OPENCLAW_HOME = previousHome;
});

it("writes and loads audit entries", () => {
const entry = appendTrustAuditEntry({
agentId: "main",
command: "echo hello",
exitCode: 0,
durationMs: 25,
now: 1_000,
});
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Temp directory not cleaned up in afterEach

homeDir is populated by makeTempDir() in beforeEach but afterEach only restores the OPENCLAW_HOME env var — it never removes the temp directory itself. Compare with the existing cleanup pattern used in exec-approvals.test.ts:

} finally {
  process.env.OPENCLAW_HOME = previousHome;
  fs.rmSync(homeDir, { recursive: true, force: true });
}

Each test run leaks a temp directory. Add fs.rmSync(homeDir, { recursive: true, force: true }) to the afterEach hook:

Suggested change
});
afterEach(() => {
process.env.OPENCLAW_HOME = previousHome;
});
it("writes and loads audit entries", () => {
const entry = appendTrustAuditEntry({
agentId: "main",
command: "echo hello",
exitCode: 0,
durationMs: 25,
now: 1_000,
});
afterEach(() => {
process.env.OPENCLAW_HOME = previousHome;
if (homeDir) {
fs.rmSync(homeDir, { recursive: true, force: true });
}
});
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/infra/trust-audit.test.ts
Line: 22-35

Comment:
**Temp directory not cleaned up in `afterEach`**

`homeDir` is populated by `makeTempDir()` in `beforeEach` but `afterEach` only restores the `OPENCLAW_HOME` env var — it never removes the temp directory itself. Compare with the existing cleanup pattern used in `exec-approvals.test.ts`:

```typescript
} finally {
  process.env.OPENCLAW_HOME = previousHome;
  fs.rmSync(homeDir, { recursive: true, force: true });
}
```

Each test run leaks a temp directory. Add `fs.rmSync(homeDir, { recursive: true, force: true })` to the `afterEach` hook:

```suggestion
  afterEach(() => {
    process.env.OPENCLAW_HOME = previousHome;
    if (homeDir) {
      fs.rmSync(homeDir, { recursive: true, force: true });
    }
  });
```

How can I resolve this? If you propose a fix, please make it concise.

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 06f4c7c60d

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment on lines +570 to +573
if (host === "gateway" && trustWindowActiveAtStart) {
try {
appendTrustAuditEntry({
agentId,
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Audit trusted commands in gateway approval-followup path

This trust-audit append is only reached for the synchronous run.promise path, so commands that execute via processGatewayAllowlist(...).pendingResult are never logged. That path is still reachable during an active trust window when requiresAsk is forced (for example by obfuscation detection), which means successful trusted-window executions can be omitted from the trust audit and revoke summary.

Useful? React with 👍 / 👎.

Comment on lines +391 to +394
if (trustWindowActiveAtStart) {
try {
appendTrustAuditEntry({
agentId: runAgentId,
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Audit trusted commands in node approval-followup path

In executeNodeHostCommand, trust auditing is only appended in the non-approval branch here; when requiresAsk is true (which can still happen under trust, e.g. obfuscation-triggered approvals), execution continues through the async followup flow and never reaches this write. The result is missing trust-audit entries for commands that did run during the trust window.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 38a2aa701c

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment on lines +190 to +192
const hostAsk = trustWindowActive
? "off"
: approvals.agent.ask === "off"
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Keep node trust runs from dropping the approval signal

When a trust window is active this branch forces hostAsk to "off", which makes executeNodeHostCommand skip the gateway approval flow and call node.invoke with approved=false; on node-hosts using the default allowlist/on-miss policy, evaluateSystemRunPolicy then rejects the run as SYSTEM_RUN_DENIED: approval required. I checked this path in src/agents/bash-tools.exec-host-node.ts (direct buildInvokeParams(false, null) call) and src/node-host/exec-policy.ts, so trusted node commands can still fail even though trust is reported active.

Useful? React with 👍 / 👎.

@keylimesoda
Copy link
Copy Markdown
Author

Closing this to clean up the commit history and address the automated feedback in a more cohesive way. The incremental fix-on-fix commits made this hard to review. I'll link the replacement PR here shortly.

@keylimesoda
Copy link
Copy Markdown
Author

Replacement PR: #46956. Clean commit history, addressed P1 safety issues and automated review feedback from this iteration.

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

Labels

agents Agent runtime and tooling app: macos App: macos app: web-ui App: web-ui cli CLI command changes gateway Gateway runtime size: XL

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant