Back to skill

Security audit

AIML Music Generator

Security checks for vulnerabilities and agentic risk

Overview

This skill coherently generates music through AIMLAPI, but users should understand that prompts, lyrics, an API key, and downloaded audio pass through external network calls.

Install only if you are comfortable sending music prompts and lyrics to AIMLAPI and using an AIMLAPI_API_KEY from your environment. Avoid including private, secret, or sensitive text in lyrics or prompts, and prefer running it in a normal project directory with spending limits on the API key.

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/gen_music.py:50
Finding
Unvalidated API-Controlled Download URL Enables Blind SSRF and Resource Exhaustion<![CDATA[ ## Vulnerability Details **File Location**: `scripts/gen_music.py`, lines 50-54 and 91-107 **Vulnerability Type**: Unvalidated remote URL retrieval and unbounded response buffering **Risk Level**: Medium ### Vulnerable Code ```python def download_file(url: str, path: pathlib.Path, user_agent: str, verbose: bool): req = urllib.request.Request(url, headers={"User-Agent": user_agent}) if verbose: print(f"Downloading: {url}") with urllib.request.urlopen(req) as res: path.write_bytes(res.read()) ``` The URL passed to this function originates directly from the API response: ```python if status == "completed": audio_url = status_res.get("audio_url") if not audio_url and "audio_file" in status_res: audio_url = status_res["audio_file"].get("url") if not audio_url: # Some models might have it in result nesting audio_url = status_res.get("audio", {}).get("url") if not audio_url: raise SystemExit(f"Completed but no audio URL found: {status_res}") # Download out_dir = pathlib.Path(args.out_dir) out_dir.mkdir(parents=True, exist_ok=True) file_path = out_dir / f"music_{gen_id}.mp3" if args.verbose: print(f"Target path: {file_path}") download_file( audio_url, file_path, args.user_agent if hasattr(args, 'user_agent') else DEFAULT_USER_AGENT, args.verbose ) ``` ### Technical Analysis The script treats an `audio_url` supplied by AIMLAPI as trusted and passes it directly to `urllib.request.urlopen`. It does not validate the URL scheme, hostname, resolved IP address, port, or redirect destination. If AIMLAPI or its response path is compromised, the supplied URL can point to localhost, private network ranges, link-local services, cloud metadata endpoints, or another unintended network destination. This creates a blind server-side request forgery condition from the machine running the Skill. The response is also consumed using `res.rea ...[truncated 2231 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Allow only `https` download URLs. 2. Maintain an explicit allowlist of documented AIMLAPI media hostnames rather than accepting arbitrary domains. 3. Resolve the hostname and reject loopback, private, link-local, multicast, reserved, and unspecified IP addresses for both IPv4 and IPv6. 4. Revalidate the destination after every redirect, or disable redirects entirely. Reject redirects to a different origin or non-HTTPS scheme. 5. Set explicit connection and read timeouts. 6. Stream the response in bounded chunks instead of calling `res.read()` without a limit. 7. Enforce a maximum permitted file size using both `Content-Length` and an independent byte counter while streaming. 8. Validate the response content type against expected audio formats, while recognizing that content type alone is not a security boundary. 9. Download to a temporary file and atomically rename it after successful validation. Delete partial files when an error or size violation occurs. 10. Consider using a maintained HTTP client with explicit timeout, redirect, and streaming controls. ]]>

T09 · Insecure Skill Coding Practices

Note
Location
scripts/gen_music.py:34
Finding
Bearer API Key May Be Disclosed Through Cross-Origin HTTP Redirects<![CDATA[ ## Vulnerability Details **File Location**: `scripts/gen_music.py`, lines 34-45 **Vulnerability Type**: Sensitive authorization header exposed to an unvalidated redirect destination **Risk Level**: Low ### Vulnerable Code ```python def request(url: str, api_key: str, method: str = "GET", payload: dict = None) -> Any: data = json.dumps(payload).encode("utf-8") if payload else None headers = { "Authorization": f"Bearer {api_key}", "Content-Type": "application/json", "User-Agent": DEFAULT_USER_AGENT, } req = urllib.request.Request(url, data=data, headers=headers, method=method) try: with urllib.request.urlopen(req) as res: return json.loads(res.read().decode("utf-8")) except urllib.error.HTTPError as e: detail = e.read().decode("utf-8") raise SystemExit(f"API Error ({e.code}): {detail}") ``` ### Technical Analysis The script correctly obtains the API key from the `AIMLAPI_API_KEY` environment variable rather than hardcoding it. Sending that key to the declared AIMLAPI HTTPS endpoint is necessary for the Skill's functionality. However, the request relies on `urllib.request.urlopen` and its automatic redirect behavior without checking the redirect destination. The request includes the bearer credential in the `Authorization` header. Python's standard redirect handling can preserve ordinary request headers across redirects, including redirects to another host. Consequently, if `api.aimlapi.com`, its DNS resolution, its TLS-serving infrastructure, or an upstream component is compromised and responds with a cross-origin HTTP redirect, the client may send the bearer token to the redirected host. The initial endpoint is a fixed HTTPS URL, so exploitation requires compromise or malicious behavior in the trusted API path; this reduces likelihood but does not eliminate the credential-disclosure risk. ### Attack Path 1. The script creates a request to `https://api.aimlapi.c ...[truncated 1011 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Disable automatic redirects for authenticated API requests and handle redirect responses explicitly. 2. If redirects are required, allow them only when the destination retains the exact expected HTTPS origin. 3. Strip the `Authorization` header whenever the scheme, hostname, or effective port changes. 4. Reject HTTPS-to-HTTP redirects unconditionally. 5. Limit the number of accepted redirects to prevent redirect loops. 6. Keep the API base URL fixed and do not expose it as an unvalidated user-controlled option. 7. Ensure the API key has the narrowest available account permissions and spending limits. 8. Rotate the key immediately if logs or monitoring indicate that it may have been sent to an unexpected origin. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
Findings (5)

Lp3

Medium
Category
MCP Least Privilege
Confidence
95% confidence
Finding
The skill advertises capabilities that include environment access, file writing, and network use, but it does not declare any explicit tool scope such as permissions or allowed-tools. That creates an authorization and transparency gap: an agent may invoke the skill without clear policy constraints, while the script can still access an API key, write output files, and transmit user content to an external service.

Vague Triggers

Medium
Confidence
91% confidence
Finding
The description uses broad trigger language like requests for music, songs, or soundtracks with specific lyrics or styles, which can match many ordinary user prompts. In agent ecosystems, overly broad routing can cause unintended invocation of a networked skill, increasing the chance that user prompts or sensitive text are sent to an external provider without the user realizing it.

Missing User Warnings

Medium
Confidence
97% confidence
Finding
The README explains a network-based generation flow but does not disclose that prompts and lyrics are transmitted to an external API for processing. Because lyrics and prompts may contain copyrighted, personal, confidential, or sensitive content, lack of disclosure undermines informed consent and can lead to unintended data exfiltration to a third party.

External Transmission

Medium
Category
Data Exfiltration
Content
import urllib.request
from typing import Any

DEFAULT_BASE_URL = "https://api.aimlapi.com/v2"
DEFAULT_USER_AGENT = "openclaw-skill-aimlapi-music/1.1"

def parse_args() -> argparse.Namespace:
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Missing User Warnings

Medium
Confidence
91% confidence
Finding
The script sends user-supplied prompts and optional lyrics to a third-party service for processing, but it does not provide a clear user-facing disclosure at the point of transmission. Because prompts and lyrics may contain sensitive or copyrighted content, this creates a privacy and data-handling risk, especially in an agent context where users may not realize their content leaves the local environment.

Static analysis

No suspicious patterns detected.