Advisory Details
Title: Non-owner embedded agent runs expose owner-only gateway and cron tools in openclaw-cn
Description:
Summary
openclaw-cn contains an improper privilege management flaw in the embedded agent tool exposure path. A chat sender who is allowed to enter the command/agent flow but is not the configured owner can still be presented with owner-only gateway and cron tools. If the model selects one of those tools, the run crosses into Gateway control-plane actions that should remain reserved for the owner/operator boundary, including configuration reads and cron mutation.
Details
I verified this issue against the canonical GitHub repository jiulingyun/openclaw-cn and anchored all occurrence references to commit 1b9f16468d9841871cb15103693c3923424c9842, which is the remote commit for release tag v0.2.0, the highest affected public GitHub release on that repository.
The vulnerable chain is straightforward:
src/auto-reply/command-auth.ts computes a sender state where isAuthorizedSender=true while senderIsOwner=false.
src/agents/pi-embedded-runner/run/attempt.ts forwards senderIsOwner into createOpenClawCodingTools(...).
src/agents/openclaw-tools.ts always adds raw cron and gateway tools to the embedded tool bundle.
src/agents/tool-policy.ts already classifies cron and gateway as owner-only and provides applyOwnerOnlyToolPolicy(...).
src/agents/pi-tools.ts returns the final tool list after profile/group/sandbox filtering, but never applies the owner-only policy before return withAbort.
- When one of those tools executes,
src/gateway/call.ts opens a Gateway client session with broad default scopes ["operator.admin","operator.approvals","operator.pairing"].
- The backend authorization layer in
src/gateway/server-methods.ts correctly treats config.* and cron.add as admin-only methods, but the embedded tool path bypasses the intended boundary by self-connecting with those broader scopes.
The key logic error is not that Gateway method authorization is absent. It is present and working. The defect is that a non-owner sender can still be offered tools that should never have been visible to that sender in the first place.
Relevant snippets:
const senderIsOwner = Boolean(matchedSender);
const isAuthorizedSender = commandAuthorized && isOwnerForCommands;
return {
senderIsOwner,
isAuthorizedSender,
...
};
const toolsRaw = createOpenClawCodingTools({
...
senderIsOwner: params.senderIsOwner,
...
});
export function applyOwnerOnlyToolPolicy(tools: AnyAgentTool[], senderIsOwner: boolean) {
...
return withGuard.filter((tool) => !isOwnerOnlyTool(tool));
}
const normalized = subagentFiltered.map(normalizeToolParameters);
...
return withAbort;
I confirmed the issue using the existing integration-test harness in the report directory. The harness starts a real loopback Gateway, demonstrates that a plain operator.write client is blocked from cron.add, then constructs a non-owner embedded agent tool surface from the live code and successfully executes gateway config.get and cron.add. The control run applies applyOwnerOnlyToolPolicy(rawTools, false) and shows that both tools disappear and the cron mutation no longer occurs.
PoC
Prerequisites
- A checkout of
https://github.com/jiulingyun/openclaw-cn.
pnpm, tsx, and python3 installed locally.
- No external chat account, reverse proxy, or standalone Gateway instance is required.
- The three PoC files must remain in the same directory, because the Python wrappers invoke
driver.ts by relative path.
- The reproduced privilege split assumes a deployment where:
- the sender is allowed into the broader command/agent surface,
- the sender is not in
commands.ownerAllowFrom,
- owner-only tools are still expected to be hidden from that sender.
Reproduction Steps
- Download the TypeScript harness from: driver.ts
- Download the verification harness from: verification_test.py
- Download the control harness from: control-owner-only-policy.py
- Check out the highest affected public release:
git checkout v0.2.0
- Run the verification PoC:
python3 llm-enhance/cve-finding/similar/improper-privilege-management/Advisory-GHSA-2hm8-rqrm-xfjq-dataflow-non-owner-agent-tool-scope-bypass-exp/verification_test.py
- Confirm the experiment output shows:
auth.isAuthorizedSender: True
auth.senderIsOwner: False
raw tool list includes gateway: True
raw tool list includes cron: True
gateway tool config.get succeeded: True
cron tool cron.add succeeded: True
canary cron job listed after non-owner tool execution: True
- Run the control PoC:
python3 llm-enhance/cve-finding/similar/improper-privilege-management/Advisory-GHSA-2hm8-rqrm-xfjq-dataflow-non-owner-agent-tool-scope-bypass-exp/control-owner-only-policy.py
- Confirm the control output shows:
effective tool list includes gateway: False
effective tool list includes cron: False
canary cron job listed after control execution: False
Log of Evidence
Verification run:
[Verification Mode] Integration-Test
Reachability path: non-owner command-authorized chat sender -> resolveCommandAuthorization(senderIsOwner=false, isAuthorizedSender=true) -> embedded agent createOpenClawCodingTools(...) -> gateway/cron tools visible -> tool.execute() -> real localhost gateway -> admin-only methods
gateway url: ws://127.0.0.1:40983
loaded config port: 40983
auth.isAuthorizedSender: True
auth.senderIsOwner: False
control blocked by operator.admin requirement: True
control error: missing scope: operator.admin
raw tool list includes gateway: True
raw tool list includes cron: True
effective tool list includes gateway: True
effective tool list includes cron: True
gateway tool config.get succeeded: True
cron tool cron.add succeeded: True
admin cron.list succeeded: True
canary cron job listed after non-owner tool execution: True
canary job name: ghsa-2hm8-dataflow-1783279193794
[DEFECT CONFIRMED WITH LIMITATIONS]
Control run:
[Control Mode] Integration-Test
Control action: applyOwnerOnlyToolPolicy(rawTools, senderIsOwner=false) before exposing the embedded tool surface.
raw tool list includes gateway: True
raw tool list includes cron: True
effective tool list includes gateway: False
effective tool list includes cron: False
gateway execution blocked: True
cron execution blocked: True
canary cron job listed after control execution: False
[CONTROL PROTECTED]
Impact
This is an improper privilege management issue at the chat-user to embedded-agent control-plane boundary. It affects deployments that intentionally allow a broader set of senders to interact with the agent than the set of senders who should be treated as owners.
A successful trigger allows a non-owner sender context to reach owner-only operational tools. Depending on which tool the model selects, the affected assets include:
- Gateway configuration confidentiality through
config.get
- Gateway configuration integrity through
config.patch / config.apply
- Scheduled task integrity through
cron.add, cron.update, cron.remove, and cron.run
- Broader operator workflows reachable through the same self-scoped Gateway bridge
This is not an unauthenticated public network issue. The attacker must already be accepted into the command/agent path. The security failure is that the owner-only boundary inside that accepted path is not enforced.
Affected products
- Ecosystem: npm
- Package name: openclaw-cn
- Affected versions:
>= 0.1.5, <= 0.2.0
- Patched versions:
Severity
- Severity: Medium
- Vector string:
CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:U/C:L/I:H/A:L
Weaknesses
- CWE: CWE-269: Improper Privilege Management
Occurrences
| Permalink |
Description |
| https://github.com/jiulingyun/openclaw-cn/blob/1b9f16468d9841871cb15103693c3923424c9842/src/auto-reply/command-auth.ts#L205-L230 |
resolveCommandAuthorization() computes both senderIsOwner and isAuthorizedSender, creating the non-owner but command-authorized sender state that should not receive owner-only tools. |
| https://github.com/jiulingyun/openclaw-cn/blob/1b9f16468d9841871cb15103693c3923424c9842/src/agents/pi-embedded-runner/run/attempt.ts#L275-L305 |
The embedded runner forwards senderIsOwner into createOpenClawCodingTools(...), so the owner/non-owner state reaches the raw tool builder. |
| https://github.com/jiulingyun/openclaw-cn/blob/1b9f16468d9841871cb15103693c3923424c9842/src/agents/openclaw-tools.ts#L102-L123 |
The embedded tool bundle always includes raw createCronTool(...) and createGatewayTool(...), which are the owner-sensitive tools exposed on the vulnerable path. |
| https://github.com/jiulingyun/openclaw-cn/blob/1b9f16468d9841871cb15103693c3923424c9842/src/agents/tool-policy.ts#L61-L106 |
The repository already classifies cron and gateway as owner-only and provides applyOwnerOnlyToolPolicy(...), which would strip them for non-owners if the embedded tool builder actually used it. |
| https://github.com/jiulingyun/openclaw-cn/blob/1b9f16468d9841871cb15103693c3923424c9842/src/agents/pi-tools.ts#L114-L164 |
createOpenClawCodingTools() does not declare or consume a senderIsOwner option in its public contract, despite downstream call sites passing that field. |
| https://github.com/jiulingyun/openclaw-cn/blob/1b9f16468d9841871cb15103693c3923424c9842/src/agents/pi-tools.ts#L318-L367 |
The tool builder imports the full OpenClaw raw tool surface, including gateway and cron, into the embedded coding tool list. |
| https://github.com/jiulingyun/openclaw-cn/blob/1b9f16468d9841871cb15103693c3923424c9842/src/agents/pi-tools.ts#L412-L455 |
After policy filtering, the final tool list is normalized and returned directly. No call to applyOwnerOnlyToolPolicy(...) occurs before return withAbort, so non-owner runs keep the owner-only tools. |
| https://github.com/jiulingyun/openclaw-cn/blob/1b9f16468d9841871cb15103693c3923424c9842/src/gateway/call.ts#L248-L255 |
callGateway() self-connects as role: "operator" with operator.admin, operator.approvals, and operator.pairing scopes by default, magnifying the impact once the wrong tools are exposed. |
| https://github.com/jiulingyun/openclaw-cn/blob/1b9f16468d9841871cb15103693c3923424c9842/src/gateway/server-methods.ts#L105-L135 |
The backend authorization layer correctly treats config.* and cron.add as admin-only methods, confirming that the bug is the embedded tool exposure path rather than missing method authorization. |
Advisory Details
Title: Non-owner embedded agent runs expose owner-only
gatewayandcrontools inopenclaw-cnDescription:
Summary
openclaw-cncontains an improper privilege management flaw in the embedded agent tool exposure path. A chat sender who is allowed to enter the command/agent flow but is not the configured owner can still be presented with owner-onlygatewayandcrontools. If the model selects one of those tools, the run crosses into Gateway control-plane actions that should remain reserved for the owner/operator boundary, including configuration reads and cron mutation.Details
I verified this issue against the canonical GitHub repository
jiulingyun/openclaw-cnand anchored all occurrence references to commit1b9f16468d9841871cb15103693c3923424c9842, which is the remote commit for release tagv0.2.0, the highest affected public GitHub release on that repository.The vulnerable chain is straightforward:
src/auto-reply/command-auth.tscomputes a sender state whereisAuthorizedSender=truewhilesenderIsOwner=false.src/agents/pi-embedded-runner/run/attempt.tsforwardssenderIsOwnerintocreateOpenClawCodingTools(...).src/agents/openclaw-tools.tsalways adds rawcronandgatewaytools to the embedded tool bundle.src/agents/tool-policy.tsalready classifiescronandgatewayas owner-only and providesapplyOwnerOnlyToolPolicy(...).src/agents/pi-tools.tsreturns the final tool list after profile/group/sandbox filtering, but never applies the owner-only policy beforereturn withAbort.src/gateway/call.tsopens a Gateway client session with broad default scopes["operator.admin","operator.approvals","operator.pairing"].src/gateway/server-methods.tscorrectly treatsconfig.*andcron.addas admin-only methods, but the embedded tool path bypasses the intended boundary by self-connecting with those broader scopes.The key logic error is not that Gateway method authorization is absent. It is present and working. The defect is that a non-owner sender can still be offered tools that should never have been visible to that sender in the first place.
Relevant snippets:
I confirmed the issue using the existing integration-test harness in the report directory. The harness starts a real loopback Gateway, demonstrates that a plain
operator.writeclient is blocked fromcron.add, then constructs a non-owner embedded agent tool surface from the live code and successfully executesgateway config.getandcron.add. The control run appliesapplyOwnerOnlyToolPolicy(rawTools, false)and shows that both tools disappear and the cron mutation no longer occurs.PoC
Prerequisites
https://github.com/jiulingyun/openclaw-cn.pnpm,tsx, andpython3installed locally.driver.tsby relative path.commands.ownerAllowFrom,Reproduction Steps
git checkout v0.2.0python3 llm-enhance/cve-finding/similar/improper-privilege-management/Advisory-GHSA-2hm8-rqrm-xfjq-dataflow-non-owner-agent-tool-scope-bypass-exp/verification_test.pyauth.isAuthorizedSender: Trueauth.senderIsOwner: Falseraw tool list includes gateway: Trueraw tool list includes cron: Truegateway tool config.get succeeded: Truecron tool cron.add succeeded: Truecanary cron job listed after non-owner tool execution: Truepython3 llm-enhance/cve-finding/similar/improper-privilege-management/Advisory-GHSA-2hm8-rqrm-xfjq-dataflow-non-owner-agent-tool-scope-bypass-exp/control-owner-only-policy.pyeffective tool list includes gateway: Falseeffective tool list includes cron: Falsecanary cron job listed after control execution: FalseLog of Evidence
Verification run:
Control run:
Impact
This is an improper privilege management issue at the chat-user to embedded-agent control-plane boundary. It affects deployments that intentionally allow a broader set of senders to interact with the agent than the set of senders who should be treated as owners.
A successful trigger allows a non-owner sender context to reach owner-only operational tools. Depending on which tool the model selects, the affected assets include:
config.getconfig.patch/config.applycron.add,cron.update,cron.remove, andcron.runThis is not an unauthenticated public network issue. The attacker must already be accepted into the command/agent path. The security failure is that the owner-only boundary inside that accepted path is not enforced.
Affected products
>= 0.1.5, <= 0.2.0Severity
CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:U/C:L/I:H/A:LWeaknesses
Occurrences
resolveCommandAuthorization()computes bothsenderIsOwnerandisAuthorizedSender, creating the non-owner but command-authorized sender state that should not receive owner-only tools.senderIsOwnerintocreateOpenClawCodingTools(...), so the owner/non-owner state reaches the raw tool builder.createCronTool(...)andcreateGatewayTool(...), which are the owner-sensitive tools exposed on the vulnerable path.cronandgatewayas owner-only and providesapplyOwnerOnlyToolPolicy(...), which would strip them for non-owners if the embedded tool builder actually used it.createOpenClawCodingTools()does not declare or consume asenderIsOwneroption in its public contract, despite downstream call sites passing that field.gatewayandcron, into the embedded coding tool list.applyOwnerOnlyToolPolicy(...)occurs beforereturn withAbort, so non-owner runs keep the owner-only tools.callGateway()self-connects asrole: "operator"withoperator.admin,operator.approvals, andoperator.pairingscopes by default, magnifying the impact once the wrong tools are exposed.config.*andcron.addas admin-only methods, confirming that the bug is the embedded tool exposure path rather than missing method authorization.