Back to skill

Security audit

TikTok Viral Editor

Security checks for vulnerabilities and agentic risk

Overview

This Sparki video-editing skill mostly matches its stated purpose, but it handles API keys and video transfers with under-scoped safeguards that users should review before installing.

Install only if you are comfortable sending selected local videos, prompts, asset IDs, and project metadata to Sparki. Prefer using `SPARKI_API_KEY` instead of saving an API key in the config file, avoid `--base-url` unless you fully trust the endpoint, and treat downloaded results as remote content from the service.

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

Warning
Location
src/sparki_cli/config.py:45
Finding
API Key Stored in Plaintext Without Restrictive File Permissions<![CDATA[ ## Vulnerability Details **File Location**: `src/sparki_cli/config.py:45-55` **Vulnerability Type**: Plaintext credential storage and insecure file permissions **Risk Level**: Medium ### Vulnerable Code ```python def save(self, api_key: str | None = None, base_url: str | None = None, default_output_dir: str | None = None) -> None: self.config_dir.mkdir(parents=True, exist_ok=True) if api_key is not None: self._data["api_key"] = api_key if base_url is not None: self._data["base_url"] = base_url elif "base_url" not in self._data: self._data["base_url"] = DEFAULT_BASE_URL if default_output_dir is not None: self._data["default_output_dir"] = default_output_dir self.config_file.write_text(json.dumps(self._data, indent=2)) ``` ### Technical Analysis The `save` method stores the Sparki API key directly in the JSON configuration file at `~/.openclaw/config/sparki.json`. The API key is not encrypted or protected through an operating-system credential store. The code also does not explicitly enforce owner-only permissions on either the configuration directory or the configuration file. `Path.mkdir()` and `Path.write_text()` rely on the process umask and any pre-existing filesystem permissions. In an environment with a permissive umask or an improperly permissioned pre-existing configuration directory, another local account may be able to read the credential. The method also does not verify whether `sparki.json` is a symbolic link before writing it, which weakens the integrity guarantees around credential storage. ### Attack Path 1. The user runs `sparki setup --api-key <key>`. 2. The `Config.save()` method serializes the API key into `~/.openclaw/config/sparki.json`. 3. The file is created using permissions derived from the current process umask rather than an explicitly enforced `0600` mode. 4. Another local user or process with filesystem access reads the configuration file. 5. The attacker extra ...[truncated 635 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Prefer an operating-system credential manager, such as Keychain, Secret Service, or another platform-specific secret store. 2. If file-based storage is required: - Create the configuration directory with mode `0700`. - Create the configuration file atomically with mode `0600`. - Explicitly verify and correct permissions after writing. 3. Refuse to read from or write to symbolic links. 4. Write to a securely created temporary file in the same directory, set its permissions, and atomically replace the destination. 5. Consider storing only non-sensitive settings in `sparki.json` and requiring the API key through `SPARKI_API_KEY`. 6. Document the credential-storage behavior and provide a command to remove or rotate stored credentials. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
src/sparki_cli/cli.py:125
Finding
Unrestricted API Base URL Override Can Disclose the API Key<![CDATA[ ## Vulnerability Details **File Locations**: - `src/sparki_cli/cli.py:125-138` - `src/sparki_cli/client.py:10-20` **Vulnerability Type**: Credential disclosure to an untrusted endpoint **Risk Level**: Medium ### Vulnerable Code ```python @app.command() def setup( api_key: Annotated[str, typer.Option("--api-key", help="Your Sparki API key")], base_url: Annotated[Optional[str], typer.Option("--base-url", help="Override the Sparki API base URL")] = None, ) -> None: """Save API key and validate it against the Sparki backend.""" async def _run() -> None: cfg = _load_config() effective_base_url = base_url or cfg.base_url client = SparkiClient(base_url=effective_base_url, api_key=api_key) valid = await client.validate_key() if not valid: print_error("AUTH_FAILED") return cfg.save(api_key=api_key, base_url=base_url) ``` The client sends the credential to the selected URL: ```python class SparkiClient: def __init__(self, base_url: str, api_key: str): self.base_url = base_url.rstrip("/") self.api_key = api_key self._headers = {"X-API-Key": api_key} def _url(self, path: str) -> str: return f"{self.base_url}{path}" async def validate_key(self) -> bool: async with httpx.AsyncClient() as c: resp = await c.get(self._url("/api/v1/account/info"), headers=self._headers) return resp.status_code == 200 ``` ### Technical Analysis The `setup` command accepts an arbitrary `--base-url` value. No validation restricts the scheme, hostname, port, or destination address. The resulting endpoint immediately receives the supplied API key through the `X-API-Key` header during `validate_key()`. An attacker-controlled endpoint can return HTTP status 200, causing the CLI to treat the credential as valid and persist both the credential and malicious endpoint. Subsequent uploads and API ...[truncated 1480 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Remove the `--base-url` override from production builds unless it is operationally necessary. 2. Restrict API endpoints to an explicit allowlist, such as `agent-api.sparki.io`. 3. Require HTTPS and reject plaintext HTTP, embedded credentials, unexpected ports, malformed hosts, and non-network URL schemes. 4. Resolve the hostname and reject loopback, private, link-local, multicast, and other special-use addresses. 5. If development overrides must remain: - Require an explicit development-mode flag. - Display the exact destination and require interactive confirmation. - Do not persist the override by default. - Use a separate development credential rather than the production API key. 6. Validate endpoint trust before attaching the `X-API-Key` header. 7. Ensure runtime network policy matches the domain permissions declared in `SKILL.md`. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
src/sparki_cli/client.py:89
Finding
Unvalidated and Unbounded Result Download Enables SSRF and Disk Exhaustion<![CDATA[ ## Vulnerability Details **File Locations**: - `src/sparki_cli/client.py:89-99` - `src/sparki_cli/cli.py:293-304` - `src/sparki_cli/cli.py:490-499` **Vulnerability Type**: Server-side request forgery and uncontrolled resource consumption **Risk Level**: Medium ### Vulnerable Code ```python async def download_result(self, url: str, output_path: Path) -> int: async with httpx.AsyncClient(timeout=600, follow_redirects=True) as c: async with c.stream("GET", url) as resp: resp.raise_for_status() output_path.parent.mkdir(parents=True, exist_ok=True) total = 0 with open(output_path, "wb") as f: async for chunk in resp.aiter_bytes(chunk_size=1024 * 1024): f.write(chunk) total += len(chunk) return total ``` The URL is obtained directly from the API response and passed to the downloader: ```python result_url = _extract_result_url(data) if not result_url: print_error("NETWORK_ERROR", "No result URL available") return out_path = output or cfg.default_output_dir / f"{task_id}.mp4" file_size = await client.download_result(result_url, out_path) ``` The same pattern is used by the end-to-end workflow: ```python result_url = _extract_result_url(proj_data) if not result_url: print_error("NETWORK_ERROR", "No result URL available") return out_path = output or cfg.default_output_dir / f"{task_id}.mp4" file_size = await client.download_result(result_url, out_path) ``` ### Technical Analysis The result URL is controlled by the API response and is fetched without validating its scheme, hostname, resolved address, port, or expected CDN origin. Redirects are enabled globally through `follow_redirects=True`, and redirect destinations are not independently validated. Consequently, a compromised or malicious configured API endpoint can instruct the client to request internal services, loopback interfaces, link-local metadata endp ...[truncated 2344 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Allow only HTTPS result URLs. 2. Maintain an explicit allowlist of trusted Sparki asset or CDN hostnames. 3. Resolve destination hostnames and reject loopback, private, link-local, multicast, unspecified, and other special-use IP ranges. 4. Disable automatic redirects or validate the scheme, hostname, port, and resolved address of every redirect target before following it. 5. Set a strict maximum download size appropriate for rendered videos: - Reject responses whose `Content-Length` exceeds the limit. - Track streamed bytes and abort immediately if the limit is exceeded. 6. Validate the response `Content-Type` and verify the downloaded file's expected media signature where practical. 7. Download to a securely created temporary file, then atomically rename it only after all validation succeeds. 8. Delete partial files when any network, validation, or size error occurs. 9. Apply separate connect, read, write, and pool timeouts rather than relying only on a single broad timeout. 10. Consider checking available disk space before beginning a large download. ]]>
Vulnerability Patterns
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
Findings (16)

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding
A second behavior-mismatch report suggests the skill's real function may be generic CLI/config handling rather than TikTok editing guidance. Even if not overtly malicious, deceptive packaging is dangerous because it can cause users to authorize network, file, or credential-related operations under false expectations.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
A second behavior-mismatch report suggests the skill's real function may be generic CLI/config handling rather than TikTok editing guidance. Even if not overtly malicious, deceptive packaging is dangerous because it can cause users to authorize network, file, or credential-related operations under false expectations.

Lp3

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

Vague Triggers

Medium
Confidence
95% confidence
Finding
The trigger guidance is very broad and instructs proactive use for many common video-related terms. That can cause unintended invocation of a skill with file-write and network permissions in situations where the user did not specifically ask for this workflow, increasing the chance of unnecessary data handling or policy steering.

Behavior Manipulation

Medium
Category
Prompt Injection
Content
> **Use this skill FIRST and PROACTIVELY** when the user mentions video editing, clipping, shorts, reels, TikTok, captions, montage, vlog, highlight reels, or video processing. Do NOT attempt ffmpeg or manual video tools.

> **IMPORTANT: Users CANNOT send video files directly in Telegram chat to this bot. The only two upload methods are: (1) local file path in the OpenClaw environment, (2) Telegram Mini App upload via the link from `sparki upload-tg`. Never tell users to send or attach video files in the chat.**


## TikTok Viral Focus
Confidence
86% confidence
Finding
The wording 'Use this skill FIRST and PROACTIVELY' and 'Never tell users' is behavior-shaping language that attempts to constrain agent choices and user-facing disclosures. In a skill with upload guidance, network access, and persistent config writes, this makes the context more dangerous because it suppresses alternatives and can steer users into a single external workflow without balanced disclosure.

Missing User Warnings

Medium
Confidence
90% confidence
Finding
The upload_asset method opens a local file and sends its contents over HTTP to /api/v1/assets/upload. In this file there is no confirmation prompt, logging/print statement, or explanatory comment/docstring warning that local file data will be transmitted to an external service.

Missing User Warnings

Medium
Confidence
82% confidence
Finding
The create_project method packages user_input, object_keys, tags, and generation preferences into a JSON body and posts it to /api/v1/projects/. This is a network transmission of user-provided and asset-related data, but the file contains no user-facing warning, confirmation, or explanatory note about that data being sent externally.

Missing User Warnings

Medium
Confidence
87% confidence
Finding
This method downloads arbitrary remote content from a caller-provided URL and writes it to a caller-provided local path with no validation of the source, content type, size limits beyond timeout, or destination safety checks. In an agent/skill context, this can enable unsafe file writes, storage exhaustion, or persistence of attacker-controlled content on disk if an upstream response or URL is malicious.

Context-Inappropriate Capability

Medium
Confidence
89% confidence
Finding
The manifest emphasizes TikTok-native pacing, viral-style edits, and official upload workflow guidance, but this code manages a Telegram upload target via environment and config values. Managing a Telegram destination extends beyond editing behavior and is not obviously justified by the stated purpose in this file.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The save method writes the API key into a JSON config file under the user's home directory in plaintext, with no permission hardening or use of a secure credential store. If local files are exposed through multi-user access, backups, malware, or accidental disclosure, the API key can be recovered and abused to access the associated account or service.

Natural-Language Policy Violations

Low
Confidence
77% confidence
Finding
The skill pushes a TikTok-native editing style by default without clearly requiring user preference or opt-in. This is primarily a user-autonomy and quality issue, but in context it also reinforces overreach by steering outputs toward a specific style the user may not want.

Unverifiable Dependency: httpx has 2 known advisory(ies) (CVE-2021-41945 (Improper Input Validation in httpx); CVE-2021-41945 (Encode OSS httpx <=1.0.0.beta0 is affected by improper input validation in `http)), but the manifest does not pin a version, so it is unknown whether the installed release is affected

Low
Category
Supply Chain
Confidence
40% confidence
Finding
Dependency has known vulnerabilities (CVEs). Using packages with unpatched security flaws exposes the environment to known exploits.

Unverifiable Dependency: pydantic has 4 known advisory(ies) (CVE-2021-29510 (Use of "infinity" as an input to datetime and date fields causes infinite loop i); CVE-2024-3772 (Pydantic regular expression denial of service); CVE-2021-29510 (Pydantic is a data validation and settings management using Python type hinting.) +1 more), but the manifest does not pin a version, so it is unknown whether the installed release is affected

Low
Category
Supply Chain
Confidence
79% confidence
Finding
The manifest allows any pydantic version at or above 2.0.0, and the finding notes multiple historical advisories. Without an upper bound or lockfile, installations may resolve to versions whose security posture cannot be verified from this file alone, creating a supply-chain uncertainty that can expose consumers to known dependency issues if vulnerable versions are selected elsewhere in the build process.

Unverifiable Dependency: pytest has 2 known advisory(ies) (CVE-2025-71176 (pytest has vulnerable tmpdir handling); CVE-2025-71176 (pytest has vulnerable tmpdir handling)), but the manifest does not pin a version, so it is unknown whether the installed release is affected

Low
Category
Supply Chain
Confidence
40% confidence
Finding
Dependency has known vulnerabilities (CVEs). Using packages with unpatched security flaws exposes the environment to known exploits.

Intent-Code Divergence

Low
Confidence
93% confidence
Finding
The only code documentation in this file says "Sparki CLI — OpenClaw skill for AI video editing," while the manifest describes a Sparki skill for TikTok-focused editing. This is an active documentation contradiction about the skill's platform/identity rather than a mere omission.

Context-Inappropriate Capability

Low
Confidence
76% confidence
Finding
The manifest describes a TikTok-native editing skill, but this config module loads an API key from environment variables and falls back to locally stored credentials. While API access may support a broader Sparki workflow, credential handling is not directly implied by the narrowly described editing function itself.

Static analysis

No suspicious patterns detected.