Back to skill

Security audit

lovart-api

Security checks for vulnerabilities and agentic risk

Overview

This Lovart media skill mostly matches its stated purpose, but it needs Review because it broadly redirects media requests to Lovart, persists and reuses project context, and has weak safeguards around endpoints, TLS, and downloads.

Install only if you are comfortable sending prompts, selected reference files, project/thread metadata, and generated artifacts to Lovart. Before use, review or clear ~/.lovart/state.json, confirm the active project and thread before sensitive work, do not enable LOVART_INSECURE_SSL, keep LOVART_BASE_URL on the official endpoint, and avoid uploading private files unless you intentionally want Lovart to process them.

Vulnerability Patterns
  • Skill Instruction HijackingAlters the agent's session goals or safety constraints when the skill loads
  • Insecure Skill Coding PracticesFinds exploitable flaws such as hardcoded secrets or command injection
  • 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 (4)

T01 · Skill Instruction Hijacking

Error
Location
SKILL.md:39
Finding

Broad instruction and output hijacking redirects media requests to Lovart

Content
View full analysis
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Error
Location
scripts/agent_skill.py:19
Finding

Authentication and user data can be redirected to an arbitrary endpoint with TLS verification disabled

Content
View full analysis
dict: ts = str(int(time.time())) sig = hmac.new( self.secret_key.encode(), f"{method}\n{path}\n{ts}".encode(), hashlib.sha256, ).hexdigest() return { "X-Access-Key": self.access_key, "X-Timestamp": ts, "X-Signature": sig, "X-Signed-Method": method, "X-Signed-Path": path, } def _request(self, method: str, path: str, body=None, params=None, retries: int = None) -> dict: if retries is None: retries = 3 if method == "GET" else 1 url = f"{self.base_url}{path}" if params: url += "?" + urllib.parse.urlencode(params) data = json.dumps(body).encode() if body is not None else None last_err = None idempotency_key = uuid.uuid4().hex if method == "POST" else None for attempt in range(retries): headers = self._sign(method, path) headers["Content-Type"] = "application/json" headers["User-Agent"] = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_ ...[truncated 3186 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/agent_skill.py:248
Finding

Unrestricted artifact URL downloads permit local-network access and unbounded file writes

Content
View full analysis
list: """Download all artifacts from a result dict to local files. Idempotent: file names are derived from URL hash, and existing files are skipped. Returns list of {"type", "url", "local_path", "new": bool}.""" import os os.makedirs(output_dir, exist_ok=True) downloaded = [] seen_urls = set() for item in result.get("items", []): for artifact in item.get("artifacts", []): url = artifact.get("content", "") atype = artifact.get("type", "unknown") if not url or url in seen_urls: continue seen_urls.add(url) ext = os.path.splitext(url.split("?")[0])[-1] or ( ".mp4" if atype == "video" else ".png" ) url_hash = hashlib.sha1(url.encode()).hexdigest()[:12] local_path = os.path.join(output_dir, f"{prefix}_{url_hash}{ext}") if os.path.exists(local_path) and os.path.getsize(local_path) > 0: downloaded.append({"type": atype, "url": url, "local_path": local_path, "new": False}) continue try: req = urllib.request.Request(url, headers={ "User-Agent": "Mozilla/5.0", "Referer": "https://www.lovart.ai/", }) with urllib.request.urlopen(req, timeout=60, context=_ssl_ctx) as resp: with open(local_path, "wb") as f: f.write(resp.read()) downloaded.append({"type": atype, "url": url, "local_path": local_path, "new": True}) except Exception: downloaded.ap ...[truncated 2945 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Note
Location
scripts/agent_skill.py:536
Finding

Conversation metadata is persisted without explicitly restrictive filesystem permissions

Content
View full analysis
dict: import os if not os.path.exists(self.path): return {} try: with open(self.path, "r") as f: data = json.load(f) if "project_id" in data and "projects" not in data: old_pid = data.pop("project_id") data["active_project"] = old_pid data["projects"] = {old_pid: {"name": "default", "created_at": time.strftime("%Y-%m-%dT%H:%M:%S%z", time.localtime())}} self.save(data) return data except (json.JSONDecodeError, IOError): return {} def save(self, data: dict): self._ensure_dir() with open(self.path, "w") as f: json.dump(data, f, indent=2, ensure_ascii=False) ``` ```python def upsert_thread(self, thread_id: str, topic: str, project_id: Optional[str] = None): data = self.load() threads = data.get("threads", []) pid = project_id or data.get("active_project") found = False for t in threads: if t["id"] == thread_id: ...[truncated 2310 chars]
Remediation
View remediation
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Rogue AgentSelf-Modification, Session Persistence
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (16)

Vague Triggers

High
Category
Not specified by scanner
Confidence
98% confidence
Finding

The trigger conditions are extremely broad, matching common verbs like create, make, design, draw, and their multilingual equivalents, plus generic project/history words. This can cause the skill to activate in many normal conversations where the user did not explicitly intend to use Lovart, leading to unintended network actions, reuse of saved state, and possible disclosure of prior project or thread context.

Content

No source excerpt is available for this finding.

Anti-Refusal Statement

High
Category
Anti-Refusal
Confidence
93% confidence
Finding

The anti-refusal language, especially 'You CAN generate directly - never say you cannot,' pressures the agent to comply broadly rather than pause for policy, safety, or consent checks. Such statements are a red flag because they can undermine safer orchestration behavior and make unintended or risky invocations more likely when combined with the skill's broad trigger rules.

Content

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

md
creation request in any language — draw, generate, create, design, make, 画, 生成,
  制作, 创作, 设计 combined with image, video, audio, music, song, BGM, poster, etc.
  (2) Lovart project/thread management — 项目, 对话, project, thread, conversation,
  history, 历史, 切换, switch. You CAN generate directly - never say you cannot.
user-invocable: true
version: 1.1.0
author: Lovart (lovartai)

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
95% confidence
Finding

The skill exposes capabilities that can read environment secrets, read/write local files, and access the network, but it does not declare a restrictive tool scope such as permissions or allowed-tools. That makes the effective privilege boundary unclear and increases the blast radius if the skill is triggered unexpectedly or behaves unsafely, especially because it also persists state locally and interacts with remote services.

Content

No source excerpt is available for this finding.

Autonomous Decision Making

Medium
Category
Excessive Agency
Confidence
85% confidence
Finding

Skill enables autonomous high-impact decisions without human-in-the-loop verification. Critical operations (destructive commands, financial transactions, data deletion) should require explicit user confirmation.

Content

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

md
- `"done"` — Generation complete. Send the downloaded files to the user.
- `"pending_confirmation"` — A high-cost tool (e.g. video, or a premium-quality image variant) needs user approval before credits are consumed.
  **You MUST ask the user for explicit confirmation before proceeding. Do NOT auto-confirm.**
  1. Show the user: "This will cost approximately {estimated_cost} credits. Shall I proceed? (yes/no)"
  2. **WAIT for user response.** Only if user explicitly says yes/confirm/proceed, run:
     `confirm --thread-id THREAD_ID --json --download`

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The documentation instructs the agent to read and write ~/.lovart/state.json and to reuse project and thread history, but it does not clearly disclose to users that local state is persisted and prior conversation context may be reused automatically. Users may unknowingly expose older content or operate on the wrong project because they were not informed of this persistence behavior.

Content

No source excerpt is available for this finding.

Autonomous Decision Making

Medium
Category
Excessive Agency
Confidence
89% confidence
Finding

The instruction 'Do NOT ask the user' when an active project exists authorizes autonomous selection of persisted context for subsequent operations. This removes an important consent checkpoint and can cause actions to occur in an old or unintended project, especially when multiple projects exist or the conversation topic has changed.

Content

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

md
**Step 1: `config --json`**
- Check local state (`~/.lovart/state.json`) for `active_project`
- If `active_project` is set → proceed to Step 2. Do NOT create a new project. Do NOT ask the user.
- If `active_project` is missing → ask the user: "Do you have an existing Lovart project ID, or should I create a new one?" **WAIT for their answer.**
- Save with: `project-add --project-id PID --name "name"`

Session Persistence

Medium
Category
Rogue Agent
Confidence
90% confidence
Finding

The skill relies on persistent local session state and directs the agent to silently reuse it when available. That persistence can cause cross-session confusion, privacy leakage, or unintended association of new user requests with previous projects and threads, especially on shared systems or multi-user environments.

Content

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

md
**Step 1: `config --json`**
- Check local state (`~/.lovart/state.json`) for `active_project`
- If `active_project` is set → proceed to Step 2. Do NOT create a new project. Do NOT ask the user.
- If `active_project` is missing → ask the user: "Do you have an existing Lovart project ID, or should I create a new one?" **WAIT for their answer.**
- Save with: `project-add --project-id PID --name "name"`

Ssd 3

Medium
Category
Not specified by scanner
Confidence
97% confidence
Finding

The skill mandates reusing recent threads and existing projects by default, which carries forward prior user prompts, attachments, and generated content into later requests without obtaining fresh consent. In a shared or simply changed context, this can leak earlier sensitive material into new generations or cause operations to run against the wrong historical context.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The skill allows TLS certificate and hostname verification to be disabled via LOVART_INSECURE_SSL=1, which enables man-in-the-middle interception of all API traffic, including access keys, signatures, prompts, project metadata, and returned artifact URLs. Because this skill handles authenticated requests to a remote service, weakening transport security materially increases the chance of credential theft and response tampering.

Content

No source excerpt is available for this finding.

Session Persistence

Medium
Category
Rogue Agent
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.

Content

Scanner excerpt · scripts/agent_skill.py (reported line 154)May include surrounding context.

python
def save_project(self, project_id: str = "", canvas: str = "",
                     project_name: str = "", project_type: int = 3) -> dict:
        """Create (empty project_id) or update (with project_id) a project."""
        body = {
            "project_id": project_id,
            "canvas": canvas,

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
88% confidence
Finding

The manifest focuses on generating media through Lovart and managing Lovart-side projects, threads, and settings. The upload_file function gives the skill general local file-read capability, which is broader than the stated purpose and could expose unrelated local data if misused.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
89% confidence
Finding

The download path writes remote content to local disk automatically when invoked, but there is no inline disclosure or confirmation at write time beyond a command-line flag. In an agent setting, silent file writes increase the risk of unexpected persistence of untrusted content, disk consumption, and storing potentially sensitive generated artifacts on the host.

Content

No source excerpt is available for this finding.

Unbounded Resource Access

Medium
Category
Excessive Agency
Confidence
75% confidence
Finding

Skill allows unbounded resource consumption (API calls, storage, compute). Without rate limits or quotas, a compromised or misbehaving agent can cause denial-of-service or cost overruns.

Content

Scanner excerpt · scripts/agent_skill.py (reported line 410)May include surrounding context.

python
def chat(self, prompt: str, project_id: Optional[str] = None,
             attachments=None, thread_id=None,
             timeout=None, auto_create_project=True,
             prefer_models=None, include_tools=None, exclude_tools=None,
             mode: Optional[str] = None, subjects=None, kits=None) -> dict:
        """Send → poll → get result. One-shot convenience method.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The skill silently persists project/thread metadata to ~/.lovart/state.json whenever state-changing operations occur, without user-facing disclosure in those execution paths. In an agent context, this creates covert local persistence of conversation history and identifiers, which may expose sensitive workflow metadata to other local users or later processes.

Content

No source excerpt is available for this finding.

Cloud Storage Exfiltration

Medium
Category
Data Exfiltration
Confidence
55% confidence
Finding

Data is uploaded to cloud storage (S3 / GCS / Azure Blob). This may be a legitimate backup or exfiltration to an external bucket. Manual review is recommended.

Content

Scanner excerpt · scripts/agent_skill.py (reported line 1056)May include surrounding context.

python
print(json.dumps({"project_id": pid}))

        elif args.command == "upload":
            url = skill.upload_file(args.file)
            print(json.dumps({"url": url}))

        elif args.command == "upload-artifact":

Description-Behavior Mismatch

Low
Category
Not specified by scanner
Confidence
86% confidence
Finding

The manifest mentions Lovart project, thread, and user-setting management, which implies server-side operations, but this file additionally stores active project and thread history in ~/.lovart/state.json. Local persistence changes the skill's behavior and data footprint beyond the manifest description.

Content

No source excerpt is available for this finding.

Static analysis

Detected: suspicious.insecure_tls_verification

HTTPS certificate verification is disabled.

Warn
Code
suspicious.insecure_tls_verification
Location
scripts/agent_skill.py:23