Back to skill

Security audit

suno-music.skill

Security checks for vulnerabilities and agentic risk

Overview

The skill mostly matches a Suno music tool, but it has under-disclosed file-upload and credential-forwarding behavior that users should review before installing.

Review this skill before installing. It needs a Suno API key and sends prompts, lyrics, task IDs, and related media data to external services. Only use SUNO_BASE_URL with a trusted HTTPS endpoint, and avoid the upload-file command unless you intentionally selected a non-sensitive audio file and accept sending it to the external upload host.

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 (2)

T09 · Insecure Skill Coding Practices

Error
Location
scripts/suno_api.py:28
Finding

Bearer Credential Disclosure Through an Unrestricted Custom API Endpoint

Content
View full analysis

Vulnerability Details

File Location: scripts/suno_api.py, lines 28-43
Vulnerability Type: Unrestricted credential forwarding to a configurable network destination
Risk Level: High

Vulnerable Code

python
BASE_URL = os.getenv("SUNO_BASE_URL", "https://api.sunoapi.org")
API_KEY = os.getenv("SUNO_API_KEY", "")

VALID_MODELS = ["V4", "V4_5", "V4_5ALL", "V4_5PLUS", "V5", "V5_5"]


def _headers() -> dict:
    h = {"Content-Type": "application/json"}
    if API_KEY:
        h["Authorization"] = f"Bearer {API_KEY}"
    return h


def _post(path: str, data: dict) -> dict:
    r = requests.post(f"{BASE_URL}{path}", headers=_headers(), json=data, timeout=120)
    r.raise_for_status()
    return r.json()


def _get(path: str, params: dict = None) -> dict:
    r = requests.get(f"{BASE_URL}{path}", headers=_headers(), params=params, timeout=30)
    r.raise_for_status()
    return r.json()

Technical Analysis

The API destination is taken directly from the SUNO_BASE_URL environment variable without validating its scheme or hostname. Both _post() and _get() unconditionally use _headers(), which adds the SUNO_API_KEY value as a bearer credential whenever the variable is populated.

Consequently, changing SUNO_BASE_URL causes the Suno credential to be forwarded to the selected destination. There is no restriction to https://api.sunoapi.org, no HTTPS-only policy, no trusted-host allowlist, and no separation between the credential for the official service and credentials intended for custom endpoints.

Although custom endpoint support is documented, transmitting the official API credential to any configured host is not necessary to support that feature safely. A custom endpoint could instead use a separate credential or require explicit authorization before credential forwarding.

Attack Path

  1. An attacker, malicious wrapper, compromised execution environment, or unsafe conf ...[truncated 1256 chars]
Remediation
View remediation

Remediation Suggestions

  1. Require SUNO_BASE_URL to use HTTPS and reject URLs containing unexpected schemes, user information, fragments, or malformed hostnames.
  2. Maintain an explicit allowlist for endpoints that may receive SUNO_API_KEY, with api.sunoapi.org as the default trusted host.
  3. Do not forward the official Suno bearer token to a custom host automatically.
  4. Introduce a separate variable such as SUNO_CUSTOM_API_KEY for custom endpoints.
  5. If credential forwarding to a non-default endpoint is required, require an explicit opt-in flag and display the destination hostname before transmission.
  6. Resolve redirects carefully and prevent authorization headers from being retained when a redirect crosses origins.
  7. Fail closed when URL validation fails, and avoid including credentials or sensitive response content in error output.

T05 · Unauthorized Access and Privilege Escalation

Warning
Location
scripts/suno_api.py:312
Finding

Undocumented Arbitrary Local File Upload to a Secondary Domain

Content
View full analysis

Vulnerability Details

File Location: scripts/suno_api.py, lines 312-332
Vulnerability Type: Excessive local file-read and external upload capability
Risk Level: Medium

Vulnerable Code

python
FILE_UPLOAD_BASE = "https://sunoapiorg.redpandaai.co"


def upload_file_stream(file_path="", upload_path="audio/uploads"):
    """POST /api/file-stream-upload - file stream upload to Suno platform

    Returns: {"success":true, "data": {"downloadUrl": "...", ...}}
    """
    headers = _headers()
    headers.pop("Content-Type", None)  # Allow requests to set multipart boundary
    with open(file_path, "rb") as f:
        r = requests.post(
            f"{FILE_UPLOAD_BASE}/api/file-stream-upload",
            headers=headers,
            data={"uploadPath": upload_path},
            files={"file": (os.path.basename(file_path), f, "audio/mpeg")},
            timeout=120,
        )
    r.raise_for_status()
    return r.json()

The capability is exposed by the CLI at lines 424-426 and invoked at lines 517-530:

python
# upload-file
uf = add(s, "upload-file", help="File-stream upload of a local file to Suno platform")
uf.add_argument("--path", required=True, help="Local file path")
uf.add_argument("--upload-path", default="audio/uploads", help="Upload path")
python
elif args.cmd == "upload-file":
    if not os.path.exists(args.path):
        print(f"File does not exist: {args.path}", flush=True); sys.exit(1)
    print(f"Uploading: {args.path}", flush=True)
    r = upload_file_stream(args.path, args.upload_path)
    if r.get("success") and r.get("data", {}).get("downloadUrl"):
        url = r["data"]["downloadUrl"]
        print(f"Upload successful!", flush=True)
        print(f"  URL: {url}", flush=True)
        print(f"  Size: {r['data'].get('fileSize','?')} bytes", flush=True)
    else:
        print(json.dumps(r, ensure_ascii=False, indent=2), flush=Tr
...[truncated 2775 chars]
Remediation
View remediation

Remediation Suggestions

  1. Remove the local-file upload command unless it is necessary for the declared Skill functionality.
  2. Document the upload capability, external recipient, transmitted metadata, credential use, and retention implications prominently in SKILL.md.
  3. Require explicit user confirmation that includes the canonical local path, destination hostname, detected file type, and file size before transmission.
  4. Restrict uploads to a dedicated user-approved directory and resolve paths with realpath() before enforcing the boundary.
  5. Require a regular file and reject symbolic links, devices, sockets, directories, and path traversal outside the approved root.
  6. Validate file signatures and supported audio formats rather than relying on filename extensions or assigning every file the audio/mpeg type.
  7. Apply conservative upload-size limits.
  8. Use a narrowly scoped, short-lived upload token instead of forwarding the primary SUNO_API_KEY to the secondary domain.
  9. Verify and document the ownership and trust relationship of sunoapiorg.redpandaai.co.
  10. Consider a two-stage workflow in which the user explicitly selects an audio artifact through a constrained file-selection mechanism rather than supplying an arbitrary filesystem path.
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • 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 Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (10)

Tainted flow: 'BASE_URL' from os.getenv (line 37, credential/environment) → requests.post (network output)

Critical
Category
Data Flow
Confidence
95% confidence
Finding

The code takes BASE_URL directly from an environment variable and uses it to construct outbound requests while automatically attaching the bearer token in the Authorization header. If an attacker can influence the runtime environment, they can redirect all API calls and the SUNO_API_KEY to an attacker-controlled server, resulting in credential exfiltration and unintended data transfer.

Content

Scanner excerpt · scripts/suno_api.py (reported line 51)May include surrounding context.

python
def _post(path: str, data: dict) -> dict:
    r = requests.post(f"{BASE_URL}{path}", headers=_headers(), json=data, timeout=120)
    r.raise_for_status()
    return r.json()

Tainted flow: 'BASE_URL' from os.getenv (line 37, credential/environment) → requests.get (network output)

Critical
Category
Data Flow
Confidence
95% confidence
Finding

As with POST requests, GET requests are sent to a host derived from an untrusted environment variable and include the bearer token header. A malicious or compromised environment can therefore redirect status/credit queries to an attacker-controlled endpoint and harvest credentials or metadata.

Content

Scanner excerpt · scripts/suno_api.py (reported line 57)May include surrounding context.

python
def _get(path: str, params: dict = None) -> dict:
    r = requests.get(f"{BASE_URL}{path}", headers=_headers(), params=params, timeout=30)
    r.raise_for_status()
    return r.json()

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding

The declared description covers core Suno music generation, status checking, and downloading audio/cover art, which the code does support. However, the actual code exposes substantially broader capabilities that are not declared and go beyond simple AI song generation. It can upload local files to an external service, perform audio transformations and editing workflows, separate vocals/stems, generate MIDI, create MP4 videos, and manage persona-related features. These are material undeclared capabilities rather than incidental implementation details, so the description does not accurately represent the full behavior.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

High
Category
Not specified by scanner
Confidence
95% confidence
Finding

The upload_file_stream function can read any local path and transmit its contents to a remote server, which is classic local-file exfiltration capability. Because the skill is presented as a music-generation tool rather than a local file transfer utility, this capability is more dangerous: users and orchestrators may not expect that invoking the skill could access arbitrary local files.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
90% confidence
Finding

The skill exposes capabilities that rely on environment variables and outbound network access, but it does not declare any explicit tool scope or permissions boundary. That makes the skill harder to audit and can allow overbroad execution in environments where the agent may have more access than intended, including use of API keys and arbitrary external requests.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

The trigger language is very broad (e.g., create music, generate songs, compose tracks, produce audio content), which can cause the skill to activate for many common user requests without clear user intent to use this specific external service. Overbroad activation increases the chance of unintended API usage, data disclosure to a third party, and surprising behavior in multi-skill environments.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

The manifest focuses on generating songs and downloading audio/cover art, but this implementation adds separate capabilities for MIDI generation, music video generation, vocal separation, mashups, persona creation, and cover-art generation. These are meaningfully broader production/editing features than the narrower manifest description suggests.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

The skill metadata emphasizes music generation and related API operations, but this code also uploads arbitrary local files specified by path to an external service. In an agent context, that creates a powerful exfiltration primitive because a user or prompt-influenced workflow could cause sensitive local files to be sent off-host under the guise of normal skill usage.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The code uploads local files to a third-party service without any explicit privacy notice, destination disclosure, or user confirmation in the CLI flow. This can lead to unintentional transfer of personal, proprietary, or regulated data, especially in an agent setting where users may not realize local filesystem content is being sent externally.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
91% confidence
Finding

The module docstring and CLI help/output strings are written entirely in Chinese, which imposes a specific language on all users. There is no opt-in, alternative locale, or documentation that this skill is intentionally region-specific, so it conflicts with the language/locale policy described in the audit rules.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.