Back to skill

Security audit

VidAU Video Generator

Security checks for vulnerabilities and agentic risk

Overview

The skill mostly does what it says, but it also asks the agent to make privileged system changes and can upload broad local files while leaving sensitive API activity in persistent local logs and caches.

Review this skill before installing. It is not clearly malicious, but only use it in an environment where Python is already installed, avoid allowing the agent to run sudo or package-manager installs, upload only non-sensitive media files you intentionally want to send to Vidau, and periodically delete or protect the Vidau log and cache files in your home directory.

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

T05 · Unauthorized Access and Privilege Escalation

Error
Location
scripts/upload_asset.py:57
Finding
Arbitrary Readable Local Files Can Be Uploaded to the Remote Vidau API<![CDATA[ ## Vulnerability Details **File Location**: `scripts/upload_asset.py`, lines 57–68 and 87–121 **Vulnerability Type**: Missing file-type, file-size, and path-scope validation **Risk Level**: High ### Vulnerable Code ```python def _build_multipart_body(file_path: str, field_name: str) -> Tuple[bytes, str]: """Build multipart/form-data body. Returns (body_bytes, boundary).""" boundary = uuid.uuid4().hex filename = os.path.basename(file_path) with open(file_path, "rb") as f: file_data = f.read() content_type = mimetypes.guess_type(file_path)[0] or "application/octet-stream" part = ( f'--{boundary}\r\n' f'Content-Disposition: form-data; name="{field_name}"; filename="{filename}"\r\n' f'Content-Type: {content_type}\r\n\r\n' ).encode("utf-8") + file_data + f'\r\n--{boundary}--\r\n'.encode("utf-8") return part, boundary ``` ```python path = os.path.expanduser(args.file) if not os.path.isfile(path): print(f"Error: not a file: {path}", file=sys.stderr) sys.exit(1) file_hash = _file_sha256(path) cache = _load_cache() cached = cache.get(file_hash) if cached and cached.get("url") and cached.get("assetId"): out = { "code": "200", "message": "success", "data": {"url": cached["url"], "assetId": cached["assetId"]}, } print(json.dumps(out, ensure_ascii=False)) return api_key = api_client.get_api_key() if not api_key: print( "Error: VIDAU_API_KEY is not set. Register at https://www.superaiglobal.com/ " "and set apiKey or env.VIDAU_API_KEY in OpenClaw skills.entries.vidau.", file=sys.stderr, ) sys.exit(1) body, boundary = _build_multipart_body(path, args.field) headers = { "Authorization": f"Bearer {api_key}", "Content-Type": f"multipart/form-data; boundary={boundary}", "Content-Length": str(len(body)), } try: req = Request(UPLOAD_URL, data=body, headers=headers, method="POST") with urlopen(req, time ...[truncated 2414 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Restrict accepted formats to an explicit allowlist of required image and video types. 2. Validate file signatures with trusted media parsers instead of relying only on extensions or `mimetypes.guess_type()`. 3. Reject `application/octet-stream` unless explicitly required and approved. 4. Resolve the path with `os.path.realpath()` and require it to reside within an approved workspace or user-selected directory. 5. Require explicit confirmation that displays the resolved local path and the remote destination before upload. 6. Enforce configurable file-size limits before hashing or reading the content. 7. Stream the multipart upload in bounded chunks rather than loading the complete file into memory. 8. Document that uploaded files leave the local environment and may be retained by the remote provider. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/api_client.py:15
Finding
API Request and Response Data Is Persisted in a Plaintext Log by Default<![CDATA[ ## Vulnerability Details **File Location**: `scripts/api_client.py`, lines 15 and 55–114 **Vulnerability Type**: Plaintext persistence of sensitive request and response data **Risk Level**: Medium ### Vulnerable Code ```python LOG_PATH = os.environ.get("VIDAU_API_LOG", os.path.join(os.path.expanduser("~"), "vidau_api.log")) ``` ```python def _write_log( method: str, url: str, params_or_body: Optional[str], response_status: Optional[int], response_body: str, error: Optional[str] = None, ) -> None: try: with open(LOG_PATH, "a", encoding="utf-8") as f: f.write("\n" + "=" * 60 + "\n") f.write(f"[{datetime.now().isoformat()}] API request\n") f.write("-" * 40 + "\n") f.write(f"URL: {url}\n") f.write(f"Method: {method}\n") f.write(f"Params: {params_or_body or '(none)'}\n") f.write("-" * 40 + "\n") f.write(f"Status: {response_status}\n") if error: f.write(f"Error: {error}\n") f.write(f"Body: {response_body[:2000]}\n") if len(response_body) > 2000: f.write("...(truncated)\n") f.write("=" * 60 + "\n") except OSError: pass ``` ```python params_str: Optional[str] = None if data: try: params_str = data.decode("utf-8") except Exception: params_str = "<binary>" try: req = Request(url, data=data, headers=headers or {}, method=method) with urlopen(req, timeout=timeout) as resp: raw = resp.read() status = getattr(resp, "status", 200) body_str = raw.decode("utf-8", errors="replace") _write_log(method, url, params_str, status, body_str) return raw, status except HTTPError as e: err_body = b"" try: err_body = e.read() except Exception: pass body_str = err_body.decode("utf-8", errors="replace") _write_log(method, url, params_str, e.code ...[truncated 2262 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Disable request and response body logging by default. 2. Make diagnostic logging explicitly opt-in through a dedicated environment variable or command-line option. 3. Redact prompts, URLs, user IDs, task UUIDs, account details, authorization data, and provider error bodies. 4. Create the log atomically with owner-only mode `0600`. 5. Use `os.open()` with appropriate flags, including protections against following symlinks where supported. 6. Verify that an existing log is a regular file owned by the current user before writing. 7. Add rotation, maximum-size limits, and a documented deletion mechanism. 8. Avoid silently ignoring logging errors when a security check fails; disable logging and report a safe diagnostic instead. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/upload_asset.py:25
Finding
Remote Asset URLs and Content Fingerprints Are Stored in an Insecure Persistent Cache<![CDATA[ ## Vulnerability Details **File Location**: `scripts/upload_asset.py`, lines 25–53, 87–99, and 145–152 **Vulnerability Type**: Insecure plaintext cache containing asset metadata **Risk Level**: Medium ### Vulnerable Code ```python def _cache_path() -> str: return os.environ.get("VIDAU_ASSET_CACHE", os.path.expanduser("~/.vidau_asset_cache.json")) def _file_sha256(path: str) -> str: h = hashlib.sha256() with open(path, "rb") as f: for chunk in iter(lambda: f.read(65536), b""): h.update(chunk) return h.hexdigest() def _load_cache() -> dict: p = _cache_path() if not os.path.isfile(p): return {} try: with open(p, "r", encoding="utf-8") as f: return json.load(f) except (OSError, json.JSONDecodeError): return {} def _save_cache(cache: dict) -> None: p = _cache_path() try: with open(p, "w", encoding="utf-8") as f: json.dump(cache, f, ensure_ascii=False, indent=2) except OSError: pass ``` ```python path = os.path.expanduser(args.file) if not os.path.isfile(path): print(f"Error: not a file: {path}", file=sys.stderr) sys.exit(1) file_hash = _file_sha256(path) cache = _load_cache() cached = cache.get(file_hash) if cached and cached.get("url") and cached.get("assetId"): out = { "code": "200", "message": "success", "data": {"url": cached["url"], "assetId": cached["assetId"]}, } print(json.dumps(out, ensure_ascii=False)) return ``` ```python data = out.get("data") or {} url_val = data.get("url") asset_id = data.get("assetId") if url_val and asset_id: cache[file_hash] = {"url": url_val, "assetId": asset_id} _save_cache(cache) ``` ### Technical Analysis After a successful upload, the script stores a mapping from the file's SHA-256 hash to the remote asset URL and asset identifier. The cache is persistent and uses the predictable default path `~/.vidau_asset_cache.json`. Th ...[truncated 1530 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Create cache files with owner-only mode `0600`. 2. Reject symlinks and verify that any existing cache is a regular file owned by the current user. 3. Write to a securely created temporary file in the same directory and atomically replace the cache. 4. Apply an expiration policy and remove stale asset URLs and identifiers. 5. Provide a command or documented procedure to clear the cache. 6. Store only the minimum metadata needed for deduplication. 7. Consider using an application-specific private state directory rather than a predictable top-level home-directory file. 8. If remote URLs grant direct access, avoid persistent URL storage or protect the cache using operating-system credential storage or encryption. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Tool MisuseTool Parameter Abuse, Chaining Abuse, Unsafe Defaults
  • Rogue AgentSelf-Modification, Session Persistence
Findings (16)

Chaining Abuse

High
Category
Tool Misuse
Content
1. Run `python3 --version` or `python --version`. If either succeeds, use that command when invoking the scripts below.
2. If both fail, try to install Python for the current platform:
   - **macOS**: `brew install python3` (requires Homebrew).
   - **Linux (Debian/Ubuntu)**: `sudo apt-get update && sudo apt-get install -y python3`.
   - **Windows**: `winget install Python.Python.3.12` if available; otherwise tell the user to download and run the installer from [python.org](https://www.python.org/downloads/).
3. If install fails (e.g. no permission or unsupported OS), reply with a short message that Python 3 is required and link to [python.org/downloads](https://www.python.org/downloads/), then stop. Do not run the Vidau scripts until Python is available.
Confidence
88% confidence
Finding
The chained command combines update and install steps with privileged execution, making it easier for an agent to perform multiple impactful actions in one shot without an intervening approval boundary. In the context of a skill whose purpose is API interaction, this kind of command chaining amplifies risk by coupling environment modification and package installation to ordinary task execution.

Lp3

Medium
Category
MCP Least Privilege
Confidence
90% confidence
Finding
The skill declares capabilities requiring environment access, file read/write, and network access but does not constrain them with an explicit tool-scope or permissions declaration. This increases the risk of overbroad execution, because an agent may invoke more powerful primitives than are necessary for querying credits or generating a video task.

Session Persistence

Medium
Category
Rogue Agent
Content
## When to use

- User asks to "generate a video", "create a short video with Veo3/Sora", "generate video from this prompt/image", "make a clip from this script", etc.
- User asks "how many credits do I have", "check my Vidau balance", "query Vidau credits", etc.
- User asks to "check my video task status", "has my Vidau task finished", "query task by UUID", etc.
Confidence
60% confidence
Finding
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.

Context-Inappropriate Capability

Medium
Confidence
91% confidence
Finding
The skill instructs the agent to install Python automatically using system package managers, including privileged commands, which goes beyond the core purpose of using an existing video API. This broadens the blast radius from API usage to host modification and can lead to unintended package installation, system changes, or execution in sensitive environments.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
1. Run `python3 --version` or `python --version`. If either succeeds, use that command when invoking the scripts below.
2. If both fail, try to install Python for the current platform:
   - **macOS**: `brew install python3` (requires Homebrew).
   - **Linux (Debian/Ubuntu)**: `sudo apt-get update && sudo apt-get install -y python3`.
   - **Windows**: `winget install Python.Python.3.12` if available; otherwise tell the user to download and run the installer from [python.org](https://www.python.org/downloads/).
3. If install fails (e.g. no permission or unsupported OS), reply with a short message that Python 3 is required and link to [python.org/downloads](https://www.python.org/downloads/), then stop. Do not run the Vidau scripts until Python is available.
Confidence
97% confidence
Finding
The skill explicitly instructs use of sudo to install Python, introducing privileged execution unrelated to the primary task of generating videos. Privileged package-management commands can alter the host, install unintended software, and create a path for broader compromise if the environment or package sources are not fully trusted.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The workflow directs uploading local image or video files to a third-party cloud service without requiring an explicit disclosure or confirmation that local file contents will leave the device. This is dangerous because users may supply sensitive local media, and the agent could exfiltrate it to an external API under the guise of normal task execution.

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

Medium
Category
Data Flow
Content
error: Optional[str] = None,
) -> None:
    try:
        with open(LOG_PATH, "a", encoding="utf-8") as f:
            f.write("\n" + "=" * 60 + "\n")
            f.write(f"[{datetime.now().isoformat()}] API request\n")
            f.write("-" * 40 + "\n")
Confidence
65% confidence
Finding
Data from a source is assigned to a variable that is later passed to a sink, creating a variable-mediated taint flow.

External Transmission

Medium
Category
Data Exfiltration
Content
import api_client
from urllib.error import URLError

API_BASE = "https://api.superaiglobal.com/v1"
CREATE_TASK_URL = f"{API_BASE}/video/createTask"
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

External Transmission

Medium
Category
Data Exfiltration
Content
import api_client
from urllib.error import URLError

API_BASE = "https://api.superaiglobal.com/v1"
CREATE_TASK_URL = f"{API_BASE}/video/createTask"
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

External Transmission

Medium
Category
Data Exfiltration
Content
import api_client
from urllib.error import URLError

API_BASE = "https://api.superaiglobal.com/v1"
CREATE_TASK_URL = f"{API_BASE}/video/createTask"
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

External Transmission

Medium
Category
Data Exfiltration
Content
import api_client
from urllib.error import URLError

API_BASE = "https://api.superaiglobal.com/v1"
CREATE_TASK_URL = f"{API_BASE}/video/createTask"
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

External Transmission

Medium
Category
Data Exfiltration
Content
import api_client
from urllib.error import URLError

API_BASE = "https://api.superaiglobal.com/v1"
CREATE_TASK_URL = f"{API_BASE}/video/createTask"
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

External Transmission

Medium
Category
Data Exfiltration
Content
import api_client
from urllib.error import URLError

API_BASE = "https://api.superaiglobal.com/v1"
CREATE_TASK_URL = f"{API_BASE}/video/createTask"
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

External Transmission

Medium
Category
Data Exfiltration
Content
import api_client
from urllib.error import URLError

API_BASE = "https://api.superaiglobal.com/v1"
CREATE_TASK_URL = f"{API_BASE}/video/createTask"
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

External Transmission

Medium
Category
Data Exfiltration
Content
import api_client
from urllib.error import URLError

API_BASE = "https://api.superaiglobal.com/v1"
CREATE_TASK_URL = f"{API_BASE}/video/createTask"
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Context-Inappropriate Capability

Low
Confidence
87% confidence
Finding
The manifest says the skill should be configured with an API key, which implies using the provided credential for Vidau API access. The code also reaches into ~/.openclaw/openclaw.json to discover a key, adding local file access to unrelated host configuration that is not clearly justified by the stated purpose of generating videos or checking credits.

Static analysis

No suspicious patterns detected.