Back to skill

Security audit

OpenClaw Feishu Multi-Agent

Security checks for vulnerabilities and agentic risk

Overview

The skill is coherent for setting up OpenClaw and Feishu multi-agent workflows, but it makes broad persistent agent/configuration changes and handles secrets and paths with insufficient safeguards.

Install only if you understand and want it to modify OpenClaw agent routing and identity/session files. Use dry-run first, inspect every planned path, keep roles files trusted, avoid putting real appSecret values in shareable role files or generated artifacts, and isolate this setup from unrelated OpenClaw sessions if possible.

Vulnerability Patterns
  • Agent Memory PoisoningWrites attacker-controlled rules into memory that affect later sessions
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
  • Insecure Skill Coding PracticesFinds exploitable flaws such as hardcoded secrets or command injection
  • Skill Instruction HijackingAlters the agent's session goals or safety constraints when the skill loads
  • Remote Payload Retrieval and ExecutionFetches external code whose behavior can change after review
Findings (4)

T05 · Unauthorized Access and Privilege Escalation

Error
Location
scripts/apply_feishu_multi_agent.py:75
Finding
Global Session Visibility Violates Least-Privilege Boundaries<![CDATA[ ## Vulnerability Details **File Location**: `scripts/apply_feishu_multi_agent.py:75-94`, `scripts/audit_feishu_multi_agent.py:36-44`, `scripts/render_feishu_multi_agent.py:157-164` **Vulnerability Type**: Excessive cross-session access **Risk Level**: High ### Vulnerable Code ```python def merge_tooling(config: dict[str, Any], roles: list[dict[str, Any]]) -> list[str]: changes: list[str] = [] tools = ensure_dict_path(config, ["tools"]) sessions = ensure_dict_path(tools, ["sessions"]) if sessions.get("visibility") != "all": sessions["visibility"] = "all" changes.append("set tools.sessions.visibility=all") agent_to_agent = ensure_dict_path(tools, ["agentToAgent"]) if agent_to_agent.get("enabled") is not True: agent_to_agent["enabled"] = True changes.append("set tools.agentToAgent.enabled=true") existing_allow = agent_to_agent.get("allow") if not isinstance(existing_allow, list): existing_allow = [] agent_to_agent["allow"] = existing_allow for role in roles: if role["agentId"] not in existing_allow: existing_allow.append(role["agentId"]) changes.append(f"allow agentToAgent: {role['agentId']}") return changes ``` The audit utility also treats global visibility as mandatory: ```python allow = set(dotted_get(config, ["tools", "agentToAgent", "allow"], []) or []) sessions_visibility = dotted_get(config, ["tools", "sessions", "visibility"]) agent_to_agent_enabled = dotted_get(config, ["tools", "agentToAgent", "enabled"]) if sessions_visibility != "all": failures.append("tools.sessions.visibility should be 'all'") if agent_to_agent_enabled is not True: failures.append("tools.agentToAgent.enabled should be true") ``` Generated configuration contains the same setting: ```python "tools": { "sessions": {"visibility": "all"}, "agentToAgent": {"enabled": True, "allow": allow}, }, ``` ### Technical Analysis The Skill enabl ...[truncated 1674 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Replace global session visibility with visibility scoped to the required agents and Feishu group sessions. 2. Keep `agentToAgent.allow` restricted to the minimum necessary set of Agent IDs. 3. If OpenClaw supports group-level or session-prefix authorization, permit only session keys matching the intended Feishu group. 4. If the platform cannot scope visibility, require explicit informed confirmation before setting it to `all`. 5. Change the audit utility so global visibility produces a security warning rather than treating it as the only valid configuration. 6. Document which data becomes visible after enabling the option and recommend deployment isolation between unrelated teams. 7. Add a post-apply verification step that enumerates effective Agent and session permissions. ]]>

T02 · Agent Memory Poisoning

Error
Location
scripts/render_feishu_multi_agent.py:98
Finding
Untrusted Role Fields Are Written into Persistent Agent Instructions<![CDATA[ ## Vulnerability Details **File Location**: `scripts/common.py:35-56`, `scripts/render_feishu_multi_agent.py:27-33`, `scripts/render_feishu_multi_agent.py:36-134`, `scripts/apply_feishu_multi_agent.py:202-220` **Vulnerability Type**: Persistent instruction injection through role configuration **Risk Level**: High ### Vulnerable Code Role validation only checks that selected values are present and that trigger terms are non-empty strings: ```python def load_roles(path: Path) -> tuple[str, list[dict[str, Any]]]: data = load_json(path) roles = data.get("roles") if not isinstance(roles, list) or not roles: raise ValueError("roles file must contain a non-empty 'roles' array") coordinators = [role for role in roles if role.get("isCoordinator")] if len(coordinators) != 1: raise ValueError("roles file must contain exactly one coordinator") for role in roles: for key in ("agentId", "roleName", "accountId", "openId", "responsibility"): if not role.get(key): raise ValueError(f"role {role!r} is missing required field: {key}") trigger_terms = role.get("triggerTerms", []) if not isinstance(trigger_terms, list) or not all(isinstance(x, str) and x for x in trigger_terms): raise ValueError(f"role {role['agentId']} has invalid triggerTerms") return data.get("systemName", "OpenClaw Feishu Multi-Agent"), roles ``` Those values are interpolated directly into instruction-bearing Markdown: ```python def render_identity(role: dict[str, Any], coordinator_name: str) -> str: trigger_text = "、".join(role["triggerTerms"]) if role["isCoordinator"]: extra = """ ## 飞书群协作 你是默认总调度。群里出现一个新问题时,先判断: 1. 这件事值不值得做 2. 该由谁主答 3. 是否需要多人并行讨论 只要需要别人参与,就必须同时做两步: 1. 在群里用飞书 `<at>` 显式点名 2. 对目标 agent 调用 `sessions_send` 绝对禁止: - 只发裸文本 `@角色` - 只发 `<at>` 不发 `sessions_send` - 给 `sessions_send` 传 `agentId` """ else: extra = f""" ## 飞书群协作 如果 {coordinator_name} 或其 ...[truncated 2685 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Treat roles files as security-sensitive executable configuration rather than passive data. 2. Apply strict length and character restrictions to identifiers and role names. 3. Reject newlines, headings, HTML, XML-like tags, and control syntax in fields that do not require rich text. 4. Keep descriptive values in clearly delimited data sections and state that their contents are untrusted data, not instructions. 5. Do not interpolate free-form role descriptions into files that establish Agent policy. 6. Generate a complete diff and require explicit confirmation before writing protocol or identity files. 7. Preserve existing instruction files by default and require a separate high-friction option for overwriting them. 8. Record the source and a cryptographic digest of the roles file used for persistent changes. 9. Add adversarial tests containing newline-based Markdown injection and tool directives. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
scripts/apply_feishu_multi_agent.py:156
Finding
Unvalidated Role Paths Permit Writes Outside Intended Directories<![CDATA[ ## Vulnerability Details **File Location**: `scripts/common.py:59-75`, `scripts/render_feishu_multi_agent.py:221-222`, `scripts/apply_feishu_multi_agent.py:156-172`, `scripts/repair_feishu_group_sessions.py:54-57` **Vulnerability Type**: Path traversal and unrestricted filesystem write **Risk Level**: High ### Vulnerable Code User-controlled paths are accepted and resolved without confinement to the OpenClaw state directory: ```python def role_workspace(role: dict[str, Any], state_dir: Path) -> Path: raw = role.get("workspace") if raw and not is_placeholder(raw): return Path(raw).expanduser().resolve() raw = role.get("agentDir") if raw and not is_placeholder(raw): return Path(raw).expanduser().resolve() return role_default_dir(state_dir, role) def role_agent_dir(role: dict[str, Any], state_dir: Path) -> Path: raw = role.get("agentDir") if raw and not is_placeholder(raw): return Path(raw).expanduser().resolve() return role_default_dir(state_dir, role) ``` The apply workflow writes an identity file to the resulting directory: ```python def apply_identity_files( roles: list[dict[str, Any]], coordinator_name: str, state_dir: Path, write: bool, backup: bool, ) -> list[str]: changes: list[str] = [] for role in roles: target_dir = role_agent_dir(role, state_dir) target = target_dir / "IDENTITY.md" content = render_identity(role, coordinator_name) if target.exists() and target.read_text(encoding="utf-8") == content: continue changes.append(f"write {target}") if write: target_dir.mkdir(parents=True, exist_ok=True) if backup and target.exists(): backup_file(target) target.write_text(content, encoding="utf-8") return changes ``` The renderer also uses an unvalidated `agentId` as part of an output filename: ```python for role in roles: target = identitie ...[truncated 2163 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Restrict `agentId` to a conservative identifier pattern such as `^[A-Za-z0-9_-]+$`. 2. Reject path separators, `.` and `..` path components, null characters, and control characters in all identifiers. 3. Resolve every output path and verify that it remains under the approved root using a canonical containment check. 4. Reject absolute `agentDir` and `workspace` values by default. 5. If external directories are required, expose a separate option that explicitly authorizes each resolved path. 6. Check for symbolic links in parent components before writing, and avoid following links where supported. 7. Use atomic file replacement after validating the final destination. 8. Display the canonical destination path during dry-run and require confirmation for destinations outside the standard state directory. 9. Add regression tests for absolute paths, `../` traversal, nested separators, and symlink escapes. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/common.py:18
Finding
Feishu Application Secrets Are Written Without Enforced Restrictive Permissions<![CDATA[ ## Vulnerability Details **File Location**: `scripts/apply_feishu_multi_agent.py:117-124`, `scripts/common.py:18-20`, `scripts/render_feishu_multi_agent.py:165-172`, `scripts/render_feishu_multi_agent.py:213-219` **Vulnerability Type**: Insecure plaintext secret storage **Risk Level**: Medium ### Vulnerable Code Application credentials from the roles file are copied directly into account configuration: ```python for key in ("appId", "appSecret"): incoming = role.get(key) if not is_placeholder(incoming): if account.get(key) != incoming: account[key] = incoming changes.append(f"set {key} for {role['accountId']}") elif key not in account: account[key] = "replace_me" changes.append(f"set placeholder {key} for {role['accountId']}") ``` The shared writer does not enforce a restrictive file mode: ```python def write_json(path: Path, data: Any) -> None: path.write_text(json.dumps(data, ensure_ascii=False, indent=2) + "\n", encoding="utf-8") ``` Generated artifacts also include the secret: ```python accounts = { role["accountId"]: { "name": role["roleName"], "appId": role.get("appId") if not is_placeholder(role.get("appId")) else "replace_me", "appSecret": role.get("appSecret") if not is_placeholder(role.get("appSecret")) else "replace_me", } for role in roles } ``` The full roles structure is duplicated into another output file: ```python (output_dir / "openclaw.generated.json").write_text( render_openclaw_snippet(roles, state_dir), encoding="utf-8" ) (output_dir / "roles.generated.json").write_text( json.dumps({"systemName": system_name, "roles": roles}, ensure_ascii=False, indent=2), encoding="utf-8", ) ``` ### Technical Analysis Real `appSecret` values supplied in a roles file can be written into the live OpenClaw configuration, `openclaw.generated.json`, and `roles.generated.json`. These files are created through ordinary `Path.write_ ...[truncated 1396 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Do not store `appSecret` in reusable role files or generated artifacts. 2. Read credentials from environment variables, an operating-system credential store, or a dedicated secret manager. 3. Redact `appSecret` when writing `roles.generated.json`. 4. Use references such as `${FEISHU_APP_SECRET_ACCOUNT}` in generated configuration instead of embedding plaintext values. 5. If plaintext storage is unavoidable, securely create files with mode `0600` and verify permissions after writing. 6. Reject or warn about output directories that are group-readable, world-readable, shared, or located inside source-control repositories. 7. Use atomic writes with a securely created temporary file in the same protected directory. 8. Document credential rotation procedures and recommend immediate rotation after suspected exposure. ]]>
Vulnerability Patterns
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Rogue AgentSelf-Modification, Session Persistence
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (15)

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding
The code only performs static validation of configuration and roles data for an OpenClaw + Feishu multi-agent setup. While this partially aligns with the declared 'audit existing configs' aspect, it does not implement most of the broader described functionality: teaching coordinator/specialist agents, enabling delegation and handoffs in chats, scaffolding new setups, repairing metadata, or generating artifacts. The primary purpose in the description is a multifunction workflow-building and troubleshooting skill, but the actual code is narrowly an audit CLI. That is a material description/behavior mismatch.

Lp3

Medium
Category
MCP Least Privilege
Confidence
82% confidence
Finding
The skill instructs reading and potentially modifying local configuration and identity files under ~/.openclaw, but it does not declare any explicit tool scope or permissions. That creates an authorization transparency gap: a caller may invoke a skill with file access behavior that is broader than the metadata suggests, increasing the chance of unintended local file reads or writes.

Session Persistence

Medium
Category
Rogue Agent
Content
--apply-identities
```

加上 `--write --backup` 才会真正写入 `~/.openclaw/`。

### 2. 生成可落地模板
Confidence
76% confidence
Finding
This duplicate finding points to the same persistence risk: the skill promotes optional write-back into ~/.openclaw, which can permanently change agent configuration and session behavior. In the context of a multi-agent coordination skill, those changes may affect message routing, visibility, and delegated execution across future runs, making mistakes or abuse more consequential.

Session Persistence

Medium
Category
Rogue Agent
Content
--apply-identities
```

加上 `--write --backup` 才会真正写入 `~/.openclaw/`。

### 2. 生成可落地模板
Confidence
76% confidence
Finding
This duplicate finding points to the same persistence risk: the skill promotes optional write-back into ~/.openclaw, which can permanently change agent configuration and session behavior. In the context of a multi-agent coordination skill, those changes may affect message routing, visibility, and delegated execution across future runs, making mistakes or abuse more consequential.

Natural-Language Policy Violations

Medium
Confidence
97% confidence
Finding
The instruction '中文说明为主' sets a default language requirement in natural language. This is a locale/language policy concern because the skill does not offer the user a language choice or indicate that Chinese output is optional.

Natural-Language Policy Violations

Medium
Confidence
90% confidence
Finding
The skill reference content is entirely in Chinese and does not indicate that users may choose another language or that the locale is intentionally restricted. Under the stated policy, forcing a specific language without user opt-in is a natural-language policy concern.

Missing User Warnings

Medium
Confidence
86% confidence
Finding
This code copies `appId` and `appSecret` values from the roles data into the local configuration and later persists that configuration to disk. Although the script supports dry-run mode and prints planned changes, it does not explicitly warn the user that sensitive credential material may be written into `~/.openclaw/openclaw.json`.

Missing User Warnings

Medium
Confidence
89% confidence
Finding
The generated OpenClaw config emits `appId` and `appSecret` fields directly into `openclaw.generated.json`, sourcing them from the roles data if present. This creates a real secret-handling risk because generated artifacts are commonly checked into source control, shared for troubleshooting, or left with permissive filesystem access, exposing Feishu credentials beyond their intended scope.

Session Persistence

Medium
Category
Rogue Agent
Content
def main(argv: list[str] | None = None) -> int:
    parser = argparse.ArgumentParser(description=__doc__)
    parser.add_argument("--roles", required=True, help="Path to roles.json")
    parser.add_argument("--output-dir", required=True, help="Directory to write generated artifacts")
    parser.add_argument(
        "--state-dir",
        default="~/.openclaw",
Confidence
60% confidence
Finding
Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

Missing User Warnings

Medium
Confidence
87% confidence
Finding
This code performs persistent file modification by mutating session data and writing it back to disk. Although the CLI includes a --fix flag and prints a post-write message, there is no pre-action confirmation, cautionary comment/docstring, or explicit user-facing warning that enabling this flag will alter OpenClaw state files.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The role inventory template explicitly includes sensitive secrets such as appSecret alongside non-secret metadata, but provides no warning, masking guidance, or secure-handling instructions. In a collaboration-oriented skill, users may paste real credentials into shared docs, chats, or version-controlled files, leading to accidental secret disclosure and subsequent compromise of Feishu applications or agent accounts.

Vague Triggers

Medium
Confidence
88% confidence
Finding
This markdown file includes test utterances built around `@{coordinatorTrigger}` but never defines a narrow, explicit set of allowed trigger phrases or exclusion conditions. The surrounding examples describe common-language requests like asking someone to discuss or decide who should handle an issue, which could overlap with ordinary chat and lead to unintended invocation patterns.

Missing User Warnings

Low
Confidence
84% confidence
Finding
This markdown file describes an apply script that can write into `~/.openclaw/` and agent directories when flags such as `--write` or `--apply-identities` are used. Although it notes dry-run and backup behavior, it does not clearly warn users that these actions modify local configuration and may affect existing agent behavior or data.

Missing User Warnings

Low
Confidence
88% confidence
Finding
The document recommends a script to scan or repair Feishu group session metadata, which can affect active routing state. While the purpose is explained, there is no direct warning that repairing session metadata may alter live behavior and should be validated carefully before applying changes.

Missing User Warnings

Low
Confidence
80% confidence
Finding
When `--apply-identities` and `--write` are used, the script creates directories and writes `IDENTITY.md` files for each role. The action is logged as planned changes, but there is no explicit warning in the help text or docstring that enabling this flag will create or overwrite per-agent files under the state directory.

Static analysis

No suspicious patterns detected.