Back to skill

Security audit

Feishu Media Sender

Security checks for vulnerabilities and agentic risk

Overview

This Feishu media sender mostly does what it says, but its credential selection and file validation are loose enough that users should review it before installing.

Install only if you trust the workspace and Feishu configuration it will run under. Before use, verify the recipient, selected file, and account binding; avoid sensitive paths; and prefer a version that fails closed on unmatched agents, uses path-aware workspace checks, rejects non-media files, enforces size limits, and asks for confirmation before upload.

Vulnerability Patterns
  • 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
  • Agent Memory PoisoningWrites attacker-controlled rules into memory that affect later sessions
  • Remote Payload Retrieval and ExecutionFetches external code whose behavior can change after review
Findings (3)

T05 · Unauthorized Access and Privilege Escalation

Error
Location
scripts/feishu_send_media.py:91
Finding
Unmatched agents can silently inherit an unrelated Feishu account<![CDATA[ ## Vulnerability Details **File Location**: `scripts/feishu_send_media.py`, lines 91-99 **Vulnerability Type**: `T05: Unauthorized Access and Privilege Escalation` **Risk Level**: High ### Vulnerable Code ```python if not account_id and bindings: account_id = bindings[0].get("match", {}).get("accountId") if not account_id and accounts: account_id = next(iter(accounts), None) if account_id: account = accounts.get(account_id, {}) app_id = account.get("appId") app_secret = account.get("appSecret") if app_id and app_secret: return app_id, app_secret ``` ### Technical Analysis When no account binding matches the resolved agent, `resolve_feishu_account()` does not fail closed. Instead, it selects the first binding and, if that does not produce an account, the first configured Feishu account. This fallback crosses account and agent authorization boundaries. In a multi-agent or multi-tenant OpenClaw installation, the first configured account may belong to an unrelated agent, workspace, or Feishu tenant. The script then uses that account's application identifier and secret to obtain a tenant access token and send the caller-selected file. The behavior is unnecessary for the declared media-delivery function. Account selection should require an exact, authorized association with the active agent. ### Attack Path 1. Multiple Feishu accounts or agent bindings are present in `~/.openclaw/openclaw.json`. 2. An attacker or untrusted agent executes the script from a workspace for which no exact binding can be found. 3. `resolve_feishu_account()` silently selects the first binding or first account. 4. The script reads that account's `appId` and `appSecret`. 5. It exchanges the credentials for a tenant access token at Feishu. 6. It uploads an attacker-selected local file and sends it to a caller-controlled recipient using the unrelated account. ### Impact Assessment An attacker could cause media to be sent under another configur ...[truncated 591 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Remove both first-binding and first-account fallback paths. - Require an exact binding between the resolved agent and the selected Feishu account. - Fail closed with a clear error when no matching binding exists. - If manual selection is necessary, introduce an explicit `--account-id` option and verify that the requested account is authorized for the resolved agent. - Do not permit an arbitrary account identifier supplied by the caller to bypass configured bindings. - Add tests covering unmatched agents, multiple accounts, missing bindings, and default-agent behavior. - Log the resolved agent and account identifiers, excluding secrets and access tokens, before performing an upload. ]]>

T05 · Unauthorized Access and Privilege Escalation

Error
Location
scripts/feishu_send_media.py:45
Finding
String-prefix workspace matching permits incorrect agent identity resolution<![CDATA[ ## Vulnerability Details **File Location**: `scripts/feishu_send_media.py`, lines 45-64 **Vulnerability Type**: `T05: Unauthorized Access and Privilege Escalation` **Risk Level**: High ### Vulnerable Code ```python def resolve_agent_id(config: Dict[str, Any]) -> str: cwd = Path.cwd().resolve() best_match = (0, None) defaults_ws = config.get("agents", {}).get("defaults", {}).get("workspace") if defaults_ws: defaults_path = Path(defaults_ws).resolve() if str(cwd).startswith(str(defaults_path)): best_match = (len(str(defaults_path)), "__defaults__") for agent in config.get("agents", {}).get("list", []): workspace = agent.get("workspace") agent_id = agent.get("id") if not workspace or not agent_id: continue workspace_path = Path(workspace).resolve() if str(cwd).startswith(str(workspace_path)): match_len = len(str(workspace_path)) if match_len > best_match[0]: best_match = (match_len, agent_id) ``` ### Technical Analysis The function determines whether the current directory is inside an agent workspace by converting both paths to strings and calling `startswith()`. String-prefix matching does not enforce filesystem component boundaries. For example, if an authorized workspace is `/work/agent`, then `/work/agent-evil` also starts with `/work/agent`, despite being a separate sibling directory. Execution from the sibling directory can therefore be incorrectly attributed to the authorized agent. This identity-resolution error becomes an authorization vulnerability because the resulting agent identifier is used to select Feishu application credentials. Combined with the script's ability to upload arbitrary caller-selected files and choose a recipient, incorrect workspace matching may let an untrusted workspace act using another agent's Feishu privileges. ### Attack Path 1. A victim agent has a configured workspace ...[truncated 1274 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Replace string-prefix matching with path-aware containment checks. - On supported Python versions, use: ```python def is_within(path: Path, parent: Path) -> bool: return path == parent or path.is_relative_to(parent) ``` - Alternatively, use `path == parent or parent in path.parents`. - Continue resolving both paths before comparison to normalize relative components and symlinks. - Fail closed if multiple workspace definitions resolve ambiguously. - Add regression tests for sibling paths such as `/work/agent` and `/work/agent-evil`. - Ensure account authorization is independently verified after agent resolution rather than treating path-derived identity as sufficient authorization. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/feishu_send_media.py:125
Finding
Insufficient file validation creates a local-file disclosure primitive<![CDATA[ ## Vulnerability Details **File Location**: `scripts/feishu_send_media.py`, lines 125-134 and 242-253 **Vulnerability Type**: `T09: Insecure Skill Coding Practices` **Risk Level**: Medium ### Vulnerable Code ```python def detect_media_type(file_path: Path, explicit: Optional[str]) -> str: if explicit: return explicit.lower() ext = file_path.suffix.lower() if ext in IMAGE_EXTENSIONS: return "image" if ext in VIDEO_EXTENSIONS: return "video" # Default to image for unknown extensions return "image" ``` ```python def main() -> None: args = parse_args() file_path = Path(args.file) if not file_path.exists(): raise FileNotFoundError(f"File not found: {file_path}") media_type = detect_media_type(file_path, args.type) config = load_openclaw_config() agent_id = resolve_agent_id(config) app_id, app_secret = resolve_feishu_account(config, agent_id) receive_id = resolve_receive_id(args.receive_id) ``` The selected path is subsequently opened and transmitted: ```python with file_path.open("rb") as f: resp = requests.post( FEISHU_IMAGE_UPLOAD_URL, headers=headers, data={"image_type": "message"}, files={"image": (file_path.name, f)}, timeout=60, ) ``` ### Technical Analysis The script only checks whether the supplied path exists. It does not enforce that the path: - Is an approved image or video format. - Has a MIME type or file signature consistent with its extension. - Is a regular file. - Is within an authorized workspace or media directory. - Is not a symlink to a sensitive file. - Complies with the documented 10 MB image or 30 MB video limits. Unknown extensions are explicitly treated as images. A caller can also force the type with `--type image` or `--type video`. Consequently, any readable regular file can reach the HTTP upload attempt, even when it is not media. The Feishu API may reject unsupported content, but ...[truncated 1855 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Reject unknown extensions instead of defaulting them to images. - Require `file_path.resolve(strict=True).is_file()` before opening the path. - Validate the actual file signature and MIME type using a trusted media parser; do not rely only on the extension. - Enforce the documented image and video size limits before upload. - Resolve symlinks and reject files outside an explicitly approved workspace or media directory. - If legitimate operation requires files outside that boundary, require explicit user confirmation before transmission. - Validate that the explicit `--type` agrees with the detected file format. - Consider opening files with protections against symlink races where supported. - Avoid logging file contents, credentials, tenant tokens, or authorization headers. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
Findings (5)

Lp3

Medium
Category
MCP Least Privilege
Confidence
92% confidence
Finding
The skill clearly describes behavior that reads local files, accesses credentials from the local configuration, and sends data over the network to Feishu, but it does not declare any explicit tool scope or permissions boundary. In an agent ecosystem, this weakens user visibility and enforcement around sensitive capabilities, increasing the chance that local files or tokens are accessed and exfiltrated without informed consent.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The quick-start and description explain how to send media but do not prominently warn that a local file is uploaded to a third-party service and then delivered into a Feishu chat. This creates a meaningful risk of accidental disclosure of sensitive images/videos, especially because users may treat a local path argument as a purely local operation rather than external transmission.

External Transmission

Medium
Category
Data Exfiltration
Content
def get_tenant_access_token(app_id: str, app_secret: str) -> str:
    resp = requests.post(
        FEISHU_TOKEN_URL,
        json={"app_id": app_id, "app_secret": app_secret},
        timeout=15,
Confidence
80% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Missing User Warnings

Medium
Confidence
93% confidence
Finding
This code uploads a user-specified local file to Feishu and then sends it as a message via external HTTP APIs. Although the network behavior is visible in code, there is no confirmation prompt, explicit disclosure in the CLI output, or warning in the module docstring that local file contents and recipient identifiers will be transmitted to a remote service.

External Transmission

Medium
Category
Data Exfiltration
Content
"msg_type": msg_type,
        "content": content,
    }
    resp = requests.post(
        FEISHU_SEND_MSG_URL,
        headers=headers,
        params=params,
Confidence
80% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Static analysis

No suspicious patterns detected.