Back to skill

Security audit

文旅B站信息源

Security checks across malware telemetry and agentic risk

Overview

The skill's main report-generation purpose is coherent, but its optional subscription path creates persistent scheduled jobs and can store an API key on disk without enough safeguards.

Review this skill before installing. Normal one-off report generation appears aligned with its purpose, but avoid using the daily subscription feature unless you are comfortable with it modifying LaunchAgents or crontab. Use a revocable RedFox API key, avoid storing high-value credentials in the environment on macOS with --subscribe, and prefer running reports manually or with your own scheduler until the subscription flow adds explicit confirmation, safer crontab handling, HTML escaping, and clearer cleanup instructions.

SkillSpector

By NVIDIA
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
  • Output HandlingUnvalidated Output Injection, Cross-Context Output, Unbounded Output
  • Rogue AgentSelf-Modification, Session Persistence
Findings (32)

subprocess module call

Medium
Category
Dangerous Code Execution
Content
script_path = os.path.abspath(__file__)
        cron_line = f"0 9 * * * /usr/bin/python3 {script_path} --keyword {keyword} --no-open"
        try:
            subprocess.run(
                f'(crontab -l 2>/dev/null; echo "{cron_line}") | crontab -',
                shell=True, check=True, capture_output=True
            )
Confidence
98% confidence
Finding
subprocess.run( f'(crontab -l 2>/dev/null; echo "{cron_line}") | crontab -', shell=True, check=True, capture_output=True )

subprocess module call

Medium
Category
Dangerous Code Execution
Content
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
            )
Confidence
98% confidence
Finding
subprocess.run( f'crontab -l 2>/dev/null | grep -v "{script_path}" | crontab -', shell=True, check=True, capture_output=True )

Lp3

Medium
Category
MCP Least Privilege
Confidence
93% confidence
Finding
The skill invokes shell execution, reads environment/config files for API keys, performs network access, and writes local HTML output, yet it declares no permissions or user-consent boundaries. This is dangerous because the agent may execute capability-bearing actions without transparent authorization, making data exfiltration, unintended file writes, or unsafe command execution harder to audit and contain.

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding
The documented purpose understates several significant behaviors: installing/removing scheduled tasks, using a third-party API, auto-opening generated HTML, and producing machine-readable output. These hidden behaviors materially change the trust model because they create persistence, external data transfer, and local execution side effects that a user would not reasonably expect from a simple content-search/reporting skill.

Context-Inappropriate Capability

Medium
Confidence
95% confidence
Finding
The skill's advertised role is searching Bilibili tourism content and generating reports, but it also installs persistent OS-level scheduled tasks. That expands capability beyond expected scope and creates persistence on the host, which is sensitive in an agent skill because users may not anticipate system-level modification.

Vague Triggers

Medium
Confidence
91% confidence
Finding
The README instructs users to invoke the skill with broad natural-language requests rather than a constrained command surface, which can make the skill activate on loosely related prompts. In an agentic environment, overly permissive triggering increases the chance of unintended execution, unnecessary API use, and accidental handling of user requests that were not meant for this specific skill.

Vague Triggers

Medium
Confidence
85% confidence
Finding
The README encourages broad natural-language triggering via generic phrases like '直接用自然语言描述需求' and multiple loosely scoped example utterances. In agent environments that auto-route based on user phrasing, this can cause unintended activation of the skill for adjacent travel/media queries, leading to unnecessary external API use and unintended data retrieval or report generation.

Vague Triggers

Medium
Confidence
82% confidence
Finding
The trigger phrases are broad and do not define clear exclusions, so the skill may activate on loosely related tourism or Bilibili queries and perform network requests, file generation, or browser opening unexpectedly. In an agent environment, overbroad invocation increases the chance of unintended side effects from a skill the user did not explicitly mean to run.

Missing User Warnings

Medium
Confidence
90% confidence
Finding
The skill mandates automatic opening of a locally generated HTML report and writes that report to disk without prominently warning the user in the high-level description. This is risky because local HTML can contain active content or misleading links, and forced browser launch creates an immediate side effect that may surprise users or expose them to unsafe rendering behavior.

Missing User Warnings

Medium
Confidence
94% confidence
Finding
The subscription flow writes and loads a persistent LaunchAgent without an interactive confirmation step or prominent warning about system modification. In an agent skill context, silent or lightly disclosed persistence is dangerous because users may trigger it expecting only report generation.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The non-macOS subscription path modifies the user's crontab immediately and without a confirmation checkpoint. Because this persists execution beyond the current session, the lack of explicit consent increases the chance of surprise persistence and misuse.

Missing User Warnings

High
Confidence
99% confidence
Finding
The code reads the API key from the environment and embeds it into the LaunchAgent plist as an EnvironmentVariables entry, causing the credential to be written to disk in persistent configuration. This exposes the key to local disclosure through filesystem access, backups, or accidental sharing of the plist file.

Env Variable Harvesting

High
Category
Data Exfiltration
Content
def get_api_key(cli_key=None):
    if cli_key:
        return cli_key
    env_key = os.environ.get(ENV_KEY)
    if env_key:
        return env_key
    if CONFIG_FILE.exists():
Confidence
70% confidence
Finding
os.environ.get(ENV_KEY

Env Variable Harvesting

High
Category
Data Exfiltration
Content
log_path = str(Path.home() / "Library" / "Logs" / "qoder-cultural-tourism-bilibili-feed.log")

        env_section = ""
        api_key = os.environ.get(ENV_KEY)
        if api_key:
            env_section = (
                '\n        <key>EnvironmentVariables</key>'
Confidence
90% confidence
Finding
os.environ.get(ENV_KEY

Unvalidated Output Injection

High
Category
Output Handling
Content
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
            )
Confidence
98% confidence
Finding
subprocess.run( f'crontab -l 2>/dev/null | grep -v "{script_path}" | crontab -', shell=True, check=True, capture_output

Hidden Instructions

High
Category
Prompt Injection
Content
<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">
Confidence
70% confidence
Finding
<!-- 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 hr

Session Persistence

Medium
Category
Rogue Agent
Content
cron_line = f"0 9 * * * /usr/bin/python3 {script_path} --keyword {keyword} --no-open"
        try:
            subprocess.run(
                f'(crontab -l 2>/dev/null; echo "{cron_line}") | crontab -',
                shell=True, check=True, capture_output=True
            )
            info(f"订阅成功! 每天 09:00 自动生成「{keyword}」文旅B站日报 (crontab)")
Confidence
97% confidence
Finding
crontab -l

Session Persistence

Medium
Category
Rogue Agent
Content
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
            )
            info("已取消订阅,crontab 任务已移除")
Confidence
93% confidence
Finding
crontab -l

Session Persistence

Medium
Category
Rogue Agent
Content
# ─── 订阅机制 ──────────────────────────────────────────────────────────────────────
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")
Confidence
95% confidence
Finding
PLIST

Session Persistence

Medium
Category
Rogue Agent
Content
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")
Confidence
95% confidence
Finding
plist

Session Persistence

Medium
Category
Rogue Agent
Content
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")
Confidence
95% confidence
Finding
PLIST

Session Persistence

Medium
Category
Rogue Agent
Content
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")
Confidence
95% confidence
Finding
PLIST

Session Persistence

Medium
Category
Rogue Agent
Content
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")
Confidence
95% confidence
Finding
plist

Session Persistence

Medium
Category
Rogue Agent
Content
'\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>
Confidence
94% confidence
Finding
plist

Session Persistence

Medium
Category
Rogue Agent
Content
)

        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>
Confidence
94% confidence
Finding
plist

VirusTotal

45/45 vendors flagged this skill as clean.

View on VirusTotal