Skip to content

fix(exec): resolve remote approval regressions#58792

Merged
scoootscooob merged 14 commits intomainfrom
fix/exec-approval-regressions
Apr 1, 2026
Merged

fix(exec): resolve remote approval regressions#58792
scoootscooob merged 14 commits intomainfrom
fix/exec-approval-regressions

Conversation

@vincentkoc
Copy link
Copy Markdown
Member

@vincentkoc vincentkoc commented Apr 1, 2026

Fixes #58710
Fixes #58722
Fixes #58739
Fixes #58752
Fixes #58662
Fixes #46573
Fixes #58769
Addresses #58715
Addresses #58616
Addresses #58691
Addresses #49266
Addresses #43279
Addresses #46846
Addresses #46848
Addresses #42574

Summary

This PR fixes the 2026.3.31 exec-approval regression cluster at the shared policy/runtime seams instead of stacking more per-channel workarounds on top.

Root causes addressed:

  • src/agents/bash-tools.exec.ts inherited ask from exec-approvals.json but not security, so security:"full" + ask:"off" in exec-approvals.json could still collapse back to allowlist and fail remote exec.
  • shared approval availability could report a chat surface as approval-capable even when native delivery was not actually enabled.
  • Slack and Discord runtime handlers drifted from the shared approver-resolution logic and could disagree with inferred approver config from allowFrom / defaultTo.
  • isolated cron runs inherited stricter approval policy with no interactive approval surface, then fell into a dead-end denial path.
  • allow-always persisted only as an allowlist-mode implementation detail, so ask:"always" kept re-prompting and remote channels effectively treated allow-always like allow-once.
  • shell-wrapper paths that intentionally fail static allowlist analysis had no durable trust representation, so repeated node allow-always approvals could still degrade back into repeated prompts.

What changed

  • make gateway exec inherit both security and ask defaults from exec-approvals.json when tool policy is unset
  • make shared native approval availability require both approvers and native delivery enablement
  • make Slack handler use the canonical request matcher instead of a local partial check
  • make Discord handler use resolved approvers everywhere, including inferred approvers
  • resolve isolated cron no-route approval dead-ends from the effective host fallback policy when trusted automation is allowed
  • make openclaw doctor warn when tools.exec is broader than ~/.openclaw/exec-approvals.json, so stricter host-policy conflicts become explicit instead of silently clamping
  • persist allow-always as durable user-approved trust and honor it on future gateway and node-host exec calls
  • keep static allowlist entries from silently bypassing ask:"always", so the durable-trust fix does not weaken explicit policy
  • persist exact-command durable trust for shell-wrapper paths that cannot safely materialize an executable allowlist entry, so repeated node allow-always approvals actually stick without requiring a restart
  • when Windows cannot build an allowlist execution plan, require explicit approval instead of hard-dead-ending the exec path
  • add regression coverage for the policy-inheritance, inferred-approver, cron/headless, durable-trust, and shell-wrapper reuse paths
  • add changelog entries

Verification

  • pnpm test -- src/agents/bash-tools.exec.approval-id.test.ts src/plugin-sdk/approval-delivery-helpers.test.ts extensions/slack/src/monitor/exec-approvals.test.ts extensions/discord/src/monitor/exec-approvals.test.ts extensions/slack/src/exec-approvals.test.ts extensions/discord/src/exec-approvals.test.ts
  • pnpm test -- src/node-host/invoke-system-run.test.ts src/infra/exec-approvals-allow-always.test.ts src/agents/bash-tools.exec.approval-id.test.ts src/infra/exec-approvals-policy.test.ts src/node-host/exec-policy.test.ts
  • pnpm check
  • pnpm build

Notes

  • This still does not add Windows allowlist-plan reconstruction. The Windows path becomes usable again because preapproved full/off policy from exec-approvals.json now actually takes effect on remote exec, unsupported allowlist-plan cases fall back to explicit approval instead of a hard dead end, and durable exact-command trust covers repeated shell-wrapper reruns that cannot safely persist an executable allowlist entry.

@vincentkoc vincentkoc self-assigned this Apr 1, 2026
@openclaw-barnacle openclaw-barnacle bot added channel: discord Channel integration: discord channel: slack Channel integration: slack agents Agent runtime and tooling size: S maintainer Maintainer-authored PR labels Apr 1, 2026
@openclaw-barnacle openclaw-barnacle bot added commands Command implementations size: L and removed size: S labels Apr 1, 2026
@vincentkoc vincentkoc changed the title fix(exec): restore remote approval policy defaults fix(exec): resolve remote approval regressions Apr 1, 2026
@vincentkoc vincentkoc marked this pull request as ready for review April 1, 2026 07:12
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

const raw = await callGatewayTool(
"node.invoke",
{ timeoutMs: invokeTimeoutMs },
buildInvokeParams(false, null),

P1 Badge Forward inline cron approval state to node system.run

When the no-route cron branch is taken, the code validates askFallback inline but then falls through to the normal invoke path with buildInvokeParams(false, null). That drops the approval outcome, so node-side policy evaluation still sees the call as unapproved and can deny with approval-required (for example ask="always" with askFallback="full"). This means the new headless fallback logic does not actually unblock node-host cron executions.

ℹ️ About Codex in GitHub

Your team has set up Codex to 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 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

if (match) {
matches.push(match);
}
segmentAllowlistEntries.push(match ?? null);
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 Mark policy-blocked segments as non-durable approvals

The new durable-trust signal is built from segmentAllowlistEntries, but entries are only appended in the non-blocked path. A policy-blocked segment returns early without adding a null marker, so a partially evaluated multi-segment command can be misclassified as fully allow-always durable if earlier segments matched. That can suppress ask:"always" prompts unexpectedly in commands that include blocked segments.

Useful? React with 👍 / 👎.

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps bot commented Apr 1, 2026

Greptile Summary

This PR fixes a cluster of remote-exec approval regressions introduced in the 2026.3.31 release by targeting the shared policy/runtime seams rather than adding more per-channel workarounds. The changes span three broad themes:

  • Policy inheritance: bash-tools.exec.ts now inherits both security and ask from exec-approvals.json defaults when the tool config leaves them unset, closing the gap that allowed security:\"full\" + ask:\"off\" to be silently overridden.
  • Durable trust: allow-always decisions are now persisted with a source: \"allow-always\" marker on the allowlist entry. hasDurableExecApproval checks that every command segment was matched by an allow-always entry before bypassing the approval prompt, while static entries (no source) continue to trigger ask:\"always\" as expected.
  • Headless / cron path: isolated cron runs with no approval route are now resolved inline against the askFallback policy instead of dead-ending, with a structured denial message when the fallback disallows it. Windows platforms that cannot build an enforced allowlist plan now route to explicit approval rather than throwing immediately.

Supporting fixes include aligning the Slack and Discord monitor handlers to use their respective canonical resolver functions, fixing createApproverRestrictedNativeApprovalAdapter to require both approvers AND native delivery before reporting as enabled, and adding openclaw doctor warnings when tools.exec is broader than the host exec policy.

Key observations:

  • The hasDurableExecApproval guard correctly requires all segments to carry source: \"allow-always\", preventing partial trust from bypassing approval.
  • The addAllowlistEntry deduplication now correctly upgrades a plain entry to allow-always but never silently downgrades it.
  • One P2 issue found: resolveHostExecPolicy in doctor-security.ts defaults the built-in security to \"deny\" when exec-approvals.json has no defaults block, but the runtime falls back to \"allowlist\" for non-sandbox hosts. This can produce false-positive policy-conflict warnings and an inaccurate "effective host exec stays security='deny'" message for users whose exec-approvals.json simply omits the defaults section.

Confidence Score: 5/5

Safe to merge; all remaining findings are P2 style/accuracy issues with no runtime correctness impact on the primary fixed paths.

The PR is well-structured, carries thorough regression tests for every addressed scenario (policy inheritance, durable trust, cron inline resolution, allowlist plan fallback, Discord/Slack inferred approvers), and the core logic changes are consistent across gateway and node-host paths. The single P2 finding is a false-positive in the doctor diagnostic output — it does not affect exec execution or approval decisions at runtime.

src/commands/doctor-security.ts — the built-in security default in resolveHostExecPolicy should match the runtime fallback.

Prompt To Fix All With AI
This is a comment left during a code review.
Path: src/commands/doctor-security.ts
Line: 88-91

Comment:
**Doctor built-in security default disagrees with runtime fallback**

`resolveHostExecPolicy` falls back to `"deny"` when `exec-approvals.json` has no `defaults.security`, but the runtime in `bash-tools.exec.ts` falls back to `"allowlist"` for non-sandbox hosts:

```ts
// runtime (bash-tools.exec.ts)
const configuredSecurity =
  defaults?.security ??
  approvalDefaults?.security ??
  (host === "sandbox" ? "deny" : "allowlist");   // ← "allowlist" for gateway/node
```

Because of this mismatch, if `exec-approvals.json` exists but has no `defaults` block and `tools.exec.security` is `"allowlist"`, the doctor computes `host.security = "deny"` (rank 0), decides `allowlist` (rank 1) is "broader", and emits the warning:

> *"Effective host exec stays security="deny" because the stricter side wins."*

The statement is factually incorrect — the runtime effective security is `"allowlist"` in this scenario, not `"deny"`, so the user is given a misleading hint about what will actually execute.

Fix: use the same non-sandbox fallback as the runtime:

```suggestion
  const security = agentEntry?.security ?? defaults?.security ?? "allowlist";
  const ask = agentEntry?.ask ?? defaults?.ask ?? "on-miss";
```

If the sandbox case matters here too, a `host` parameter could be threaded in, but `"allowlist"` as the fallback matches the dominant remote-exec path this PR targets.

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

Reviews (1): Last reviewed commit: "Merge branch 'main' into fix/exec-approv..." | Re-trigger Greptile

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: a44ec5e815

ℹ️ About Codex in GitHub

Your team has set up Codex to 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 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +86 to +90
const agentEntry =
params.agentId && params.approvals.agents && params.approvals.agents[params.agentId]
? params.approvals.agents[params.agentId]
: undefined;
const defaults = params.approvals.defaults;
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 Account for wildcard host policy in doctor conflict checks

resolveHostExecPolicy only reads agents[agentId] and defaults, but effective runtime policy also merges agents["*"] (see resolveExecApprovalsFromFile). As a result, openclaw doctor can misreport tools.exec/per-agent conflict warnings for configurations that rely on wildcard host policy, either missing real conflicts or flagging incorrect host values.

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: c09d914eb9

ℹ️ About Codex in GitHub

Your team has set up Codex to 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 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@aisle-research-bot
Copy link
Copy Markdown

aisle-research-bot bot commented Apr 1, 2026

🔒 Aisle Security Analysis

We found 4 potential security issue(s) in this PR:

# Severity Title
1 🟠 High Durable exec approval can bypass allowlist enforcement and shell-wrapper protections after an 'allow-always' on unanalyzable commands
2 🟠 High Headless cron trigger can auto-approve exec when no approval route is available (askFallback=full)
3 🟠 High Allowlist mode executes raw command on Windows when enforced command plan unavailable (PATH/CWD hijack risk)
4 🟡 Medium Durable exec approvals rely on 64-bit truncated SHA-256, enabling feasible collision-based approval bypass
1. 🟠 Durable exec approval can bypass allowlist enforcement and shell-wrapper protections after an 'allow-always' on unanalyzable commands
Property Value
Severity High
CWE CWE-285
Location src/node-host/invoke-system-run.ts:566-583

Description

Durable approval (durableApprovalSatisfied) is used to skip approval prompts and to treat allowlist misses as allowed.

In invoke-system-run.ts, an allow-always decision now persists a durable exact-command approval when resolveAllowAlwaysPatterns() returns no patterns:

  • This happens even when command analysis is not OK (e.g., shell parsing fails, line continuations, unsupported constructs), because there is no analysisOk gate.
  • The resulting durable entry later makes requiresExecApproval() return false early and makes evaluateSystemRunPolicy() return allowed: true even on allowlist-miss.

This creates an authorization bypass where a user (or automation) can permanently “bless” a command that the allowlist analyzer cannot safely understand, and then execute it later without prompts and without enforced allowlist planning (including the fail-closed shell-wrapper block).

Vulnerable flow:

  • Input: commandText (potentially unanalyzable / contains shell wrappers / complex shell syntax)
  • Persistence: durable approval added when patterns.length === 0
  • Enforcement bypass: allowlist-miss becomes allowed when durableApprovalSatisfied is true

Vulnerable code:

if (phase.policy.approvalDecision === "allow-always" && phase.inlineEvalHit === null) {
  const patterns = resolveAllowAlwaysPatterns({ segments: phase.segments, ... });
  ...
  if (patterns.length === 0) {
    addDurableCommandApproval(phase.approvals.file, phase.agentId, phase.commandText);
  }
}

And later:

if (params.security === "allowlist" && (!analysisOk || !allowlistSatisfied) && !approvedByAsk) {
  if (params.durableApprovalSatisfied) {
    return { allowed: true, ... };
  }
  return { allowed: false, eventReason: "allowlist-miss", ... };
}

Recommendation

Do not grant (or honor) durable approvals unless the command was successfully analyzed and deemed safe under the allowlist model.

Recommended changes:

  1. Only persist durable approvals when analysis succeeds and the allowlist is satisfied, and when no additional risky flags are present (obfuscation/heredoc/shell-wrapper/inline-eval/etc.). For example:
if (
  phase.security === "allowlist" &&
  phase.policy.approvalDecision === "allow-always" &&
  phase.policy.analysisOk &&
  phase.policy.allowlistSatisfied &&
  phase.inlineEvalHit === null &&
  !phase.shellPayload // if present, require explicit approval each time or persist per-inner-exe patterns only
) {
  const patterns = resolveAllowAlwaysPatterns({ ... });
  if (patterns.length > 0) {
    for (const pattern of patterns) {
      addAllowlistEntry(file, agentId, pattern, { source: "allow-always" });
    }
  } else {// Consider NOT creating durable approvals here; instead require explicit approval on each run.
  }
}
  1. In evaluateSystemRunPolicy(), do not allow durable approval to override shell-wrapper blocking (fail-closed). Durable approval should not re-enable blocked constructs:
if (shellWrapperBlocked) {// never bypass with durable approval
}
  1. Optionally, treat analysisOk === false as always requiring approval (ask), even if a durable entry exists, to avoid permanently trusting unanalyzable command strings.
2. 🟠 Headless cron trigger can auto-approve exec when no approval route is available (askFallback=full)
Property Value
Severity High
CWE CWE-285
Location src/agents/bash-tools.exec-host-shared.ts:163-190

Description

exec approvals can be bypassed in “headless” mode by setting trigger: "cron".

Flow:

  • When exec approvals cannot be delivered (e.g., no interactive approval route), createAndRegisterDefaultExecApprovalRequest() sets preResolvedDecision to null, which maps to unavailableReason: "no-approval-route".
  • shouldResolveExecApprovalUnavailableInline() explicitly allows an inline resolution when:
    • trigger === "cron"
    • unavailableReason === "no-approval-route"
    • preResolvedDecision === null
  • createExecApprovalDecisionState() delegates to resolveBaseExecApprovalDecision(), where decision === null and askFallback === "full" returns approvedByAsk: true.
  • Both gateway and node hosts treat this approvedByAsk as sufficient to proceed:
    • Gateway returns { allowWithoutEnforcedCommand: true } when it cannot build an enforced allowlist command.
    • Node forwards approved: true and approvalDecision: "allow-once" into system.run.

Impact:

  • Any code path that can set ExecToolDefaults.trigger to "cron" while askFallback is configured to "full" can execute commands without any human approval, even when approval prompting is configured (e.g., ask="always").
  • This is an approval-gating bypass unless trigger is strictly restricted to trusted local automation contexts.

Vulnerable logic:

if (!params.decision) {
  if (params.askFallback === "full") {
    return { approvedByAsk: true, deniedReason: null, timedOut: true };
  }
}

combined with:

return (
  trigger === "cron" &&
  unavailableReason === "no-approval-route" &&
  preResolvedDecision === null
);

Recommendation

Treat "no-approval-route" as a hard deny for all triggers, or require an explicit, non-user-controllable policy flag distinct from trigger to enable headless auto-approval.

Suggested approach:

  • Remove the implicit approval (askFallback === "full") for decision === null when approvals are unavailable.
  • Or gate it behind a dedicated config such as headlessAutoApprove: true that is only set by trusted cron runner code (not via generic ExecToolDefaults).

Example fix (deny when no approval route):

export function resolveBaseExecApprovalDecision(params: {
  decision: string | null;
  askFallback: ResolvedExecApprovals["agent"]["askFallback"];
  obfuscationDetected: boolean;
  unavailableReason?: ExecApprovalUnavailableReason | null;
}) {
  if (params.decision === null && params.unavailableReason === "no-approval-route") {
    return { approvedByAsk: false, deniedReason: "no-approval-route", timedOut: true };
  }// existing logic...
}

Additionally:

  • Ensure trigger cannot be set by untrusted callers (e.g., tool input / remote request / plugins) or validate it against an allowlist of trusted sources.
3. 🟠 Allowlist mode executes raw command on Windows when enforced command plan unavailable (PATH/CWD hijack risk)
Property Value
Severity High
CWE CWE-284
Location src/agents/bash-tools.exec.ts:772-811

Description

In allowlist security mode, processGatewayAllowlist builds an enforced shell command intended to execute the resolved/approved executable path (via buildEnforcedShellCommand). On Windows, buildEnforcedShellCommand returns { ok:false, reason:"unsupported platform" }, leaving enforcedCommand undefined.

The new logic introduces a path where execution proceeds without an enforced command:

  • When allowlistSatisfied is true but enforcedCommand cannot be built (allowlistPlanUnavailableReason != null), the code requires an approval.
  • For headless/cron triggers with no approval route, shouldResolveExecApprovalUnavailableInline() can resolve the approval inline based on askFallback.
  • The function then returns { allowWithoutEnforcedCommand: true }.
  • createExecTool clears execCommandOverride when this flag is set, so runExecProcess() executes the original raw command string.

This weakens allowlist guarantees: the allowlist evaluation may have been satisfied based on a particular resolved executable (or intended resolution), but the actual raw execution on Windows can select a different executable due to Windows search order (current working directory before PATH, PATHEXT, etc.) or other resolution differences. This enables a practical allowlist bypass such as placing a malicious git.bat/git.exe in the working directory while the allowlist intended to permit only a trusted Git installation.

Vulnerable flow:

  • Input: params.command (user/model-provided command text)
  • Policy check: evaluateShellAllowlist(...) + allowlistSatisfied
  • Enforcement failure: buildEnforcedShellCommand(..., platform=win32) => unsupported
  • Sink: runExecProcess uses execCommand ?? command, so raw command executes when override cleared

Recommendation

Fail closed in allowlist mode when an enforced execution plan cannot be built, especially on Windows.

Options:

  1. Deny execution when security=="allowlist" and buildEnforcedShellCommand fails (unless the user explicitly switches to security="full").
  2. Alternatively, implement a Windows-specific enforced execution strategy (e.g., spawn without shell using a resolved absolute executable path + argv) so that allowlist evaluation and execution target are identical.

Example fail-closed change (conceptual):

if (security === "allowlist" && allowlistSatisfied && !enforcedCommand) {
  throw new Error(`exec denied: allowlist execution plan unavailable (${allowlistPlanUnavailableReason})`);
}

If a headless/cron flow must proceed, require an explicit durable allowlist entry that binds to an absolute path (not just command text) or require security="full" in configuration.

4. 🟡 Durable exec approvals rely on 64-bit truncated SHA-256, enabling feasible collision-based approval bypass
Property Value
Severity Medium
CWE CWE-326
Location src/infra/exec-approvals.ts:560-563

Description

hasDurableExecApproval() treats exact-command approvals as durable trust by matching =command:${sha256(commandText).slice(0,16)}.

Because only 64 bits of the SHA-256 digest are used, an attacker who can influence the command text and can get a user to approve one command could potentially create a different command with the same 64-bit prefix (birthday-style collision) and reuse the durable approval to bypass further approval prompts.

Key points:

  • Approval token is derived from sha256(commandText) truncated to 16 hex chars (64 bits).
  • 64-bit collision resistance is ~2^32 work, which is within reach for motivated attackers with GPU/parallel hashing.
  • hasDurableExecApproval() uses this match to skip approvals even when analysisOk is false (intended for exact-command reruns), so a collision can become an authorization bypass.

Vulnerable code:

function buildDurableCommandApprovalPattern(commandText: string): string {
  const digest = crypto.createHash("sha256").update(commandText).digest("hex").slice(0, 16);
  return `=command:${digest}`;
}

Recommendation

Increase the approval identifier length so collisions are computationally infeasible, or use a keyed construction.

Safer options (choose one):

  1. Store full SHA-256 (or at least 128 bits)
function buildDurableCommandApprovalPattern(commandText: string): string {
  const digest = crypto.createHash("sha256").update(commandText).digest("hex");// or: .slice(0, 32) for 128-bit if size is a concern
  return `=command:${digest}`;
}
  1. Use HMAC with a local secret (prevents attacker-controlled chosen-collision reuse without the secret)
function buildDurableCommandApprovalPattern(commandText: string, secret: string): string {
  const digest = crypto.createHmac("sha256", secret).update(commandText).digest("hex");
  return `=command:${digest}`;
}

If backwards-compatibility is needed, consider supporting legacy 64-bit patterns only temporarily and migrating to longer tokens on next write.


Analyzed PR: #58792 at commit 5f77f81

Last updated on: 2026-04-01T08:36:49Z

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: b1f32e9c13

ℹ️ 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".

);
}
if (hostAsk === "on-miss" && hostSecurity === "allowlist" && analysisOk) {
if ((hostAsk === "always" || hostSecurity === "allowlist") && analysisOk) {
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 Load node approvals even when allowlist analysis fails

The new durable-trust path is currently unreachable for commands whose local allowlist analysis fails, because the exec.approvals.node.get fetch is gated behind analysisOk. This means exact-command allow-always entries (which hasDurableExecApproval is designed to match even when analysis is not OK) are never checked in that case, so valid shell commands that fail static analysis (for example line-continuation forms) still re-prompt or deny on every run instead of reusing durable approval.

Useful? React with 👍 / 👎.

@openclaw openclaw deleted a comment from aisle-research-bot bot Apr 1, 2026
@SonicBotMan

This comment was marked as spam.

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: 2ba00325f1

ℹ️ About Codex in GitHub

Your team has set up Codex to 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 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

security: hostSecurity,
analysisOk,
allowlistSatisfied,
durableApprovalSatisfied,
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 Keep approval prompt when durable trust can't satisfy allowlist

Including durableApprovalSatisfied in requiresExecApproval here can suppress the approval flow for allowlist-miss commands that only have exact-command durable entries (for example commands that previously got allow-always with no persistable pattern). On the next run, requiresAsk becomes false, but the later allowlist guard still throws exec denied: allowlist miss, so the command is hard-denied instead of re-prompting or honoring the durable decision; this regression is specific to gateway allowlist mode with analysis/allowlist misses.

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: 5f77f81069

ℹ️ 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 65 to 66
explicit: resolveSlackAccount(params).config.execApprovals?.approvers,
normalizeApprover: normalizeSlackApproverId,
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 Preserve inferred Slack approvers when explicit list is absent

This change removes allowFrom/dm.allowFrom/defaultTo from Slack approver resolution, so getSlackExecApprovalApprovers now returns an empty list whenever execApprovals.approvers is unset. Existing Slack setups that relied on inferred owner IDs will be treated as having no approvers, which disables native exec-approval handling and pushes requests into fallback/denial paths instead of reaching the intended approver.

Useful? React with 👍 / 👎.

Comment on lines +31 to 34
explicit:
params.configOverride?.approvers ??
resolveDiscordAccount(params).config.execApprovals?.approvers,
normalizeApprover: (value) => normalizeDiscordApproverId(String(value)),
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 Discord owner-based approver inference

The Discord resolver now passes only explicit approvers into resolveApprovalApprovers, dropping inference from allowFrom, legacy dm.allowFrom, and direct-message defaultTo. That regresses previously working configs where execApprovals.approvers was intentionally omitted, causing approver resolution to become empty and preventing Discord exec approvals from being delivered or accepted.

Useful? React with 👍 / 👎.

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