Back to skill

Security audit

文旅抖音信息源

Security checks for vulnerabilities and agentic risk

Overview

This skill is for Douyin tourism reports, but its subscription and report-generation code creates avoidable security risks that users should review before installing.

Install only if you trust the publisher and are comfortable with RedFoxHub API queries, local HTML report files, and optional recurring scheduled jobs. Avoid subscription mode until shell handling, HTML escaping, and credential storage are fixed; prefer one-time runs with a revocable API key and do not open reports from untrusted data automatically.

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)

T09 · Insecure Skill Coding Practices

Error
Location
scripts/cultural_tourism_douyin_report.py:560
Finding

Arbitrary command execution through subscription keyword injection

Content
View full analysis
/dev/null; echo "{cron_line}") | crontab -', shell=True, check=True, capture_output=True ) ``` ### Technical Analysis The `keyword` value originates from the `--keyword` command-line argument and is inserted into `cron_line` without validation or shell escaping. That string is subsequently embedded in a larger command passed to `subprocess.run(..., shell=True)`. Because a shell interprets the resulting string, shell expansions and metacharacters in the keyword are evaluated. In particular, command substitution remains active inside the double-quoted argument used by `echo`. An input such as: ```text $(id > /tmp/subscription-command-executed) ``` would be evaluated by the shell while constructing the crontab entry. This results in immediate arbitrary command execution rather than merely storing the supplied text in crontab. The operation runs with the privileges of the user invoking the Skill. No additional operating-system privilege escalation is required for exploitation. ### Attack Path 1. An attacker persuades the user or Agent to request a daily subscription using a crafted keyword. 2. The Agent invokes the script with the crafted value, for example: ```bash python3 scripts/cultural_tourism_douyin_report.py \ --subscribe \ --keyword '$(id > /tmp/subscription-command-executed)' ``` 3. `install_subscription()` embeds the untrusted keyword in `cron_line`. 4. The complete string is passed to `/bin/sh` because `shell=True` is enabled. 5. The shell evaluates the command substitution. 6. The attacker's command executes with the invoking user's per ...[truncated 603 chars]
Remediation
View remediation

T06 · System Persistence

Error
Location
scripts/cultural_tourism_douyin_report.py:493
Finding

Cross-session persistence through LaunchAgent and crontab installation

Content
View full analysis
/dev/null; echo "{cron_line}") | crontab -', shell=True, check=True, capture_output=True ) info(f"订阅成功! 每天 09:00 自动生成「{keyword}」文旅抖音日报 (crontab)") info("日报目录: ~/Downloads/QoderReports/") return True ``` The corresponding removal logic is: ```python def remove_subscription(): if sys.platform == "darwin": plist_path = PLIST_DIR / f"{PLIST_LABEL}.plist" if not plist_path.exists(): warn("未找到订阅配置,无需取消") return False try: subprocess.run(["launchctl", "unload", str(plist_path)], check=True, capture_output=True) except subproces ...[truncated 2444 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Error
Location
scripts/cultural_tourism_douyin_report.py:337
Finding

Stored HTML injection in automatically opened reports

Content
View full analysis
\'">' ) else: cover_html = '
' time_html = "" if pub_time: time_html = '📅 ' + pub_time + '' articles_html += ( '
' + cover_html + '
' + '' + title + '' + '
' + '' + author + '' + '' + '👍 ' + likes + '' + '💬 ' + comments + '' ...[truncated 3512 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/cultural_tourism_douyin_report.py:508
Finding

API credential stored in plaintext LaunchAgent configuration

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") ``` ### Technical Analysis When the API key is present in `REDFOX_API_KEY`, the macOS subscription installer copies it into the `EnvironmentVariables` section of a persistent plist under `~/Library/LaunchAgents`. The key is stored as plaintext. `Path.write_text()` does not explicitly request restrictive file permissions, so the resulting mode depends on the process umask and any pre-existing file permissions. The code also does not verify ownership, reject symbolic links, or atomically create the file with an owner-only mode. This behavior conflicts w ...[truncated 1251 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 (45)

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">

Tool Parameter Abuse

High
Category
Tool Misuse
Confidence
99% confidence
Finding

This is a classic tool-parameter abuse pattern: a shell command is assembled with interpolated path data and executed with shell=True to rewrite crontab entries. If the script path contains shell metacharacters or crafted content, an attacker can trigger arbitrary command execution on the host during unsubscribe operations.

Content

Scanner excerpt · scripts/cultural_tourism_douyin_report.py (reported line 591)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
92% confidence
Finding

The README advertises automatic daily subscription and auto-delivery but does not prominently warn that this creates recurring external data access and ongoing report generation/delivery. Users may unknowingly authorize persistent background actions, which can lead to privacy surprises, unexpected resource consumption, or repeated outbound communications beyond the initial request.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The README instructs users to invoke the skill with broad natural-language requests such as generic tourism or Douyin queries, which can cause the agent to select this skill in situations the user did not clearly intend. Because the skill performs external data retrieval and report generation, over-broad triggering increases the risk of unintended network access, data collection, and scheduled actions being initiated from ambiguous prompts.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
87% confidence
Finding

The README advertises daily automatic subscription and report generation but does not clearly warn that this creates ongoing automated actions and persistent outputs. Users may unknowingly enable recurring data collection or file generation, which can create privacy, storage, operational, or consent issues in shared agent environments.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The README tells users to invoke the skill with broad natural-language requests and says they do not need to remember commands, but it does not define clear activation boundaries or confirmation requirements. In an agent environment, this can cause over-triggering or accidental execution on loosely related prompts, leading to unintended searches, report generation, or subscription setup.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
95% confidence
Finding

The skill invokes shell execution, reads environment/config secrets, performs network requests, and writes/opens local HTML, but it declares no explicit tool scope or permission boundaries. That increases the chance the agent can execute the skill with broader-than-necessary capabilities and reduces reviewability of what sensitive actions the skill is allowed to take.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The trigger terms are broad and loosely bounded, so the skill may activate for general tourism or Douyin-related requests without clear user intent to call an external API, generate files, or open a browser. Over-broad activation increases the risk of unintended data transmission and side effects occurring in contexts where the user did not expect them.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

The skill description, required output template, filenames, and trigger rules are entirely specified in Chinese and instruct the agent to respond in a fixed Chinese format. There is no opt-in, language selection, or documentation that this is a region-specific tool with a justified locale constraint.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The skill sends user-derived search queries to an external service authenticated with an API key, but it does not warn users that their query content will leave the local environment. This creates a privacy and consent problem, especially when user prompts may contain sensitive travel plans, names, or proprietary topics.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The skill writes HTML reports to local storage and mandates automatically opening them in a browser, but it does not clearly warn the user about these side effects. Automatic file creation and browser launch can expose sensitive content locally, surprise users in shared environments, and increase risk if the generated HTML contains untrusted external data.

Content

No source excerpt is available for this finding.

Session Persistence

Medium
Category
Rogue Agent
Confidence
92% confidence
Finding

The skill instructs users to persist the API key in a plaintext file under the home directory, creating long-lived credential exposure on disk. Stored secrets are vulnerable to leakage through backups, local compromise, overbroad file access by other tools, or accidental inclusion in support bundles and synced folders.

Content

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

export REDFOX_API_KEY=ak_xxxx

方式二:配置文件

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

text

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The HTML document is explicitly set to the zh-CN locale, which imposes a specific language/locale on all rendered output. The file does not provide any user opt-in, alternate locale handling, or natural-language justification that this template is intended only for a China-specific or Chinese-language context.

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_douyin_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-douyin-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_douyin_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-douyin-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_douyin_report.py (reported line 502)May include surrounding context.

python
PAGE_SIZE = 200

DEFAULT_OUTPUT_DIR = Path.home() / "Downloads" / "QoderReports"
PLIST_LABEL = "com.qoder.cultural-tourism-douyin-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_douyin_report.py (reported line 503)May include surrounding context.

python
PAGE_SIZE = 200

DEFAULT_OUTPUT_DIR = Path.home() / "Downloads" / "QoderReports"
PLIST_LABEL = "com.qoder.cultural-tourism-douyin-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_douyin_report.py (reported line 519)May include surrounding context.

python
PAGE_SIZE = 200

DEFAULT_OUTPUT_DIR = Path.home() / "Downloads" / "QoderReports"
PLIST_LABEL = "com.qoder.cultural-tourism-douyin-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_douyin_report.py (reported line 523)May include surrounding context.

python
PAGE_SIZE = 200

DEFAULT_OUTPUT_DIR = Path.home() / "Downloads" / "QoderReports"
PLIST_LABEL = "com.qoder.cultural-tourism-douyin-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_douyin_report.py (reported line 577)May include surrounding context.

python
PAGE_SIZE = 200

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

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

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The manifest describes a content-search and HTML-report generation skill for cultural-tourism Douyin data. However, this file also creates LaunchAgent and crontab entries, removes them, and configures daily execution, which is a host persistence/automation capability beyond the described search-and-report behavior.

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_douyin_report.py (reported line 503)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-douyin-feed.log")

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_douyin_report.py (reported line 518)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-douyin-feed.log")

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_douyin_report.py (reported line 519)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-douyin-feed.log")

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_douyin_report.py (reported line 520)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-douyin-feed.log")