Back to skill

Security audit

文旅小红书信息源

Security checks for vulnerabilities and agentic risk

Overview

The skill performs the advertised Xiaohongshu report workflow, but subscription mode adds persistent scheduled execution and unsafe handling of commands, credentials, and generated HTML.

Review carefully before installing. Normal report generation will call RedFox with your API key, write local HTML reports, and open them in a browser. Avoid using subscription mode until it is fixed, because it installs recurring jobs and may persist your API key; use a revocable low-scope key and rotate it if you already subscribed.

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 (5)

T06 · System Persistence

Warning
Location
scripts/cultural_tourism_xiaohongshu_report.py:505
Finding

Persistent Scheduled Execution Through LaunchAgent or Crontab

Content
View full analysis
/dev/null; echo "{cron_line}") | crontab -', shell=True, check=True, capture_output=True ) return True except subprocess.CalledProcessError: return False ``` Daily subscription is an advertised feature and is only reached when `--subscribe` is supplied, so the persistence is not covert. Nevertheless, installing a cross-session scheduler exceeds the privileges required for a one-time search and report operation. Once installed, the task repeatedly execute ...[truncated 955 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Error
Location
scripts/cultural_tourism_xiaohongshu_report.py:560
Finding

Shell Command Injection Through the Subscription Keyword

Content
View full analysis
/dev/null; echo "{cron_line}") | crontab -', shell=True, check=True, capture_output=True ) ``` No shell quoting, escaping, or input validation is applied. A keyword containing a double quote followed by shell metacharacters can terminate the `echo` argument and introduce a new shell command. For example, a value structurally equivalent to: ```text "; attacker_command; # ``` can cause `attacker_command` to be interpreted by `/bin/sh` while the subscription is being installed. The attack does not need to wait for the scheduled task to run. The same construction can also write malicious syntax into the installed crontab, allowing both immediate execution and recurring execution. ### Attack Path 1. An attacker persuades a user or agent to subscribe using a crafted cultural-tourism keyword. 2. The crafted keyword is accepted by `argparse` without validation. 3. `install_subscription()` concatenates it into `cron_line`. 4. The resulting string is concatenated into an `echo` pipeline. 5. `subprocess.run(..., shell=True)` passes the complete string to the system shell. 6. Shell metacharacters in the keyword terminate the intended command and execute attacker-controlled commands. 7. The injected content may additionally be retained in crontab for recurring execution. ### Impact Assessment The attacker can execute arbitrary operating-system commands wit ...[truncated 372 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Error
Location
scripts/cultural_tourism_xiaohongshu_report.py:343
Finding

Unescaped API and User Data in Automatically Opened HTML Reports

Content
View full analysis
\'">' ) else: cover_html = '
' articles_html += ( '
' + cover_html + '
' + '' + title + '' + '
' + '' + author + '' + '' + '👍 ' + likes + '' + '💬 ' + comments + '' + '🔁 ' + shares + '' + time_html + '
' ) cards_html += f'''
{i:02d}

{cluster["category"]}

{cluster["count"]} 篇
{articles_html}
''' ``` The keyword is ...[truncated 1834 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Error
Location
scripts/cultural_tourism_xiaohongshu_report.py:510
Finding

API Key Written in Plaintext to a Persistent LaunchAgent File

Content
View full analysis
EnvironmentVariables' '\n ' f'\n {ENV_KEY}' f'\n {api_key}' '\n ' ) plist_content = f''' Label {PLIST_LABEL} RunAtLoad {env_section} ''' plist_path.write_text(plist_content, encoding="utf-8") ``` `Path.write_text()` does not explicitly enforce restrictive permissions. The resulting file persists under `~/Library/LaunchAgents` and can be copied by backups, diagnostic collectors, malware, or another process able to read the user's files. This behavior also conflicts with the documentation's instruction not to expose the key in output files. Although the API key is legitimately required for authenticated requests to `https://redfox.hk`, persistently embedding it in scheduler configuration is not necessary. ### Attack Path 1. The user exports `REDFOX_API_KEY`. 2. The user or agent invokes subscription installation on macOS. 3. The script reads the key from the environment. 4. The key is serialized as plaintext into the LaunchAgent plist. 5. The plist remains on disk across sessions. 6. A local process, backup reader, support bundle, or account-level attacker reads the file and recovers the credential. 7. The recovered key is ...[truncated 481 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Error
Location
scripts/cultural_tourism_xiaohongshu_report.py:510
Finding

Unsafe XML Construction for the macOS LaunchAgent

Content
View full analysis
EnvironmentVariables' '\n ' f'\n {ENV_KEY}' f'\n {api_key}' '\n ' ) plist_content = f''' Label {PLIST_LABEL} ProgramArguments /usr/bin/python3 {script_path} --keyword {keyword} --no-open StartCalendarInterval Hour 9 Minute 0 StandardOutPath {log_path} StandardErrorPath {log_path} RunAtLoad {env_section} ''' plist_path.write_text(plist_content, encoding="utf-8") subprocess.run(["launchctl", "load", str(plist_path)], check=True, capture_output=True) ``` XML metacharacters such as `<`, `>`, `&`, and quotation-related payloads are not escaped. A crafted keyword can close its `` element and introduce additional property-list nodes. Depending on how the property-list parser handles duplicate or injected keys, this may corrupt the subscription, modify scheduler properties, or alter th ...[truncated 1392 chars]
Remediation
View remediation
Vulnerability Patterns
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Tool MisuseTool Parameter Abuse, Chaining Abuse, Unsafe Defaults
  • Rogue AgentSelf-Modification, Session Persistence
Findings (43)

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding

The documented purpose says the skill searches Xiaohongshu travel content and generates an HTML report, but the behavior also includes persistence via scheduled tasks, automatic browser launching, and local secret retrieval from environment/config files. This mismatch undermines informed user consent and can lead to unexpected persistence, local data access, and command execution beyond the advertised function.

Content

No source excerpt is available for this finding.

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>文旅小红书信息源 - {{KEYWORD}} - {{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">

YARA rule 'agent_skill_mcp_tool_poisoning_metadata': MCP/tool metadata poisoning indicators in tool schemas or skill manifests [agent_skills]

High
Category
YARA Match
Confidence
80% confidence
Finding

YARA rule matched a hack tool or exploit indicator (offensive tools, reconnaissance, privilege escalation, or exploit frameworks).

Content

Scanner excerpt · scripts/cultural_tourism_xiaohongshu_report.py (reported line 487)May include surrounding context.

python
66; font-size:0.8rem; }'
        '</style></head><body>'
        '<div class="header">'
        '<h1>文旅小红书信息源</h1>'
        '<p>「{{KEYWORD}}」| {{DATE_CN}}</p>'
        '</div>'
        '<div class="warning-banner">⚠️ 受小红书风控规则限制,部分作品链接可能无法正常跳转,您可复制对应作品标题前往小红书 App 搜索查看,感谢理解🙇‍♀️🙇‍♀️</div>'
        '<div class="stats">'
        '<div class="stat-item"><div class="stat-value">{{TOPIC_COUNT}}</div><div class="stat-label">分类</div></div>'
        '<div class="stat-item"><div class="stat-value">{{TOTAL_COUNT}}</div><div class="stat-label">作品</div></div>'
        '<div class="stat-item"><div class="stat-value">{{AVG_LIKES}}</div><div class="stat-label">平均点赞</div></div>'
        '<div class="stat-item"><div class="stat-value">{{AVG_COMMENTS}}</div><div class="stat-label">平均评论</div></div>'
        '<div class="stat-item"><div class="s

Context-Inappropriate Capability

High
Category
Not specified by scanner
Confidence
98% confidence
Finding

The skill writes LaunchAgent/crontab configuration and executes launchctl/crontab commands, which is far beyond a simple report generator. In agent contexts, such host-level scheduling changes are dangerous because they create persistence and recurring execution pathways that can survive the initial user request.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

High
Category
Not specified by scanner
Confidence
99% confidence
Finding

The subscription installer copies the API key from the environment into a persistent LaunchAgent plist, storing a credential on disk in a recoverable form. This increases exposure to local credential theft, backup leakage, and unintended reuse by other processes or users with access to the file.

Content

No source excerpt is available for this finding.

Tool Parameter Abuse

High
Category
Tool Misuse
Confidence
97% confidence
Finding

The code pipes crontab output through grep using a shell command that interpolates script_path directly. This is a classic tool-parameter abuse pattern because shell interpretation can be influenced by unexpected path characters, causing arbitrary command execution while manipulating persistent scheduler state.

Content

Scanner excerpt · scripts/cultural_tourism_xiaohongshu_report.py (reported line 593)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
            )

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The README advertises 'daily subscription support' and scheduled auto-delivery, but it does not prominently warn that this creates persistent recurring behavior. Without a clear warning and consent flow, users may unknowingly enable ongoing jobs that continue making external requests, generating reports, or consuming quota after the initial interaction.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The README says users can 'just describe what you need in natural language' and the skill metadata includes a wide set of trigger phrases, which can cause the skill to activate for loosely related travel or content-analysis requests. Overly broad invocation guidance increases the chance of unintended execution, unnecessary third-party API calls, and accidental data retrieval or report generation the user did not explicitly request.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
87% confidence
Finding

The usage section is written as a Chinese-only natural-language invocation guide and provides only Chinese example utterances, which effectively constrains use to a specific language. There is no indication that other languages are supported or that Chinese is optional, so this can violate the language/locale choice policy.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
92% confidence
Finding

The skill performs sensitive actions including shell execution, network access, reading environment/configured API keys, and writing files, but it declares no explicit tool scope or permission boundaries. Without an allowlist, an agent may invoke broader capabilities than users expect, increasing the chance of unauthorized file, network, or shell operations.

Content

No source excerpt is available for this finding.

Session Persistence

Medium
Category
Rogue Agent
Confidence
78% confidence
Finding

The skill instructs users to persist an API key in a local config file under the home directory, creating session persistence of a secret on disk. While common, storing reusable credentials in plaintext-like local files increases exposure if the workstation, account, or other tools can access that path.

Content

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

export REDFOX_API_KEY=ak_xxxx

方式二:配置文件

mkdir -p ~/.qoder/apis echo '{"api_key":"ak_xxxx"}' > ~/.qoder/apis/redfox.json

text

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The skill writes an HTML file to disk and states that it must auto-open the report on every run, but this side effect is not surfaced as a prominent user warning in the skill description. Automatic opening of generated HTML can expose users to unexpected local content rendering, privacy leakage via browser behavior, or nuisance/unsafe UX if the report contains untrusted content.

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 · scripts/cultural_tourism_xiaohongshu_report.py (reported line 39)May include surrounding context.

python
PAGE_SIZE = 200

DEFAULT_OUTPUT_DIR = Path.home() / "Downloads" / "QoderReports"
PLIST_LABEL = "com.qoder.cultural-tourism-xiaohongshu-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 · scripts/cultural_tourism_xiaohongshu_report.py (reported line 40)May include surrounding context.

python
PAGE_SIZE = 200

DEFAULT_OUTPUT_DIR = Path.home() / "Downloads" / "QoderReports"
PLIST_LABEL = "com.qoder.cultural-tourism-xiaohongshu-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 · scripts/cultural_tourism_xiaohongshu_report.py (reported line 579)May include surrounding context.

python
PAGE_SIZE = 200

DEFAULT_OUTPUT_DIR = Path.home() / "Downloads" / "QoderReports"
PLIST_LABEL = "com.qoder.cultural-tourism-xiaohongshu-feed"
PLIST_DIR = Path.home() / "Library" / "LaunchAgents"

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

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The skill metadata describes a search-and-report workflow, but the code adds persistent daily subscription behavior that alters host state. This expansion of capability is risky because users may invoke a content skill without expecting installation of recurring background tasks.

Content

No source excerpt is available for this finding.

Session Persistence

Medium
Category
Rogue Agent
Confidence
95% confidence
Finding

The install_subscription function is the entry point for creating OS-level persistence. In this skill context, embedding persistence management in a data-reporting tool broadens capability in a way that can surprise users and increase attack surface.

Content

Scanner excerpt · scripts/cultural_tourism_xiaohongshu_report.py (reported line 504)May include surrounding context.

python
# ─── 订阅机制 ──────────────────────────────────────────────────────────────────────
def install_subscription(keyword):
    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-cultural-tourism-xiaohongshu-feed.log")

Session Persistence

Medium
Category
Rogue Agent
Confidence
93% confidence
Finding

This same line contributes to the macOS persistence mechanism by targeting a plist under LaunchAgents. In a content skill, host persistence is outside expected scope and therefore materially riskier.

Content

Scanner excerpt · scripts/cultural_tourism_xiaohongshu_report.py (reported line 505)May include surrounding context.

python
def install_subscription(keyword):
    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-cultural-tourism-xiaohongshu-feed.log")

Session Persistence

Medium
Category
Rogue Agent
Confidence
93% confidence
Finding

This same line contributes to the macOS persistence mechanism by targeting a plist under LaunchAgents. In a content skill, host persistence is outside expected scope and therefore materially riskier.

Content

Scanner excerpt · scripts/cultural_tourism_xiaohongshu_report.py (reported line 505)May include surrounding context.

python
def install_subscription(keyword):
    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-cultural-tourism-xiaohongshu-feed.log")

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The code persists the API key into a LaunchAgent configuration without clearly warning the user that a secret will be stored on disk. Even if functionality works as intended, silent credential persistence violates least surprise and increases operational security risk.

Content

No source excerpt is available for this finding.

Session Persistence

Medium
Category
Rogue Agent
Confidence
92% confidence
Finding

Generating plist content is a direct step in creating a persistent LaunchAgent job. This persistence is more concerning here because the skill also handles API credentials and network access, giving the recurring task continued ability to use those resources.

Content

Scanner excerpt · scripts/cultural_tourism_xiaohongshu_report.py (reported line 520)May include surrounding context.

python
'\n        </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
92% confidence
Finding

This line is part of the same persistent LaunchAgent definition. The concern is not the XML token itself but the installation of a long-lived scheduled task by a report skill.

Content

Scanner excerpt · scripts/cultural_tourism_xiaohongshu_report.py (reported line 521)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>

Session Persistence

Medium
Category
Rogue Agent
Confidence
92% confidence
Finding

This line is part of the same persistent LaunchAgent definition. The concern is not the XML token itself but the installation of a long-lived scheduled task by a report skill.

Content

Scanner excerpt · scripts/cultural_tourism_xiaohongshu_report.py (reported line 521)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>

Session Persistence

Medium
Category
Rogue Agent
Confidence
91% confidence
Finding

Setting the Label in the plist contributes to making the persistence operational and identifiable to launchctl. In context, it is one component of unauthorized or unexpected long-term host modification.

Content

Scanner excerpt · scripts/cultural_tourism_xiaohongshu_report.py (reported line 522)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
91% confidence
Finding

ProgramArguments in the plist define the command that will run repeatedly, completing the persistence setup. This is risky because it enables unattended future execution of the script with network access and report generation behavior.

Content

Scanner excerpt · scripts/cultural_tourism_xiaohongshu_report.py (reported line 525)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>