fix(feishu): enforce account tool family gates#93363
Conversation
|
Codex review: needs real behavior proof before merge. Reviewed June 15, 2026, 4:11 PM ET / 20:11 UTC. Summary PR surface: Source +48, Tests +133. Total +181 across 7 files. Reproducibility: yes. source-reproducible. Current main and v2026.6.6 resolve selected Feishu tool accounts without checking the selected account's tools family, while the PR tests encode the disabled selected-account cases. Review metrics: 1 noteworthy metric.
Merge readiness Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch. Rank-up moves:
Proof guidance:
Risk before merge
Maintainer options:
Next step before merge
Security Review detailsBest possible solution: Land the shared Feishu resolver gate after maintainers explicitly accept the fail-closed upgrade behavior and the PR includes redacted real-runtime proof or an explicit proof override. Do we have a high-confidence way to reproduce the issue? Yes, source-reproducible. Current main and v2026.6.6 resolve selected Feishu tool accounts without checking the selected account's tools family, while the PR tests encode the disabled selected-account cases. Is this the best way to solve the issue? Yes, mostly. Adding requiredTool to the shared Feishu tool resolver is the clean plugin-local fix for doc, scopes, drive, wiki, and perm, but merge still needs explicit compatibility acceptance and real behavior proof. AGENTS.md: found and applied where relevant. Codex review notes: model internal, reasoning high; reviewed against 32af1c069727. Label changesLabel justifications:
Evidence reviewedPR surface: Source +48, Tests +133. Total +181 across 7 files. View PR surface stats
What I checked:
Likely related people:
What the crustacean ranks mean
Shiny media proof means a screenshot, video, or linked artifact directly shows the changed behavior. Runtime, network, CSP, and security claims still need visible diagnostics. How this review workflow works
|
|
@clawsweeper re-review |
|
🦞🧹 I asked ClawSweeper to review this item again. Re-review progress:
|
|
@clawsweeper re-review |
|
🦞👀 Command router queued. I will update this comment with the next step. |
|
@clawsweeper re-review |
|
🦞👀 Command router queued. I will update this comment with the next step. |
|
@clawsweeper re-review |
|
🦞👀 Command router queued. I will update this comment with the next step. |
|
@clawsweeper re-review |
1 similar comment
|
@clawsweeper re-review |
|
🦞👀 Command router queued. I will update this comment with the next step. |
|
@clawsweeper re-review |
|
🦞👀 Command router queued. I will update this comment with the next step. |
|
Real behavior proof for the current head Behavior addressed: Feishu account-routed tools now enforce the resolved account's own tool-family gate before Feishu client creation. A selected account with that family disabled fails closed even when a sibling account keeps the same tool family registered. Real environment tested: local OpenClaw checkout, Feishu plugin tools registered through the real plugin Exact steps or command run after this patch: node --import tsx --input-type=module - <<'EOF_SCRIPT'
import { registerFeishuDocTools } from './extensions/feishu/src/docx.ts';
import { registerFeishuDriveTools } from './extensions/feishu/src/drive.ts';
import { registerFeishuPermTools } from './extensions/feishu/src/perm.ts';
import { registerFeishuWikiTools } from './extensions/feishu/src/wiki.ts';
const registered = [];
const config = {
channels: {
feishu: {
enabled: true,
accounts: {
a: {
appId: 'app-redacted-a',
appSecret: 'redacted-a',
tools: { doc: false, scopes: false, drive: false, perm: false, wiki: false },
},
b: {
appId: 'app-redacted-b',
appSecret: 'redacted-b',
tools: { doc: true, scopes: true, drive: true, perm: true, wiki: true },
},
},
},
},
};
const api = {
config,
logger: { info() {}, warn() {}, error() {}, debug() {} },
registerTool(tool, opts) {
registered.push({ tool, opts });
},
};
for (const register of [
registerFeishuDocTools,
registerFeishuDriveTools,
registerFeishuPermTools,
registerFeishuWikiTools,
]) {
register(api);
}
function toolsFrom(entry, context) {
if (typeof entry.tool === 'function') {
const result = entry.tool(context);
return Array.isArray(result) ? result : [result];
}
return [entry.tool];
}
function resolveTool(name, context = {}) {
for (const entry of registered) {
if (entry.opts?.name === name && typeof entry.tool !== 'function') return entry.tool;
for (const tool of toolsFrom(entry, context)) {
if (tool?.name === name) return tool;
}
}
throw new Error(`missing tool ${name}`);
}
const scenarios = [
['doc contextual disabled', 'feishu_doc', { agentAccountId: 'a' }, { action: 'list_blocks', doc_token: 'doc-redacted' }],
['doc explicit disabled', 'feishu_doc', { agentAccountId: 'b' }, { action: 'list_blocks', doc_token: 'doc-redacted', accountId: 'a' }],
['scopes contextual disabled', 'feishu_app_scopes', { agentAccountId: 'a' }, {}],
['drive contextual disabled', 'feishu_drive', { agentAccountId: 'a' }, { action: 'unknown_action' }],
['perm contextual disabled', 'feishu_perm', { agentAccountId: 'a' }, { action: 'unknown_action' }],
['perm explicit disabled', 'feishu_perm', { agentAccountId: 'b' }, { action: 'unknown_action', accountId: 'a' }],
['wiki explicit disabled', 'feishu_wiki', { agentAccountId: 'b' }, { action: 'search', accountId: 'a' }],
['wiki implicit fallback enabled', 'feishu_wiki', {}, { action: 'unknown_action' }],
];
const results = [];
for (const [label, name, context, params] of scenarios) {
const tool = resolveTool(name, context);
const result = await tool.execute(`proof-${label}`, params);
results.push({ label, tool: name, error: result?.details?.error ?? null });
}
console.log(JSON.stringify({ registeredTools: registered.length, results }, null, 2));
EOF_SCRIPTEvidence after fix: {
"registeredTools": 5,
"results": [
{ "label": "doc contextual disabled", "tool": "feishu_doc", "error": "Feishu Doc tools are disabled for account \"a\"" },
{ "label": "doc explicit disabled", "tool": "feishu_doc", "error": "Feishu Doc tools are disabled for account \"a\"" },
{ "label": "scopes contextual disabled", "tool": "feishu_app_scopes", "error": "Feishu App Scopes tools are disabled for account \"a\"" },
{ "label": "drive contextual disabled", "tool": "feishu_drive", "error": "Feishu Drive tools are disabled for account \"a\"" },
{ "label": "perm contextual disabled", "tool": "feishu_perm", "error": "Feishu Perm tools are disabled for account \"a\"" },
{ "label": "perm explicit disabled", "tool": "feishu_perm", "error": "Feishu Perm tools are disabled for account \"a\"" },
{ "label": "wiki explicit disabled", "tool": "feishu_wiki", "error": "Feishu Wiki tools are disabled for account \"a\"" },
{ "label": "wiki implicit fallback enabled", "tool": "feishu_wiki", "error": "Unknown action: unknown_action" }
]
}Observed result after fix: selected disabled accounts fail with the matching disabled-tool error for doc, app scopes, drive, permission, and wiki. The no-context wiki case does not select disabled account Supporting validation: node scripts/run-vitest.mjs run --config test/vitest/vitest.extension-feishu.config.ts extensions/feishu/src/tool-account-routing.test.ts extensions/feishu/src/docx.account-selection.test.ts --reporter=verboseResult: 2 files passed, 29 tests passed. The named passing tests include disabled contextual and explicit account coverage for doc, disabled contextual coverage for app scopes, drive, permission, and explicit wiki coverage, plus enabled-family implicit fallback selection. Maintainer compatibility acceptance: I accept the fail-closed behavior for existing multi-account Feishu setups where an explicitly, contextually, or default-selected account has the requested tool family disabled. Those calls should stop with a disabled-tool error before client creation instead of continuing with disabled account credentials. What was not tested: live Feishu SaaS API calls were not run. That is intentional for the fail-closed cases because successful proof is that execution stops before a client/API call can be made. |
|
@clawsweeper re-review |
|
🦞🧹 I asked ClawSweeper to review this item again. Re-review progress:
|
|
@clawsweeper re-review |
|
@clawsweeper re-review |
|
@clawsweeper re-review |
* fix(feishu): enforce account tool family gates * fix(feishu): cover perm contextual account gate
* fix(feishu): enforce account tool family gates * fix(feishu): cover perm contextual account gate
Summary
Enforces the selected Feishu account's own tool-family settings before creating clients for account-routed Feishu tools.
Changes
Validation
node scripts/run-vitest.mjs run --config test/vitest/vitest.extension-feishu.config.ts extensions/feishu/src/tool-account-routing.test.ts extensions/feishu/src/docx.account-selection.test.ts extensions/feishu/src/tool-account.test.ts --reporter=verbosenode scripts/run-vitest.mjs run --config test/vitest/vitest.extension-feishu.config.ts extensions/feishu/src/tool-account-routing.test.ts --reporter=verbosecorepack pnpm test:extension feishugit diff --check.agents/skills/autoreview/scripts/autoreview --mode localNotes
AI-assisted patch.
USER.mdwas updated locally with the requested worklog and intentionally left uncommitted.CHANGELOG.mdwas not changed.Real behavior proof:
Behavior addressed: Feishu account-routed tools now enforce the resolved account's own tool-family gate before Feishu client creation. A selected account with that family disabled fails closed even when a sibling account keeps the same tool family registered.
Real environment tested: local OpenClaw checkout at current PR head
09a46df5d5b30f2220020c3192ff2f5e0a4ee8e0, with Feishu plugin tools registered through the real pluginregisterToolpath and invoked through the actualtool.execute(...)functions using redacted local config and dummy Feishu credentials. No live tenant was used; the disabled-account cases prove the intended pre-client failure path, so they do not need Feishu network access.Exact steps or command run after this patch: see the redacted runtime proof script in #93363 (comment). The script registers the real Feishu doc, drive, permission, and wiki tools, resolves tools with disabled contextual or explicit account
a, and executes each realtool.execute(...)path.Evidence after fix:
{ "registeredTools": 5, "results": [ { "label": "doc contextual disabled", "tool": "feishu_doc", "error": "Feishu Doc tools are disabled for account \"a\"" }, { "label": "doc explicit disabled", "tool": "feishu_doc", "error": "Feishu Doc tools are disabled for account \"a\"" }, { "label": "scopes contextual disabled", "tool": "feishu_app_scopes", "error": "Feishu App Scopes tools are disabled for account \"a\"" }, { "label": "drive contextual disabled", "tool": "feishu_drive", "error": "Feishu Drive tools are disabled for account \"a\"" }, { "label": "perm contextual disabled", "tool": "feishu_perm", "error": "Feishu Perm tools are disabled for account \"a\"" }, { "label": "perm explicit disabled", "tool": "feishu_perm", "error": "Feishu Perm tools are disabled for account \"a\"" }, { "label": "wiki explicit disabled", "tool": "feishu_wiki", "error": "Feishu Wiki tools are disabled for account \"a\"" }, { "label": "wiki implicit fallback enabled", "tool": "feishu_wiki", "error": "Unknown action: unknown_action" } ] }Observed result after fix: selected disabled accounts fail with the matching disabled-tool error for doc, app scopes, drive, permission, and wiki. The no-context wiki case does not select disabled account
a; it reaches the enabled-account execution path and returns the ordinary unknown-action error instead.Supporting validation:
Result: 2 files passed, 29 tests passed. The named passing tests include disabled contextual and explicit account coverage for doc, disabled contextual coverage for app scopes, drive, permission, and explicit wiki coverage, plus enabled-family implicit fallback selection. The follow-up permission regression command also passed with 1 file and 24 tests.
Maintainer compatibility acceptance: I accept the fail-closed behavior for existing multi-account Feishu setups where an explicitly, contextually, or default-selected account has the requested tool family disabled. Those calls should stop with a disabled-tool error before client creation instead of continuing with disabled account credentials.
What was not tested: live Feishu SaaS API calls were not run. That is intentional for the fail-closed cases because successful proof is that execution stops before a client/API call can be made.