Back to skill

Security audit

Huo15 Img Prompt

Security checks for vulnerabilities and agentic risk

Overview

This appears to be a real image-prompt toolkit, but it needs Review because some integrations can send local files, prompts, images, and API keys to network services with weak scoping.

Install only if you are comfortable with a creative toolkit that can call paid external model APIs, persist project data under ~/.huo15, and expose MCP tools to your agent. Avoid registering the MCP server in untrusted or shared agent environments, do not use review_image on arbitrary paths, keep ANTHROPIC_BASE_URL unset unless it points to a trusted HTTPS gateway, and run the Web UI only on loopback with non-sensitive character data.

Vulnerability Patterns
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
  • 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 (4)

T05 · Unauthorized Access and Privilege Escalation

Error
Location
scripts/image_review.py:130
Finding

Arbitrary Local File Read and Upload Through the MCP Image Review Tool

Content
View full analysis
Dict: from image_review import review_image return review_image( args["image"], prompt=args.get("prompt", ""), quick=args.get("quick", False), ) ``` ```python # scripts/image_review.py:132-151 if src.startswith(("http://", "https://")): req = Request(src, headers={"User-Agent": "huo15-review/1.0"}) with urlopen(req, timeout=30) as r: blob = r.read() else: with open(os.path.expanduser(src), "rb") as f: blob = f.read() if blob[:8] == b"\x89PNG\r\n\x1a\n": media = "image/png" elif blob[:3] == b"\xff\xd8\xff": media = "image/jpeg" elif blob[:6] in (b"GIF87a", b"GIF89a"): media = "image/gif" elif blob[:4] == b"RIFF" and blob[8:12] == b"WEBP": media = "image/webp" else: media = "image/png" return base64.b64encode(blob).decode("ascii"), media ``` ```python # scripts/image_review.py:161-207 img_b64, media_type = load_image_b64(image_src) user_content = [] user_content.append({ "type": "image", "source": { "type": "base64", "media_type": media_type, "data": img_b64, }, }) body = { "model": model, "max_tokens": max_tokens, "system": [ ...[truncated 2556 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Error
Location
scripts/claude_polish.py:42
Finding

API Credentials and Private Content Can Be Forwarded to an Unvalidated Anthropic Base URL

Content
View full analysis
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/doctor.py:106
Finding

Diagnostic Output Discloses API-Key Prefixes

Content
View full analysis
Dict: out = {"category": "api_keys", "items": []} for env, desc, required in API_KEYS: item = {"env": env, "desc": desc, "required": required} val = os.environ.get(env, "") if val: item["status"] = "ok" item["msg"] = f"Configured ({val[:8]}...)" else: item["status"] = "fail" if required else "warn" item["msg"] = "Not configured" out["items"].append(item) return out ``` ### Technical Analysis The diagnostic command reads each configured provider credential and includes its first eight characters in the result. The returned structure is used by both terminal output and JSON output. Although this is not the complete secret, eight characters can reveal credential type, environment, tenant-specific prefixes, or enough distinguishing information to correlate a credential across logs. Diagnostic output is commonly copied into tickets, CI artifacts, chat conversations, recordings, and support bundles. The diagnostic function only needs to report whether a value exists. Revealing any portion of the secret is unnecessary for its declared health-check function. ### Attack Path 1. A user configures one or more provider API keys. 2. The user or CI system runs `doctor.py` or `doctor.py -j`. 3. The first eight characters of every configured credential are written to the terminal or JSON output. 4. The output is retained in build logs, uploaded as an artifact, copied into a support issue, or shared with another person. 5. An unauthorized party obtains partial secret material and can identify or correlate the credential. ### Impact Assessment This issue does not independently reveal complete credentials ...[truncated 327 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/web_ui.py:314
Finding

Unauthenticated Web API Exposes Persisted Character Data Through Wildcard CORS

Content
View full analysis
Remediation
View remediation
Vulnerability Patterns
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • Taint TrackingDirect Taint Flow, Variable-Mediated Taint Flow, Credential Exfiltration Chain
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
Findings (98)

Tainted flow: 'req' from os.environ.get (line 131, credential/environment) → urllib.request.urlopen (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/auto_iterate.py (reported line 143)May include surrounding context.

python
)

    try:
        with urlopen(req, timeout=60) as r:
            resp = json.loads(r.read().decode("utf-8"))
    except HTTPError as e:
        raise RuntimeError(f"Claude HTTP {e.code}: {e.read().decode('utf-8', errors='replace')}")

Tainted flow: 'req' from os.environ.get (line 241, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Confidence
97% confidence
Finding

The request destination is derived from the environment-controlled ANTHROPIC_BASE_URL and then sent with the x-api-key header via urlopen. If an attacker can influence the environment, they can redirect requests to an arbitrary host and exfiltrate the Anthropic API key and all prompt data; this is especially risky because the tool is explicitly designed to send user content to that endpoint.

Content

Scanner excerpt · scripts/claude_polish.py (reported line 195)May include surrounding context.

python
method="POST",
    )
    try:
        with urlopen(req, timeout=60) as r:
            return json.loads(r.read().decode("utf-8"))
    except HTTPError as e:
        raise RuntimeError(f"Claude HTTP {e.code}: {e.read().decode('utf-8', errors='replace')}")

Tainted flow: 'req' from os.environ.get (line 241, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Confidence
98% confidence
Finding

This second API call has the same issue: ANTHROPIC_BASE_URL can redirect authenticated traffic to an attacker-controlled server, leaking credentials and prompt content. Because the script supports both suggestion and full-polish flows, the exposure affects multiple code paths and increases the chance of secret exfiltration in normal use.

Content

Scanner excerpt · scripts/claude_polish.py (reported line 253)May include surrounding context.

python
)

    try:
        with urlopen(req, timeout=120) as r:
            return json.loads(r.read().decode("utf-8"))
    except HTTPError as e:
        err_body = e.read().decode("utf-8", errors="replace")

Tainted flow: 'req' from os.environ.get (line 216, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Confidence
90% confidence
Finding

The service probe URLs are sourced from environment variables and then requested over the network without validation. In practice this enables SSRF-style behavior against arbitrary endpoints if COMFYUI_URL or SDWEBUI_URL is attacker-controlled, which is more relevant in shared or automated agent environments than on a single-user local machine.

Content

Scanner excerpt · scripts/doctor.py (reported line 138)May include surrounding context.

python
from urllib.parse import urljoin
            full = urljoin(url, probe_path)
            req = Request(full)
            with urlopen(req, timeout=2) as r:
                item["status"] = "ok"
                item["msg"] = f"{r.status} {r.reason}"
        except (HTTPError, URLError, socket.timeout, ConnectionResetError, OSError) as e:

Tainted flow: 'req' from os.environ.get (line 216, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Confidence
95% confidence
Finding

This code transmits the Anthropic API key from the environment to an external service and performs a live outbound request. Although this is an intended connectivity test, it still causes credential-backed network egress and billing/activity without a strong explicit consent prompt at runtime, which matters in agent/skill contexts where execution may be less visible.

Content

Scanner excerpt · scripts/doctor.py (reported line 226)May include surrounding context.

python
},
            method="POST",
        )
        with urlopen(req, timeout=15) as r:
            resp = json.loads(r.read().decode("utf-8"))
        if "content" in resp:
            out["status"] = "ok"

Tainted flow: 'req' from os.environ.get (line 195, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Confidence
97% confidence
Finding

The script fetches arbitrary user-supplied URLs with urllib before sending the image onward for analysis. This creates an SSRF-style primitive: a caller can make the process issue network requests to internal services, cloud metadata endpoints, or other sensitive hosts, and the response body is then consumed by the script. In an agent/automation context, that is dangerous because the skill is explicitly designed to accept remote URLs, so untrusted input directly drives outbound network access.

Content

Scanner excerpt · scripts/image_review.py (reported line 134)May include surrounding context.

python
"""返回 (base64_string, media_type)。"""
    if src.startswith(("http://", "https://")):
        req = Request(src, headers={"User-Agent": "huo15-review/1.0"})
        with urlopen(req, timeout=30) as r:
            blob = r.read()
    else:
        with open(os.path.expanduser(src), "rb") as f:

Tainted flow: 'req' from os.environ.get (line 195, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Confidence
93% confidence
Finding

The destination of the API call is taken from the ANTHROPIC_BASE_URL environment variable and used directly for an authenticated request carrying the API key, image data, and prompt text. If that environment variable is altered by a local attacker, poisoned runtime, or unsafe deployment, the script will exfiltrate credentials and user content to an attacker-controlled endpoint. This is a real trust-boundary issue even though the source is an environment variable rather than direct user input.

Content

Scanner excerpt · scripts/image_review.py (reported line 207)May include surrounding context.

python
)

    try:
        with urlopen(req, timeout=180) as r:
            return json.loads(r.read().decode("utf-8"))
    except HTTPError as e:
        err_body = e.read().decode("utf-8", errors="replace")

Tainted flow: 'req' from os.environ.get (line 130, credential/environment) → urllib.request.urlopen (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/storyboard.py (reported line 141)May include surrounding context.

python
method="POST",
    )
    try:
        with urlopen(req, timeout=120) as r:
            resp = json.loads(r.read().decode("utf-8"))
    except HTTPError as e:
        raise RuntimeError(f"Claude HTTP {e.code}: {e.read().decode('utf-8', errors='replace')}")

Tainted flow: 'req' from os.environ.get (line 179, credential/environment) → urllib.request.urlopen (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/style_learn.py (reported line 112)May include surrounding context.

python
method="POST",
    )
    try:
        with urlopen(req, timeout=120) as r:
            resp = json.loads(r.read().decode("utf-8"))
    except HTTPError as e:
        raise RuntimeError(f"Claude HTTP {e.code}: {e.read().decode('utf-8', errors='replace')}")

Tainted flow: 'req' from os.environ.get (line 179, credential/environment) → urllib.request.urlopen (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/style_learn.py (reported line 190)May include surrounding context.

python
method="POST",
    )
    try:
        with urlopen(req, timeout=60) as r:
            resp = json.loads(r.read().decode("utf-8"))
    except HTTPError as e:
        raise RuntimeError(f"Claude HTTP {e.code}: {e.read().decode('utf-8', errors='replace')}")

MCP Config Access

High
Category
Agent Snooping
Confidence
95% confidence
Finding

The document instructs users to register an MCP server by editing ~/.claude/mcp.json, which grants the Claude Code environment a new executable integration. While presented as normal setup, this expands the trusted execution surface and, if the referenced script path or repository contents are tampered with, could lead to unintended code execution within the user's development environment.

Content

Scanner excerpt · RECIPES.md (reported line 210)May include surrounding context.

食谱 4:Claude Code 工作流(IDE 内调用)

注册 MCP server 到 ~/.claude/mcp.json:

json
{

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding

This mismatch specifically notes undisclosed network rendering requests and file-saving behavior against a description framed as prompt tooling. Hidden outbound API calls and local file writes are security-relevant because they may leak user content, consume paid credentials, or persist data unexpectedly.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
86% confidence
Finding

This mismatch specifically notes undisclosed network rendering requests and file-saving behavior against a description framed as prompt tooling. Hidden outbound API calls and local file writes are security-relevant because they may leak user content, consume paid credentials, or persist data unexpectedly.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
84% confidence
Finding

This mismatch specifically notes undisclosed network rendering requests and file-saving behavior against a description framed as prompt tooling. Hidden outbound API calls and local file writes are security-relevant because they may leak user content, consume paid credentials, or persist data unexpectedly.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
92% confidence
Finding

This mismatch specifically notes undisclosed network rendering requests and file-saving behavior against a description framed as prompt tooling. Hidden outbound API calls and local file writes are security-relevant because they may leak user content, consume paid credentials, or persist data unexpectedly.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding

This mismatch specifically notes undisclosed network rendering requests and file-saving behavior against a description framed as prompt tooling. Hidden outbound API calls and local file writes are security-relevant because they may leak user content, consume paid credentials, or persist data unexpectedly.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding

This mismatch specifically notes undisclosed network rendering requests and file-saving behavior against a description framed as prompt tooling. Hidden outbound API calls and local file writes are security-relevant because they may leak user content, consume paid credentials, or persist data unexpectedly.

Content

No source excerpt is available for this finding.

Vague Triggers

High
Category
Not specified by scanner
Confidence
96% confidence
Finding

The aliases and trigger phrases are extremely broad, including generic terms like 文生图, 文生视频, 提示词增强, storyboard, brand kit, and style learn. Over-broad triggers can cause accidental invocation on ordinary user requests, leading the agent to access files, persistent storage, MCP config paths, or networked backends without the user's explicit intent to use this specific skill.

Content

No source excerpt is available for this finding.

MCP Config Access

High
Category
Agent Snooping
Confidence
95% confidence
Finding

The skill instructs users or agents to register a server in ~/.claude/mcp.json, which is a sensitive agent-configuration file. Referencing and potentially modifying MCP configuration is security-relevant because it can alter trusted tool availability and expand future execution capabilities beyond a single invocation.

Content

Scanner excerpt · SKILL.md (reported line 149)May include surrounding context.

md
| 7 | `image_review.py` | Claude Vision 五维评审 | `image_review.py img.png -p "原 prompt"` |
| 8 | `auto_iterate.py` | 闭环自动迭代 | `auto_iterate.py "持剑女侠" -p 赛博朋克 --backend dalle --target 7.5` |
| 9 | `character.py` | 角色卡持久化 | `enhance_prompt.py "新场景" --char 银发机甲少女` |
| 10 | `mcp_server.py` | MCP stdio server | `python3 mcp_server.py`(注册到 ~/.claude/mcp.json) |
| 11 | `web_ui.py` | 本地 Web UI | `python3 web_ui.py`(http://127.0.0.1:7155) |
| 12 | `storyboard.py` ⭐v3.0 | 剧本→关键帧+转场视频脚本包 | `storyboard.py "..." -p 电影感 --scenes 6 --output ./story` |
| 13 | `brand_kit.py` ⭐v3.0 | 品牌套件持久化 | `enhance_prompt.py "..." --brand-kit huo15` |

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 150)May include surrounding context.

md
| 11 | `web_ui.py` | 本地 Web UI | `python3 web_ui.py`(http://127.0.0.1:7155) |

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 484)May include surrounding context.

md
| 11 | `web_ui.py` | 本地 Web UI | `python3 web_ui.py`(http://127.0.0.1:7155) |

Vague Triggers

High
Category
Not specified by scanner
Confidence
97% confidence
Finding

The documentation instructs the agent to auto-trigger on broad keyword matches such as '赛博朋克', which normal users may mention casually. Automatic activation based on generic content is dangerous because it can bypass user intent and launch privileged tooling or external service calls in response to benign conversation.

Content

No source excerpt is available for this finding.

MCP Config Access

High
Category
Agent Snooping
Confidence
95% confidence
Finding

The explicit JSON example for ~/.claude/mcp.json shows direct integration into the agent's MCP configuration, a high-trust control surface. In a skill context, any access path to agent configuration increases the risk of toolchain persistence or privilege expansion if acted on automatically.

Content

Scanner excerpt · SKILL.md (reported line 466)May include surrounding context.

让 Claude Code / Cursor / Cline / Continue.dev 直接调用 9 个工具:

json
// ~/.claude/mcp.json
{
  "mcpServers": {
    "huo15-img-prompt": {

External Model or Provider Selection

High
Category
Excessive Agency
Confidence
90% confidence
Finding

Skill selects an external model or provider that may use a different account or billing plan than the operator expects. Undisclosed model switches can cause unexpected cost or quota consumption.

Content

Scanner excerpt · scripts/claude_polish.py (reported line 14)May include surrounding context.

python
调用:
  claude_polish.py "一个温柔的女孩在花丛中"
  claude_polish.py "赛博朋克猫" --model claude-sonnet-4-5
  claude_polish.py "敦煌神女" --include-safety   # 同时跑 safety_lint
  claude_polish.py "汉服少女" -j > polished.json
  claude_polish.py "雪山下的小屋" --pipe         # 输出可直接喂给 enhance_prompt.py 的 CLI

External Model or Provider Selection

High
Category
Excessive Agency
Confidence
90% confidence
Finding

Skill selects an external model or provider that may use a different account or billing plan than the operator expects. Undisclosed model switches can cause unexpected cost or quota consumption.

Content

Scanner excerpt · scripts/claude_polish.py (reported line 411)May include surrounding context.

python
epilog="""
示例:
  claude_polish.py "一个温柔的女孩在花丛中"
  claude_polish.py "赛博朋克猫" --model claude-sonnet-4-6
  claude_polish.py "敦煌神女" -j > polished.json
  claude_polish.py "雪山下的小屋" --pipe   # 输出可直接喂给 enhance_prompt.py 的命令

Static analysis

No suspicious patterns detected.