T09 · Insecure Skill Coding Practices
- Location
scripts/generate_img.py:251- Finding
Unvalidated Server-Provided URL Enables Arbitrary Network Requests
- Content
View full analysis
- Remediation
View remediation
Security audit
Security checks for vulnerabilities and agentic risk
This image-generation skill appears purpose-aligned, but its API-key handling and actual network behavior are under-scoped enough to require review before installation.
Review before installing. Prefer setting MODELSCOPE_API_KEY through a protected environment or secret mechanism rather than pasting the key into chat or using --api-key. Use only trusted ModelScope models and LoRAs, and be aware that prompts and input images are sent to ModelScope. The publisher should tighten the network boundary by validating result-image URLs, update the fixed-endpoint claims, and pin dependencies.
scripts/generate_img.py:251Unvalidated Server-Provided URL Enables Arbitrary Network Requests
scripts/generate_img.py:166API Credential Can Be Exposed Through Command-Line Arguments
scripts/generate_img.py:3Unpinned Runtime Dependencies Create Supply-Chain and Reproducibility Risk
Skill manipulates agent memory, state, or stored context. Memory corruption can alter personality, override safety rules, or cause unpredictable behavior.
- Prompt "A serene Japanese garden" → `2025-11-23-14-23-05-japanese-garden.jpg`
- Prompt "sunset over mountains" → `2025-11-23-15-30-12-sunset-mountains.jpg`
- Prompt "create an image of a robot" → `2025-11-23-16-45-33-robot.jpg`
- Unclear context → `2025-11-23-17-12-48-x9k2.jpg`
## Image Editing
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
def get_api_key(provided_key: str | None) -> str | None:
"""Get API key from argument first, then environment."""
if provided_key:
return provided_key
return os.environ.get("MODELSCOPE_API_KEY")
The skill documents capabilities that require access to environment variables and outbound network connections, but it does not declare any explicit tool scope such as permissions or allowed-tools. That gap weakens reviewability and policy enforcement because an agent may invoke the skill without a clear, auditable declaration of the sensitive capabilities it needs.
The security notice asserts that the endpoint is fixed and cannot be redirected, but the same skill allows arbitrary --model and --lora repository identifiers that influence what remote resources the service processes. This is not direct SSRF from the local environment, but it is a misleading security claim that can cause over-trust while still permitting user-controlled remote dependency selection with possible data-handling, prompt-routing, or supply-chain implications on the provider side.
Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.
Examples:
- Prompt "A serene Japanese garden" → `2025-11-23-14-23-05-japanese-garden.jpg`
- Prompt "sunset over mountains" → `2025-11-23-15-30-12-sunset-mountains.jpg`
- Prompt "create an image of a robot" → `2025-11-23-16-45-33-robot.jpg`
- Unclear context → `2025-11-23-17-12-48-x9k2.jpg`
## Image Editing
The file-level security notice claims the tool only connects to the official ModelScope API endpoint, but later the script downloads the generated image from output_image_url returned by the API without validating the host. If the API, an intermediary, or a compromised upstream service returns an arbitrary URL, the tool can be induced to make outbound requests to attacker-controlled or internal network locations, contradicting the stated trust boundary.
The inline comments assert that using a hardcoded API endpoint prevents redirection of data or credentials, but the result-download step still performs an unrestricted GET to a URL provided by the API. This creates a misleading security guarantee and can mask a real outbound-request primitive that may enable SSRF-style access or retrieval of malicious content.
Data from a source is assigned to a variable that is later passed to a sink, creating a variable-mediated taint flow.
for attempt in range(max_attempts):
try:
# SECURITY: Using hardcoded endpoint only
result = requests.get(
f"{_API_ENDPOINT}v1/tasks/{task_id}",
headers={**common_headers, "X-ModelScope-Task-Type": "image_generation"},
timeout=30