Back to skill

Security audit

AI公众号信息源

Security checks for vulnerabilities and agentic risk

Overview

This skill appears to be a real AI WeChat report generator, but it needs review because it can install persistent scheduled jobs and handles API keys and remote article content unsafely.

Install only if you are comfortable with a local report tool that contacts redfox.hk using your RedFox API key. Avoid --subscribe unless you accept a persistent daily scheduled task, and do not use subscription with sensitive API keys until credential storage and crontab command construction are fixed. Treat generated reports as untrusted HTML because article metadata from the remote API is not escaped.

Vulnerability Patterns
  • System PersistenceInstalls backdoors, hooks, services, or scheduled tasks that survive the run
  • 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 (4)

T06 · System Persistence

Error
Location
assets/daily_report.py:588
Finding

Persistent Daily Execution Through LaunchAgent or Crontab

Content
View full analysis
Label {PLIST_LABEL} ProgramArguments /usr/bin/python3 {script_path} --no-open StartCalendarInterval Hour 9 Minute 0 RunAtLoad ''' plist_path.write_text(plist_content, encoding="utf-8") try: subprocess.run( ["launchctl", "load", str(plist_path)], check=True, capture_output=True ) return True except subprocess.CalledProcessError as e: return False else: script_path = os.path.abspath(__file__) cron_line = f"0 9 * * * /usr/bin/python3 {script_path} --no-open" try: subprocess.run( f'(crontab -l 2>/dev/null; echo "{cron_line}") | crontab -', shell=True, check=True, capture_output=True ) return True except subprocess.CalledProcessError: return False ``` ### Technical Analysis The `--subscribe` option installs a user LaunchAgent on macOS or modifies the user's crontab ...[truncated 1511 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Error
Location
assets/daily_report.py:643
Finding

Shell Command Injection Through an Unescaped Script Path

Content
View full analysis
/dev/null; echo "{cron_line}") | crontab -', shell=True, check=True, capture_output=True ) except subprocess.CalledProcessError: pass ``` The same unsafe construction is used during removal: ```python script_path = os.path.abspath(__file__) try: subprocess.run( f'crontab -l 2>/dev/null | grep -v "{script_path}" | crontab -', shell=True, check=True, capture_output=True ) except subprocess.CalledProcessError: pass ``` ### Technical Analysis The absolute path of the current script is interpolated directly into commands executed with `shell=True`. No shell escaping is applied before placing the path inside a double-quoted shell expression. Filesystem names can contain spaces, quotes, command substitutions, semicolons, and other shell metacharacters. A specially crafted extraction or installation directory can therefore terminate the quoted argument and append an attacker-selected command. This issue affects both subscription and unsubscription. Merely using an absolute path does not make the path safe for shell interpretation. ### Attack Path 1. An attacker distributes or places the Skill under a directory whose name contains shell metacharacters. 2. The user runs the Skill with `--subscribe` or `--unsubscribe`. 3. `os.path.abspath(__file__)` preserves the malicious characters in the path. 4. The path is interpolated into a string passed to `/bin/sh` through `shell=True`. 5. The shell interprets the injected characters and executes the attacker's command with the user's privileges. ### Impact Assessment Successful exploitation provides arbitrary command execution as the user ...[truncated 320 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Error
Location
assets/daily_report.py:427
Finding

Stored and DOM-Based Cross-Site Scripting Through Unescaped Article Metadata

Content
View full analysis
' articles_html += f'''
{cover_html}
{title}
{author}
''' ``` Live search results repeat the issue by assigning attacker-influenced strings to `innerHTML`: ```javascript articles.forEach(function(a) { var title = a.title || '无标题'; var url = a.url || '#'; var author = a.userName || ''; var cover = a.coverUrl || ''; var coverHtml = cover ? '' : ''; html += '
' + coverHtml + '
' + '' + title + '' + '
' + '' + author + '' + '
'; }); searchResultList.innerHTML = html; ``` ### Technical Analysis Article titles, author names, categories, cover URLs, and destination URLs originate from the remot ...[truncated 2138 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
assets/daily_report.py:594
Finding

API Key Stored in Plaintext LaunchAgent Configuration

Content
View full analysis
EnvironmentVariables {ENV_KEY} {api_key} """ plist_content = f''' Label {PLIST_LABEL} ProgramArguments /usr/bin/python3 {script_path} --no-open RunAtLoad {env_section} ''' plist_path.write_text(plist_content, encoding="utf-8") ``` ### Technical Analysis When subscription is installed on macOS while `REDFOX_API_KEY` is present in the environment, the key is copied verbatim into a LaunchAgent plist under the user's `~/Library/LaunchAgents` directory. The file is created with `Path.write_text` without explicitly enforcing a restrictive mode. Its effective permissions consequently depend on the process umask and surrounding filesystem controls. The credential also becomes part of filesystem backups, diagnostics, and any process capable of reading the user's LaunchAgent configuration. The credential persistence is unnecessary for one-time report generation. For scheduled operation, a durable credential source may be required, but storing the raw secret in general-purpose scheduler configuration is not the least-privilege option. ### Attack Path 1. The user exports `REDFOX_API_KEY`. 2. The user invokes the Skill with `--subscribe`. 3. The Skill interpolates the key into the LaunchAgent XML. 4. The plist is written to disk in plaintext. 5. A local process, account, backup system, or support bundle with access to that file ob ...[truncated 543 chars]
Remediation
View remediation
Vulnerability Patterns
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Tool MisuseTool Parameter Abuse, Chaining Abuse, Unsafe Defaults
  • Rogue AgentSelf-Modification, Session Persistence
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
Findings (46)

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding

The declared purpose focuses on content scanning and report generation, but the skill also starts a local HTTP proxy service, installs persistent scheduled tasks, and handles API keys in ways not clearly disclosed in the top-level description. Hidden or under-disclosed behaviors materially increase risk because they expand attack surface, persistence, and credential exposure beyond normal expectations for a reporting skill.

Content

No source excerpt is available for this finding.

Missing User Warnings

High
Category
Not specified by scanner
Confidence
99% confidence
Finding

The generated HTML embeds the API key directly into the report content, which is then saved to disk and potentially opened in a browser. This exposes credentials to anyone with file access, browser extensions, local scripts, or any JavaScript in the report context, and it combines dangerously with the local proxy feature.

Content

No source excerpt is available for this finding.

Missing User Warnings

High
Category
Not specified by scanner
Confidence
97% confidence
Finding

When installing the LaunchAgent, the code writes the API key into the plist EnvironmentVariables section. That persists the credential on disk in a user-readable configuration file without prominent warning, increasing exposure to local compromise, backups, and accidental disclosure.

Content

No source excerpt is available for this finding.

Tool Parameter Abuse

High
Category
Tool Misuse
Confidence
98% confidence
Finding

This is a classic tool-parameter abuse scenario: a shell command is built by concatenating the script path into a grep pipeline under shell=True. If the path contains shell-significant characters, an attacker can influence command behavior or trigger arbitrary command execution during cron removal.

Content

Scanner excerpt · assets/daily_report.py (reported line 677)May include surrounding context.

python
else:
        script_path = os.path.abspath(__file__)
        try:
            subprocess.run(
                f'crontab -l 2>/dev/null | grep -v "{script_path}" | crontab -',
                shell=True, check=True, capture_output=True
            )

Hidden Instructions

High
Category
Prompt Injection
Confidence
70% confidence
Finding

Hidden instructions were detected in comments or invisible text. These could contain malicious directives. Manual review is recommended.

Content

Scanner excerpt · assets/report_template.html (reported line 8)May include surrounding context.

html
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>AI公众号信息源 - {{DATE}}</title>

<!-- Fonts: Archivo Black (display) + Space Grotesk (body) -->
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Archivo+Black&family=Space+Grotesk:wght@300;400;500;600;700&display=swap" rel="stylesheet">

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The README advertises 'One-click cron setup' and subscription-based automated generation, which implies persistent system modification, but it does not prominently warn that installing a cron job changes scheduled tasks on the host. Without clear disclosure and confirmation requirements, a user or orchestrating agent may authorize persistence unintentionally, creating a security and operational risk.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The instruction to 'Describe your needs in natural language' is extremely broad and provides no explicit activation boundaries for when this skill should engage. In an agent environment, such generic triggering can cause the skill to activate on unrelated user requests, increasing the risk of unintended data access, external fetching, or report-generation actions without clear user intent.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
88% confidence
Finding

The example queries are phrased as common conversational requests like 'What's hot in AI today?' and 'Show me the AI report for May 26,' which may overlap with ordinary discussion rather than intentional skill invocation. In multi-skill or assistant settings, this ambiguity can lead to accidental routing into this skill and unintended execution of fetch, filtering, or reporting behavior.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The README says users can 'directly use natural language to describe needs' and then lists broad everyday-style example phrases such as '今天 AI 圈有什么热点?' and '帮我生成今天的 AI 公众号日报'. This does not define clear trigger boundaries, exclusions, or a constrained invocation scope, increasing the risk of unintended activation from ordinary conversation about AI news.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
90% confidence
Finding

The skill advertises executable behavior involving environment access, file I/O, network calls, and shell execution but does not declare any tool scope or permission boundaries. That omission weakens user consent and reviewability, making it easier for a skill to perform broader actions than a user would reasonably infer from the manifest alone.

Content

No source excerpt is available for this finding.

Session Persistence

Medium
Category
Rogue Agent
Confidence
88% confidence
Finding

The skill instructs users to persist an API key in a plaintext file under ~/.qoder/apis/redfox.json, creating long-lived local credential exposure. If the host is shared, backed up insecurely, or compromised by other local processes, the token can be recovered and abused for unauthorized API access.

Content

Scanner excerpt · SKILL.md (reported line 55)May include surrounding context.

md
export REDFOX_API_KEY=ak_你的密钥

# 方式二:配置文件
mkdir -p ~/.qoder/apis
echo '{"api_key":"ak_你的密钥"}' > ~/.qoder/apis/redfox.json

# 然后运行

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

The file's natural-language interface, output text, and HTML language are all fixed to Chinese-oriented content and locale conventions, with no opt-in or language selection path for users. Under the stated policy, forcing a specific language without user choice is a natural-language policy violation unless the restriction is explicitly justified.

Content

No source excerpt is available for this finding.

Session Persistence

Medium
Category
Rogue Agent
Confidence
75% 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.

Content

Scanner excerpt · assets/daily_report.py (reported line 44)May include surrounding context.

python
PAGES_PER_KEYWORD = 5  # 首关键词最大翻页数(后续关键词仅翻 2 页)
PAGE_SIZE = 20

PLIST_LABEL = "com.qoder.gzh-ai-feed"
PLIST_DIR = Path.home() / "Library" / "LaunchAgents"

# ─── 终端颜色 ──────────────────────────────────────────────────────────────────────

Session Persistence

Medium
Category
Rogue Agent
Confidence
75% 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.

Content

Scanner excerpt · assets/daily_report.py (reported line 45)May include surrounding context.

python
PAGES_PER_KEYWORD = 5  # 首关键词最大翻页数(后续关键词仅翻 2 页)
PAGE_SIZE = 20

PLIST_LABEL = "com.qoder.gzh-ai-feed"
PLIST_DIR = Path.home() / "Library" / "LaunchAgents"

# ─── 终端颜色 ──────────────────────────────────────────────────────────────────────

Session Persistence

Medium
Category
Rogue Agent
Confidence
75% 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.

Content

Scanner excerpt · assets/daily_report.py (reported line 663)May include surrounding context.

python
PAGES_PER_KEYWORD = 5  # 首关键词最大翻页数(后续关键词仅翻 2 页)
PAGE_SIZE = 20

PLIST_LABEL = "com.qoder.gzh-ai-feed"
PLIST_DIR = Path.home() / "Library" / "LaunchAgents"

# ─── 终端颜色 ──────────────────────────────────────────────────────────────────────

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The skill's stated purpose is report generation, but it also establishes OS-level persistence through LaunchAgents and crontab. While user-invoked via --subscribe, this is security-sensitive because it modifies long-lived execution state and increases the blast radius if the script or its credentials are later abused.

Content

No source excerpt is available for this finding.

Session Persistence

Medium
Category
Rogue Agent
Confidence
93% confidence
Finding

The function is explicitly designed to install a recurring scheduled task, which is persistence. In this context that is broader than a simple reporting utility and increases security sensitivity because the code will continue executing unattended.

Content

Scanner excerpt · assets/daily_report.py (reported line 586)May include surrounding context.

python
def install_subscription():
    """安装定时任务,每天自动生成日报"""
    if sys.platform == "darwin":
        PLIST_DIR.mkdir(parents=True, exist_ok=True)
        plist_path = PLIST_DIR / f"{PLIST_LABEL}.plist"

        script_path = os.path.abspath(__file__)

Session Persistence

Medium
Category
Rogue Agent
Confidence
88% confidence
Finding

Creating and targeting a plist in LaunchAgents is part of establishing persistent execution on macOS. This is expected for subscription functionality but still a real persistence mechanism with security implications.

Content

Scanner excerpt · assets/daily_report.py (reported line 587)May include surrounding context.

python
"""安装定时任务,每天自动生成日报"""
    if sys.platform == "darwin":
        PLIST_DIR.mkdir(parents=True, exist_ok=True)
        plist_path = PLIST_DIR / f"{PLIST_LABEL}.plist"

        script_path = os.path.abspath(__file__)
        log_path = str(Path.home() / "Library" / "Logs" / "qoder-ai-hot-articles.log")

Session Persistence

Medium
Category
Rogue Agent
Confidence
88% confidence
Finding

Creating and targeting a plist in LaunchAgents is part of establishing persistent execution on macOS. This is expected for subscription functionality but still a real persistence mechanism with security implications.

Content

Scanner excerpt · assets/daily_report.py (reported line 587)May include surrounding context.

python
"""安装定时任务,每天自动生成日报"""
    if sys.platform == "darwin":
        PLIST_DIR.mkdir(parents=True, exist_ok=True)
        plist_path = PLIST_DIR / f"{PLIST_LABEL}.plist"

        script_path = os.path.abspath(__file__)
        log_path = str(Path.home() / "Library" / "Logs" / "qoder-ai-hot-articles.log")

Session Persistence

Medium
Category
Rogue Agent
Confidence
91% confidence
Finding

This plist content includes the API key in an environment block for a persistent agent, combining persistence with credential storage on disk. That materially increases the damage from local compromise or accidental disclosure.

Content

Scanner excerpt · assets/daily_report.py (reported line 603)May include surrounding context.

python
<string>{api_key}</string>
        </dict>"""

        plist_content = f'''<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>

Session Persistence

Medium
Category
Rogue Agent
Confidence
86% confidence
Finding

Constructing the plist document is part of the persistence mechanism. While not malicious on its face, it contributes to unattended recurring execution, which is a sensitive capability for a content-reporting tool.

Content

Scanner excerpt · assets/daily_report.py (reported line 604)May include surrounding context.

python
</dict>"""

        plist_content = f'''<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>Label</key>

Session Persistence

Medium
Category
Rogue Agent
Confidence
86% confidence
Finding

Constructing the plist document is part of the persistence mechanism. While not malicious on its face, it contributes to unattended recurring execution, which is a sensitive capability for a content-reporting tool.

Content

Scanner excerpt · assets/daily_report.py (reported line 604)May include surrounding context.

python
</dict>"""

        plist_content = f'''<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>Label</key>

Session Persistence

Medium
Category
Rogue Agent
Confidence
86% confidence
Finding

Embedding the LaunchAgent label in the plist is a component of registering persistent execution. The danger comes from enabling unattended re-runs of networked code with credential access.

Content

Scanner excerpt · assets/daily_report.py (reported line 605)May include surrounding context.

python
plist_content = f'''<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>Label</key>
    <string>{PLIST_LABEL}</string>

Session Persistence

Medium
Category
Rogue Agent
Confidence
87% confidence
Finding

Defining ProgramArguments in the plist specifies what will be run persistently. In context, this is part of a genuine auto-start mechanism, not just inert configuration.

Content

Scanner excerpt · assets/daily_report.py (reported line 608)May include surrounding context.

python
<plist version="1.0">
<dict>
    <key>Label</key>
    <string>{PLIST_LABEL}</string>
    <key>ProgramArguments</key>
    <array>
        <string>/usr/bin/python3</string>

Session Persistence

Medium
Category
Rogue Agent
Confidence
87% confidence
Finding

Finalizing the plist content with RunAtLoad and environment insertion completes the persistent agent definition. This increases the long-term attack surface if the script, output, or credentials are later tampered with.

Content

Scanner excerpt · assets/daily_report.py (reported line 629)May include surrounding context.

python
<key>RunAtLoad</key>
    <false/>{env_section}
</dict>
</plist>'''

        plist_path.write_text(plist_content, encoding="utf-8")

Static analysis

No suspicious patterns detected.