Back to skill

Security audit

rodin3d-skill

Security checks for vulnerabilities and agentic risk

Overview

The skill mostly matches its stated 3D-generation purpose, but its credential handling and remote-controlled download behavior need review before installation.

Review this skill before installing. Use a dedicated Hyper3D API key with limited billing exposure, avoid passing keys with --api-key or printing them, and treat uploaded images/prompts as shared with Hyper3D. If you run the script, use a disposable output directory and avoid running it in sensitive project or home directories until filename and URL validation are fixed.

Vulnerability Patterns
  • Insecure DependenciesIntroduces malicious components through unsafe dependency sources
  • 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 (4)

T09 · Insecure Skill Coding Practices

Error
Location
scripts/generate_3d_model.py:170
Finding

API-Controlled Path Components Allow Arbitrary File Writes

Content
View full analysis

Vulnerability Details

File Location: scripts/generate_3d_model.py, lines 170-176 and 184-208
Vulnerability Type: Path traversal and unrestricted file write
Risk Level: High

Vulnerable Code

python
# If output directory is specified, download model
if args.output:
    # Create task-specific output directory
    task_output_dir = os.path.join(args.output, task_uuid)
    print(f"\nDownloading models to: {task_output_dir}")
    for file_info in file_list:
        download_model(file_info.get('url'), task_output_dir, file_info.get('name'))
python
def download_model(model_url, output_dir, filename=None):
    """
    Download 3D model to specified directory

    Args:
        model_url: Model download link
        output_dir: Output directory
        filename: Filename (optional)
    """
    import requests

    # Ensure output directory exists
    os.makedirs(output_dir, exist_ok=True)

    # Get filename
    if not filename:
        filename = os.path.basename(model_url.split("?")[0])
    output_path = os.path.join(output_dir, filename)

    # Download file
    try:
        response = requests.get(model_url, stream=True, timeout=60)
        response.raise_for_status()

        with open(output_path, "wb") as f:
            for chunk in response.iter_content(chunk_size=8192):
                if chunk:
                    f.write(chunk)

Technical Analysis

The task_uuid and file_info["name"] values originate from the remote API response. They are passed to os.path.join() without validation or canonical containment checks.

os.path.join() does not enforce confinement beneath its first argument. A filename containing traversal components such as ../../target can escape the output directory. On supported platforms, an absolute second path can also cause the intended base path to be discarded. The API-controlled task_uuid creates an additi ...[truncated 1313 chars]

Remediation
View remediation

Remediation Suggestions

  • Do not use API-provided identifiers directly as local path components. Generate local filenames and task-directory names using trusted UUID generation.

  • Reduce API-provided filenames to a safe basename and reject absolute paths, parent-directory components, path separators, empty names, and platform-specific alternate separators.

  • Resolve both the output root and candidate destination with pathlib.Path.resolve(), then verify that the destination is a descendant of the output root before opening it.

  • Open newly created files with exclusive creation where overwriting is unnecessary.

  • Maintain an explicit allowlist of expected filename extensions.

  • Apply restrictive permissions to output directories and files.

  • A suitable containment pattern is:

    python
    from pathlib import Path
    import uuid
    
    root = Path(args.output).resolve()
    task_dir = (root / str(uuid.uuid4())).resolve()
    task_dir.mkdir(parents=True, exist_ok=True)
    
    supplied_name = Path(filename).name
    if supplied_name != filename or supplied_name in {"", ".", ".."}:
        raise ValueError("Unsafe filename")
    
    destination = (task_dir / supplied_name).resolve()
    if task_dir not in destination.parents:
        raise ValueError("Destination escapes output directory")
    

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/generate_3d_model.py:170
Finding

Unvalidated Remote Download URL Enables Server-Side Request Forgery

Content
View full analysis

Vulnerability Details

File Location: scripts/generate_3d_model.py, lines 170-176 and 184-205
Vulnerability Type: Server-side request forgery through an untrusted download URL
Risk Level: Medium

Vulnerable Code

python
if args.output:
    # Create task-specific output directory
    task_output_dir = os.path.join(args.output, task_uuid)
    print(f"\nDownloading models to: {task_output_dir}")
    for file_info in file_list:
        download_model(file_info.get('url'), task_output_dir, file_info.get('name'))
python
def download_model(model_url, output_dir, filename=None):
    """
    Download 3D model to specified directory

    Args:
        model_url: Model download link
        output_dir: Output directory
        filename: Filename (optional)
    """
    import requests

    # Ensure output directory exists
    os.makedirs(output_dir, exist_ok=True)

    # Get filename
    if not filename:
        filename = os.path.basename(model_url.split("?")[0])
    output_path = os.path.join(output_dir, filename)

    # Download file
    try:
        response = requests.get(model_url, stream=True, timeout=60)
        response.raise_for_status()

Technical Analysis

The model URL is taken from the API response and passed directly to requests.get(). The code does not validate the URL scheme, hostname, port, resolved IP address, or redirect chain.

Consequently, a manipulated API response could direct the client toward loopback services, private network hosts, link-local cloud metadata endpoints, or other resources reachable from the user's network context. The default redirect behavior of requests also means an initially acceptable URL could redirect to a prohibited destination unless every hop is validated.

This is an SSRF condition contingent on an attacker being able to manipulate the API response or compromise the remote service or associated delivery i ...[truncated 1127 chars]

Remediation
View remediation

Remediation Suggestions

  • Accept only https download URLs.
  • Maintain an explicit allowlist of documented Hyper3D download and CDN hostnames.
  • Resolve the hostname and reject loopback, private, link-local, multicast, unspecified, and reserved address ranges for both IPv4 and IPv6.
  • Disable automatic redirects or manually validate the scheme, hostname, port, and resolved address of every redirect target.
  • Protect against DNS rebinding by connecting only to a validated resolved address while preserving correct TLS hostname validation.
  • Reject URLs containing unexpected credentials or ports.
  • Set strict response-size limits while streaming and validate expected content types.
  • Prefer an API design in which the trusted Hyper3D endpoint proxies downloads or returns an opaque identifier rather than an arbitrary URL.

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/generate_3d_model.py:66
Finding

API Keys Can Be Exposed Through Terminal Output and Process Arguments

Content
View full analysis

Vulnerability Details

File Location: SKILL.md, lines 14-19, 78-89, and 120-145; scripts/generate_3d_model.py, lines 66 and 79-88
Vulnerability Type: Sensitive credential exposure
Risk Level: Medium

Vulnerable Code and Instructions

bash
echo $HYPER3D_API_KEY
bash
python <skill_dir>/scripts/generate_3d_model.py --image path/to/image.jpg --geometry-file-format glb --quality medium --output path/to/output_dir --api-key $HYPER3D_API_KEY
python
parser.add_argument("--api-key", help="Hyper3D API key (overrides environment variable)")
python
# Check Hyper3D API key
env_api_key = os.environ.get("HYPER3D_API_KEY")

# If user provided API key via command line argument, use it directly
if args.api_key:
    print("Using API key from command line argument")
    client = Hyper3DAPIClient(api_key=args.api_key)
elif env_api_key:
    # Found API key in environment variable, ask user if they want to use it
    print(f"Found API key in environment variable: {env_api_key[:5]}...{env_api_key[-5:]}")

Technical Analysis

The documentation instructs users to print the complete API key with echo. Terminal sessions can be recorded, copied, streamed, or collected by logging systems. The examples also encourage supplying the key through --api-key, placing the expanded secret in the process argument list. Process arguments may be visible to other local users, process-monitoring software, telemetry agents, job logs, or command wrappers.

The script additionally prints the first and last five characters of an environment-provided key. This leaks credential material unnecessarily. For keys of ten characters or fewer, the slices overlap or collectively disclose the entire key.

Attack Path

  1. A user follows the documentation and displays the environment variable or invokes the script with --api-key.
  2. A terminal recorder, process observer, monitoring agent, j ...[truncated 670 chars]
Remediation
View remediation

Remediation Suggestions

  • Replace echo $HYPER3D_API_KEY with a presence-only check that does not reveal the value, such as:

    bash
    test -n "$HYPER3D_API_KEY" && echo "API key is configured" || echo "API key is missing"
    
  • Remove command examples that expand credentials into --api-key.

  • Prefer environment variables, a credential manager, or a permissions-restricted configuration file.

  • If interactive entry is needed, use Python's getpass.getpass() rather than input() so the key is not echoed.

  • Remove the API-key prefix and suffix from log output. Log only whether a key source was found.

  • Consider deprecating the --api-key option. If compatibility requires retaining it, emit a warning and document that it is unsafe on shared systems.

  • Rotate any key that may already have appeared in terminal recordings, shell wrappers, process telemetry, or CI logs.

T08 · Insecure Dependencies

Note
Location
requirements.txt:1
Finding

Unpinned Third-Party Dependencies Prevent Reproducible and Verifiable Installation

Content
View full analysis

Vulnerability Details

File Location: requirements.txt, lines 1-2
Vulnerability Type: Unpinned third-party dependencies
Risk Level: Low

Vulnerable Code

text
requests
Pillow

Technical Analysis

Neither dependency has a fixed version or integrity hash. Each installation can therefore resolve to a different release depending on repository state and resolver behavior. This prevents reproducible review and makes it harder to ensure that the exact installed artifacts were previously assessed.

The package names are legitimate and no malicious dependency was identified in the audited files. The risk arises from unconstrained future dependency resolution rather than evidence that the current project intentionally installs a malicious package.

Attack Path

  1. A user or automated environment installs the dependencies from requirements.txt.
  2. The resolver selects whatever compatible versions are available at installation time.
  3. A newly released vulnerable or compromised version is selected.
  4. The dependency is imported by the project and its code executes in the Python process.
  5. Any malicious dependency behavior receives the same operating-system privileges and accessible data as the script.

Impact Assessment

A compromised dependency could execute arbitrary Python code with the privileges of the user running the skill and access the same files, environment variables, network connections, and API key available to that process. A merely vulnerable release may have narrower impact based on the affected functionality. No specific compromised release or currently exploitable dependency vulnerability was established by this static audit.

Remediation
View remediation

Remediation Suggestions

  • Pin each dependency to a reviewed version rather than allowing unconstrained resolution.
  • Generate and commit a lock file containing transitive dependency versions and cryptographic hashes.
  • Install with hash verification, for example through a hash-locked requirements file and pip --require-hashes.
  • Use a trusted package index and restrict unexpected alternate indexes.
  • Run dependency vulnerability and license scans in continuous integration.
  • Establish a controlled update process that reviews release notes, security advisories, and lock-file changes before upgrades.
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (21)

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · SKILL.md (reported line 100)May include surrounding context.

  1. Get a key: Go to https://hyper3d.ai/api-dashboard → Click "Create New API Key" → Create Secret Key
  2. Save to .env (recommended for persistence):
    bash
    echo 'HYPER3D_API_KEY=your_api_key_here' >> .env
    echo '.env' >> .gitignore  # Don't commit secrets
    

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · SKILL.md (reported line 101)May include surrounding context.

  1. Save to .env (recommended for persistence):
    bash
    echo 'HYPER3D_API_KEY=your_api_key_here' >> .env
    echo '.env' >> .gitignore  # Don't commit secrets
    

Code Examples

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
88% confidence
Finding

The skill clearly instructs access to environment variables and outbound network communication, but it does not declare any explicit tool scope or permissions boundaries. In an agent environment, missing scope declarations reduce transparency and can allow broader-than-expected execution behavior, especially when handling secrets and external API calls.

Content

No source excerpt is available for this finding.

Ssd 3

Medium
Category
Not specified by scanner
Confidence
99% confidence
Finding

Instructing users to fall back to a shared/free API key encourages use of a credential not uniquely tied to the user or deployment. Shared credentials undermine accountability, can be revoked unexpectedly, may violate provider policy, and create a path for untrusted third parties to induce unauthorized usage or quota exhaustion.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
97% confidence
Finding

The documentation tells users to supply the API key via a command-line argument, which can expose the secret through shell history, process listings, logs, and telemetry. This is especially risky in shared systems, CI runners, or agent platforms where invocation details may be captured automatically.

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 · SKILL.md (reported line 40)May include surrounding context.

md
def __init__(self, api_key=None):
        self.api_key = api_key or os.environ.get("HYPER3D_API_KEY")
        self.api_url = "https://api.hyper3d.com/api/v2/rodin"
        self.status_url = "https://api.hyper3d.com/api/v2/status"
        self.download_url = "https://api.hyper3d.com/api/v2/download"
        self.headers = {

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 · SKILL.md (reported line 41)May include surrounding context.

md
def __init__(self, api_key=None):
        self.api_key = api_key or os.environ.get("HYPER3D_API_KEY")
        self.api_url = "https://api.hyper3d.com/api/v2/rodin"
        self.status_url = "https://api.hyper3d.com/api/v2/status"
        self.download_url = "https://api.hyper3d.com/api/v2/download"
        self.headers = {

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 · SKILL.md (reported line 42)May include surrounding context.

md
def __init__(self, api_key=None):
        self.api_key = api_key or os.environ.get("HYPER3D_API_KEY")
        self.api_url = "https://api.hyper3d.com/api/v2/rodin"
        self.status_url = "https://api.hyper3d.com/api/v2/status"
        self.download_url = "https://api.hyper3d.com/api/v2/download"
        self.headers = {

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/api_client.py (reported line 10)May include surrounding context.

python
def __init__(self, api_key=None):
        self.api_key = api_key or os.environ.get("HYPER3D_API_KEY")
        self.api_url = "https://api.hyper3d.com/api/v2/rodin"
        self.status_url = "https://api.hyper3d.com/api/v2/status"
        self.download_url = "https://api.hyper3d.com/api/v2/download"
        self.headers = {

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/api_client.py (reported line 11)May include surrounding context.

python
def __init__(self, api_key=None):
        self.api_key = api_key or os.environ.get("HYPER3D_API_KEY")
        self.api_url = "https://api.hyper3d.com/api/v2/rodin"
        self.status_url = "https://api.hyper3d.com/api/v2/status"
        self.download_url = "https://api.hyper3d.com/api/v2/download"
        self.headers = {

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/api_client.py (reported line 12)May include surrounding context.

python
def __init__(self, api_key=None):
        self.api_key = api_key or os.environ.get("HYPER3D_API_KEY")
        self.api_url = "https://api.hyper3d.com/api/v2/rodin"
        self.status_url = "https://api.hyper3d.com/api/v2/status"
        self.download_url = "https://api.hyper3d.com/api/v2/download"
        self.headers = {

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

The client uploads user-supplied images and prompt text to a third-party service, but this code provides no consent gate, disclosure, or data-minimization check at the point of transmission. In a skill that may handle private design assets or proprietary prompts, silent external transfer can expose sensitive data to the vendor unexpectedly.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
80% 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/api_client.py (reported line 121)May include surrounding context.

python
dict: Task status information
        """
        try:
            response = requests.post(
                self.status_url,
                headers=self.headers,
                json={"subscription_key": subscription_key},

External Transmission

Medium
Category
Data Exfiltration
Confidence
80% 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/api_client.py (reported line 147)May include surrounding context.

python
dict: Download links list
        """
        try:
            response = requests.post(
                self.download_url,
                headers=self.headers,
                json={"task_uuid": task_uuid},

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
98% confidence
Finding

The script reveals part of the environment-stored API key to the console and asks the user to confirm use of that specific secret. Exposing even a partial credential unnecessarily increases the chance of credential leakage through logs, terminal capture, screenshots, or shared execution environments, and this behavior is not required to generate 3D models.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
79% confidence
Finding

The instructions say that if the user does not specify a tier, the skill should use Sketch by default. This is a natural-language behavior constraint that overrides user preference granularity without opt-in, which can be a policy concern because it forces a specific output setting instead of prompting or offering a choice.

Content

No source excerpt is available for this finding.

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
96% confidence
Finding

The dependency 'requests' is unpinned, so installs may resolve to different versions over time, including versions with known security defects or breaking changes. In a skill that performs external API communication, this increases supply-chain uncertainty and makes builds non-reproducible, which can expose the runtime to avoidable library vulnerabilities.

Content

Scanner excerpt · requirements.txt (reported line 1)May include surrounding context.

text
requests
Pillow

Unverifiable Dependency: requests has 16 known advisory(ies) (CVE-2014-1830 (Exposure of Sensitive Information to an Unauthorized Actor in Requests); CVE-2024-47081 (Requests vulnerable to .netrc credentials leak via malicious URLs); CVE-2024-35195 (Requests `Session` object does not verify requests after making first request wi) +13 more), but the manifest does not pin a version, so it is unknown whether the installed release is affected

Low
Category
Supply Chain
Confidence
90% confidence
Finding

Because 'requests' is not version-pinned, it is impossible to verify whether the deployed environment uses a release affected by known advisories. This matters more in this skill because 'requests' is central to outbound network communication with a third-party API, so flaws in HTTP handling, redirect behavior, TLS verification state, or credential usage could directly affect confidentiality or integrity.

Content

No source excerpt is available for this finding.

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
96% confidence
Finding

The dependency 'Pillow' is unpinned, allowing package resolution to drift to arbitrary versions at install time. Because this skill processes images, using an unpinned image library is particularly risky: older or vulnerable Pillow releases have had memory corruption and resource-consumption issues that could be triggered by crafted image input.

Content

Scanner excerpt · requirements.txt (reported line 2)May include surrounding context.

text
requests
Pillow

Unverifiable Dependency: Pillow has 16 known advisory(ies) (CVE-2016-2533 (Pillow buffer overflow in ImagingPcdDecode); CVE-2023-50447 (Arbitrary Code Execution in Pillow); CVE-2021-27922 (Pillow Uncontrolled Resource Consumption) +13 more), but the manifest does not pin a version, so it is unknown whether the installed release is affected

Low
Category
Supply Chain
Confidence
93% confidence
Finding

The manifest does not pin 'Pillow', so there is no assurance that deployment will avoid versions with known vulnerabilities. In this skill, that is more dangerous than a generic packaging issue because Pillow will likely handle user-supplied images, and malformed images have historically been used to trigger denial-of-service, crashes, or worse in image parsers.

Content

No source excerpt is available for this finding.

Missing User Warnings

Low
Category
Not specified by scanner
Confidence
78% confidence
Finding

The client automatically reads HYPER3D_API_KEY from the environment and uses it for authenticated API requests. While this is common practice, the file provides no user-facing notice that a credential from the environment will be accessed and attached to outbound requests.

Content

No source excerpt is available for this finding.