Back to skill

Security audit

Media Gen

Security checks for vulnerabilities and agentic risk

Overview

This media-generation skill is mostly coherent, but its client has under-scoped network download and file-write behavior that users should review before installing.

Install only if you trust AIsa and are comfortable with prompts and the AISA_API_KEY being used for that service. Prefer the AISA_API_KEY environment variable over --api-key, run the script from a limited workspace, avoid sensitive --out paths, and review downloaded media behavior because returned URLs are fetched without host or size checks.

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/media_gen_client.py:105
Finding

Unrestricted Retrieval of Server-Supplied Media URLs

Content
View full analysis

Vulnerability Details

File Location: scripts/media_gen_client.py, lines 105-122; invoked with API-supplied URLs at lines 382-385, 407-410, and 450-454
Vulnerability Type: Unvalidated outbound request and unbounded file download
Risk Level: Medium

Vulnerable Code

python
def _download_to_file(url: str, out_path: str, timeout_s: int = 300) -> Dict[str, Any]:
    """
    Download a (possibly signed) URL to local file.
    Designed for OSS signed URLs returned by video generation tasks.
    """
    os.makedirs(os.path.dirname(out_path) or ".", exist_ok=True)
    req = urllib.request.Request(url, headers={"User-Agent": "AIsa-Media-Gen/1.0"})
    try:
        with urllib.request.urlopen(req, timeout=timeout_s) as resp, open(out_path, "wb") as f:
            total = 0
            while True:
                chunk = resp.read(1024 * 1024)  # 1MB
                if not chunk:
                    break
                f.write(chunk)
                total += len(chunk)
        return {"success": True, "saved_to": out_path, "bytes": total}
    except Exception as e:
        return {"success": False, "error": str(e), "url": url, "saved_to": out_path}

Representative call sites automatically pass URLs extracted from upstream responses:

python
out_path = args.out or _safe_filename("png")
dl = _download_to_file(urls[0], out_path)
_print_json({"success": dl.get("success", False), "route": route, "model": args.model,
             "url": urls[0], "saved_to": out_path, "images_returned": len(urls),
             "download": dl})
python
if video_url:
    out_path = args.out or _safe_filename("mp4")
    dl = _download_to_file(video_url, out_path)
    resp = {**resp, "download": dl}

Technical Analysis

The client trusts media URLs returned by the remote API and passes them directly to urllib.request.urlopen. It does not validate:

  • The URL scheme or require HTTPS.
  • The destination hostname or resolved IP address.
  • Whether ...[truncated 2035 chars]
Remediation
View remediation

Remediation Suggestions

  1. Parse every returned URL before use and permit only https URLs.
  2. Allowlist documented media-storage hostnames where operationally possible.
  3. Resolve the hostname and reject loopback, private, link-local, multicast, unspecified, and reserved addresses for both IPv4 and IPv6.
  4. Disable automatic redirects or validate every redirect target using the same scheme, hostname, and IP-address controls.
  5. Enforce an expected media content type, such as image/* or video/*, before writing the response.
  6. Enforce a configurable maximum download size using both Content-Length and a streaming byte counter.
  7. Download into a temporary file and atomically rename it only after successful validation and completion.
  8. Delete partial files when downloads fail.
  9. Consider requiring explicit user confirmation before downloading from a host outside an approved media-host allowlist.

T09 · Insecure Skill Coding Practices

Note
Location
scripts/media_gen_client.py:504
Finding

API Key Can Be Exposed Through Command-Line Arguments

Content
View full analysis

Vulnerability Details

File Location: scripts/media_gen_client.py, lines 60-64 and 504; unsafe usage is encouraged by SKILL.md, lines 49-50
Vulnerability Type: Insecure secret handling
Risk Level: Low

Vulnerable Code and Documentation

python
def _get_api_key(explicit: Optional[str] = None) -> str:
    api_key = explicit or os.environ.get("AISA_API_KEY")
    if not api_key:
        raise ValueError("AISA_API_KEY is required (env or --api-key).")
    return api_key
python
p.add_argument("--api-key", help="Override AISA_API_KEY")

The Skill instructions actively prefer explicit command-line authentication:

markdown
- `AISA_API_KEY` is required for AIsa-backed API access.
- Use repo-relative `scripts/` paths from the shipped package.
- Prefer explicit CLI auth flags when a script exposes them.

Technical Analysis

Supplying a secret through --api-key places it in the process argument vector. Depending on the operating system, runtime environment, and surrounding tooling, command-line arguments may be observable through process inspection, execution telemetry, audit logs, debugging output, job metadata, or shell history.

Supporting an explicit authentication override can occasionally be operationally useful, but recommending it over environment-based or protected secret input unnecessarily increases credential exposure. The API key is otherwise transmitted as a Bearer token only to fixed HTTPS URLs under api.aisa.one, which is consistent with the declared service functionality.

Attack Path

  1. A user follows the Skill documentation and invokes the client with --api-key.
  2. The shell may retain the complete command in its history.
  3. The secret also appears in the running process's argument vector.
  4. Another local user, monitoring agent, logging system, support bundle, or job telemetry collector records or reads the arguments.
  5. The observer recovers the API key and uses it against the AIsa A ...[truncated 566 chars]
Remediation
View remediation

Remediation Suggestions

  1. Remove the documentation recommendation to prefer explicit CLI authentication.
  2. Prefer AISA_API_KEY supplied through an appropriate secret manager or a narrowly scoped process environment.
  3. For interactive use, support secret entry through getpass.getpass() so the value is not echoed or stored in command history.
  4. Alternatively, support a credential file with restrictive permissions and validate that it is not group- or world-readable.
  5. If --api-key must remain for compatibility, mark it as deprecated and clearly warn that command-line secrets can be exposed through process listings, shell history, and logs.
  6. Recommend short-lived, least-privileged keys where the upstream service supports them.
  7. Ensure errors and diagnostic output never print the supplied key or complete authorization headers.
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Taint TrackingDirect Taint Flow, Variable-Mediated Taint Flow, Credential Exfiltration Chain
  • 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 (13)

Tainted flow: 'req' from os.environ.get (line 94, 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/media_gen_client.py (reported line 96)May include surrounding context.

python
req = urllib.request.Request(url, data=data, headers=all_headers, method=method.upper())
    try:
        with urllib.request.urlopen(req, timeout=timeout_s) as resp:
            raw = resp.read().decode("utf-8")
            return json.loads(raw) if raw else {}
    except urllib.error.HTTPError as e:

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

Critical
Category
Data Flow
Confidence
91% confidence
Finding

The client downloads arbitrary URLs returned by the remote API and writes the response to a local file without validating the scheme, host, content type, or size. If the upstream service is compromised or can be influenced to return attacker-controlled URLs, this creates an SSRF-style outbound fetch primitive and can also enable disk exhaustion or retrieval of unexpected internal resources.

Content

Scanner excerpt · scripts/media_gen_client.py (reported line 122)May include surrounding context.

python
os.makedirs(os.path.dirname(out_path) or ".", exist_ok=True)
    req = urllib.request.Request(url, headers={"User-Agent": "AIsa-Media-Gen/1.0"})
    try:
        with urllib.request.urlopen(req, timeout=timeout_s) as resp, open(out_path, "wb") as f:
            total = 0
            while True:
                chunk = resp.read(1024 * 1024)  # 1MB

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
89% confidence
Finding

The skill requires an API key and invokes a Python client for remote media generation, which implies access to environment variables and outbound network calls, yet it declares no explicit tool scope such as permissions or allowed-tools. This creates a least-privilege gap: an agent platform may grant broader execution capability than users or reviewers expect, increasing the risk of secret exposure or unintended network use if the skill implementation is changed or abused.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
60% 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 · scripts/media_gen_client.py (reported line 8)May include surrounding context.

python
Image generation (3 endpoint paths, routed by model):

  gemini-3-pro-image-preview
      POST https://api.aisa.one/v1/models/{model}:generateContent
      Gemini GenerateContent; images arrive as base64 in candidates[].parts[].inline_data.

  wan2.7-image, wan2.7-image-pro

External Transmission

Medium
Category
Data Exfiltration
Confidence
60% 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 · scripts/media_gen_client.py (reported line 12)May include surrounding context.

python
Image generation (3 endpoint paths, routed by model):

  gemini-3-pro-image-preview
      POST https://api.aisa.one/v1/models/{model}:generateContent
      Gemini GenerateContent; images arrive as base64 in candidates[].parts[].inline_data.

  wan2.7-image, wan2.7-image-pro

External Transmission

Medium
Category
Data Exfiltration
Confidence
60% 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 · scripts/media_gen_client.py (reported line 17)May include surrounding context.

python
Image generation (3 endpoint paths, routed by model):

  gemini-3-pro-image-preview
      POST https://api.aisa.one/v1/models/{model}:generateContent
      Gemini GenerateContent; images arrive as base64 in candidates[].parts[].inline_data.

  wan2.7-image, wan2.7-image-pro

External Transmission

Medium
Category
Data Exfiltration
Confidence
60% 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 · scripts/media_gen_client.py (reported line 23)May include surrounding context.

python
Image generation (3 endpoint paths, routed by model):

  gemini-3-pro-image-preview
      POST https://api.aisa.one/v1/models/{model}:generateContent
      Gemini GenerateContent; images arrive as base64 in candidates[].parts[].inline_data.

  wan2.7-image, wan2.7-image-pro

External Transmission

Medium
Category
Data Exfiltration
Confidence
60% 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 · scripts/media_gen_client.py (reported line 24)May include surrounding context.

python
Image generation (3 endpoint paths, routed by model):

  gemini-3-pro-image-preview
      POST https://api.aisa.one/v1/models/{model}:generateContent
      Gemini GenerateContent; images arrive as base64 in candidates[].parts[].inline_data.

  wan2.7-image, wan2.7-image-pro

External Transmission

Medium
Category
Data Exfiltration
Confidence
60% 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 · scripts/media_gen_client.py (reported line 46)May include surrounding context.

python
Image generation (3 endpoint paths, routed by model):

  gemini-3-pro-image-preview
      POST https://api.aisa.one/v1/models/{model}:generateContent
      Gemini GenerateContent; images arrive as base64 in candidates[].parts[].inline_data.

  wan2.7-image, wan2.7-image-pro

External Transmission

Medium
Category
Data Exfiltration
Confidence
60% 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 · scripts/media_gen_client.py (reported line 47)May include surrounding context.

python
Image generation (3 endpoint paths, routed by model):

  gemini-3-pro-image-preview
      POST https://api.aisa.one/v1/models/{model}:generateContent
      Gemini GenerateContent; images arrive as base64 in candidates[].parts[].inline_data.

  wan2.7-image, wan2.7-image-pro

Tainted flow: 'out_path' from os.environ.get (line 406, credential/environment) → open (file write)

Medium
Category
Data Flow
Confidence
88% confidence
Finding

The output path is taken from user-controlled CLI input and opened for writing directly, allowing arbitrary file overwrite within the executing user's permissions. In an agent or automation context, a crafted --out path could clobber sensitive local files or place data in unintended locations.

Content

Scanner excerpt · scripts/media_gen_client.py (reported line 122)May include surrounding context.

python
os.makedirs(os.path.dirname(out_path) or ".", exist_ok=True)
    req = urllib.request.Request(url, headers={"User-Agent": "AIsa-Media-Gen/1.0"})
    try:
        with urllib.request.urlopen(req, timeout=timeout_s) as resp, open(out_path, "wb") as f:
            total = 0
            while True:
                chunk = resp.read(1024 * 1024)  # 1MB

Tainted flow: 'out_path' from os.environ.get (line 406, credential/environment) → open (file write)

Medium
Category
Data Flow
Confidence
89% confidence
Finding

This code writes generated image bytes to a user-supplied output path without path validation or overwrite protections. In environments where an agent executes this skill on behalf of a user, that can be abused to overwrite arbitrary files accessible to the process.

Content

Scanner excerpt · scripts/media_gen_client.py (reported line 407)May include surrounding context.

python
return 1
        mime, data = images[0]
        out_path = args.out or _safe_filename(_ext_from_mime(mime))
        with open(out_path, "wb") as f:
            f.write(data)
        _print_json({"success": True, "route": route, "model": args.model, "mime_type": mime,
                     "saved_to": out_path, "images_returned": len(images)})

Tainted flow: 'out_path' from os.environ.get (line 406, credential/environment) → open (file write)

Medium
Category
Data Flow
Confidence
89% confidence
Finding

The same arbitrary file-write risk exists for Seedream base64 image output: user-controlled path input is opened directly for binary write. This is especially relevant in tool-execution contexts where the caller may not be fully trusted.

Content

Scanner excerpt · scripts/media_gen_client.py (reported line 435)May include surrounding context.

python
kind, data, url = images[0]
        out_path = args.out or _safe_filename("png")
        if kind == "b64" and data is not None:
            with open(out_path, "wb") as f:
                f.write(data)
            _print_json({"success": True, "route": route, "model": args.model,
                         "saved_to": out_path, "images_returned": len(images),

Static analysis

No suspicious patterns detected.