Back to skill

Security audit

文旅B站信息源

Security checks for vulnerabilities and agentic risk

Overview

The skill mostly matches its stated reporting purpose, but its subscription feature creates persistent scheduled jobs with unsafe command/XML handling and credential exposure risks.

Review before installing. Use only with a RedFox API key you can revoke, prefer an environment variable over the plaintext config file, avoid `--subscribe` until the scheduler code is fixed, and do not use untrusted keywords. Treat generated HTML reports as untrusted web content because remote API values are inserted into the page and opened automatically.

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

T09 · Insecure Skill Coding Practices

Error
Location
scripts/cultural_tourism_bilibili_report.py:554
Finding

Shell Command Injection and Persistent Cron Injection Through the Subscription Keyword

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 user-controlled `--keyword` command-line argument and is interpolated directly into a command executed with `shell=True`. Neither the keyword nor `script_path` is validated or safely quoted. The surrounding double quotes do not prevent shell expansion. Shell constructs such as command substitution, backticks, embedded quotes, and newline characters can alter command behavior. Newlines can additionally insert arbitrary cron entries, converting immediate command injection into persistent execution. ### Attack Path 1. An attacker supplies or induces the Agent to use a malicious subscription keyword containing shell syntax. 2. The Agent invokes the script with `--subscribe` and the crafted keyword. 3. `install_subscription()` inserts the keyword into `cron_line`. 4. The complete string is passed to a shell through `subprocess.run(..., shell=True)`. 5. The shell evaluates the injected syntax with the privileges of the user running the Skill. 6. A crafted newline or cron expression can also register an attacker-controlled recurring command. ### Impact Assessment Successful exploitation provides arbitrary command execution under the Agent user's account. The attacker could read or modify user-accessible files, steal credentials, download additional payloads, alter the user's crontab, or establish recurring execution. No administrative privilege is inherently obtained, but the comp ...[truncated 64 chars]
Remediation
View remediation

T06 · System Persistence

Error
Location
scripts/cultural_tourism_bilibili_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 ) ``` ### Technical Analysis The subscription feature modifies operating-system scheduler state by writing a macOS LaunchAgent or replacing the user's crontab. These tasks survive the current Skill invocation and future Agent sessions. Subscription support is declared and activated only through `--subscribe`, so the persistence is not hidden. Nevertheless, scheduler modification is materially broader than the network and filesystem access needed for ordinary report generation. The scheduled task also executes the script from its existing path without an integrity check. A later replacement or modification of that file changes the code executed by the scheduler. The Linux implementation appends entries without checking whether an equivalent task already exists, allow ...[truncated 838 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Error
Location
scripts/cultural_tourism_bilibili_report.py:503
Finding

LaunchAgent XML Injection and Plaintext API-Key Persistence

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 The API key and user-controlled keyword are interpolated into XML without XML escaping. A keyword containing closing tags can break out of its intended `` element and alter the resulting LaunchAgent configuration. Other paths are also inserted without structured serialization. When `REDFOX_API_KEY` is present in the environment, its value is copied into a persistent plist in plaintext. This contradicts the documentation's instruction not to expose the key in output or configuration files. The cod ...[truncated 969 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Error
Location
scripts/cultural_tourism_bilibili_report.py:340
Finding

Stored HTML Injection in Automatically Opened Reports

Content
View full analysis
\'">' ) else: cover_html = ( '
' ) articles_html += ( '
' + cover_html + '
' + '' + title + '' + '
' + '' + author + '' + '' + '👍 ' + likes + '' + '💬 ' + comments + '' + '🔁 ' + shares + '' + time_html + '
' ) ``` Related user-controlled insertion and automatic opening occur at lines 438–448 and 716–733: ```python if keyword: keyword_badge = '
「' + keyword + '」
' html = html.replace("{{KEYWORD_BADGE}}", keyword_badge) html = html.replace("{{KEYWORD}}", keyword or "全部") ``` ```python output_path.write_text(html_content, encoding="utf-8") if not args.no_open: if sys.platform == "darwin": subprocess.Popen(["open", file_path]) elif sys.platform == "li ...[truncated 1746 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
SKILL.md:31
Finding

Credential Setup Instructions Create an Unprotected Plaintext API-Key File

Content
View full analysis
~/.qoder/apis/redfox.json ``` ### Technical Analysis The documented setup alternative stores the API key in a plaintext JSON file without explicitly setting restrictive directory or file permissions. The resulting permissions depend on the user's current `umask`. On permissively configured systems, other local users or processes may be able to read the credential. The script subsequently trusts and reads this file as an authentication source. ### Attack Path 1. A user follows the documented configuration command. 2. The shell creates the directory and credential file using permissions derived from the current `umask`. 3. Another local principal or compromised process reads `~/.qoder/apis/redfox.json`. 4. The recovered key is used to access the RedFox API within the key's assigned scope. ### Impact Assessment The exposure is limited to the privileges of the RedFox API key and requires local file access. Depending on the key's scope, an attacker may consume API quota, access associated API data, or impersonate the user to the service. ]]>
Remediation
View remediation
~/.qoder/apis/redfox.json chmod 600 ~/.qoder/apis/redfox.json ``` - Validate file ownership and permissions before reading the key. - Warn users and refuse to use credential files writable or readable by unintended principals. - Document credential rotation and revocation procedures. ]]>

T08 · Insecure Dependencies

Note
Location
SKILL.md:98
Finding

Unpinned Third-Party Dependency Installation

Content
View full analysis
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 (45)

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding

The declared behavior understates several materially sensitive actions: installing/removing scheduled tasks, automatically opening local HTML in a browser, relying on a third-party API, and using ranking logic different from the description. This mismatch undermines informed user consent and review, and can hide persistence or external data exfiltration behaviors behind an apparently simple reporting skill.

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>文旅B站信息源 - {{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">

Context-Inappropriate Capability

High
Category
Not specified by scanner
Confidence
97% confidence
Finding

This code performs system-level installation and removal of scheduled jobs, creating persistence on the host outside the narrow expectation of a search/report skill. In this context, the capability is more dangerous because it alters user environment state and, on Linux, is partly implemented with unsafe shell construction.

Content

No source excerpt is available for this finding.

Tool Parameter Abuse

High
Category
Tool Misuse
Confidence
99% confidence
Finding

The shell command used to edit crontab includes interpolated script_path inside double quotes and runs with shell=True. If the path contains shell metacharacters or command substitutions, an attacker can abuse tool parameters to execute unintended commands or corrupt the user's crontab.

Content

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

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 then provides broad example phrasings, but it does not define specific trigger conditions, exclusions, or scope boundaries. This makes activation ambiguous and increases the chance of unintended invocation from everyday analytical requests about Bilibili or tourism topics.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

The README instructs users to invoke the skill with very broad natural-language phrases like '看看B站最新文旅爆款' and '订阅B站文旅日报', without clear constraints or explicit confirmation boundaries. In an agent ecosystem, this can cause unintended activation when a user mentions similar topics conversationally, potentially triggering external data retrieval, report generation, or subscriptions without sufficiently deliberate intent.

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, network, filesystem, and environment-variable access but does not declare any explicit tool scope or permissions. This weakens least-privilege controls and makes it harder for a host agent or reviewer to understand and constrain what the skill is allowed to do, increasing the risk of unintended command execution, local file writes, or secret exposure.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

Broad trigger phrases such as generic travel-related terms increase the chance that the skill activates in contexts where the user did not intend it. Because the skill performs network access, file generation, browser launching, and possible subscription setup, unintended activation can lead to unnecessary side effects and privacy exposure.

Content

No source excerpt is available for this finding.

Session Persistence

Medium
Category
Rogue Agent
Confidence
88% confidence
Finding

The skill instructs users to store a long-lived API key in a persistent file under the home directory. Persisting secrets in a predictable local path increases the chance of accidental disclosure to other tools, backups, logs, or compromised local processes, especially when the skill also uses shell and file operations.

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

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The skill requires automatic browser opening and supports daily subscription behavior, but the description does not clearly warn users about these side effects before execution. Hidden side effects reduce transparency and can surprise users with local application launches or persistent scheduled jobs.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The HTML document is explicitly fixed to the zh-CN locale via the lang attribute. Under the policy for natural-language violations, forcing a specific language or locale without user opt-in or a documented region-specific justification is a reportable issue.

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_bilibili_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-bilibili-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_bilibili_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-bilibili-feed"
PLIST_DIR = Path.home() / "Library" / "LaunchAgents"

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

Intent-Code Divergence

Medium
Category
Not specified by scanner
Confidence
99% confidence
Finding

The script inserts untrusted title, author, cover URL, and work URL values directly into generated HTML attributes and element bodies without escaping. If the upstream API returns crafted content, opening the local report can trigger script execution or malicious markup in the user's browser, turning the report into a stored XSS-style local HTML attack.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The skill adds persistent scheduled-task installation behavior that is not reflected in the manifest's feed/reporting description, which expands the trust boundary unexpectedly. In an agent-skill context, hidden persistence is especially risky because users may invoke a content-report skill without realizing it can modify long-lived system automation.

Content

No source excerpt is available for this finding.

Session Persistence

Medium
Category
Rogue Agent
Confidence
95% confidence
Finding

This line begins the macOS LaunchAgent installation flow by creating the persistence directory and preparing a plist path. It is part of a feature that establishes recurring execution on the host, which is sensitive in this skill context.

Content

Scanner excerpt · scripts/cultural_tourism_bilibili_report.py (reported line 497)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-bilibili-feed.log")

Session Persistence

Medium
Category
Rogue Agent
Confidence
95% confidence
Finding

This constructs the LaunchAgent plist path used to persist the skill across sessions. While not executable by itself, it is an integral step in setting up recurring host execution.

Content

Scanner excerpt · scripts/cultural_tourism_bilibili_report.py (reported line 498)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-bilibili-feed.log")

Session Persistence

Medium
Category
Rogue Agent
Confidence
95% confidence
Finding

This constructs the LaunchAgent plist path used to persist the skill across sessions. While not executable by itself, it is an integral step in setting up recurring host execution.

Content

Scanner excerpt · scripts/cultural_tourism_bilibili_report.py (reported line 498)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-bilibili-feed.log")

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The macOS subscription path embeds the API key into a LaunchAgent plist, creating another persistent plaintext secret location without warning the user. This increases exposure of the credential to local disclosure via filesystem access, backups, or accidental sharing of configuration files.

Content

No source excerpt is available for this finding.

Session Persistence

Medium
Category
Rogue Agent
Confidence
95% confidence
Finding

This begins building the LaunchAgent plist content used for persistence. The risk is the creation of a scheduled, recurring execution artifact for a skill whose primary purpose appears to be ad hoc reporting.

Content

Scanner excerpt · scripts/cultural_tourism_bilibili_report.py (reported line 513)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
95% confidence
Finding

This continues construction of the persistent LaunchAgent definition. In context, it contributes to unattended future execution and therefore represents persistence capability.

Content

Scanner excerpt · scripts/cultural_tourism_bilibili_report.py (reported line 514)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
95% confidence
Finding

This continues construction of the persistent LaunchAgent definition. In context, it contributes to unattended future execution and therefore represents persistence capability.

Content

Scanner excerpt · scripts/cultural_tourism_bilibili_report.py (reported line 514)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
95% confidence
Finding

The plist declaration here is part of assembling a LaunchAgent that causes recurring execution. Persistence is the core concern, not this XML syntax itself.

Content

Scanner excerpt · scripts/cultural_tourism_bilibili_report.py (reported line 515)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
95% confidence
Finding

This line adds ProgramArguments to the LaunchAgent, defining what will run automatically later. That makes the persistence concrete and therefore security-relevant in a user workstation context.

Content

Scanner excerpt · scripts/cultural_tourism_bilibili_report.py (reported line 518)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
96% confidence
Finding

Writing the plist to the LaunchAgents directory creates the persistence artifact on disk. This is a substantive state change that outlives the current process and should be treated as a true persistence risk.

Content

Scanner excerpt · scripts/cultural_tourism_bilibili_report.py (reported line 541)May include surrounding context.

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

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