Back to skill

Security audit

Feishu Bitable

Security checks for vulnerabilities and agentic risk

Overview

This Feishu Bitable skill is mostly purpose-aligned, but it needs Review because it can directly change or delete live business data and has weak safeguards around Feishu credentials and cached tokens.

Install only if you trust the environment where it will run and can scope the Feishu app to the minimum tables and permissions needed. Treat insert, update, and delete commands as production data changes, verify records with select/describe first, avoid filter-based deletes unless you intentionally bound them, and do not set FEISHU_API_HOST except to a trusted Feishu endpoint. On shared machines, review or disable the token cache because it stores a bearer token on disk.

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

Error
Location
scripts/feishu_bitable.py:31
Finding
Unrestricted API host override enables credential, token, and Bitable data redirection## Vulnerability Details **File Location**: `scripts/feishu_bitable.py:31-37, 88-104, 108-126` **Vulnerability Type**: Arbitrary network destination for sensitive authentication material and Bitable data **Risk Level**: High ### Vulnerable Code ```python DEFAULT_HOST = os.getenv("FEISHU_API_HOST", "https://open.feishu.cn") DEFAULT_CACHE = os.getenv( "FEISHU_TOKEN_CACHE", str(Path.home() / ".cache" / "openclaw" / "feishu_tenant_token.json") ) TOKEN_URL = f"{DEFAULT_HOST}/open-apis/auth/v3/tenant_access_token/internal" ``` ```python def get_tenant_token() -> str: app_id = os.getenv("FEISHU_APP_ID") app_secret = os.getenv("FEISHU_APP_SECRET") if not app_id or not app_secret: raise RuntimeError("Missing env: FEISHU_APP_ID / FEISHU_APP_SECRET") cached = _load_cached_token(DEFAULT_CACHE) if cached: log_debug("Using cached token") return cached resp = _http_json("POST", TOKEN_URL, headers={}, body={"app_id": app_id, "app_secret": app_secret}) if resp.get("code") != 0: raise RuntimeError(f"Token error: {resp}") token = resp["tenant_access_token"] expire = resp.get("expire", 3600) _save_cached_token(DEFAULT_CACHE, token, expire) log_debug("Token refreshed") return token ``` ```python def _api_get(url: str) -> Dict[str, Any]: token = get_tenant_token() return _http_json("GET", url, headers={"Authorization": f"Bearer {token}"}) def _api_post(url: str, body: Dict[str, Any]) -> Dict[str, Any]: token = get_tenant_token() return _http_json("POST", url, headers={"Authorization": f"Bearer {token}"}, body=body) def _api_put(url: str, body: Dict[str, Any]) -> Dict[str, Any]: token = get_tenant_token() return _http_json("PUT", url, headers={"Authorization": f"Bearer {token}"}, body=body) def _api_delete(url: str) -> Dict[str, Any]: token = get_tenant_token() return ...[truncated 2869 chars]
Remediation
## Remediation Suggestions 1. Remove `FEISHU_API_HOST` configurability if alternate endpoints are not strictly required. 2. If configurability is required, parse the value with a standard URL parser and require HTTPS. 3. Allowlist exact trusted Feishu API hostnames rather than relying on suffix matching. Reject user information, fragments, unexpected ports, raw IP addresses, and deceptive hostname suffixes. 4. Use independently validated, fixed origins for authentication and Bitable API traffic. 5. Prevent credentials and `Authorization` headers from being forwarded to a different origin during redirects. Prefer disabling redirects for authenticated requests or validating every redirect target. 6. Reject cleartext HTTP endpoints and fail closed when destination validation fails. 7. Document the accepted endpoint allowlist and treat any endpoint change as a security-sensitive configuration operation. 8. Apply minimum Feishu application scopes so that compromise of credentials does not grant unnecessary write or delete permissions.

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/feishu_bitable.py:32
Finding
Tenant access token is cached without enforced restrictive permissions or symlink protection## Vulnerability Details **File Location**: `scripts/feishu_bitable.py:32-35, 75-85` **Vulnerability Type**: Insecure plaintext storage of an authentication bearer token **Risk Level**: Medium ### Vulnerable Code ```python DEFAULT_CACHE = os.getenv( "FEISHU_TOKEN_CACHE", str(Path.home() / ".cache" / "openclaw" / "feishu_tenant_token.json") ) ``` ```python def _save_cached_token(cache_path: str, token: str, expire_seconds: int) -> None: p = Path(cache_path) p.parent.mkdir(parents=True, exist_ok=True) obj = { "tenant_access_token": token, "expires_at": int(time.time()) + int(expire_seconds), } p.write_text(json.dumps(obj, ensure_ascii=False), "utf-8") ``` ### Technical Analysis The tenant bearer token is persisted as plaintext JSON. The implementation does not explicitly create the cache directory with mode `0700` or the cache file with mode `0600`. Instead, resulting permissions depend on the process umask and any pre-existing directory or file. `Path.write_text()` also follows symbolic links and performs no ownership, file-type, or permission validation. The cache location can be changed through `FEISHU_TOKEN_CACHE`, but the path is not constrained to a trusted, user-owned directory. In a shared environment, a permissive umask or pre-existing cache file can make the bearer token readable by other local users. If an attacker can prepare the cache path or influence its configuration, a symbolic link can redirect the token write to another accessible location. ### Attack Path 1. An attacker identifies a shared or permissively accessible cache directory, influences `FEISHU_TOKEN_CACHE`, or prepares the selected cache file as a symbolic link. 2. The Skill authenticates to Feishu after finding no valid cached token. 3. `_save_cached_token()` writes the newly issued tenant access token through `Path.write_text()` without enforcing secure permissions or rejecting ...[truncated 731 chars]
Remediation
## Remediation Suggestions 1. Create the cache directory with mode `0700` and verify that it is owned by the current user. 2. Create the token file atomically with mode `0600`, using low-level file APIs such as `os.open()` with appropriate creation flags. 3. Use `O_NOFOLLOW` where supported and reject symbolic links, non-regular files, and files not owned by the current user. 4. Validate permissions and ownership before reading an existing cache file; reject files accessible to group or other users. 5. Write to a securely created temporary file in the same trusted directory and atomically replace the final cache file. 6. Constrain `FEISHU_TOKEN_CACHE` to a trusted user-owned cache directory, or remove arbitrary path configurability if it is unnecessary. 7. Prefer an operating-system credential store or keyring instead of plaintext token storage where available. 8. Delete expired tokens and avoid including token values in logs or exception messages.
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • Taint TrackingDirect Taint Flow, Variable-Mediated Taint Flow, Credential Exfiltration Chain
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
Findings (5)

Missing User Warnings

High
Confidence
97% confidence
Finding
The delete command can remove single, multiple, or filter-matched records immediately with no confirmation prompt, dry-run mode, or safeguard against broad matches. In an agent or automation context, malformed input, prompt injection, or operator error could trigger irreversible bulk deletion of business data.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The skill documents irreversible delete operations against a remote Feishu Bitable without any safety warning, confirmation step, or guidance to verify target record IDs first. In an agent setting, this increases the chance of accidental destructive actions and data loss, especially because the interface is framed as a convenient SQL-like tool for direct remote modification.

Missing User Warnings

Medium
Confidence
94% confidence
Finding
The script stores a credential-derived tenant access token on disk in a predictable cache location without setting restrictive file permissions or warning the user. On multi-user systems or misconfigured environments, another local user or process could read the token and use it to access Feishu APIs with the application's privileges until expiration.

Tainted flow: 'obj' from pathlib.Path.read_text (line 68, file read) → pathlib.Path.write_text (file write)

Medium
Category
Data Flow
Content
"tenant_access_token": token,
        "expires_at": int(time.time()) + int(expire_seconds),
    }
    p.write_text(json.dumps(obj, ensure_ascii=False), "utf-8")


def get_tenant_token() -> str:
Confidence
65% confidence
Finding
Data from a source is assigned to a variable that is later passed to a sink, creating a variable-mediated taint flow.

Missing User Warnings

Low
Confidence
89% confidence
Finding
The insert and update examples encourage direct writes to a live remote data source without clearly warning that they will change production Feishu Bitable contents. This can lead to unintended data corruption or unauthorized modification when an agent or user treats examples as harmless exploratory commands.

Static analysis

No suspicious patterns detected.