Back to skill

Security audit

Chinese Bedtime Story Generator

Security checks for vulnerabilities and agentic risk

Overview

The skill’s bedtime-story function is coherent, but it sends child-related content and API credentials to configurable external endpoints without enough validation or privacy disclosure.

Review before installing. Use a dedicated virtual environment, avoid running it from directories containing untrusted .env files, set only provider endpoints you trust, prefer --no-tts when audio is not needed, and delete artifact/outputs if you do not want child-associated story state, text, or audio retained locally.

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

Warning
Location
scripts/run_story.py:64
Finding

API Credentials Can Be Exfiltrated Through Unvalidated Custom Endpoints

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) def refresh_runtime_settings() -> None: global LLM_BASE_URL, LLM_MODEL, LLM_API_KEY, LLM_TIMEOUT, LLM_TEMPERATURE global TTS_URL, TTS_MODEL, TTS_API_KEY, TTS_TIMEOUT LLM_BASE_URL = os.environ.get("STORY_LLM_BASE_URL", "https://models.audiozen.cn/v1") LLM_MODEL = os.environ.get("STORY_LLM_MODEL", "doubao-seed-2-0-lite-260215") LLM_API_KEY = os.environ.get("STORY_LLM_API_KEY", os.environ.get("IME_MODEL_API_KEY", "")) LLM_TIMEOUT = float(os.environ.get("STORY_LLM_TIMEOUT", "90")) LLM_TEMPERATURE = float(os.environ.get("STORY_LLM_TEMPERATURE", "0.7")) TTS_URL = os.environ.get("STORY_TTS_URL", "https://api.senseaudio.cn/v1/t2a_v2") TTS_MODEL = os.environ.get("STORY_TTS_MODEL", "SenseAudio-TTS-1.0") TTS_API_KEY = os.environ.get("SENSEAUDIO_API_KEY", "") TTS_TIMEOUT = float(os.environ.get("STORY_TTS_TIMEOUT", "60")) def require_env(name: str, value: str) -> str: if value: return value raise SystemExit(f"缺少必要配置: {name}") def build_client() -> OpenAI: api_key = require_env("STORY_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 def synthesize_tts(text: str, voice_cfg: dict[str, Any], tag: str) -> bytes | None: """Synthesize a single TTS segment and return raw MP3 bytes.""" if not text: return None api_key = require_env("SENSEAUDIO_API_KEY", TTS_API_KEY) headers = {"Authorization": f"Bearer {api_key}", "Content-Type": "application/json"} voice_setting: dict[str, Any] ...[truncated 2727 chars]
Remediation
View remediation

T08 · Insecure Dependencies

Note
Location
requirements.txt:1
Finding

Unbounded Dependency Versions Create a Supply-Chain Exposure

Content
View full analysis
=1.30.0 requests>=2.31.0 python-dotenv>=1.0.0 ``` The documented installation command is: ```bash pip install -r requirements.txt ``` ### Technical Analysis Every dependency is specified only with a minimum version. Consequently, a fresh installation can resolve to any newer release available from the configured package index. There is no lockfile, exact version constraint, or package hash to make dependency resolution reproducible or verify artifact integrity. This does not establish that any currently named package is malicious. It creates a supply-chain weakness in which future compromised, malicious, or unexpectedly incompatible releases may be installed automatically without project-level review. Python packages may execute build backend or installation-related code during installation, and imported packages execute module initialization code at runtime. A compromised dependency can therefore affect the environment using the privileges of the user performing installation or running the Skill. ### Attack Path 1. A future release of one of the accepted dependencies is compromised, or an attacker compromises the package publishing account or distribution channel. 2. The malicious version still satisfies the broad `>=` constraint. 3. A user performs the documented `pip install -r requirements.txt` command in a fresh or updated environment. 4. The package resolver selects the compromised version. 5. Malicious code executes during package building, installation, or later import by `scripts/run_story.py`. 6. The malicious dependency obtains the permissions and accessible data of the installing or executing user. ### Impact Assessment Potential impact depends on the privileges used for installation and execution. It can include: - Execution of arbitrary code ...[truncated 494 chars]
Remediation
View remediation
Vulnerability Patterns
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • 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
Findings (19)

Tainted flow: 'TTS_URL' from os.environ.get (line 82, 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_story.py (reported line 161)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:

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_story.py (reported line 67)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
95% confidence
Finding

The skill documentation describes capabilities that require environment-variable access, local file reads/writes, and outbound network calls, but it does not declare any explicit tool scope or permission boundaries. This creates an over-privilege and transparency problem: a host may permit broader access than users expect, and reviewers cannot easily verify that only the minimum required capabilities are used.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The workflow states that external LLM and TTS services are used, but it does not explicitly warn users that child-provided information such as name, age, interests, and generated story content may be transmitted to third-party services. This is a privacy risk because users may assume processing is local, especially in a child-focused skill, and may share more sensitive information than intended.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The skill persists child-related inputs and story state across runs in local files, but the description does not clearly warn users that this data will be stored on disk. Because the content includes a child's name, age, interests, and ongoing narrative state, users may unknowingly create a local record of sensitive child-associated information that could be exposed to other local users, backups, or later processes.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The system prompts hard-code that the model should act as a Chinese children's story writer and produce Chinese content, and the CLI description also frames the tool as Chinese-only. This imposes a specific language/locale without any opt-in, fallback, or documented justification that the skill is region-specific.

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_story.py (reported line 82)May include surrounding context.

python
LLM_TIMEOUT = float(os.environ.get("STORY_LLM_TIMEOUT", "90"))
    LLM_TEMPERATURE = float(os.environ.get("STORY_LLM_TEMPERATURE", "0.7"))

    TTS_URL = os.environ.get("STORY_TTS_URL", "https://api.senseaudio.cn/v1/t2a_v2")
    TTS_MODEL = os.environ.get("STORY_TTS_MODEL", "SenseAudio-TTS-1.0")
    TTS_API_KEY = os.environ.get("SENSEAUDIO_API_KEY", "")
    TTS_TIMEOUT = float(os.environ.get("STORY_TTS_TIMEOUT", "60"))

External Transmission

Medium
Category
Data Exfiltration
Confidence
86% confidence
Finding

The skill sends story content to an external TTS provider, and that content includes child name, age-derived tailoring, interests, and generated narrative text. In a bedtime-story skill for children, this data-sharing is more sensitive because it may involve minors' personal information and is not minimized or gated by explicit consent in code.

Content

Scanner excerpt · scripts/run_story.py (reported line 161)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:

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
95% confidence
Finding

The file explicitly states 不做:- 英文故事, which restricts the skill to Chinese output. Under the policy, forcing a specific language without user opt-in is a natural-language policy violation unless the locale restriction is clearly justified as region-specific, which is not stated here.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
93% confidence
Finding

The display name is presented only in Chinese ("睡前故事生成器"), which can impose a specific language on users without indicating any language choice or opt-in. This may violate language/locale policy where skills should not force a locale unless clearly documented and justified.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
94% confidence
Finding

The instruction explicitly fixes the role as a "professional Chinese children's story writer," which imposes a specific language/locale. This appears again in later modules and does not provide the user any option to select another language or justify the restriction as region-specific.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
94% confidence
Finding

This module again instructs the model to act as a "professional Chinese children's story writer," which sets a mandatory language/locale. Under the policy, fixed language behavior should either be optional for the user or clearly justified.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
94% confidence
Finding

The continuation prompt repeats the same mandatory Chinese-language framing. Because the file does not offer a user language choice or a documented justification for the locale restriction, this is a natural-language policy issue.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
82% confidence
Finding

This markdown reference is entirely presented in Chinese, including headings, field examples, and content strings, with no indication that the skill is region-specific or that users may choose another language. Under the policy rule, forcing a specific language without opt-in can be a natural-language policy concern.

Content

No source excerpt is available for this finding.

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
94% confidence
Finding

The dependency is specified with a minimum version only, which allows future installs to resolve to different versions over time. This weakens build reproducibility and can unintentionally introduce vulnerable or breaking releases into the skill's environment.

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
97% confidence
Finding

requests is unpinned, so installations may pull different versions depending on when and where the skill is deployed. Because this package has multiple historical advisories, lack of pinning creates uncertainty and increases the chance of consuming an affected release.

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
90% confidence
Finding

The manifest does not pin requests, and the package has known advisories across some versions. Since the resolved version is unknown, it is impossible to verify from this file alone whether deployments are protected, which creates real supply-chain and runtime risk.

Content

No source excerpt is available for this finding.

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
93% confidence
Finding

python-dotenv is also specified as a floating minimum version, which permits non-reproducible dependency resolution. This can expose deployments to newly introduced vulnerable or incompatible releases without any code change in the skill itself.

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
88% confidence
Finding

python-dotenv has known advisories in some versions, but the dependency is not pinned, so the installed release cannot be validated from the manifest. If the skill or supporting tooling uses affected functionality, an unsafe resolved version could enable file overwrite or environment-handling issues.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.