Back to skill

Security audit

Feishu Message Reader

Security checks for vulnerabilities and agentic risk

Overview

The skill is a Feishu message reader, but thread mode can pull nearby unrelated chat messages and the documented token option can expose credentials locally.

Review this before installing in privacy-sensitive Feishu workspaces. Use a least-privileged Feishu app, prefer configured app credentials over passing --token on the command line, and be aware that --thread may transiently retrieve unrelated messages from the same chat even though it only prints filtered thread results.

Vulnerability Patterns
  • 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
  • Embedded Malicious CodeShips malicious scripts inside the skill and executes them locally
Findings (2)

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/fetch_message.py:148
Finding
Tenant Access Token Exposure Through Command-Line Arguments<![CDATA[ ## Vulnerability Details **File Location**: `scripts/fetch_message.py`, lines 148-157; documented in `SKILL.md`, line 39 **Vulnerability Type**: Credential exposure through process arguments **Risk Level**: Medium ### Vulnerable Code ```python def resolve_token(args): if args.token: return args.token app_id = os.environ.get("FEISHU_APP_ID") app_secret = os.environ.get("FEISHU_APP_SECRET") if not app_id or not app_secret: app_id, app_secret = get_openclaw_feishu_creds() if not app_id or not app_secret: print("Error: Provide --token, set FEISHU_APP_ID/FEISHU_APP_SECRET, " "or have OpenClaw config at ~/.openclaw/openclaw.json", file=sys.stderr) sys.exit(1) return get_tenant_token(app_id, app_secret) ``` ```python parser.add_argument("--token", help="tenant_access_token (auto if not provided)") ``` The documented interface also recommends this option: ```markdown Alternatively set `FEISHU_APP_ID` + `FEISHU_APP_SECRET` env vars, or pass `--token <tenant_access_token>`. ``` ### Technical Analysis The Skill permits a Feishu tenant access token to be supplied directly as a command-line argument. Command-line arguments are not an appropriate secret-transport mechanism because they may be exposed through: - Shell history files. - Process inspection utilities or operating-system process interfaces. - Command auditing, terminal recording, and job execution logs. - Wrapper scripts, orchestration systems, or diagnostic reports that record complete command lines. The script does not intentionally transmit the token to an unrelated service. It uses the token as a bearer credential only for the fixed official Feishu API origin. The vulnerability is instead the local exposure created before the network request occurs. ### Attack Path 1. A user obtains a valid Feishu tenant access token. 2. The user runs the documented command with `--token <tenant_access_token>`. 3. The complete invocation ...[truncated 1068 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Remove the `--token` command-line option so bearer tokens cannot be supplied through process arguments. 2. Prefer a permission-restricted credential store or the existing OpenClaw configuration mechanism. 3. If direct token input is necessary, read it from standard input without terminal echo, for example through Python's `getpass` module. 4. An environment variable may be retained as a compatibility mechanism, but a protected credential store is preferable because environment variables can also be exposed through diagnostics or inherited by child processes. 5. Update `SKILL.md` to remove examples that encourage users to place tenant tokens on the command line. 6. Ensure errors never include tokens, application secrets, authorization headers, or complete authentication responses. 7. Document the minimum Feishu scopes required for message retrieval and recommend short-lived credentials and prompt revocation after suspected disclosure. ]]>

other

Warning
Location
scripts/fetch_message.py:128
Finding
Thread Retrieval Downloads Unrelated Chat Messages Before Local Filtering<![CDATA[ ## Vulnerability Details **File Location**: `scripts/fetch_message.py`, lines 66-70 and 128-136 **Vulnerability Type**: Excessive collection of private message data **Risk Level**: Medium ### Vulnerable Code ```python def fetch_chat_messages(token, chat_id, start_time, end_time, page_size=50): path = (f"/im/v1/messages?container_id_type=chat&container_id={chat_id}" f"&start_time={start_time}&end_time={end_time}" f"&page_size={page_size}&sort_type=ByCreateTimeAsc") return feishu_get(token, path) ``` ```python # Fetch messages in a time window around the thread create_time = item.get("create_time", "0") # Use root create time as start, current message + buffer as end start_ts = int(root_create or create_time) // 1000 - 1 end_ts = int(create_time) // 1000 + 60 chat_resp = fetch_chat_messages(token, chat_id, start_ts, end_ts, 50) all_items = chat_resp.get("data", {}).get("items", []) # Filter to same thread thread_items = [m for m in all_items if m.get("root_id") == root_id or m.get("message_id") == root_id] ``` ### Technical Analysis The declared thread functionality needs the root message and replies belonging to that thread. Instead of requesting only those records, the implementation lists as many as 50 messages from the entire chat within a time window. The response is filtered by `root_id` only after all returned messages have been downloaded and parsed locally. Consequently, unrelated messages posted in the same chat during the selected interval enter the process's memory even though they are not needed for the requested output. The collection window starts at the root message and ends 60 seconds after the selected message. For a long-running thread, this may cover a substantial period. The fixed page limit may simultaneously over-collect unrelated messages and fail to return all relevant replies when more than 50 messages exist. The network request is sent only to Feishu's official API and n ...[truncated 1788 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Use a Feishu endpoint or server-side query that returns replies for a specific thread or root message, if the platform provides one. 2. Request thread messages using `root_id` or an equivalent server-side filter rather than listing the containing chat and filtering locally. 3. If no thread-specific endpoint exists: - Minimize the requested time interval. - Reduce the page size based on demonstrated need. - Stop pagination as soon as the necessary thread records are obtained. - Avoid retaining the unfiltered response after relevant records are selected. 4. Never log or print the unfiltered `chat_resp` object. 5. Document that thread mode can transiently retrieve unrelated chat messages so operators can make an informed privacy decision. 6. Document and request only the minimum Feishu application scopes required. 7. Add tests verifying that thread mode neither returns nor persists unrelated messages. 8. Consider disabling thread mode by default in privacy-sensitive deployments when server-side thread filtering is unavailable. ]]>
Vulnerability Patterns
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Rogue AgentSelf-Modification, Session Persistence
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
Findings (3)

Lp3

Medium
Category
MCP Least Privilege
Confidence
90% confidence
Finding
The skill explicitly instructs running a Python script that uses network access to the Feishu API and reads secrets from local config files or environment variables, but the manifest declares no permissions or allowed tool scope. That mismatch weakens security boundaries and can lead to unexpected credential use or outbound data access without clear policy enforcement.

Session Persistence

Medium
Category
Rogue Agent
Content
# Fetch root message
    root_resp = fetch_message(token, root_id)
    root_items = root_resp.get("data", {}).get("items", [])
    root_create = None
    if root_items:
        root_create = root_items[0].get("create_time")
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.

Context-Inappropriate Capability

Low
Confidence
82% confidence
Finding
The manifest describes a skill for fetching Feishu message content by message_id, with optional thread context. While obtaining authentication is necessary, this implementation also scans a specific local config file and environment variables for reusable credentials, which is a broader capability than the manifest states and exposes local secret-reading behavior not mentioned in the skill purpose.

Static analysis

No suspicious patterns detected.