Back to skill

Security audit

Tech news from RSS(rich terminal support)

Security checks for vulnerabilities and agentic risk

Overview

This is a straightforward tech-news RSS skill, with ordinary cautions around untrusted feed text and unpinned Python dependencies.

Install in a virtual environment, review the RSS links before opening them, and treat article text in the generated table as untrusted content from external publishers. Use --no-cache if you do not want local cached feed data kept under your home directory.

Vulnerability Patterns
  • 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
  • Remote Payload Retrieval and ExecutionFetches external code whose behavior can change after review
Findings (2)

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/tech_news.py:462
Finding
Untrusted RSS Content Is Rendered as Markdown Without Adequate Sanitization<![CDATA[ ## Vulnerability Details **File Location**: `scripts/tech_news.py:462-513` **Vulnerability Type**: Untrusted content injection into Markdown output **Risk Level**: Medium ### Vulnerable Code ```python def display_news_list_markdown(news_list: list[NewsItem], show_summary: bool = False) -> str: """返回 Markdown 格式的新闻列表(source 带链接)""" if not news_list: return "⚠️ 没有获取到新闻" headers = ["#", "标题", "来源", "发布时间"] if show_summary: headers.append("摘要") md = [] md.append("| " + " | ".join(headers) + " |") md.append("| " + " | ".join(["---"] * len(headers)) + " |") for idx, news in enumerate(news_list, 1): # 👉 source 加链接(假设 news.link 存在) source_md = news.source if getattr(news, "link", None): source_md = f"[{news.source}]({news.link})" row = [ str(idx), news.title, source_md, news.published or "N/A", ] if show_summary: summary = ( news.summary.replace("<br/>", " ").replace("<br>", " ") if news.summary else "" ) summary = summary[:100] + "..." if len(summary) > 100 else summary row.append(summary) # 转义 Markdown 特殊字符(避免破坏表格) row = [str(cell).replace("|", "\\|") for cell in row] md.append("| " + " | ".join(row) + " |") return "\n".join(md) ``` ### Technical Analysis Article titles, links, publication dates, and summaries originate from remote RSS documents. The Markdown formatter inserts these values into the generated table while escaping only the pipe character. Summary handling removes only two specific HTML break-tag forms. The implementation does not adequately handle: - Embedded newlines capable of creating additional Markdown blocks or table rows. - Markdown link syntax and other formatting metacharacters. - Raw HTML accepted by some Markdown renderers. - Terminal or Unicode control cha ...[truncated 2060 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Treat every RSS field as untrusted input, including the title, summary, date, and link. 2. Remove HTML with a dedicated allowlist-based sanitizer rather than replacing only `<br>` variants. 3. Remove terminal escape sequences, control characters, and bidirectional text-control characters. 4. Collapse carriage returns and newlines before inserting values into a Markdown table. 5. Escape all Markdown metacharacters relevant to the output context, not only pipe characters. 6. Parse every article URL before rendering it: - Allow only `https`. - Reject credentials, malformed hostnames, and non-web schemes. - Consider restricting links to the feed provider's expected domains or clearly label external domains. 7. Keep remote article text inside a clearly delimited data section and instruct the consuming agent never to interpret feed content as commands. 8. Consider emitting structured JSON and allowing a trusted presentation layer to perform context-specific encoding. 9. Add tests containing malicious titles, summaries, and links, including embedded newlines, raw HTML, Markdown links, control characters, and non-HTTPS schemes. ]]>

T08 · Insecure Dependencies

Note
Location
SKILL.md:37
Finding
Third-Party Dependencies Are Installed Without Version or Integrity Pinning<![CDATA[ ## Vulnerability Details **File Location**: `SKILL.md:37-42` **Vulnerability Type**: Unpinned third-party dependency installation **Risk Level**: Low ### Vulnerable Code ```markdown ## Dependencies Requires `feedparser`, `requests`, `rich` — install if missing: \``` pip install feedparser requests rich \``` ``` ### Technical Analysis The documented installation command asks users to install mutable latest versions of three packages and their transitive dependencies. The project does not provide exact reviewed versions, a lockfile, package hashes, or an isolated installation procedure. Consequently, two installations performed at different times may execute different dependency code. If a future package release or package-index account is compromised, users following the instructions could install attacker-controlled code even though the audited project files remain unchanged. The listed package names appear to match the modules imported by the script; no evidence of typosquatting or an intentionally malicious package was identified. The weakness is the absence of reproducibility and integrity controls rather than proof that the current packages are malicious. ### Attack Path 1. A maintainer account, package release, distribution artifact, transitive dependency, or package-index delivery path is compromised. 2. A user follows the documented `pip install feedparser requests rich` command. 3. Pip resolves the compromised or unexpectedly changed package version because no approved version or hash is specified. 4. Malicious package installation behavior can run during installation, or malicious code can run when `tech_news.py` imports the affected package. 5. The package code executes with the operating-system privileges of the user running pip or the news script. ### Impact Assessment A compromised dependency can potentially obtain the full privileges of the installing or executing user. Depending on that user's permissions, possible effects ...[truncated 358 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Create a reviewed requirements file containing exact package versions. 2. Generate and verify cryptographic hashes for every direct and transitive dependency. 3. Install dependencies using an integrity-enforcing command such as: ```bash python -m pip install --require-hashes -r requirements.txt ``` 4. Use a lockfile generation tool to produce reproducible transitive dependency resolution. 5. Install packages in a dedicated virtual environment rather than the system Python environment. 6. Periodically scan pinned dependencies for known vulnerabilities and update them through a reviewed process. 7. Configure pip to use trusted HTTPS package indexes and avoid unreviewed extra indexes. 8. Document the supported Python version so dependency resolution remains reproducible across environments. ]]>
Vulnerability Patterns
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
Findings (2)

Natural-Language Policy Violations

Low
Confidence
95% confidence
Finding
This code file contains natural-language strings that present the tool description and interaction text only in Chinese, such as the module docstring and later CLI/help output. Because the skill does not offer any language or locale selection, it imposes a specific language on users without opt-in, which matches the language/locale policy concern.

Intent-Code Divergence

Low
Confidence
94% confidence
Finding
The help text at L338 says '刷新当前源' (refresh current source), which implies a limited operation. However, the refresh command handler at L351-L353 calls fetcher.fetch_all(), causing retrieval from every configured RSS source rather than only the currently selected one.

Static analysis

No suspicious patterns detected.