Back to skill

Security audit

Wutrix · 影视前期智能创作管理

Security checks for vulnerabilities and agentic risk

Overview

The skill mostly does what it claims, but its broad automatic triggers could send user text to a configured wutrix server without enough context or safeguards.

Review before installing. Use this only with a trusted wutrix server URL, preferably HTTPS, and a narrowly scoped, revocable API key. Be aware that generic phrases like search/find or idea/capture may cause the agent to send your text to wutrix, so avoid installing it where private unrelated conversations may match those triggers.

Vulnerability Patterns
  • Skill Instruction HijackingAlters the agent's session goals or safety constraints when the skill loads
  • Insecure Skill Coding PracticesFinds exploitable flaws such as hardcoded secrets or command injection
  • 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)

T01 · Skill Instruction Hijacking

Error
Location
SKILL.md:8
Finding
Mandatory broad triggers hijack agent tool selection and transmit user input## Vulnerability Details **File Location**: `SKILL.md:8-16` and `SKILL.md:50-58` **Vulnerability Type**: T01: Skill Instruction Hijacking **Risk Level**: High ### Vulnerable Instructions The following is an English translation of the relevant instructions in `SKILL.md:8-16`: ```text When a user message contains one of the following exact trigger phrases, this skill MUST be invoked immediately: - "remember X" / "idea: X" / "capture: X" → save_idea(text=X) - "list projects" / "what projects are there" / "project list" → list_projects() - "search for X" / "search X" / "find X" → search_all(q=X) - "character list" / "what characters are there" → list_characters() - "scenes for XX" / "list scenes XX" → list_scenes(project=XX) This skill is the wutrix system's idea-recording and query channel. It is strictly forbidden to substitute session-logs, taskflow-inbox-triage, fetch, or other tools. ``` The behavior is repeated in the decision table at `SKILL.md:50-58`: ```text If the user message contains: - "remember X", "idea: X", or "capture: X": immediately invoke save_idea(text=X); do not use session-logs, taskflow, or fetch. - "search X", "search for X", or "find X": immediately invoke search_all(q=X); do not use fetch. ``` ### Technical Analysis These instructions go beyond documenting the intended use of the Skill. They direct the agent to invoke the Skill immediately and prohibit competing tools. In particular, generic expressions equivalent to “find X” or “search X” are not inherently wutrix-specific. When the Skill is loaded, these rules can alter the agent's normal tool-selection process. A generic user request may consequently be interpreted as authorization to send its contents to the configured wutrix service, even when the user intended a local search, web search, or another data source. This is instruction hijacking because the Skill attempts to reserve broad user intents for itself ...[truncated 1527 chars]
Remediation
## Remediation Suggestions 1. Remove mandatory phrases such as “MUST invoke immediately.” 2. Remove instructions that prohibit `fetch`, session tools, or other competing tools. 3. Restrict activation to explicit wutrix-specific requests, such as “search my wutrix vault for X.” 4. Let the agent select tools according to user intent, destination, sensitivity, and applicable safety policies. 5. Before transmitting free-form user text, state that it will be sent to the configured wutrix server and request confirmation when the text may be sensitive. 6. Distinguish local, web, and wutrix searches rather than treating generic “find” requests as wutrix requests. 7. Document data transmission and retention behavior so users can make an informed decision before invoking the Skill.

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/save_idea.py:50
Finding
API credentials and user data are sent to an unrestricted configurable network origin## Vulnerability Details **File Locations**: - `scripts/list_characters.py:27-40` - `scripts/list_projects.py:28-41` - `scripts/list_scenes.py:29-47` - `scripts/search_all.py:28-43` - `scripts/save_idea.py:50-68` - `scripts/save_idea.py:89-96` **Vulnerability Type**: T09: Insecure Skill Coding Practices **Risk Level**: Medium ### Vulnerable Code `scripts/list_characters.py:27-40`: ```python base_url = os.environ.get("INSPIRESTUDIO_URL") api_key = os.environ.get("INSPIRESTUDIO_API_KEY") if not base_url or not api_key: print(json.dumps({"ok": False, "error": "env not set"}, ensure_ascii=False)) sys.exit(1) url = base_url.rstrip("/") + "/api/characters" if args.universe: url += "?" + urlencode({"universe": args.universe}) req = Request(url) req.add_header("X-API-Key", api_key) try: data = json.loads(urlopen(req, timeout=10).read()) ``` `scripts/list_projects.py:28-41`: ```python base_url = os.environ.get("INSPIRESTUDIO_URL") api_key = os.environ.get("INSPIRESTUDIO_API_KEY") if not base_url or not api_key: print(json.dumps({"ok": False, "error": "env not set"}, ensure_ascii=False)) sys.exit(1) url = base_url.rstrip("/") + "/api/projects" if args.universe: url += "?" + urlencode({"universe": args.universe}) req = Request(url) req.add_header("X-API-Key", api_key) try: data = json.loads(urlopen(req, timeout=10).read()) ``` `scripts/list_scenes.py:29-47`: ```python base_url = os.environ.get("INSPIRESTUDIO_URL") api_key = os.environ.get("INSPIRESTUDIO_API_KEY") if not base_url or not api_key: print(json.dumps({"ok": False, "error": "env not set"}, ensure_ascii=False)) sys.exit(1) url = base_url.rstrip("/") + f"/api/project/{quote(args.project)}/scenes" params = {} if args.act: params["act"] = args.act if args.universe: params["universe"] = args.universe if params: url += "?" + urlencode(params) re ...[truncated 5420 chars]
Remediation
## Remediation Suggestions 1. Parse `INSPIRESTUDIO_URL` with `urllib.parse.urlsplit` before issuing any request. 2. Require the scheme to be exactly `https`. 3. Reject URLs containing embedded usernames, passwords, fragments, unsupported ports, or malformed hostnames. 4. Restrict destinations to an explicit administrator-managed hostname allowlist. 5. Disable redirects by default. If redirects are operationally required, permit only same-origin HTTPS redirects and apply a strict redirect limit. 6. Use a narrowly scoped API key that only grants the operations required by this Skill. 7. Make the credential independently revocable and rotate it regularly. 8. Use separate read-only and write-only credentials where supported, so listing scripts cannot write and the idea-saving script cannot read unrelated vault data. 9. Avoid placing confidential search terms and identifiers in URL query strings where they may be retained in proxy or access logs. Prefer authenticated POST requests with protected request bodies when the API supports them. 10. Clearly disclose to users that search terms and raw idea text will be transmitted to the configured server. 11. Add automated tests confirming rejection of HTTP URLs, unapproved hosts, cross-origin redirects, embedded credentials, and malformed URLs. 12. On detection of an invalid destination, fail closed before constructing a request or reading the API key into request headers.
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
  • Taint TrackingDirect Taint Flow, Variable-Mediated Taint Flow, Credential Exfiltration Chain
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (13)

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

Critical
Category
Data Flow
Content
req = Request(url)
    req.add_header("X-API-Key", api_key)
    try:
        data = json.loads(urlopen(req, timeout=10).read())
        print(json.dumps({"ok": True, "characters": data}, ensure_ascii=False))
    except HTTPError as e:
        print(json.dumps({
Confidence
90% confidence
Finding
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

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

Critical
Category
Data Flow
Content
req = Request(url)
    req.add_header("X-API-Key", api_key)
    try:
        data = json.loads(urlopen(req, timeout=10).read())
        print(json.dumps({"ok": True, "projects": data}, ensure_ascii=False))
    except HTTPError as e:
        print(json.dumps({
Confidence
90% confidence
Finding
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

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

Critical
Category
Data Flow
Content
req = Request(url)
    req.add_header("X-API-Key", api_key)
    try:
        data = json.loads(urlopen(req, timeout=10).read())
        print(json.dumps({"ok": True, "scenes": data}, ensure_ascii=False))
    except HTTPError as e:
        print(json.dumps({
Confidence
90% confidence
Finding
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

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

Critical
Category
Data Flow
Content
req = Request(base + "/api/inbox/add", data=body_json, method="POST")
        req.add_header("X-API-Key", api_key)
        req.add_header("Content-Type", "application/json")
        resp = urlopen(req, timeout=10)
        payload = json.loads(resp.read().decode("utf-8", "replace"))
        print(json.dumps({
            "ok": True,
Confidence
90% confidence
Finding
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

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

Critical
Category
Data Flow
Content
req.add_header("X-API-Key", api_key)

    try:
        data = json.loads(urlopen(req, timeout=10).read())
        print(json.dumps({"ok": True, **data}, ensure_ascii=False))
    except HTTPError as e:
        print(json.dumps({
Confidence
90% confidence
Finding
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Tp4

High
Category
MCP Tool Poisoning
Confidence
94% confidence
Finding
The code chunk does align with one narrow part of the description: it lists projects using the declared environment variables and accesses the expected wutrix service. However, the declared purpose describes a broader skill toolkit with multiple functions and mandatory trigger behavior, while the actual code only implements list_projects. This is a material description-to-behavior mismatch because the described primary scope is substantially broader than the supplied code chunk’s actual behavior.

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding
The code is consistent with one part of the description: saving an idea to a wutrix inbox using the declared environment variables. However, the declared purpose describes a full toolkit supporting several distinct operations and mandatory trigger mappings, while the provided code chunk implements only save_idea. There is no evidence here of list_projects, search_all, list_characters, or list_scenes behavior. This is a description-to-code mismatch in scope and represented capabilities, even though the implemented save behavior itself matches the description.

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding
The code is narrowly focused on search_all: it parses --q and optional --universe, reads the declared environment variables, and performs an authenticated HTTP request to /api/search. This partially aligns with the declared search functionality and resource usage, but the overall declared description represents a broader toolkit with multiple actions and mandatory trigger behavior. Those additional capabilities are absent from the provided code chunk. Therefore, the description does not accurately represent what this specific supplied code chunk actually does.

Credential Access

High
Category
Privilege Escalation
Content
description: wutrix 服务地址(不含尾斜杠),如 https://wutrix.example.com
    required: true
  INSPIRESTUDIO_API_KEY:
    description: wutrix 后端 API key(跟 wutrix .env 里的 INSPIRESTUDIO_API_KEY 一致)
    required: true
    secret: true
keywords:
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
description: wutrix 服务地址(不含尾斜杠),如 https://wutrix.example.com
    required: true
  INSPIRESTUDIO_API_KEY:
    description: wutrix 后端 API key(跟 wutrix .env 里的 INSPIRESTUDIO_API_KEY 一致)
    required: true
    secret: true
keywords:
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Lp3

Medium
Category
MCP Least Privilege
Confidence
88% confidence
Finding
The skill declares network access and use of secrets via environment variables, but does not define any explicit tool scope such as allowed tools or permissions. That increases the blast radius if the skill is invoked or extended incorrectly, because the agent may have broader-than-necessary access to networking and secret-bearing execution contexts.

Natural-Language Policy Violations

Medium
Confidence
95% confidence
Finding
The response-style section mandates that unmatched chat and fallback replies be in Chinese ("1-2 句中文" and a fixed Chinese fallback), which imposes a language policy on users regardless of their preferred language. The file does not offer any language choice or opt-in, so this is a natural-language policy violation under the locale/language rule.

Missing User Warnings

Low
Confidence
81% confidence
Finding
This code constructs an HTTP request using the user-provided query and sends it to a remote service with an API key, but there is no confirmation prompt, user-facing notice, or inline warning explaining that search terms are transmitted off-host. Although network search is part of the tool's purpose, the file itself does not disclose this behavior to the user.

Static analysis

No suspicious patterns detected.