T09 · Insecure Skill Coding Practices
- Location
scripts/save_to_feishu.py:66- Finding
Unrestricted URL Fetching Enables SSRF and Secondary Data Disclosure
- Content
View full analysis
- Remediation
View remediation
Security audit
Security checks for vulnerabilities and agentic risk
This skill mostly matches its article-parsing purpose, but its Feishu save path can automatically alter table schema, read all records, and batch-delete rows without clear user control.
Review this skill before installing. Use the local parser only for public article URLs you trust, and do not enable Feishu saving unless you are comfortable granting the configured app broad table read/write permissions. Avoid pointing it at shared or important Feishu tables until the automatic field creation and blank-row deletion behavior is removed or gated behind explicit confirmation.
scripts/save_to_feishu.py:66Unrestricted URL Fetching Enables SSRF and Secondary Data Disclosure
scripts/save_to_feishu.py:220Article Save Operation Performs Unrelated Destructive Table-Wide Cleanup
scripts/save_to_feishu.py:180Normal Save Workflow Silently Performs Privileged Table Schema Changes
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:
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.
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:
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.
# 飞书批量删除最多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)} 个空白行")
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.
"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 limited to parsing WeChat public-article content, but the analyzed behavior reportedly includes handling unrelated domains, modifying Feishu table schema, and deleting blank rows. Hidden write and deletion capabilities are security-relevant because users may authorize a benign parser while unknowingly granting a tool that can alter or destroy external data.
The documented purpose is limited to parsing WeChat public-article content, but the analyzed behavior reportedly includes handling unrelated domains, modifying Feishu table schema, and deleting blank rows. Hidden write and deletion capabilities are security-relevant because users may authorize a benign parser while unknowingly granting a tool that can alter or destroy external data.
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 implementation does more than save an article: it modifies Feishu table schema and deletes existing rows it considers 'empty'. Those destructive side effects exceed the stated skill purpose and can cause unintended data loss in a user's existing table, making the mismatch particularly dangerous in this context.
The README instructs users to save parsed article data to Feishu but does not clearly disclose that article content, links, summaries, and related metadata will be transmitted to a third-party cloud service. This can lead to unintended data exposure, especially if users process private, sensitive, or copyrighted material and assume the tool operates only locally.
The skill advertises executable behaviors involving environment variables, file writes, network access, and shell commands, but it does not declare any explicit tool scope or permissions boundaries. This is dangerous because reviewers and runtime policy systems cannot easily determine or constrain what the skill may access, increasing the chance of unintended data exposure or unauthorized side effects.
The skill describes a save-to-Feishu feature but does not clearly warn users that article content may be transmitted to a third-party service. This is dangerous because users may share copyrighted, private, or sensitive article data without informed consent, creating privacy, compliance, and data-handling risks.
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 skill claims to target WeChat article parsing, but the code intentionally processes links from multiple unrelated platforms. This scope expansion increases privacy and consent risk because users and reviewers may expect a narrower data-handling boundary than the implementation actually enforces.
The skill invokes an external CLI tool to fetch content, which introduces extra execution capability and trust in whatever binary is present in the runtime environment. In an agent skill advertised as article parsing, this hidden dependency broadens the attack surface and makes behavior less auditable.
subprocess module calls execute external commands. Without careful input validation, this enables command injection.
"--max-chars", "5000"
]
result = subprocess.run(
cmd,
capture_output=True,
text=True,
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
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
This external request performs batch deletion of Feishu records. The danger is not merely that data leaves the system, but that the code can irreversibly delete user data based on a weak 'empty row' heuristic, which is especially unsafe for a skill whose description only mentions optional saving.
# 飞书批量删除最多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 sends article URL, title, summary, and full content to Feishu without any explicit consent gate or warning at the point of transfer. In this skill context, users may believe the tool only parses content locally, so undisclosed third-party transmission creates a meaningful privacy and data-governance risk.
The code uploads extracted article content and metadata to Feishu, which is expected for a save feature but still security-relevant because it transfers potentially sensitive content off-box. In this skill context, it becomes a true issue when done without explicit notice and consent, especially since full article bodies are included.
"fields": fields
}
response = requests.post(api_url, headers=headers, json=data)
result = response.json()
if result.get("code") == 0:
The parser accepts any URL and passes it directly to requests.get without validating that it belongs to mp.weixin.qq.com. In an agent setting, this can be abused for SSRF-style network access, unintended requests to internal services, or fetching attacker-controlled content under the guise of article parsing.
The manifest describes parsing WeChat articles and optionally saving results to a Feishu spreadsheet, but this file implements saving parsed article data to an arbitrary local JSON file. Local filesystem export is a broader behavior than the stated destination-specific save capability in the manifest.
A language or locale policy issue can arise when a skill experience is effectively constrained to a single language without opt-in or justification. This quickstart presents all user-facing instructions in Chinese and does not mention whether Chinese is required, optional, or specific to a regional audience.
No suspicious patterns detected.