T09 · Insecure Skill Coding Practices
- Location
scripts/save_to_feishu.py:69- Finding
Arbitrary URL Fetching Enables SSRF and Internal Data Transfer to Feishu
- Content
View full analysis
- Remediation
View remediation
Security audit
Security checks for vulnerabilities and agentic risk
This skill has a legitimate article parsing and Feishu saving purpose, but its save path can fetch arbitrary URLs and mutate or delete Feishu table data without clear disclosure or confirmation.
Review carefully before installing. Use this only with a dedicated Feishu table and least-privilege app credentials, avoid private or internal URLs, and consider removing or disabling automatic field creation and blank-row deletion before use. Treat Feishu saving as an external upload of article title, URL, summary, body content, source, and timestamp.
scripts/save_to_feishu.py:69Arbitrary URL Fetching Enables SSRF and Internal Data Transfer to Feishu
scripts/save_to_feishu.py:222Article Save Operation Performs Undisclosed Table-Wide Record Deletion
requirements.txt:1Open-Ended Dependency Constraints Make Installations Non-Reproducible
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.
"app_secret": APP_SECRET
}
response = requests.post(url, headers=headers, json=data)
result = response.json()
if result.get("code") == 0:
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.
api_url = f"https://open.feishu.cn/open-apis/bitable/v1/apps/{APP_TOKEN}/tables/{TABLE_ID}/fields"
headers = {"Authorization": f"Bearer {token}"}
response = requests.get(api_url, headers=headers)
result = response.json()
if result.get("code") == 0:
This code modifies the remote Feishu table schema automatically by creating missing fields. In the context of a content-parsing skill, altering external data structures goes beyond simple save behavior and can cause unauthorized changes, break workflows, or be abused to mutate connected business data without operator approval.
created_count = 0
for field_name, field_def in required_fields.items():
if field_name not in existing:
response = requests.post(api_url, headers=headers, json=field_def)
result = response.json()
if result.get("code") == 0:
created_count += 1
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.
if page_token:
params["page_token"] = page_token
response = requests.get(api_url, headers=headers, params=params)
result = response.json()
if result.get("code") != 0:
The script deletes Feishu records it heuristically judges to be blank, with no user confirmation and no clear scoping beyond the configured table. In a skill advertised as saving article content, silent deletion of existing remote data is dangerous because false positives or misconfiguration could destroy legitimate records.
# 飞书批量删除最多500条
for i in range(0, len(empty_ids), 500):
batch = empty_ids[i:i+500]
response = requests.post(delete_url, headers=delete_headers, json={"records": batch})
delete_result = response.json()
if delete_result.get("code") == 0:
print(f" 🗑️ 批量删除 {len(batch)} 个空白行")
The script uploads fetched article content, summary, title, and URL to Feishu without any explicit confirmation or consent gate in this code path. In a skill context handling third-party article links, this creates a real data-transfer risk because user-provided content is persisted to an external SaaS service automatically once invoked.
"fields": fields
}
response = requests.post(api_url, headers=headers, json=data)
result = response.json()
if result.get("code") == 0:
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
复制配置模板:
cp .env.example .env
编辑 .env 文件:
The documented purpose is narrow WeChat article parsing, but the detected behavior includes operating on Feishu table structure, deleting blank rows, and supporting sites beyond the declared scope. Description-behavior mismatches are dangerous because users may authorize the skill expecting passive parsing while it performs broader write and cleanup actions against external systems, creating risk of data loss and unauthorized modification.
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
import os
from dotenv import load_dotenv
# 加载 .env 文件
load_dotenv()
APP_ID = os.getenv("FEISHU_APP_ID", "")
The script auto-creates Feishu fields and deletes records, which materially exceeds an 'optional save' capability. In skill context this is especially risky because users invoking a parser/save action would not reasonably expect schema mutation and destructive maintenance operations on existing remote data.
The README documents a feature that sends parsed article data to Feishu but does not clearly warn users that article content, summaries, URLs, and related metadata will be transmitted to a third-party service when the save feature is used. This creates a transparency and privacy risk because users may unintentionally export sensitive or copyrighted content outside the local parsing workflow.
The skill advertises executable capabilities including environment access, file writing, network access, and shell execution, but does not declare any tool scope or permission boundaries. This makes it harder for users and the host platform to understand or constrain what the skill can do, increasing the risk of unintended data access, outbound transmission, or destructive side effects if the scripts are invoked automatically.
The activation and usage description is entirely written as a Chinese-only interaction flow, including examples like '解析这篇文章' and '收藏', without indicating that other languages are supported or that Chinese is required for a justified regional/compliance reason. Under the stated policy, forcing a specific language without user opt-in is a natural-language policy concern.
The skill offers saving parsed content to Feishu but does not clearly warn that article data will be transmitted to an external third-party service. This creates a privacy and data-governance risk because users may share article text, metadata, or potentially sensitive content without informed consent or awareness of where the data is going.
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
"app_secret": APP_SECRET
}
response = requests.post(url, headers=headers, json=data)
result = response.json()
if result.get("code") == 0:
The code supports extracting and classifying content from multiple platforms beyond WeChat, which exceeds the stated scope of the skill. Scope expansion matters in security review because it increases the set of user inputs and destinations handled by the skill, raising the likelihood of unsafe use and undisclosed data processing.
The skill relies on an external CLI subprocess to fetch web content, a capability not justified by the manifest and harder to audit than direct library use. This increases supply-chain and execution risk, since the security posture of the openclaw binary and its handling of untrusted URLs are not visible here.
The script invokes an external CLI (openclaw web-fetch) on user-supplied URLs. Although it does not use shell=True, this still expands the skill's execution surface to another program whose behavior, security properties, and network access are outside this script's control, creating risk of unsafe content fetching, unexpected side effects, or abuse through untrusted input.
"--max-chars", "5000"
]
result = subprocess.run(
cmd,
capture_output=True,
text=True,
This outbound call creates fields in a remote Feishu table, which is a state-changing external action beyond simple article saving. In this skill context, undisclosed remote schema changes can impact unrelated workflows and should be treated as dangerous functionality.
created_count = 0
for field_name, field_def in required_fields.items():
if field_name not in existing:
response = requests.post(api_url, headers=headers, json=field_def)
result = response.json()
if result.get("code") == 0:
created_count += 1
The code deletes rows deemed blank with no confirmation step, preview, or rollback. In context, this is an unjustified destructive action for a parser/save skill and can cause accidental loss of legitimate user data due to simplistic heuristics or table misconfiguration.
This outbound call performs batch deletion of remote records. Because the skill is framed as an article parser/saver, destructive record deletion is an unexpected and high-risk side effect that can lead to data loss if triggered on the wrong table or with faulty heuristics.
# 飞书批量删除最多500条
for i in range(0, len(empty_ids), 500):
batch = empty_ids[i:i+500]
response = requests.post(delete_url, headers=delete_headers, json={"records": batch})
delete_result = response.json()
if delete_result.get("code") == 0:
print(f" 🗑️ 批量删除 {len(batch)} 个空白行")
The script transmits article content and metadata to Feishu without an explicit warning, consent prompt, or clear minimization of uploaded data. Because the skill processes third-party links and article bodies, undisclosed external persistence increases privacy and compliance risk.
This call transmits full article data to Feishu for storage. While saving is part of the feature, the lack of explicit consent and data minimization makes the external transmission risky, especially when handling potentially sensitive article content or private links.
"fields": fields
}
response = requests.post(api_url, headers=headers, json=data)
result = response.json()
if result.get("code") == 0:
The manifest says the skill parses WeChat articles and may optionally save results to 飞书表格, but this file implements local filesystem persistence via JSON output instead. That is a behavior expansion beyond the described save target, not just an implementation detail of parsing.
The document forces a single language/locale presentation without any opt-in or explanation of a region-specific constraint. Under the language/locale policy rule, natural-language content that mandates one language without user choice can be a policy violation.
No suspicious patterns detected.