T09 · Insecure Skill Coding Practices
- Location
scripts/rss_fetcher.py:39- Finding
RSS downloads disable TLS certificate verification
- Content
View full analysis
- Remediation
View remediation
Security audit
Security checks for vulnerabilities and agentic risk
This is a game-news report skill, but it needs Review because it installs persistent scheduled execution and uses overbroad web-fetching behavior that is not safely scoped.
Install only if you are comfortable with a daily scheduled job that fetches web pages from configured URLs. Before use, preserve your existing crontab, restrict the config to trusted public news domains, avoid sensitive/internal URLs, disable or explicitly approve r.jina.ai proxy use, and fix TLS verification and dependency pinning.
scripts/rss_fetcher.py:39RSS downloads disable TLS certificate verification
scripts/crawler.py:79Configuration-controlled network requests permit server-side request forgery
scripts/crawler_v2.py:166Runtime resolution of unpinned third-party dependencies
README.md:182Documented cron installation overwrites the user’s complete crontab
YARA rule matched a known malware signature (reverse shell, backdoor, ransomware, C2 framework, or info stealer).
kspace/logs/daily-game-news.log
查看日志:
```bash
# 查看最新日志
tail -f /home/admin/.openclaw/workspace/logs/daily-game-news.log
# 查看今日日志
cat /home/admin/.openclaw/workspace/logs/daily-game-news.log | grep "2026-03-07"
A: 编辑配置文件中的定时任务部分,然后重新配置 cron:
crontab /home/admin/.openclaw/workspace/skills/daily-game-news/crontab.txt
A: 编辑 news-crawler-config.json,在"网站配置"数组中添加或删除网站配置。
A: 在配置文件中找到对应网站的"分区"配置,修改"筛选数量"字段。
A: 检查以下步骤:
crontab -ltail logs/daily-game-news.loguv run --versionThe code copies the entire parent environment into a subprocess via {**os.environ, 'SEARXNG_URL': ...}, potentially exposing API keys, tokens, and sensitive runtime settings to another executable and all of its imported code. In an agent skill context, helper scripts and transient dependencies materially increase the risk that secrets are read, logged, or exfiltrated beyond the crawler's stated need.
['uv', 'run', script_path,
'search', search_query, '-n', str(limit), '--format', 'json'],
capture_output=True, text=True, timeout=60,
env={**os.environ, 'SEARXNG_URL': 'http://localhost:8080'},
cwd=os.path.dirname(script_path)
)
Code enumerates, copies, or searches environment variables for secrets. Bulk environment access can collect credentials unrelated to the skill's stated purpose.
['uv', 'run', script_path,
'search', search_query, '-n', str(limit * 3), '--format', 'json'],
capture_output=True, text=True, timeout=60,
env={**os.environ, 'SEARXNG_URL': 'http://localhost:8080'},
cwd=os.path.dirname(script_path)
)
The README title and operating instructions are explicitly Chinese-language, and the listed spoken commands are all in Chinese, which can amount to a forced language/locale assumption. The policy allows locale constraints when they are clearly documented and justified or when users are given a choice, but neither is present here.
Code scans file system directories looking for sensitive files. This could be reconnaissance for credential theft.
# 查看所有报告
ls -la /home/admin/.openclaw/workspace/reports/daily-game-news/
# 查看最近 5 个报告
ls -lt /home/admin/.openclaw/workspace/reports/daily-game-news/ | head -6
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.
### Q: 报告没有按时发送?
A: 检查以下步骤:
1. cron 任务是否正常运行:`crontab -l`
2. 日志文件是否有错误:`tail logs/daily-game-news.log`
3. Python 环境是否正常:`uv run --version`
4. 网络连接是否正常
The skill’s natural-language description is presented in Chinese with no indication that users can choose another language. This can violate language/locale policy requirements when a specific language is effectively forced without opt-in or documented justification.
The module docstring and user-facing messages claim the script directly crawls daily game news, but the implementation only processes a hardcoded sample list. This creates a deceptive integrity issue: operators may trust the report as fresh externally sourced data when it is actually static, which can mislead downstream decisions and monitoring.
The main function documentation says article data is read from prior crawl results or configuration, but the code ignores configuration for article input and uses fixed in-code articles instead. In an agent skill context, this can cause silent production of fabricated or stale reports, undermining the trustworthiness of automation and potentially concealing pipeline failures.
Natural-language strings throughout the file describe the skill, console output, time labels, and generated reports exclusively in Chinese, which effectively forces a specific language experience. The file does not present this as an explicit opt-in or justified region-specific constraint, so it conflicts with the language/locale policy criteria.
The module docstring states that it directly fetches site list pages using curl and BeautifulSoup. However, fetch_html conditionally sends requests to https://r.jina.ai/ using httpx for gamersky and gamespot, which is materially different behavior because it relies on a third-party fetch/proxy service rather than direct site retrieval.
The crawler sends target URLs to a third-party proxy service at r.jina.ai, which means browsing targets and potentially sensitive query strings are disclosed outside the expected destination. In this skill context, URLs are loaded from config and could include internal or sensitive resources, so silent proxying increases privacy, compliance, and possible SSRF-style data exposure risk.
The file presents itself as a game news crawler/report generator, but its implementation delegates core work to other Python scripts through subprocess.run and the uv launcher. Spawning subprocesses is a materially broader capability than straightforward HTTP fetching/parsing and is not justified by the stated crawler purpose alone because it enables execution of additional local code and dependency resolution outside this script.
subprocess module calls execute external commands. Without careful input validation, this enables command injection.
# 使用 uv run 执行 RSS 抓取脚本
script_path = os.path.join(os.path.dirname(__file__), 'rss_fetcher.py')
result = subprocess.run(
['uv', 'run', '--with', 'feedparser', script_path, rss_url, '--json'],
capture_output=True, text=True, timeout=60,
cwd=os.path.dirname(script_path)
subprocess module calls execute external commands. Without careful input validation, this enables command injection.
import subprocess
script_path = os.path.join(os.path.dirname(__file__), 'direct_fetcher.py')
result = subprocess.run(
['uv', 'run', '--with', 'requests', '--with', 'beautifulsoup4',
script_path, site_id, base_url],
capture_output=True, text=True, timeout=60,
Although the command is passed safely as an argument list, this code launches an external script while inheriting the full parent environment. If the helper script or its dependencies are compromised or overly permissive, inherited secrets and service credentials from os.environ become accessible to that subprocess unnecessarily for a simple search operation.
script_path = os.path.join(os.path.dirname(__file__), '../../searxng/scripts/searxng.py')
script_path = os.path.abspath(script_path)
result = subprocess.run(
['uv', 'run', script_path,
'search', search_query, '-n', str(limit), '--format', 'json'],
capture_output=True, text=True, timeout=60,
This fallback path explicitly propagates the current process environment into a subprocess, which broadens the helper's access to credentials, tokens, and internal configuration unrelated to search. That is not justified by the crawler's functionality and makes any compromise in the helper path more damaging.
subprocess module calls execute external commands. Without careful input validation, this enables command injection.
script_path = os.path.join(os.path.dirname(__file__), '../../searxng/scripts/searxng.py')
script_path = os.path.abspath(script_path)
result = subprocess.run(
['uv', 'run', script_path,
'search', search_query, '-n', str(limit * 3), '--format', 'json'],
capture_output=True, text=True, timeout=60,
subprocess module calls execute external commands. Without careful input validation, this enables command injection.
script_path = os.path.join(os.path.dirname(__file__), 'web_fetch_wrapper.py')
if os.path.exists(script_path):
result = subprocess.run(
['uv', 'run', script_path, url],
capture_output=True, text=True, timeout=30
)
The script invokes curl on a URL that can come from configuration and performs outbound requests with redirects enabled. In an agent skill context, this can enable SSRF-like behavior or unintended access to internal services if an attacker can influence the configured base_url or site mappings.
try:
# 使用 curl 抓取 HTML
result = subprocess.run(
['curl', '-s', '-A', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
'--max-time', '30', '-L', target_url],
capture_output=True, text=True, timeout=35
When use_web_fetch is enabled, the code sends the full target URL to r.jina.ai, a third-party proxy, rather than fetching the destination directly. That leaks browsing targets and possibly sensitive query strings or internal URLs from configuration to an external service without clear notice, consent, or allowlisting, which is a data-exposure and SSRF-amplification concern.
The module docstring presents this as a 'daily' game news crawler, and the main flow prints a 24-hour date range. However, date_start and date_end are computed and passed into fetch_website but never used to restrict fetched or reported articles, so the output may include older items from list pages rather than only daily news.
subprocess module calls execute external commands. Without careful input validation, this enables command injection.
def fetch_html(url, timeout=30):
"""使用 curl 抓取 HTML"""
try:
result = subprocess.run(
['curl', '-s', '-A', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
'--max-time', str(timeout), '-L', '--compressed', url],
capture_output=True, text=True, timeout=timeout + 5
subprocess module calls execute external commands. Without careful input validation, this enables command injection.
def fetch_html(url, timeout=30):
"""使用 curl 抓取 HTML"""
try:
result = subprocess.run(
['curl', '-s', '-A', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
'--max-time', str(timeout), '-L', '--compressed', url],
capture_output=True, text=True, timeout=timeout + 5
subprocess module calls execute external commands. Without careful input validation, this enables command injection.
def fetch_html(url, timeout=30):
"""使用 curl 抓取 HTML"""
try:
result = subprocess.run(
['curl', '-s', '-A', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
'--max-time', str(timeout), '-L', '--compressed', url],
capture_output=True, text=True, timeout=timeout + 5