-
-
Notifications
You must be signed in to change notification settings - Fork 80.7k
[Bug]: Concurrent allow-always approvals silently lose allowlist entries (last-write-wins race in exec-approvals.json) #44749
Copy link
Copy link
Closed
Labels
P1High-priority user-facing bug, regression, or broken workflow.High-priority user-facing bug, regression, or broken workflow.clawsweeper:fix-shape-clearClawSweeper found a clear likely implementation shape for this issue.ClawSweeper found a clear likely implementation shape for this issue.clawsweeper:needs-maintainer-reviewClawSweeper marked this issue as needing maintainer review before automation.ClawSweeper marked this issue as needing maintainer review before automation.clawsweeper:needs-security-reviewClawSweeper marked this issue as needing security-sensitive review.ClawSweeper marked this issue as needing security-sensitive review.clawsweeper:no-new-fix-prClawSweeper does not recommend queueing a new automated fix PR for this issue.ClawSweeper does not recommend queueing a new automated fix PR for this issue.clawsweeper:source-reproClawSweeper found a high-confidence source-level issue reproduction.ClawSweeper found a high-confidence source-level issue reproduction.impact:data-lossCan lose, corrupt, or silently drop user/session/config data.Can lose, corrupt, or silently drop user/session/config data.impact:securitySecurity boundary, credential, authz, sandbox, or sensitive-data risk.Security boundary, credential, authz, sandbox, or sensitive-data risk.issue-rating: 🦞 diamond lobsterVery strong issue quality with high-confidence source-level or clear reproduction.Very strong issue quality with high-confidence source-level or clear reproduction.maturity:stableIssue affects a taxonomy feature currently scored M4/M5.Issue affects a taxonomy feature currently scored M4/M5.
Description
Metadata
Metadata
Assignees
Labels
P1High-priority user-facing bug, regression, or broken workflow.High-priority user-facing bug, regression, or broken workflow.clawsweeper:fix-shape-clearClawSweeper found a clear likely implementation shape for this issue.ClawSweeper found a clear likely implementation shape for this issue.clawsweeper:needs-maintainer-reviewClawSweeper marked this issue as needing maintainer review before automation.ClawSweeper marked this issue as needing maintainer review before automation.clawsweeper:needs-security-reviewClawSweeper marked this issue as needing security-sensitive review.ClawSweeper marked this issue as needing security-sensitive review.clawsweeper:no-new-fix-prClawSweeper does not recommend queueing a new automated fix PR for this issue.ClawSweeper does not recommend queueing a new automated fix PR for this issue.clawsweeper:source-reproClawSweeper found a high-confidence source-level issue reproduction.ClawSweeper found a high-confidence source-level issue reproduction.impact:data-lossCan lose, corrupt, or silently drop user/session/config data.Can lose, corrupt, or silently drop user/session/config data.impact:securitySecurity boundary, credential, authz, sandbox, or sensitive-data risk.Security boundary, credential, authz, sandbox, or sensitive-data risk.issue-rating: 🦞 diamond lobsterVery strong issue quality with high-confidence source-level or clear reproduction.Very strong issue quality with high-confidence source-level or clear reproduction.maturity:stableIssue affects a taxonomy feature currently scored M4/M5.Issue affects a taxonomy feature currently scored M4/M5.
Type
Fields
Priority
None yet
[Bug]: Concurrent
allow-alwaysapprovals silently lose allowlist entries (last-write-wins race in exec-approvals.json)Bug type
Data loss / Race condition
Summary
When multiple exec approval requests are resolved with
allow-alwaysconcurrently, only the last write to~/.openclaw/exec-approvals.jsonpersists. Earlier allowlist entries are silently overwritten. This is a classic read-modify-write race with no file locking.Environment
/approve <id> allow-always)researcher, notmain)Steps to reproduce
security: "allowlist"andask: "on-miss"./approve <id> allow-always.~/.openclaw/exec-approvals.json.Expected: All approved binaries appear in the agent's allowlist.
Actual: Only the last approval to complete has its entries persisted. All earlier entries are silently lost.
Reproduction evidence
Test 1 — 4 simultaneous single-binary commands
Commands:
echo "test",sort --version,grep --version,find --versionAll approved with
allow-always.Completion order: grep → find → sort → echo.
Result: Only
/usr/bin/echo(last to complete) was added to the allowlist.sort,grep,findall lost.Test 2 — 2 simultaneous single-binary commands
Commands:
grep --version,cat --versionBoth approved with
allow-always.Result: Only
/usr/bin/cat(last to complete) persisted./usr/bin/greplost.Test 3 — Single piped command (control — no race)
Command:
aaa.sh | bbb.sh | ccc.shApproved with
allow-always.Result: All 3 scripts persisted correctly. ✅
(Sequential
addAllowlistEntrycalls within one approval handler share the same in-memory object.)Test 4 — 2 simultaneous piped commands (race)
Command 1:
aaa.sh | bbb.shCommand 2:
ccc.sh | sortBoth approved with
allow-always.Completion order: Command 2 first, Command 1 second.
Result: Only
aaa.shandbbb.shpersisted (from Command 1, last to complete).ccc.shfrom Command 2 was lost. ❌Test 5 — Single command (control — no race)
Command:
sort --versionApproved with
allow-always.Result:
/usr/bin/sortpersisted correctly. ✅Test 6 — Single command (control — no race)
Command:
grep --versionApproved with
allow-always.Result:
/usr/bin/greppersisted correctly. ✅Pattern
Root cause
File:
src/infra/exec-approvals.tsaddAllowlistEntry(line ~525)saveExecApprovals(line ~364)Caller in
bash-tools.exec-host-gateway.ts(line ~237)Why it happens
The
approvals.fileobject is loaded once at the start of the approval handler (when the exec request is first processed). When two approval requests resolve concurrently:approvals.fileat time T₀ (state:[ls, echo, sort, find, cat, grep])approvals.fileat time T₀ (same state)ccc.sh→ writes file (state now:[..., ccc.sh])aaa.sh,bbb.shto its stale T₀ copy → writes file (state now:[..., aaa.sh, bbb.sh]—ccc.shis gone)There is no file lock, mutex, or re-read-before-write in the write path.
Same race exists in node-host path
src/node-host/invoke-system-run.ts(line ~490) has the same pattern:Suggested fix
Option A — Re-read before write (simplest, minimal diff):
This shrinks the race window significantly (read+write are adjacent) but doesn't eliminate it entirely under extreme concurrency.
Option B — In-process mutex (most robust for single-process Node.js):
Option C — File-level locking (cross-process safe):
Use
proper-lockfileorfd-level locking. Relevant if macOS companion app and gateway can both write to the sameexec-approvals.json.Impact
allow-alwaysis brokenagents.<id>section gets overwritten more easily)Workaround
Approve commands one at a time, waiting for each to complete before approving the next.