Back to skill

Security audit

neodomain-ai

Security checks for vulnerabilities and agentic risk

Overview

The skill matches an AI media-generation purpose, but it handles account tokens and uploaded local media in ways users should review carefully before installing.

Install only if you trust the Neodomain service and are comfortable with prompts, reference URLs, selected local media, and generated outputs being sent through its API/OSS workflow. Avoid putting tokens directly in commands or shell startup files, treat printed tokens as sensitive, and do not upload private media unless you understand where it will be stored and how it can be removed.

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

T09 · Insecure Skill Coding Practices

Error
Location
scripts/login.py:68
Finding
Access Token and One-Time Verification Code Exposed Through Process Arguments and Console Output<![CDATA[ ## Vulnerability Details **File Location**: `scripts/login.py`, lines 68-70 and 91-99 **Vulnerability Type**: Plaintext credential exposure **Risk Level**: High ### Vulnerable Code ```python parser.add_argument("--contact", required=True, help="Phone number or email") parser.add_argument("--code", help="Verification code (for login)") parser.add_argument("--invitation-code", help="Invitation code (optional)") ``` ```python print(f"\nAccess Token:") print(result.get("authorization")) print(f"\nUser Info:") print(f" User ID: {result.get('userId')}") print(f" Nickname: {result.get('nickname')}") print(f" Email: {result.get('email')}") print(f" Mobile: {result.get('mobile')}") print("\n📝 Add to your environment:") print(f'export NEODOMAIN_ACCESS_TOKEN="{result.get("authorization")}"') ``` The same command-line token pattern appears throughout the API scripts, including: ```python parser.add_argument("--token", "--access-token", dest="token", help="Access token") ``` The documentation explicitly demonstrates expanding the access token into command-line arguments, for example: ```bash python3 {baseDir}/scripts/image_models.py --token $NEODOMAIN_ACCESS_TOKEN ``` ### Technical Analysis The login workflow accepts the one-time verification code through `--code`, while generation scripts accept the long-lived access token through `--token`. Command-line arguments can be exposed through process inspection facilities, shell history, terminal recording, CI logs, debugging tools, and process-monitoring software. After login, the script writes the complete access token to standard output twice, including as a ready-to-copy shell command. It also prints account attributes such as email address and mobile number. This increases the chance that credentials and personal information will be retained in agent transcripts, execution logs, terminal scrollback, or automation logs. Sending the contact and verification code to the declared Neodomain HTTPS authent ...[truncated 1511 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Read verification codes interactively with `getpass.getpass()` rather than accepting them through command-line arguments. - Remove `--token` from normal usage and obtain the token exclusively from a protected credential store or environment variable. - If noninteractive credential input is required, support a file descriptor or a token file restricted to the current user rather than a command-line value. - Do not print the complete token, shell export command, email address, or mobile number by default. - Return only a success message and masked token identifier. Provide an explicit, security-warned option for revealing a token when unavoidable. - Ensure agent and CI runners redact `NEODOMAIN_ACCESS_TOKEN`, verification codes, authorization responses, and personal account fields from logs. - Recommend a dedicated short-lived, scope-limited API token rather than a general session token. - Revoke and rotate any token that may already have been retained in logs or transcripts. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/upload_oss.py:57
Finding
Uploaded Media Is Assigned a Low-Entropy Object Name and Returned Through a Direct OSS URL Without Cleanup Controls<![CDATA[ ## Vulnerability Details **File Location**: `scripts/upload_oss.py`, lines 57-70 **Vulnerability Type**: Insecure cloud-object handling and potential sensitive-file disclosure **Risk Level**: Medium ### Vulnerable Code ```python # Generate remote path filename = os.path.basename(local_file) ext = os.path.splitext(filename)[1] date_str = datetime.now().strftime("%Y%m%d") remote_path = f"temp/{date_str}/{uuid.uuid4().hex[:8]}{ext}" # Determine content type content_type = mimetypes.guess_type(local_file)[0] or 'application/octet-stream' # Upload with open(local_file, 'rb') as f: bucket.put_object(remote_path, f, headers={'Content-Type': content_type}) url = f"https://wlpaas.oss-cn-shanghai.aliyuncs.com/{remote_path}" return url ``` Equivalent behavior appears in `scripts/batch_video.py`, where local storyboard files are uploaded under `temp/story/<date>/<8-hex-character-id>`. ### Technical Analysis The upload helper accepts any user-selected local file and transfers its entire contents to Alibaba OSS using temporary credentials obtained from Neodomain. Uploading media is necessary for the documented image-to-video workflow when the upstream service requires a remotely accessible URL. However, the implementation does not enforce that the selected file is an expected image/video type, does not set an explicit private object ACL, does not generate a signed expiring retrieval URL, and does not delete the temporary object after processing. The object identifier is truncated to eight hexadecimal characters, providing only 32 bits of randomness. The current date and path prefix are predictable. A direct bucket URL is constructed and printed, indicating that the workflow expects the object to be retrievable through that URL. If the bucket or prefix permits anonymous reads, anyone who obtains or discovers the URL can retrieve the uploaded content. The repository does not contain the bucket policy, so anonymous accessibility cannot be proven solely fr ...[truncated 1450 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Explicitly upload objects with a private ACL and verify the effective bucket policy. - Pass private OSS object identifiers to the service or use short-lived signed URLs rather than permanent direct URLs. - Use the full UUID value or at least 128 bits of cryptographically strong randomness for object names. - Delete temporary objects in a `finally` block after the generation service has consumed them. - Configure a short bucket lifecycle expiration as defense in depth. - Restrict STS permissions to a dedicated per-user/per-task prefix and only the required `PutObject`, `GetObject`, and `DeleteObject` operations. - Validate file type, extension, size, and regular-file status before upload. Reject devices, sockets, symlinks where inappropriate, and unsupported formats. - Display an explicit notice identifying the destination domain, retention period, and file being uploaded before transferring private local content. - Avoid printing complete retrieval URLs in shared logs; mask or suppress them unless explicitly requested. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/generate_video.py:82
Finding
Unvalidated Server-Supplied URLs Are Fetched From the Local Execution Environment<![CDATA[ ## Vulnerability Details **File Location**: `scripts/generate_video.py`, lines 82-88 **Vulnerability Type**: Server-side request forgery from the client environment and unbounded download **Risk Level**: Medium ### Vulnerable Code ```python def download_file(url: str, output_path: str): """Download file from URL.""" try: with urllib.request.urlopen(url, timeout=120) as response: with open(output_path, "wb") as f: f.write(response.read()) return True except Exception as e: print(f"Failed to download {url}: {e}", file=sys.stderr) return False ``` The URL is taken from an API status response and passed directly to this function: ```python video_url = status_data.get("ossVideoUrl") thumbnail_url = status_data.get("thumbnailUrl") if video_url: video_path = output_dir / "video.mp4" if download_file(video_url, str(video_path)): print(f" ✅ Saved: {video_path}") if thumbnail_url: thumbnail_path = output_dir / "thumbnail.jpg" if download_file(thumbnail_url, str(thumbnail_path)): print(f" ✅ Saved: {thumbnail_path}") ``` The same unrestricted download pattern appears in `scripts/generate_image.py`, `scripts/generate_image_ref.py`, `scripts/motion_control.py`, and `scripts/batch_video.py`. ### Technical Analysis Generation-result URLs are controlled by the remote API response and are passed directly to `urllib.request.urlopen`. There is no validation of the scheme, hostname, resolved IP address, redirect destination, response content type, or response size. If the API endpoint, account response, DNS path, or upstream generation service is compromised, it can make the local runner issue GET requests to loopback addresses, private network services, cloud metadata endpoints, or local HTTP services. Python's URL opener also follows HTTP redirects, so validating only the initial URL would not be sufficient. The code uses `response.read()` without a size ...[truncated 1861 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Permit downloads only from an explicit allowlist of expected OSS/CDN hostnames. - Require HTTPS and reject URLs containing credentials, unusual ports, fragments, or unsupported schemes. - Resolve the hostname before connecting and reject loopback, link-local, multicast, reserved, and private IP ranges. - Disable automatic redirects or validate every redirect target using the same hostname and IP rules. - Stream responses in bounded chunks rather than calling `response.read()` without a limit. - Enforce strict maximum sizes for images, thumbnails, and videos using both `Content-Length` and a running byte counter. - Validate response MIME type and, where practical, inspect file signatures before retaining the output. - Write to a temporary file and atomically rename it only after validation succeeds; delete partial files on failure. - Apply these controls consistently to all five download helpers rather than fixing only `generate_video.py`. ]]>
Vulnerability Patterns
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
Findings (57)

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding
The documented behavior includes access to local files and cloud/object-storage upload flows that are not apparent from the high-level skill purpose. Hidden or under-disclosed file exfiltration paths are dangerous because user-supplied files or local data could be transmitted to third-party storage without adequate user understanding or policy restriction.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding
The documented behavior includes access to local files and cloud/object-storage upload flows that are not apparent from the high-level skill purpose. Hidden or under-disclosed file exfiltration paths are dangerous because user-supplied files or local data could be transmitted to third-party storage without adequate user understanding or policy restriction.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
The documented behavior includes access to local files and cloud/object-storage upload flows that are not apparent from the high-level skill purpose. Hidden or under-disclosed file exfiltration paths are dangerous because user-supplied files or local data could be transmitted to third-party storage without adequate user understanding or policy restriction.

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding
The documented behavior includes access to local files and cloud/object-storage upload flows that are not apparent from the high-level skill purpose. Hidden or under-disclosed file exfiltration paths are dangerous because user-supplied files or local data could be transmitted to third-party storage without adequate user understanding or policy restriction.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
The documented behavior includes access to local files and cloud/object-storage upload flows that are not apparent from the high-level skill purpose. Hidden or under-disclosed file exfiltration paths are dangerous because user-supplied files or local data could be transmitted to third-party storage without adequate user understanding or policy restriction.

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding
The documented behavior includes access to local files and cloud/object-storage upload flows that are not apparent from the high-level skill purpose. Hidden or under-disclosed file exfiltration paths are dangerous because user-supplied files or local data could be transmitted to third-party storage without adequate user understanding or policy restriction.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
The documented behavior includes access to local files and cloud/object-storage upload flows that are not apparent from the high-level skill purpose. Hidden or under-disclosed file exfiltration paths are dangerous because user-supplied files or local data could be transmitted to third-party storage without adequate user understanding or policy restriction.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
The documented behavior includes access to local files and cloud/object-storage upload flows that are not apparent from the high-level skill purpose. Hidden or under-disclosed file exfiltration paths are dangerous because user-supplied files or local data could be transmitted to third-party storage without adequate user understanding or policy restriction.

Credential Access

High
Category
Privilege Escalation
Content
> **重要**: 每次 token 过期或首次使用时,动态询问用户登录账号(手机号/邮箱),不要硬编码保存。

If you need to obtain an access token:

```bash
# 1. 发送验证码 (询问用户手机号或邮箱)
Confidence
80% confidence
Finding
The skill instructs the agent to dynamically ask for a user's phone number or email to obtain an access token, introducing direct collection of sensitive identifiers and an authentication workflow inside the skill. In agent contexts, this is dangerous because it can normalize credential/PII solicitation and create opportunities for phishing, mishandling of verification codes, or unauthorized account access.

Credential Access

High
Category
Privilege Escalation
Content
python3 {baseDir}/scripts/login.py --login --contact "用户手机号或邮箱" --code "验证码"
```

The login script will output an access token that you can store in `NEODOMAIN_ACCESS_TOKEN`.

## Workflow
Confidence
87% confidence
Finding
The login workflow states that the script will output an access token that can then be stored for future use. Emitting bearer tokens in plaintext to the terminal or agent output creates a significant exposure risk because logs, transcripts, or other tools may capture and reuse the credential.

Credential Access

High
Category
Privilege Escalation
Content
#!/usr/bin/env python3
"""
Neodomain AI - Authentication Script
Send verification code and login to get access token.
"""

import argparse
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
#!/usr/bin/env python3
"""
Neodomain AI - Authentication Script
Send verification code and login to get access token.
"""

import argparse
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
#!/usr/bin/env python3
"""
Neodomain AI - Authentication Script
Send verification code and login to get access token.
"""

import argparse
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
#!/usr/bin/env python3
"""
Neodomain AI - Authentication Script
Send verification code and login to get access token.
"""

import argparse
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
#!/usr/bin/env python3
"""
Neodomain AI - Authentication Script
Send verification code and login to get access token.
"""

import argparse
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
result = login(args.contact, args.code, args.invitation_code)
        print("\n✅ Login successful!")
        print(f"\nAccess Token:")
        print(result.get("authorization"))
        print(f"\nUser Info:")
        print(f"  User ID: {result.get('userId')}")
Confidence
97% confidence
Finding
The script prints the access token and an export command containing the token directly to stdout. In an agent or automation context, stdout may be captured by logs, chat transcripts, shell history, telemetry, or other tools, causing credential disclosure and enabling unauthorized reuse of the bearer token.

Credential Access

High
Category
Privilege Escalation
Content
parser = argparse.ArgumentParser(description="Get available video generation models")
    parser.add_argument("--request-type", type=int, default=2, 
                        help="Request type: 1-视频工具, 2-画布")
    parser.add_argument("--token", "--access-token", dest="token", help="Access token")
    
    args = parser.parse_args()
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
parser = argparse.ArgumentParser(description="Get available video generation models")
    parser.add_argument("--request-type", type=int, default=2, 
                        help="Request type: 1-视频工具, 2-画布")
    parser.add_argument("--token", "--access-token", dest="token", help="Access token")
    
    args = parser.parse_args()
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
parser = argparse.ArgumentParser(description="Get available video generation models")
    parser.add_argument("--request-type", type=int, default=2, 
                        help="Request type: 1-视频工具, 2-画布")
    parser.add_argument("--token", "--access-token", dest="token", help="Access token")
    
    args = parser.parse_args()
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
parser = argparse.ArgumentParser(description="Get available video generation models")
    parser.add_argument("--request-type", type=int, default=2, 
                        help="Request type: 1-视频工具, 2-画布")
    parser.add_argument("--token", "--access-token", dest="token", help="Access token")
    
    args = parser.parse_args()
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
parser = argparse.ArgumentParser(description="Get available video generation models")
    parser.add_argument("--request-type", type=int, default=2, 
                        help="Request type: 1-视频工具, 2-画布")
    parser.add_argument("--token", "--access-token", dest="token", help="Access token")
    
    args = parser.parse_args()
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
parser = argparse.ArgumentParser(description="Get available video generation models")
    parser.add_argument("--request-type", type=int, default=2, 
                        help="Request type: 1-视频工具, 2-画布")
    parser.add_argument("--token", "--access-token", dest="token", help="Access token")
    
    args = parser.parse_args()
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
parser = argparse.ArgumentParser(description="Get available video generation models")
    parser.add_argument("--request-type", type=int, default=2, 
                        help="Request type: 1-视频工具, 2-画布")
    parser.add_argument("--token", "--access-token", dest="token", help="Access token")
    
    args = parser.parse_args()
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
parser = argparse.ArgumentParser(description="Get available video generation models")
    parser.add_argument("--request-type", type=int, default=2, 
                        help="Request type: 1-视频工具, 2-画布")
    parser.add_argument("--token", "--access-token", dest="token", help="Access token")
    
    args = parser.parse_args()
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
parser = argparse.ArgumentParser(description="Get available video generation models")
    parser.add_argument("--request-type", type=int, default=2, 
                        help="Request type: 1-视频工具, 2-画布")
    parser.add_argument("--token", "--access-token", dest="token", help="Access token")
    
    args = parser.parse_args()
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Static analysis

No suspicious patterns detected.