Back to skill

Security audit

ai-video-creator-assistant

Security checks for vulnerabilities and agentic risk

Overview

The skill is broadly coherent as a paid video-assistance workflow, but its payment authorization and encrypted order metadata handling are weak enough to require review before installation.

Review this skill carefully before installing, especially if you rely on its payment gate. It stores local order files under the OpenClaw skills orders directory and its current payment verification can be forged by editing those files. It also may store payment metadata in plaintext when SM4 encryption is not configured or fails. Do not provide video API keys unless you trust the publisher and understand that the skill mainly guides use of third-party video APIs rather than shipping a full video-generation backend.

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/service.py:35
Finding
Payment Authorization Can Be Forged Through Local Order-File Modification<![CDATA[ ## Vulnerability Details **File Location**: `scripts/service.py:35-61` **Vulnerability Type**: Improper payment credential validation and fail-open authorization **Risk Level**: High ### Vulnerable Code ```python def is_credential_valid(order_data: dict) -> bool: credential = order_data.get("payCredential") if not credential: return False ts = order_data.get("credentialTimestamp") if ts and time.time() - ts > CREDENTIAL_TTL: return False return True if __name__ == "__main__": parser = argparse.ArgumentParser(description="Verify payment and authorize video creation service") parser.add_argument("order_no", help="Order number from Phase 1") args = parser.parse_args(); indicator = compute_indicator(SLUG) try: order_data = _load_order(indicator, args.order_no) except Exception as e: print("PAY_STATUS: ERROR"); print(f"ERROR_INFO: Order file read failed: {e}"); sys.exit(1) if not is_credential_valid(order_data): print("PAY_STATUS: ERROR") print("ERROR_INFO: No valid payment credential found. Complete payment via clawtip first.") sys.exit(1) pay_status = order_data.get("payStatus", "SUCCESS") print(f"PAY_STATUS: {pay_status}") if pay_status != "SUCCESS": print(f"ERROR_INFO: Payment status is '{pay_status}', cannot proceed"); sys.exit(1) ``` ### Technical Analysis Payment authorization is based entirely on fields loaded from a JSON file located in the current user's home directory. That file is writable by the same user who invokes the service. The `is_credential_valid` function only verifies that `payCredential` contains a truthy value. It does not verify a digital signature, message authentication code, trusted issuer, order number, amount, recipient, or skill identifier. The timestamp check is also optional. If `credentialTimestamp` is absent, credential expiration is not enforced. In addition, `payStatus` defaults to `SUCCESS` when it is missing, cau ...[truncated 1249 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Replace trust in editable JSON fields with a cryptographically authenticated payment credential. - Require the payment service to issue a digitally signed token or a MAC-protected receipt using a key unavailable to the local user. - Verify all security-relevant claims, including: - Trusted issuer - Order number - Exact amount - Payment recipient - Skill identifier - Explicit successful payment status - Issuance and expiration timestamps - Require `credentialTimestamp` and reject missing, malformed, future-dated, or expired timestamps. - Remove the `SUCCESS` default: ```python pay_status = order_data.get("payStatus") if pay_status != "SUCCESS": reject_payment() ``` - Bind the authenticated credential to the local order and reject mismatched order numbers or amounts. - Where possible, query the trusted payment service directly and verify the response over authenticated TLS. - Add tests proving that fabricated credentials, missing timestamps, missing statuses, modified amounts, and replayed credentials are rejected. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/create_order.py:148
Finding
Encryption Failure Silently Stores Payment Metadata in Plaintext<![CDATA[ ## Vulnerability Details **File Location**: `scripts/create_order.py:148-177` **Vulnerability Type**: Fail-open encryption and plaintext storage of payment metadata **Risk Level**: Medium ### Vulnerable Code ```python def create_order_file(indicator: str) -> dict: """ Create order file with payment metadata only. No user question, video content, API keys, or personal data is stored. """ pay_to = os.environ.get("CLAWTIP_PAY_TO", "") sm4_key = os.environ.get("CLAWTIP_SM4_KEY", "") if not pay_to: print("WARNING: CLAWTIP_PAY_TO environment variable not set") if not sm4_key: print("WARNING: CLAWTIP_SM4_KEY environment variable not set") order_no = generate_order_no() encrypt_payload = json.dumps({ "orderNo": order_no, "amount": str(AMOUNT), "payTo": pay_to, }, ensure_ascii=False) encrypted_data = encrypt_payload if sm4_key: try: if len(sm4_key) == 32 and all(c in "0123456789abcdefABCDEF" for c in sm4_key): encrypted_data = _sm4_encrypt_hex(sm4_key, encrypt_payload) else: import base64 encrypted_data = _sm4_encrypt_hex(base64.b64decode(sm4_key).hex(), encrypt_payload) except Exception as e: print(f"WARNING: SM4 encryption failed: {e}") # Only essential order metadata is persisted. No user-provided content is stored. order_data = { "payTo": pay_to, "amount": AMOUNT, "order_no": order_no, "encrypted_data": encrypted_data, "slug": SLUG, "description": DESCRIPTION, "resource_url": RESOURCE_URL, } ``` ### Technical Analysis The variable named `encrypted_data` is initialized with the plaintext JSON payload before any encryption is attempted. If `CLAWTIP_SM4_KEY` is absent, the encryption block is skipped and the plaintext is persisted. If the key is malformed or encryption otherwise fails, the except ...[truncated 1385 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Treat an absent or invalid `CLAWTIP_SM4_KEY` as a fatal configuration error. - Validate the key before constructing or persisting the order: - Require exactly 16 decoded bytes. - Use strict Base64 validation when accepting Base64 input. - Reject ambiguous or unsupported encodings. - Do not initialize an encrypted-data variable with plaintext: ```python encrypted_data = None ``` - Abort order creation if encryption fails, and ensure no partially created order file remains. - Use distinct fields or an explicit versioned envelope that identifies the encryption algorithm and format. - Avoid including duplicate plaintext payment fields in the same record if confidentiality of those fields is required. - Set restrictive file permissions when creating order directories and files. - Add tests confirming that missing, malformed, incorrectly sized, and undecodable keys prevent the order from being saved. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/create_order.py:105
Finding
Payment Metadata Uses Unauthenticated SM4-ECB Encryption<![CDATA[ ## Vulnerability Details **File Location**: `scripts/create_order.py:105-122` **Vulnerability Type**: Use of deterministic and unauthenticated encryption **Risk Level**: Medium ### Vulnerable Code ```python def _enc_block(pt, rk): x = list(struct.unpack(">4I", pt)) for i in range(32): tmp = x[i + 1] ^ x[i + 2] ^ x[i + 3] ^ rk[i] x.append(x[i] ^ _t(tmp)) return struct.pack(">4I", x[35], x[34], x[33], x[32]) def _sm4_encrypt_ecb(key, data): if len(key) != 16: raise ValueError("SM4 key must be 16 bytes") pad = 16 - (len(data) % 16) data = data + bytes([pad] * pad) rk = _key_expand(key) out = b"" for i in range(0, len(data), 16): out += _enc_block(data[i:i + 16], rk) return out def _sm4_encrypt_hex(key_hex, plain): return _sm4_encrypt_ecb(bytes.fromhex(key_hex), plain.encode("utf-8")).hex() ``` ### Technical Analysis The code implements SM4 in Electronic Codebook mode. ECB encrypts each block independently and deterministically, so identical plaintext blocks under the same key produce identical ciphertext blocks. It does not use a nonce or initialization vector. More importantly, ECB does not provide message integrity or authenticity. An attacker with write access to stored ciphertext can alter, remove, reorder, or substitute ciphertext blocks without the encryption layer detecting that modification. Padding alone does not authenticate the message. Payment-related metadata should use authenticated encryption so that both unauthorized disclosure and unauthorized modification are addressed. Whether a modified ciphertext is ultimately accepted depends on the behavior of the external `clawtip` consumer, which is outside the reviewed project, but this implementation itself provides no tamper detection. ### Attack Path 1. Obtain access to one or more locally stored order files. 2. Compare ciphertext blocks across orders encrypted under the same static key to identify repeate ...[truncated 1039 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Replace SM4-ECB with an authenticated-encryption construction such as SM4-GCM where supported. - Generate a fresh, cryptographically secure nonce for every encrypted order. - Store the algorithm version, nonce, ciphertext, and authentication tag in a structured envelope. - Bind security-critical context as authenticated associated data, including the skill identifier and encryption-format version. - If authenticated SM4 is unavailable, use encrypt-then-MAC with independent encryption and MAC keys and verify the MAC before decrypting. - Never reuse a nonce with the same key when using a nonce-based authenticated mode. - Ensure downstream payment processing rejects missing, invalid, or reused authentication tags. - Prefer a reviewed cryptographic library over a custom block-cipher implementation. - Add tests proving that single-bit changes, block substitution, truncation, reordering, and replay are detected and rejected. ]]>
Vulnerability Patterns
  • Rogue AgentSelf-Modification, Session Persistence
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • 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
Findings (6)

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
The declared description focuses on AI-assisted short video generation functionality, including user-selected video parameters and use of external video APIs. However, this code chunk is solely an order creation/payment metadata script. Its primary behavior is payment processing support: reading payment configuration from environment variables, generating an order number, encrypting a payment payload, and saving order data locally. While the description does mention a one-time payment of ¥16.90, it does not accurately represent that this code specifically implements payment order creation and local persistence rather than video creation behavior. This is a material description-behavior mismatch for the supplied code chunk.

Intent-Code Divergence

High
Confidence
98% confidence
Finding
The header comment claims only encrypted payment fields are persisted, but the code falls back to storing the raw JSON payload in encrypted_data when CLAWTIP_SM4_KEY is unset or encryption fails. That means payment metadata such as orderNo, amount, and payTo may be written in plaintext despite documentation asserting otherwise, creating a confidentiality and trust issue.

Lp3

Medium
Category
MCP Least Privilege
Confidence
70% confidence
Finding
Without declared permissions the skill's intent is opaque and cannot be validated.

Description-Behavior Mismatch

Medium
Confidence
90% confidence
Finding
The manifest describes an AI-assisted short video creation skill, but this script’s primary behavior is creating and persisting local payment order metadata for clawtip processing. Payment handling may be part of the product lifecycle, but it is a distinct operational capability not disclosed in the stated skill description.

Session Persistence

Medium
Category
Rogue Agent
Content
def create_order_file(indicator: str) -> dict:
    """
    Create order file with payment metadata only.
    No user question, video content, API keys, or personal data is stored.
    """
    pay_to = os.environ.get("CLAWTIP_PAY_TO", "")
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.

Natural-Language Policy Violations

Low
Confidence
85% confidence
Finding
The script tells the user to consult "SKILL.md > API 配置指引," which hard-codes a Chinese-language section reference in user-facing instructions. This can violate language/locale policy when no user opt-in or alternative language path is offered.

Static analysis

No suspicious patterns detected.