Back to skill

Security audit

Cultural Relics Guide

Security checks for vulnerabilities and agentic risk

Overview

The skill does what it claims, but it stores and transmits API credentials in ways users should review before installing.

Review before installing. Use only HTTPS API endpoints unless the endpoint is a trusted local loopback service, avoid putting sensitive or unpublished collection details into prompts, and treat config/user_config.json as a secret because it can contain your API key. Rotate the key if it was ever used with a remote http:// endpoint or stored in a shared workspace.

Vulnerability Patterns
  • 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
  • Embedded Malicious CodeShips malicious scripts inside the skill and executes them locally
Findings (2)

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/utils.py:37
Finding

API Credential Stored in Plaintext Without Restrictive File Permissions

Content
View full analysis

Vulnerability Details

File Location: scripts/utils.py:37-47
Related Location: scripts/narration_generator.py:69-77
Vulnerability Type: Plaintext credential storage with insufficient file-permission enforcement
Risk Level: Medium

The application includes the user-supplied API key in the configuration object and persists that entire object to config/user_config.json.

python
def save_user_config(config):
    """
    Save user configuration.

    Args:
        config: Configuration dictionary
    """
    config_path = get_config_path()
    
    with open(config_path, "w", encoding="utf-8") as f:
        json.dump(config, f, ensure_ascii=False, indent=2)

The configuration object passed to this function contains the API key:

python
config["api"] = {
    "base_url": base_url,
    "api_key": api_key,
    "model_narration": model_narration,
    "model_style": model_style,
    "timeout": timeout
}

save_user_config(config)

Technical Analysis

Python's standard open() call does not impose owner-only permissions on the resulting file. A newly created file inherits permissions determined by the process umask, while an existing file retains its previous permissions. The implementation neither creates the credential file with mode 0600 nor verifies and corrects existing permissions.

The API key is therefore stored as recoverable plaintext in the project directory. On a shared system, permissive umask, broadly readable workspace, backup, source archive, or accidentally published repository, another party may obtain the credential.

Attack Path

  1. A user starts the application and enters a valid API key.
  2. get_api_config() places the key in config["api"]["api_key"].
  3. save_user_config() serializes the complete configuration to config/user_config.json.
  4. The operating system creates or updates the file without application-enforced owner- ...[truncated 664 chars]
Remediation
View remediation

Remediation Suggestions

  • Prefer an operating-system credential manager or an environment variable over project-local plaintext storage.
  • If file persistence is necessary, create the file atomically with owner-only mode 0600.
  • Check and correct the permissions of existing configuration files before writing or reading credentials.
  • Store non-secret settings separately from the API key.
  • Add config/user_config.json to version-control and packaging exclusion rules while retaining only a secret-free example file.
  • Warn users not to place the project directory in shared or synchronized storage.
  • Provide documented credential revocation and rotation procedures.
  • Avoid logging or displaying the API key in errors or diagnostic output.

T09 · Insecure Skill Coding Practices

Error
Location
scripts/utils.py:79
Finding

Bearer Credential and User Data Can Be Sent over Plaintext HTTP

Content
View full analysis

Vulnerability Details

File Location: scripts/utils.py:79-105
Vulnerability Type: Missing HTTPS enforcement for credential-bearing API requests
Risk Level: High

The application accepts the configured endpoint without validating its URL scheme or restricting plaintext HTTP. It then attaches the API key as a bearer credential and submits the request through urllib.request.urlopen().

python
api_config = config.get("api", {})
base_url = api_config.get("base_url")
api_key = api_config.get("api_key")
timeout = api_config.get("timeout", 60)

if not base_url or not api_key:
    raise ValueError("API configuration is incomplete")

payload = {
    "model": model,
    "messages": messages
}

data = json.dumps(payload).encode("utf-8")
req = urllib.request.Request(
    base_url,
    data=data,
    headers={
        "Authorization": f"Bearer {api_key}",
        "Content-Type": "application/json"
    },
    method="POST"
)

try:
    with urllib.request.urlopen(req, timeout=timeout) as response:
        return json.loads(response.read().decode("utf-8"))

Technical Analysis

The endpoint is taken directly from user configuration. No URL parsing or scheme check requires https://, so a remote http:// endpoint is accepted. When that occurs, the bearer token, model name, relic details, audience profile, and optional user question are transmitted without transport encryption.

Python provides normal certificate validation when a genuine HTTPS URL is used, but that protection does not apply to HTTP. The vulnerability is therefore reachable through misconfiguration, misleading setup instructions supplied by an untrusted service, or deliberate selection of a plaintext endpoint.

Local HTTP endpoints can be legitimate for software such as Ollama, but the exception should be narrowly limited to loopback addresses rather than permitting arbitrary remote HTTP hosts.

Attack Pat

...[truncated 1302 chars]

Remediation
View remediation

Remediation Suggestions

  • Parse the endpoint with urllib.parse.urlparse() before constructing the request.
  • Require the https scheme for all non-loopback destinations.
  • If local model support is required, allow HTTP only for explicitly recognized loopback hosts such as 127.0.0.1, ::1, and localhost.
  • Reject URLs containing unexpected schemes, missing hostnames, or embedded credentials.
  • Display a clear warning and require explicit opt-in for any narrowly permitted loopback HTTP connection.
  • Continue using default TLS certificate and hostname verification; do not introduce an unverified SSL context.
  • Consider an allowlist of trusted API origins in managed deployments.
  • Rotate the API key immediately if it has previously been transmitted to a remote plaintext HTTP endpoint.
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • Taint TrackingDirect Taint Flow, Variable-Mediated Taint Flow, Credential Exfiltration Chain
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
Findings (18)

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
93% confidence
Finding

The skill documentation declares configuration that enables file reads/writes and outbound network access, but it does not define any explicit tool scope or permission boundaries. This can lead to over-privileged execution where the runtime may allow broader filesystem or network operations than users expect, increasing risk around local credential files and exfiltration.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

The description states the skill generates a ‘专业讲解词’ of 400-500 characters in Chinese, and the rest of the documentation is written as a Chinese-only workflow without an explicit user language option. This is a natural-language locale constraint that should either be optional for the user or clearly documented as a justified region-specific limitation.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
50% 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 · SKILL.md (reported line 17)May include surrounding context.

md
properties:
                  base_url:
                    type: string
                    description: 大模型 API Base URL(如 https://api.openai.com/v1/chat/completions)
                  api_key:
                    type: string
                    description: 大模型 API 密钥

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The trigger list includes broad, common phrases like '介绍', '讲解', and '说一下', which may cause accidental invocation in unrelated conversations. Because the skill can read local config and send data to an external API, unintended activation could expose user-provided content or initiate unexpected network requests.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The documentation does not clearly warn users that artifact details, museum context, audience information, and optional questions may be transmitted to a third-party model service. This weakens informed consent and increases privacy risk, especially if users include sensitive or unpublished collection information.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The natural-language prompt content is entirely in Chinese and instructs the model to output only the requested style or narration text, with no indication that the user can choose another language. This creates a locale/language constraint that is not documented as optional or justified as region-specific.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The natural-language strings in the configuration file are entirely in Chinese and instruct the user in that language, without offering any language choice or documenting a locale-specific purpose. This can violate language/locale policy when a skill imposes a specific language on users by default.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The file’s user-facing description, prompts, and workflow are entirely in Chinese and present the skill as operating only in that language. Under the policy, forcing a specific language without user choice is a natural-language policy violation unless the locale restriction is explicitly documented and justified, which is not present here.

Content

No source excerpt is available for this finding.

Tainted flow: 'output_file' from input (line 228, user input) → open (file write)

Medium
Category
Data Flow
Confidence
95% confidence
Finding

The filename is built directly from user-controlled input (relic_name) and then passed to open() for writing. An attacker can supply path traversal sequences or special path components to cause files to be written outside the intended directory, potentially overwriting arbitrary files the user has permission to modify.

Content

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

python
output_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
    output_file = os.path.join(output_dir, f"讲解词_{relic_name}.txt")
    
    with open(output_file, "w", encoding="utf-8") as f:
        f.write(narration)
    
    return output_file

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
84% confidence
Finding

The module docstrings, comments, and user-visible exception messages are all written in Chinese, which effectively imposes a single language on users interacting with these messages. There is no indication of user opt-in, language selection, or a documented justification that this skill is intentionally limited to a Chinese-speaking context.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The function sends the provided messages to a remote endpoint and includes an API key in the Authorization header. Although the docstring says it calls an API, there is no confirmation prompt, logging, or explicit user-facing warning that user content will be transmitted off-system.

Content

No source excerpt is available for this finding.

Overly Broad Trigger

Low
Category
Trigger Abuse
Confidence
88% confidence
Finding

The trigger '讲解' is very short and generic, making accidental invocation plausible. In this skill context, unintended activation matters because it may prompt collection of inputs and eventual outbound API usage, though the impact is somewhat limited by the interactive workflow.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Low
Category
Not specified by scanner
Confidence
91% confidence
Finding

The manifest description at L003 says the user only needs to provide a cultural relic name and API information, and L064 repeats a simplified flow. However, the usage section at L123-L128 describes additional required or interactive inputs such as museum and audience type, which contradicts the stated minimal-input behavior.

Content

No source excerpt is available for this finding.

Overly Broad Trigger

Low
Category
Trigger Abuse
Confidence
88% confidence
Finding

The trigger '介绍' is a common conversational word that can match many unrelated user requests. This broad matching raises the chance the skill runs unexpectedly and collects or transmits content outside the user's intent.

Content

No source excerpt is available for this finding.

Overly Broad Trigger

Low
Category
Trigger Abuse
Confidence
86% confidence
Finding

The trigger '说说' is especially broad and conversational, so it is likely to collide with normal chat. While not severe on its own, it increases the probability of unintended use of local config and network capabilities.

Content

No source excerpt is available for this finding.

Overly Broad Trigger

Low
Category
Trigger Abuse
Confidence
87% confidence
Finding

The trigger '解说' is generic and may activate on unrelated requests for explanation or commentary. In combination with external API usage, this can result in unnecessary or unexpected data transmission.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Low
Category
Not specified by scanner
Confidence
63% confidence
Finding

The module docstring claims the style judgment is '用户不可见风格判断过程', but the program visibly collects audience data, performs a distinct style-judgment step, and prints confirmation context before generation. While the exact style label is not printed, the documentation overstates invisibility of this processing path relative to the implemented interactive flow.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Low
Category
Not specified by scanner
Confidence
86% confidence
Finding

The manifest describes a skill that generates a 400-500字 professional讲解词 from user-provided relic and API information, but it does not mention persisting output to disk. The implementation unconditionally saves the generated narration as a text file, which expands behavior beyond pure generation/output.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.