Back to skill

Security audit

Daily Game News

Security checks for vulnerabilities and agentic risk

Overview

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.

Vulnerability Patterns
  • System PersistenceInstalls backdoors, hooks, services, or scheduled tasks that survive the run
  • Insecure DependenciesIntroduces malicious components through unsafe dependency sources
  • 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
Findings (4)

T09 · Insecure Skill Coding Practices

Error
Location
scripts/rss_fetcher.py:39
Finding
RSS downloads disable TLS certificate verification<![CDATA[ ## Vulnerability Details **File Location**: `scripts/rss_fetcher.py:39-52` **Vulnerability Type**: Improper certificate validation **Risk Level**: High ### Vulnerable Code ```python # Use requests or urllib to retrieve RSS content if HAS_REQUESTS: response = requests.get(url, timeout=30, verify=False, headers={ 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36' }) rss_content = response.text else: import ssl context = ssl._create_unverified_context() req = urllib.request.Request(url, headers={ 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36' }) with urllib.request.urlopen(req, context=context, timeout=30) as f: rss_content = f.read().decode('utf-8') ``` ### Technical Analysis Both supported HTTP implementations explicitly disable server-certificate verification. `requests.get(..., verify=False)` accepts an untrusted certificate, while `ssl._create_unverified_context()` disables equivalent checks in the urllib fallback. TLS authentication is necessary even when the retrieved information is public because the RSS response is treated as trusted report input. A network-positioned attacker could impersonate an RSS server and provide forged titles, links, authors, and summaries. These values are subsequently parsed and included in generated reports. The code also does not call `response.raise_for_status()`, meaning HTTP error bodies can be treated as RSS input. ### Attack Path 1. The crawler invokes `rss_fetcher.py` for an HTTPS RSS endpoint. 2. An attacker gains a network interception position, such as through a malicious access point, compromised proxy, or DNS/network infrastructure. 3. The attacker presents an arbitrary certificate and returns a forged RSS document. 4. The helper accepts the certificate because validation is disabled. 5. Attacker-controlled article metadata is parsed and returned to the crawler. 6. The malicious titles, ...[truncated 430 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Remove `verify=False` and use the default verified TLS configuration. - Remove `ssl._create_unverified_context()` and use `ssl.create_default_context()`. - Call `response.raise_for_status()` before parsing the response body. - Restrict RSS retrieval to an explicit allowlist of expected HTTPS hosts. - Validate redirect destinations and reject redirects to loopback, private, link-local, or otherwise unexpected addresses. - Apply a reasonable response-size limit before parsing RSS data. - If a site has a certificate problem, fix its trust chain or configure a narrowly scoped custom CA bundle rather than globally disabling verification. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
scripts/crawler.py:79
Finding
Configuration-controlled network requests permit server-side request forgery<![CDATA[ ## Vulnerability Details **File Location**: `scripts/crawler.py:79-104, 418-445` **Vulnerability Type**: Server-side request forgery through unrestricted URL fetching **Risk Level**: High Equivalent unrestricted fetching is duplicated in `scripts/crawler_v4.py` and `scripts/crawler_v5.py`. ### Vulnerable Code ```python def fetch_html(url, timeout=60, use_web_fetch=False): """Fetch HTML using curl or web_fetch.""" try: # Gamersky and GameSpot use web_fetch to bypass Cloudflare if use_web_fetch: print(f" Using web_fetch (timeout={timeout}s)...") import httpx response = httpx.get( f'https://r.jina.ai/{url}', headers={'X-Return-Format': 'markdown'}, timeout=timeout ) if response.status_code == 200: content = response.text print(f" web_fetch succeeded, returned {len(content)} bytes") return content print(f" web_fetch failed: {response.status_code}") return None 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 ) if result.returncode == 0: return result.stdout except Exception as e: print(f" Fetch failed: {e}") return None ``` ```python def fetch_website(config, date_start, date_end): """Fetch a single website.""" site_id = config['id'] site_name = config['name'] base_url = config['base_url'] articles = [] for section in config.get('分区', []): limit = section.get('筛选数量', 2) section_name = section.get('name', '') print(f" - Fetching {site_name} - {section_name}...") parsers = { 'gcores': parse_gcores, ...[truncated 2611 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Replace arbitrary `base_url` values with an explicit mapping from each supported `site_id` to an approved HTTPS origin. - Parse URLs with `urllib.parse.urlsplit()` and allow only `https`. - Reject embedded credentials, unexpected ports, malformed hostnames, and non-HTTP schemes. - Resolve the hostname and reject loopback, private, link-local, multicast, reserved, and unspecified IP ranges for both IPv4 and IPv6. - Disable automatic redirects or validate the destination after every redirect. - Protect the external configuration with restrictive file permissions and validate it against a schema. - Make use of `r.jina.ai` explicit and optional, and document that target URLs and retrieval metadata are disclosed to that service. - Apply response-size and content-type limits before parsing remote responses. ]]>

T08 · Insecure Dependencies

Warning
Location
scripts/crawler_v2.py:166
Finding
Runtime resolution of unpinned third-party dependencies<![CDATA[ ## Vulnerability Details **File Location**: `scripts/crawler_v2.py:166-175, 210-218` **Vulnerability Type**: Mutable runtime dependency installation **Risk Level**: Medium The repository also uses lower-bound-only dependency constraints in `pyproject.toml:5-10` and provides no reviewed lockfile or package hashes. ### Vulnerable Code ```python import subprocess # Execute the RSS fetcher through uv run 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) ) ``` ```python 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, cwd=os.path.dirname(script_path) ) ``` Supporting dependency declarations: ```toml dependencies = [ "beautifulsoup4>=4.14.3", "httpx>=0.28.1", "lxml>=6.0.2", "python-docx>=1.1.0", ] ``` ### Technical Analysis The V2 crawler asks `uv` to resolve packages by name at execution time. These package specifications contain no exact versions or integrity hashes. Consequently, the effective code executed by a future invocation can differ from the code reviewed during this audit. The project-level dependencies likewise use unrestricted upper ranges and no lockfile is included. This is not evidence that the named packages are currently malicious, but it unnecessarily exposes scheduled or manual execution to future compromised releases, index compromise, or dependency-resolution changes. ### Attack Path 1. An attacker compromises a dependency release or the package index/resolution path used by `uv`. 2. A user manually invokes V2, or it is otherwise selected as the crawler implementation. 3. `uv run ...[truncated 619 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Remove runtime `--with` dependency installation from executable code. - Declare every required dependency, including `feedparser` and `requests`, in `pyproject.toml`. - Generate and commit a reviewed lockfile with exact versions. - Use hash-verified installations where supported. - Configure an approved package index and prevent untrusted fallback indexes. - Update dependencies through a controlled review process rather than resolving the newest compatible version during scheduled execution. - Run dependency vulnerability and provenance checks in CI before accepting lockfile updates. - Execute the crawler in a restricted environment with minimal filesystem and network access. ]]>

T06 · System Persistence

Warning
Location
README.md:182
Finding
Documented cron installation overwrites the user’s complete crontab<![CDATA[ ## Vulnerability Details **File Location**: `README.md:182-185` **Vulnerability Type**: Unsafe scheduled-task installation **Risk Level**: Medium The scheduled entry being installed is defined in `crontab.txt:1-4`. ### Vulnerable Code ```bash crontab /home/admin/.openclaw/workspace/skills/daily-game-news/crontab.txt ``` Installed crontab content: ```cron # Daily Game News Cron Job # Run every day at 10:00 Beijing time 0 10 * * * cd /home/admin/.openclaw/workspace/skills/daily-game-news && source .venv/bin/activate && python scripts/crawler.py >> /home/admin/.openclaw/workspace/logs/daily-game-news.log 2>&1 ``` ### Technical Analysis A daily scheduled task is explicitly declared by the Skill and is reasonably related to its advertised automatic-report functionality. The persistence mechanism itself is therefore not hidden or unrelated. However, `crontab FILE` replaces the current user’s entire crontab with the contents of the supplied file. Because the provided file contains only this Skill’s entry, following the documented command can silently delete unrelated scheduled tasks. Replacing all user jobs exceeds the minimum system modification required to add one daily crawler job. The task is installed at user scope and does not request root privileges, but it persists across sessions and repeatedly executes code from a writable workspace path. ### Attack Path 1. A user follows the README installation or schedule-update instructions. 2. The `crontab` command replaces the user’s existing crontab. 3. All unrelated entries omitted from `crontab.txt` are removed. 4. Existing backups, maintenance tasks, monitoring jobs, or application workflows cease to execute. 5. The Daily Game News crawler remains as the only installed entry from this file. ### Impact Assessment The immediate impact is loss of unrelated scheduled automation and possible service disruption under the affected user account. The command does not grant elevated privileges and no ...[truncated 165 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Do not install the job with `crontab crontab.txt`. - Preserve existing entries and append a uniquely marked managed entry only after checking for duplicates. - Back up the current crontab before making changes. - Display the proposed change and require explicit user confirmation. - Provide an idempotent installer and uninstaller that modify only this Skill’s marked block. - Prefer a user-level scheduler mechanism with a dedicated unit or job definition when available. - Use an absolute interpreter path instead of relying on shell activation. - Ensure the Skill directory and scheduled script are not writable by less-trusted users. - Document how to inspect, disable, and remove the scheduled task safely. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Rogue AgentSelf-Modification, Session Persistence
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
  • YARA SignaturesMalware Match, Webshell Match, Cryptominer Match
Findings (44)

YARA rule 'backdoor_persistence': Backdoor persistence with malicious payloads (shell commands, SSH key injection, hidden root users) [malware]

High
Category
YARA Match
Content
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"
```

---

## 🆘 常见问题

### Q: 如何修改抓取时间?
A: 编辑配置文件中的定时任务部分,然后重新配置 cron:
```bash
crontab /home/admin/.openclaw/workspace/skills/daily-game-news/crontab.txt
```

### Q: 如何添加/删除网站?
A: 编辑 `news-crawler-config.json`,在"网站配置"数组中添加或删除网站配置。

### Q: 如何修改每个网站的抓取数量?
A: 在配置文件中找到对应网站的"分区"配置,修改"筛选数量"字段。

### Q: 报告没有按时发送?
A: 检查以下步骤:
1. cron 任务是否正常运行:`crontab -l`
2. 日志文件是否有错误:`tail logs/daily-game-news.log`
3. Python 环境是否正常:`uv run --version`
4. 网络连接是否正
Confidence
75% confidence
Finding
YARA rule matched a known malware signature (reverse shell, backdoor, ransomware, C2 framework, or info stealer).

Env Variable Harvesting

High
Category
Data Exfiltration
Content
['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)
        )
Confidence
92% confidence
Finding
The 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.

Env Variable Harvesting

High
Category
Data Exfiltration
Content
['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)
        )
Confidence
60% confidence
Finding
Code enumerates, copies, or searches environment variables for secrets. Bulk environment access can collect credentials unrelated to the skill's stated purpose.

Natural-Language Policy Violations

Medium
Confidence
91% confidence
Finding
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.

File System Enumeration

Medium
Category
Data Exfiltration
Content
### 方式 2:列出所有历史报告
```bash
# 查看所有报告
ls -la /home/admin/.openclaw/workspace/reports/daily-game-news/

# 查看最近 5 个报告
ls -lt /home/admin/.openclaw/workspace/reports/daily-game-news/ | head -6
Confidence
60% confidence
Finding
Code scans file system directories looking for sensitive files. This could be reconnaissance for credential theft.

Session Persistence

Medium
Category
Rogue Agent
Content
### Q: 报告没有按时发送?
A: 检查以下步骤:
1. cron 任务是否正常运行:`crontab -l`
2. 日志文件是否有错误:`tail logs/daily-game-news.log`
3. Python 环境是否正常:`uv run --version`
4. 网络连接是否正常
Confidence
85% 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.

Natural-Language Policy Violations

Medium
Confidence
95% confidence
Finding
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.

Intent-Code Divergence

Medium
Confidence
96% confidence
Finding
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.

Intent-Code Divergence

Medium
Confidence
95% confidence
Finding
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 Policy Violations

Medium
Confidence
91% confidence
Finding
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.

Intent-Code Divergence

Medium
Confidence
93% confidence
Finding
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.

Missing User Warnings

Medium
Confidence
94% confidence
Finding
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.

Context-Inappropriate Capability

Medium
Confidence
90% confidence
Finding
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 call

Medium
Category
Dangerous Code Execution
Content
# 使用 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)
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
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,
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
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,
Confidence
78% confidence
Finding
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.

Context-Inappropriate Capability

Medium
Confidence
87% confidence
Finding
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 call

Medium
Category
Dangerous Code Execution
Content
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,
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
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
            )
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
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
Confidence
85% confidence
Finding
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.

Missing User Warnings

Medium
Confidence
97% confidence
Finding
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.

Intent-Code Divergence

Medium
Confidence
96% confidence
Finding
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 call

Medium
Category
Dangerous Code Execution
Content
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
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
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
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
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
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Static analysis

No suspicious patterns detected.