Back to skill

Security audit

Chinese Voice Detective Mystery Game

Security checks for vulnerabilities and agentic risk

Overview

The skill is a coherent voice detective game, but it can send API keys, player audio, and game dialogue to arbitrary endpoints loaded from environment files without clear scoping or consent.

Review this skill before installing. Use it only from a trusted directory, inspect or remove any local .env files, avoid custom endpoint overrides unless you trust the host, prefer --no-asr or --no-tts if you do not want audio or text sent to remote services, and install in an isolated environment with reviewed dependency versions.

Vulnerability Patterns
  • 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
  • Remote Payload Retrieval and ExecutionFetches external code whose behavior can change after review
Findings (2)

T09 · Insecure Skill Coding Practices

Error
Location
scripts/run_mystery.py:85
Finding

Untrusted Environment Configuration Can Redirect Credentials and Sensitive Content

Content
View full analysis
None: if load_dotenv is None: return for path in (SKILL_DIR / ".env", Path.cwd() / ".env"): if path.exists(): load_dotenv(path) ``` ```python LLM_BASE_URL = os.environ.get( "MYSTERY_LLM_BASE_URL", "https://models.audiozen.cn/v1", ) LLM_API_KEY = os.environ.get( "MYSTERY_LLM_API_KEY", os.environ.get("IME_MODEL_API_KEY", ""), ) ASR_URL = os.environ.get( "MYSTERY_ASR_URL", "https://api.senseaudio.cn/v1/audio/transcriptions", ) ASR_API_KEY = os.environ.get("SENSEAUDIO_API_KEY", "") TTS_URL = os.environ.get( "MYSTERY_TTS_URL", "https://api.senseaudio.cn/v1/t2a_v2", ) TTS_API_KEY = os.environ.get("SENSEAUDIO_API_KEY", "") ``` ```python def build_client() -> OpenAI: api_key = require_env( "MYSTERY_LLM_API_KEY / IME_MODEL_API_KEY", LLM_API_KEY, ) return OpenAI( api_key=api_key, base_url=LLM_BASE_URL, timeout=LLM_TIMEOUT, ) ``` ```python headers = { "Authorization": f"Bearer {api_key}", "Content-Type": "application/json", } response = requests.post( TTS_URL, headers=headers, json=payload, timeout=TTS_TIMEOUT, ) ``` ```python headers = {"Authorization": f"Bearer {api_key}"} data = {"model": ASR_MODEL, "response_format": "verbose_json"} with audio_path.open("rb") as handle: files = {"file": (audio_path.name, handle)} response = requests.post( ASR_URL, headers=headers, data=data, files=files, timeout=ASR_TIMEOUT, ) ``` ### Technical Analysis The Skill legitimately requires network access for LLM generation, speech recognition, and speech synthesis. However, it automatically loads configuration f ...[truncated 2943 chars]
Remediation
View remediation

T08 · Insecure Dependencies

Warning
Location
requirements.txt:1
Finding

Unbounded Dependency Versions Create Supply-Chain Exposure

Content
View full analysis
=1.30.0 requests>=2.31.0 python-dotenv>=1.0.0 ``` The documented installation process is: ```bash pip install -r requirements.txt ``` ### Technical Analysis All three runtime dependencies use open-ended minimum-version constraints. The resolver may therefore install any future compatible release available when the command is executed. No lock file, upper bound, package hash, or reviewed artifact source is provided. This makes installations non-reproducible and allows future upstream changes to enter the Skill without being covered by the current audit. If an upstream account, package release, package index, or dependency chain is compromised, installation or import-time code can execute with the same operating-system privileges as the user running `pip` or the Skill. This finding does not establish that the named packages are currently malicious. The vulnerability is the absence of controls that bind installation to reviewed package artifacts. ### Attack Path 1. A direct or transitive dependency publishes a compromised release, or an upstream package-maintainer account is compromised. 2. The victim follows the documented command: ```bash pip install -r requirements.txt ``` 3. Because the requirements accept every version at or above the stated minimum, the resolver selects the compromised release if it is considered the newest compatible version. 4. Malicious installation hooks or package code execute during installation or when `run_mystery.py` imports the dependency. 5. The payload runs with the privileges of the victim's Python or `pip` process. ### Impact Assessment A compromised dependency could obtain all privileges available to the Skill process, potentially including: - Reading environment variables a ...[truncated 522 chars]
Remediation
View remediation
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Taint TrackingDirect Taint Flow, Variable-Mediated Taint Flow, Credential Exfiltration Chain
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (22)

Tainted flow: 'TTS_URL' from os.environ.get (line 106, credential/environment) → requests.post (network output)

Critical
Category
Data Flow
Confidence
90% confidence
Finding

Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Content

Scanner excerpt · scripts/run_mystery.py (reported line 185)May include surrounding context.

python
"voice_setting": voice_setting,
        "audio_setting": {"format": "mp3", "sample_rate": 32000},
    }
    response = requests.post(TTS_URL, headers=headers, json=payload, timeout=TTS_TIMEOUT)
    response.raise_for_status()
    result = response.json()
    if result.get("base_resp", {}).get("status_code") != 0:

Tainted flow: 'ASR_URL' from os.environ.get (line 101, credential/environment) → requests.post (network output)

Critical
Category
Data Flow
Confidence
90% confidence
Finding

Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Content

Scanner excerpt · scripts/run_mystery.py (reported line 220)May include surrounding context.

python
data = {"model": ASR_MODEL, "response_format": "verbose_json"}
    with audio_path.open("rb") as handle:
        files = {"file": (audio_path.name, handle)}
        response = requests.post(ASR_URL, headers=headers, data=data, files=files, timeout=ASR_TIMEOUT)
    response.raise_for_status()
    payload = response.json()
    text = payload.get("text", "").strip()

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · scripts/run_mystery.py (reported line 85)May include surrounding context.

python
def load_env() -> None:
    if load_dotenv is None:
        return
    for path in (SKILL_DIR / ".env", Path.cwd() / ".env"):
        if path.exists():
            load_dotenv(path)

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
93% confidence
Finding

The skill advertises capabilities that imply environment variable access, file read/write, and network use, but it does not declare any explicit tool scope such as permissions or allowed-tools. This creates an authorization ambiguity where a host may grant broader access than users expect, increasing the risk of unnecessary file, secret, or network exposure if the skill or its prompts are abused.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The description states this is a Chinese voice detective game, and the scope section further says it does not do English games. That is a natural-language locale restriction without user opt-in or a documented policy/compliance justification, which matches the language/locale policy violation category.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The line 英文游戏 under 不做 explicitly excludes English gameplay. Because no user language choice or justification is provided, this is a locale-policy issue under the natural-language policy rule.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
97% confidence
Finding

The module docstring states this is a Chinese voice detective game, and the embedded prompts and CLI descriptions consistently enforce Chinese-language operation. There is no visible opt-in, alternative locale selection, or justification that this locale restriction is required by the skill's operational scope.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
60% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · scripts/run_mystery.py (reported line 101)May include surrounding context.

python
LLM_TIMEOUT = float(os.environ.get("MYSTERY_LLM_TIMEOUT", "120"))
    LLM_TEMPERATURE = float(os.environ.get("MYSTERY_LLM_TEMPERATURE", "0.5"))

    ASR_URL = os.environ.get("MYSTERY_ASR_URL", "https://api.senseaudio.cn/v1/audio/transcriptions")
    ASR_MODEL = os.environ.get("MYSTERY_ASR_MODEL", "sense-asr-pro")
    ASR_API_KEY = os.environ.get("SENSEAUDIO_API_KEY", "")
    ASR_TIMEOUT = float(os.environ.get("MYSTERY_ASR_TIMEOUT", "300"))

External Transmission

Medium
Category
Data Exfiltration
Confidence
60% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · scripts/run_mystery.py (reported line 106)May include surrounding context.

python
LLM_TIMEOUT = float(os.environ.get("MYSTERY_LLM_TIMEOUT", "120"))
    LLM_TEMPERATURE = float(os.environ.get("MYSTERY_LLM_TEMPERATURE", "0.5"))

    ASR_URL = os.environ.get("MYSTERY_ASR_URL", "https://api.senseaudio.cn/v1/audio/transcriptions")
    ASR_MODEL = os.environ.get("MYSTERY_ASR_MODEL", "sense-asr-pro")
    ASR_API_KEY = os.environ.get("SENSEAUDIO_API_KEY", "")
    ASR_TIMEOUT = float(os.environ.get("MYSTERY_ASR_TIMEOUT", "300"))

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

Game text, including potentially sensitive user-provided reasoning or dialogue content, is sent to a remote TTS provider without clear disclosure at the point of use. Users may reasonably assume speech synthesis is local, so undisclosed transmission increases privacy and trust risk.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
80% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · scripts/run_mystery.py (reported line 185)May include surrounding context.

python
"voice_setting": voice_setting,
        "audio_setting": {"format": "mp3", "sample_rate": 32000},
    }
    response = requests.post(TTS_URL, headers=headers, json=payload, timeout=TTS_TIMEOUT)
    response.raise_for_status()
    result = response.json()
    if result.get("base_resp", {}).get("status_code") != 0:

Tainted flow: 'payload' from requests.post (line 222, network input) → requests.post (network output)

Medium
Category
Data Flow
Confidence
65% confidence
Finding

Data from a source is assigned to a variable that is later passed to a sink, creating a variable-mediated taint flow.

Content

Scanner excerpt · scripts/run_mystery.py (reported line 185)May include surrounding context.

python
"voice_setting": voice_setting,
        "audio_setting": {"format": "mp3", "sample_rate": 32000},
    }
    response = requests.post(TTS_URL, headers=headers, json=payload, timeout=TTS_TIMEOUT)
    response.raise_for_status()
    result = response.json()
    if result.get("base_resp", {}).get("status_code") != 0:

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

When the player chooses audio input, the file is uploaded to a remote ASR service, but the code does not provide a clear upfront privacy warning or explicit consent about third-party transmission. Audio may contain sensitive personal or environmental information, so silent upload creates a real privacy and compliance risk.

Content

No source excerpt is available for this finding.

Missing User Warnings

Low
Category
Not specified by scanner
Confidence
86% confidence
Finding

The skill supports save/load across sessions and writes a case_report.json file, but it does not warn users that gameplay content may be stored persistently. Because the game captures interrogation history and possibly voice-derived text, users may unknowingly leave sensitive personal or conversational data on disk.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
93% confidence
Finding

The manifest sets the skill's user-facing display name entirely in Chinese ("语音侦探推理游戏") with no indication of language choice or multilingual support. This can violate language/locale policy when a skill imposes a specific language on users without documented opt-in or justification.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
94% confidence
Finding

该提示明确要求“生成一个完整的中文侦探推理案件”,属于自然语言层面的语言/locale 约束。文件中未见用户可选择语言的说明,也未说明该模板为何必须仅限中文,因此构成语言策略方面的潜在违规。

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
87% confidence
Finding

This markdown file is entirely written in Chinese, including headings, field descriptions, and example content, with no indication that users can choose another language. Under the language/locale policy, forcing a specific language without user opt-in can be a natural-language policy violation unless the locale restriction is documented and justified.

Content

No source excerpt is available for this finding.

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
97% confidence
Finding

The dependency is specified with a lower bound only (openai>=1.30.0), which allows future unreviewed versions to be installed. This weakens build reproducibility and can introduce breaking changes or newly introduced vulnerable releases without any change to the skill source.

Content

Scanner excerpt · requirements.txt (reported line 1)May include surrounding context.

text
openai>=1.30.0
requests>=2.31.0
python-dotenv>=1.0.0

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
99% confidence
Finding

requests>=2.31.0 is unpinned, so installations may resolve to different versions over time, including versions with regressions or security issues. Because requests is network-facing and widely targeted, leaving version selection open increases supply-chain and patch-management risk.

Content

Scanner excerpt · requirements.txt (reported line 2)May include surrounding context.

text
openai>=1.30.0
requests>=2.31.0
python-dotenv>=1.0.0

Unverifiable Dependency: requests has 16 known advisory(ies) (CVE-2014-1830 (Exposure of Sensitive Information to an Unauthorized Actor in Requests); CVE-2024-47081 (Requests vulnerable to .netrc credentials leak via malicious URLs); CVE-2024-35195 (Requests `Session` object does not verify requests after making first request wi) +13 more), but the manifest does not pin a version, so it is unknown whether the installed release is affected

Low
Category
Supply Chain
Confidence
92% confidence
Finding

The manifest does not pin requests, and that package has multiple known advisories across its release history. Without an exact version, it is impossible to verify whether deployments will avoid affected releases, so the project cannot reliably demonstrate it is not exposed to known requests vulnerabilities.

Content

No source excerpt is available for this finding.

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
98% confidence
Finding

python-dotenv>=1.0.0 permits any later version, making builds non-deterministic and complicating assurance that deployed environments use a safe tested release. This is especially relevant for configuration-loading libraries, where behavior changes can affect secret handling or file operations.

Content

Scanner excerpt · requirements.txt (reported line 3)May include surrounding context.

text
openai>=1.30.0
requests>=2.31.0
python-dotenv>=1.0.0

Unverifiable Dependency: python-dotenv has 2 known advisory(ies) (CVE-2026-28684 (python-dotenv: Symlink following in set_key allows arbitrary file overwrite via ); CVE-2026-28684 (python-dotenv reads key-value pairs from a .env file and can set them as environ)), but the manifest does not pin a version, so it is unknown whether the installed release is affected

Low
Category
Supply Chain
Confidence
89% confidence
Finding

python-dotenv has known advisories in some versions, but the dependency is not pinned, so the installed version may vary and could be vulnerable. This creates uncertainty around secret-loading and file-handling behavior, which is undesirable in any application that may load environment configuration from disk.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.