Back to skill

Security audit

Larry — Autonomous TikTok Slideshow Agent

Security checks for vulnerabilities and agentic risk

Overview

The skill mostly does what it claims, but it ships real-looking credentials and can use them to create TikTok/Postiz drafts through network calls with weak destination safeguards.

Review before installing. Do not use the bundled config.json values; rotate any exposed keys/passwords, replace them with your own least-privilege credentials, restrict file permissions, and only point postiz_base_url at a trusted HTTPS or verified local instance. Treat --auto/cron as unattended social-media publishing workflow and use dry-run/manual review until you are comfortable with the drafts, logs, and external uploads.

Vulnerability Patterns
  • Insecure DependenciesIntroduces malicious components through unsafe dependency sources
  • 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 (4)

T09 · Insecure Skill Coding Practices

Error
Location
config.json:2
Finding

Production Credentials Stored in Plaintext Configuration

Content
View full analysis
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Error
Location
scripts/postiz.py:10
Finding

Postiz Credentials, Session Cookies, and API Tokens Can Be Sent over Plain HTTP

Content
View full analysis
dict: """Login + Cookie als Header zurückgeben (Postiz Self-Hosted Auth).""" resp = requests.post( f"{base_url}/auth/login", json={"email": config.get("postiz_email", ""), "password": config.get("postiz_password", ""), "provider": "LOCAL"}, timeout=10 ) if not resp.ok: raise Exception(f"Postiz Login fehlgeschlagen: {resp.status_code}") token = resp.cookies.get("auth") if not token: raise Exception("Kein Auth-Cookie erhalten") return {"Cookie": f"auth={token}"} ``` Cookie-authenticated content creation: ```python base_url = config.get("postiz_base_url", "https://api.postiz.com/v1") auth = _get_auth_headers(base_url, config) headers = {**auth, "Content-Type": "application/json"} resp = requests.post( f"{base_url}/posts", headers=headers, json=payload, timeout=30 ) ``` Cookie-authenticated media upload: ```python with open(img_path, "rb") as f: resp = requests.post( f"{base_url}/media/upload-simple", headers=auth, files={"file": (path.name, f, "image/jpeg")}, timeout=60 ) ``` Bearer-token analytics request: ```python base_url = config.get("postiz_base_url", "https://api.postiz.com/v1") api_key = config["postiz_api_key"] headers = {"Authorization": f"Bearer {api_key}"} resp = requests.get( f"{base_url}/posts/{post_id}/analytics", headers=headers, timeout=10 ) ``` ...[truncated 2671 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/generate_slides.py:21
Finding

Predictable Shared Temporary Paths Permit Symlink-Based File Overwrite

Content
View full analysis
Remediation
View remediation

T08 · Insecure Dependencies

Note
Location
SKILL.md:28
Finding

Unpinned Runtime Dependencies Are Installed without Integrity Verification

Content
View full analysis
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
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
  • Taint TrackingDirect Taint Flow, Variable-Mediated Taint Flow, Credential Exfiltration Chain
Findings (31)

Tainted flow: 'headers' from os.environ.get (line 56, credential/environment) → requests.post (network output)

Critical
Category
Data Flow
Confidence
90% confidence
Finding

Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Content

Scanner excerpt · scripts/generate_slides.py (reported line 60)May include surrounding context.

python
payload = {"prompt": prompt, "width": 1024, "height": 1024, "steps": NVIDIA_STEPS}

    try:
        resp = requests.post(NVIDIA_ENDPOINT, headers=headers, json=payload, timeout=120)
        if resp.status_code == 200:
            raw_bytes = _b64.b64decode(resp.json()["artifacts"][0]["base64"])
            img = Image.open(BytesIO(raw_bytes)).convert("RGB")

Session Persistence

Medium
Category
Rogue Agent
Confidence
79% confidence
Finding

The documented autonomous cron-based mode implies ongoing operation using persisted local configuration, API keys, and connected TikTok/Postiz sessions to continue posting across runs. In this context, session persistence is not inherently malicious, but it is security-relevant because compromise of the host, config, or connected Postiz environment could let an attacker post content or abuse affiliated accounts without additional user interaction.

Content

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

text
# Manual single post:
"Larry, create a TikTok post for [portal] about [topic]"

# Autonomous mode (via cron):
python3 ~/.openclaw/skills/larry/scripts/larry.py --portal my-portal --auto

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The skill documents an autonomous posting mode and persistent performance logging, but it does not present a clear, explicit warning to users that enabling --auto will schedule or upload content to TikTok and store ongoing activity data. This creates a real safety and consent issue: users may trigger external actions and data retention without fully understanding the consequences, increasing the risk of unintended posting, reputational harm, or privacy/compliance problems.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
83% confidence
Finding

This manifest/config contains multiple natural-language values that target German-language brands and hashtags, such as German domains and German hashtags, but provides no indication that the skill offers a language or locale choice. Under the policy, forcing a specific language or locale without user opt-in can be a natural-language policy violation unless the regional constraint is clearly documented and justified.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

This JSON content is entirely authored in German, including the title, slide text, caption hook, and final caption, with no indication that the user can choose another language or that the skill is restricted to German-speaking users. The policy explicitly calls for flagging language or locale constraints that are imposed without user opt-in or clear justification.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

This manifest-like JSON contains all user-facing title, slide, caption, and CTA text in German, indicating a fixed language for the skill's generated content. The file does not offer any language or locale choice, nor does it document that the skill is intentionally restricted to German users or a Germany-specific compliance context.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Medium
Category
Not specified by scanner
Confidence
98% confidence
Finding

The top-level documentation describes the skill as generating images via NVIDIA FLUX.1-schnell, while the actual endpoint constant at L013 targets black-forest-labs/flux.1-dev. Because the documentation identifies a different model than the one actually called, this is a direct intent-code divergence.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The top-level natural-language description is entirely in German and presents the skill behavior as German by default, with no indication that users may choose another language or that the tool is region-specific. This matches the language/locale policy violation category because it imposes a specific language without opt-in.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The function documentation says it generates images via NVIDIA FLUX.1-schnell, but the configured endpoint at L013 is explicitly for flux.1-dev and _generate_single also documents FLUX.1-dev usage. This is an active contradiction in the code documentation about which model/service is invoked.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
80% confidence
Finding

The function transmits generated prompts derived from concept, portal, and slide data to an external third-party service. In a skill context, those fields may contain sensitive or proprietary user content, so sending them off-box without explicit consent, minimization, or policy checks can create a real data exposure risk even though the destination appears legitimate.

Content

Scanner excerpt · scripts/generate_slides.py (reported line 60)May include surrounding context.

python
payload = {"prompt": prompt, "width": 1024, "height": 1024, "steps": NVIDIA_STEPS}

    try:
        resp = requests.post(NVIDIA_ENDPOINT, headers=headers, json=payload, timeout=120)
        if resp.status_code == 200:
            raw_bytes = _b64.b64decode(resp.json()["artifacts"][0]["base64"])
            img = Image.open(BytesIO(raw_bytes)).convert("RGB")

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

The script writes draft post data to the queue directory during dry runs and later appends performance data to a persistent log file, but these data-affecting writes are not disclosed in the CLI help or through any confirmation before execution. Although there is a runtime log after the draft write, there is no prior warning that running the skill will create or modify local files.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The non-dry-run path sends post data, including captions and image paths, to an external posting integration via upload_to_tiktok. While posting is part of the skill's purpose, the current interface does not clearly warn at invocation time that live execution will transmit content to an external service unless the user infers it from the script name and comments.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The module title, docstrings, and all user-facing status messages are written in German, indicating the skill is designed to operate in a specific language without offering any user opt-in or alternative locale. The policy for this audit flags language-forcing behavior unless the locale restriction is explicitly justified as region-specific.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The module docstring describes a narrowly scoped integration that uploads TikTok carousels as drafts for later manual publishing. However, the same file also contains check_stats, which reads a local performance log, fetches post analytics from the API, and rewrites the log with updated metrics. That is a different operational intent than the stated upload-only behavior.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
85% confidence
Finding

This code transmits email and password credentials to a configurable base_url without validating the destination. If an attacker can influence configuration, credentials can be sent to an attacker-controlled endpoint, resulting in account compromise and possible broader service access.

Content

Scanner excerpt · scripts/postiz.py (reported line 12)May include surrounding context.

python
def _get_auth_headers(base_url: str, config: dict) -> dict:
    """Login + Cookie als Header zurückgeben (Postiz Self-Hosted Auth)."""
    resp = requests.post(
        f"{base_url}/auth/login",
        json={"email": config.get("postiz_email", ""),
              "password": config.get("postiz_password", ""),

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The function sends email and password from configuration to a remote authentication endpoint, which is a safety-relevant network operation involving credentials. While the function has an internal docstring, there is no explicit user-facing disclosure, confirmation, or warning that stored credentials will be used and transmitted over the network.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
88% confidence
Finding

The function uses a configurable remote API base URL with a production default and then authenticates and uploads media to that endpoint. In an agent-skill context, allowing arbitrary external endpoints materially increases the risk of data exfiltration of credentials and media if configuration is tampered with.

Content

Scanner excerpt · scripts/postiz.py (reported line 32)May include surrounding context.

python
Uploaded Slideshow als TikTok Draft via Postiz.
    Self-Hosted: Cookie-basierte Auth statt Bearer Token.
    """
    base_url = config.get("postiz_base_url", "https://api.postiz.com/v1")
    account_id = portal.get("tiktok_account_id", "")

    auth = _get_auth_headers(base_url, config)

External Transmission

Medium
Category
Data Exfiltration
Confidence
80% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · scripts/postiz.py (reported line 64)May include surrounding context.

python
}

    try:
        resp = requests.post(
            f"{base_url}/posts",
            headers=headers,
            json=payload,

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
89% confidence
Finding

The code reads local image files and uploads them to the Postiz media endpoint, which transfers user data off the local system. Although errors are printed, there is no upfront disclosure in the code warning that local media contents will be sent to an external or self-hosted service.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
84% confidence
Finding

This function retrieves analytics using an API key from a configurable remote endpoint and writes returned data to local storage. If the endpoint is attacker-controlled, the API key can be disclosed and untrusted response data can be persisted, creating credential exposure and integrity risks.

Content

Scanner excerpt · scripts/postiz.py (reported line 108)May include surrounding context.

python
Ruft Performance-Daten für alle gespeicherten Posts ab.
    Wird täglich aufgerufen um Hook-Performance zu tracken.
    """
    base_url = config.get("postiz_base_url", "https://api.postiz.com/v1")
    api_key = config["postiz_api_key"]
    headers = {"Authorization": f"Bearer {api_key}"}

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
86% confidence
Finding

This function reads a bearer API key, retrieves remote analytics for stored posts, and overwrites a local JSON log with updated metrics. The operation combines credential use, network access, and file writes, but the code provides no explicit user-facing disclosure beyond internal comments/docstrings.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
97% confidence
Finding

The prompt explicitly instructs the model that the generated hook must be 'Auf Deutsch', which hard-codes a language choice. The policy allows locale/language constraints only when the user is offered a choice or the constraint is clearly documented and justified; this file does not show such opt-in or justification.

Content

No source excerpt is available for this finding.

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
70% confidence
Finding

subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Content

Scanner excerpt · scripts/research.py (reported line 127)May include surrounding context.

python
Antworte NUR mit dem Hook-Text, nichts weiter."""

    try:
        result = subprocess.run(
            ["claude", "-p", prompt],
            capture_output=True, text=True, timeout=30
        )

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
88% confidence
Finding

With no manifest available, there is no declared scope that would justify launching external processes. The code uses subprocess.run to invoke the local Claude CLI, which is a materially broader capability than pure in-process research/content generation logic and can have side effects outside the module's own data handling.

Content

No source excerpt is available for this finding.

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
70% confidence
Finding

subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Content

Scanner excerpt · scripts/research.py (reported line 184)May include surrounding context.

python
}}"""

    try:
        result = subprocess.run(
            ["claude", "-p", prompt],
            capture_output=True, text=True, timeout=45
        )

Static analysis

No suspicious patterns detected.