Back to skill

Security audit

POST AI Automation

Security checks for vulnerabilities and agentic risk

Overview

The skill is mostly purpose-aligned, but it needs review because it handles reusable social-media credentials, encourages automated posting, and contains unsafe CSV-driven file and URL handling.

Review this skill before installing. Use only test or limited-scope social accounts, do not store real cookies or session IDs in a shared or synced workspace, avoid unattended cron posting until safeguards are added, and process only trusted CSV files until URL and path validation are fixed.

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

T09 · Insecure Skill Coding Practices

Error
Location
scripts/batch_process.py:58
Finding
Unrestricted URL Retrieval Enables Server-Side Request Forgery and Local Resource Access<![CDATA[ ## Vulnerability Details **File Location**: `scripts/batch_process.py:58-75` **Vulnerability Type**: Server-Side Request Forgery (SSRF) and unrestricted URI handling **Risk Level**: High ### Vulnerable Code ```python def download_image(url, output_path): """ Download image from URL. Args: url: Image URL output_path: Destination path """ try: print(f" ↓ Downloading: {url}") with urlopen(url) as response: with open(output_path, "wb") as f: f.write(response.read()) print(f" ✅ Saved to: {output_path}") return True except Exception as e: print(f" ❌ Download failed: {e}") return False ``` ### Technical Analysis The `image_url` field is loaded directly from a user-supplied CSV file and passed to `urllib.request.urlopen()` without validating: - The URI scheme - The destination hostname - The resolved IP address - Redirect destinations - Whether the destination is a loopback, link-local, private, reserved, or cloud metadata address `urlopen()` supports more than ordinary public HTTPS requests. Depending on the runtime environment, an attacker may provide URLs targeting internal HTTP services or local resources through schemes such as `file:`. The operation is part of the declared product-image workflow, but unrestricted access to arbitrary network and local destinations exceeds the minimum privileges required. The legitimate feature only needs to retrieve product images from trusted public HTTPS locations. ### Attack Path 1. An attacker creates or modifies a product CSV processed by `batch_process.py`. 2. The attacker places a crafted URI in the `image_url` column, such as: - A loopback or internal service URL - A cloud instance metadata URL - A `file:` URI referencing a local file readable by the process - A public URL that redirects to a private destination 3. `load_products()` accepts the value without validation. ...[truncated 958 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Permit only `https` URLs; reject `file:`, `ftp:`, `data:`, and all other schemes. 2. Reject URLs containing embedded user credentials. 3. Use a strict allowlist of trusted image-hosting domains where feasible. 4. Resolve the hostname before connecting and reject all loopback, private, link-local, multicast, reserved, and unspecified IP addresses. 5. Repeat destination validation after every redirect. 6. Disable redirects unless they are required. 7. Block known cloud metadata destinations, including link-local metadata addresses. 8. Use a hardened HTTP client with explicit connection and read timeouts. 9. Verify that the returned content is a supported image before using it. 10. Run network retrieval in a sandbox with restricted outbound access and no unnecessary filesystem permissions. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/batch_process.py:68
Finding
Unbounded Remote Response Download Can Exhaust Memory and Disk<![CDATA[ ## Vulnerability Details **File Location**: `scripts/batch_process.py:68-70` **Vulnerability Type**: Uncontrolled resource consumption **Risk Level**: Medium ### Vulnerable Code ```python with urlopen(url) as response: with open(output_path, "wb") as f: f.write(response.read()) ``` ### Technical Analysis The code reads the entire response into memory with `response.read()` and then writes it to disk. It imposes no: - Connection timeout - Read timeout - Maximum response size - Streaming byte limit - `Content-Length` validation - MIME type restriction - Image-format validation Although `config.example.json` defines `defaults.max_file_size_mb`, the download function never reads or enforces that setting. A malicious or compromised image server can therefore return an extremely large response, delay indefinitely, or serve non-image content. Loading the complete response into memory magnifies the risk because memory consumption occurs before the write completes. ### Attack Path 1. An attacker supplies a CSV containing an attacker-controlled `image_url`. 2. The URL points to a server that returns a very large body, streams indefinitely, or responds extremely slowly. 3. `download_image()` opens the URL without an explicit timeout. 4. `response.read()` attempts to buffer the entire body in memory. 5. The process consumes excessive memory and subsequently writes an unrestricted amount of data to disk. 6. The Skill process or other services on the same host become unavailable due to memory, disk, or execution-time exhaustion. ### Impact Assessment The vulnerability can cause denial of service within the privileges and resource limits of the Skill process. Potential effects include: - Python process termination due to memory exhaustion - Workspace or filesystem exhaustion - Prolonged worker blockage - Failure of unrelated tasks sharing the same host - Increased bandwidth consumption The issue does not grant additional privileges, but it al ...[truncated 143 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Set explicit connection and read timeouts. 2. Stream the response in small fixed-size chunks rather than calling `response.read()` without a limit. 3. Enforce a strict cumulative byte limit using the configured `max_file_size_mb` value. 4. Reject a response immediately when its declared `Content-Length` exceeds the limit. 5. Continue enforcing the limit while streaming because `Content-Length` may be absent or false. 6. Accept only expected image MIME types. 7. Decode and validate the resulting file with a trusted image library before further processing. 8. Delete partial files after timeout, validation failure, or size-limit violation. 9. Apply process-level memory, disk, bandwidth, and execution-time quotas. ]]>

T05 · Unauthorized Access and Privilege Escalation

Warning
Location
scripts/batch_process.py:157
Finding
CSV-Controlled Product Name Can Escape the Intended Output Directory<![CDATA[ ## Vulnerability Details **File Location**: `scripts/batch_process.py:157-171` **Vulnerability Type**: Absolute-path injection and improper output-path containment **Risk Level**: Medium ### Vulnerable Code ```python # Generate videos output_dir = SKILL_DIR / "outputs" / product['name'][:20].replace(' ', '_') print(f"\n🎬 Generating {videos_per_product} videos...") # Call generate_videos.py cmd = [ sys.executable, str(SKILL_DIR / "scripts" / "generate_videos.py"), "--image", str(image_path), "--count", str(videos_per_product), "--platform", platforms.split(",")[0], # Use first platform "--output", str(output_dir) ] ``` The destination is subsequently created in `scripts/generate_videos.py:50-55`: ```python if output_dir: output_path = Path(output_dir) else: output_path = SKILL_DIR / "outputs" / datetime.now().strftime("%Y%m%d_%H%M%S") output_path.mkdir(parents=True, exist_ok=True) ``` ### Technical Analysis The output directory is derived from the untrusted `product_name` CSV field. Replacing spaces does not remove path separators, absolute-path prefixes, parent-directory components, or platform-specific path syntax. With `pathlib`, joining an absolute final path component can discard the preceding `SKILL_DIR / "outputs"` path. For example, a product name beginning with an absolute path can cause `output_dir` to resolve outside the intended output root. The value is passed as a subprocess argument list, so this is not shell command injection. The vulnerability is instead a filesystem boundary failure: untrusted metadata controls where directories are created and where future generated files would be written. ### Attack Path 1. An attacker supplies or modifies the input CSV. 2. The attacker sets `product_name` to an absolute path or another path-like value that escapes the intended output root. 3. `process_product()` derives `output_dir` directly from that value. 4. The path is passed to `generate_videos.py` ...[truncated 1145 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Do not use raw product names as filesystem paths. 2. Convert product names to a strict slug containing only a limited set such as ASCII letters, digits, hyphens, and underscores. 3. Explicitly reject absolute paths, path separators, drive prefixes, and `.` or `..` path components. 4. Resolve both the output root and candidate path before creating the directory. 5. Verify containment with `candidate.relative_to(output_root)` and reject the path if that operation fails. 6. Generate an internal identifier, such as a UUID, for the directory and retain the product name only as metadata. 7. Avoid writing into existing directories unless explicitly intended. 8. Run the Skill under a dedicated user that can write only to its designated workspace. Example containment pattern: ```python output_root = (SKILL_DIR / "outputs").resolve() safe_name = slugify(product["name"]) candidate = (output_root / safe_name).resolve() candidate.relative_to(output_root) ``` ]]>

T09 · Insecure Skill Coding Practices

Note
Location
config.example.json:2
Finding
Reusable API and Social-Media Credentials Are Stored in Plaintext Workspace Configuration<![CDATA[ ## Vulnerability Details **File Location**: `config.example.json:2-22` **Additional Locations**: `SKILL.md:23-42`, `README.md:13-18` **Vulnerability Type**: Insecure sensitive credential storage **Risk Level**: Low ### Vulnerable Configuration Pattern ```json { "postai": { "api_key": "YOUR_POST_AI_API_KEY", "account_id": "YOUR_ACCOUNT_ID", "endpoint": "https://api.postai.com/v1", "subscription": "lifetime" }, "tiktok": { "account": "@your_tiktok_account", "cookie_file": "/path/to/tiktok_cookies.json", "session_id": "your_session_id_here", "enabled": true }, "instagram": { "account": "@your_instagram_account", "cookie_file": "/path/to/instagram_cookies.json", "enabled": false }, "threads": { "account": "@your_threads_account", "cookie_file": "/path/to/threads_cookies.json", "enabled": false } } ``` The documented setup directs users to create and edit a workspace file: ```bash cp config.example.json config.json # Edit config with your credentials nano config.json ``` ### Technical Analysis The example file contains placeholders rather than real secrets, so no credential is shipped in the audited project. However, the documented workflow directs users to place an API key and reusable social-media session credentials in a plaintext `config.json` file within the Skill workspace. The project does not provide: - A `.gitignore` rule for `config.json` - Restrictive file-permission instructions - Environment-variable support - Secret-manager integration - Credential rotation guidance - Least-privilege token guidance Cookie files and session identifiers can represent authenticated social-media sessions. Their disclosure may therefore have greater impact than disclosure of a username or public account identifier. ### Attack Path 1. A user follows the installation instructions. 2. The user copies `config.example.json` to `config.json`. 3. The user enters a POST AI API key and Tik ...[truncated 1059 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Read secrets from environment variables or an operating-system secret store instead of storing them in the workspace. 2. Add `config.json`, cookie files, session exports, and other credential artifacts to `.gitignore`. 3. If a local file is unavoidable, create it with permissions limited to its owner, such as mode `0600`. 4. Separate non-sensitive settings from secret values. 5. Never print API keys, session identifiers, cookies, or authorization headers. 6. Prefer revocable, short-lived, narrowly scoped tokens over reusable browser session credentials. 7. Document credential rotation and immediate revocation procedures. 8. Warn users not to include configuration or cookie files in support bundles, backups, or source-control commits. 9. Validate that any future configurable API endpoint uses HTTPS and, preferably, restrict it to trusted POST AI domains before sending authorization data. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Rogue AgentSelf-Modification, Session Persistence
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
Findings (19)

Missing User Warnings

Medium
Confidence
90% confidence
Finding
The README promotes automated upload and scheduled posting to external social-media accounts without warning users that these actions can affect live accounts, trigger platform enforcement, or publish unintended content. In this context, automation against TikTok/Instagram increases the risk of account misuse or mass unwanted posting if the tool is misconfigured or credentials are stolen.

Missing User Warnings

Medium
Confidence
94% confidence
Finding
The README instructs users to store API keys, account identifiers, TikTok cookies, and session IDs in a local config file, but it provides no guidance on protecting those secrets or avoiding accidental disclosure. This is dangerous because session cookies and API credentials can enable account takeover, unauthorized posting, or abuse of paid services if they are leaked through source control, logs, backups, or shared workspaces.

Session Persistence

Medium
Category
Rogue Agent
Content
## Use Cases

- **Affiliate Marketing**: Generate 10-20 videos per product automatically
- **E-commerce**: Create product showcases at scale
- **Content Automation**: Daily posting without manual effort
- **A/B Testing**: Test multiple video variants to see what works
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.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The setup instructions direct users to place API keys, cookies, and session identifiers into a local config file without any security handling guidance. These credentials can grant API access and account takeover capability if exposed through source control, logs, backups, or multi-user environments, especially since cookies and session IDs may bypass normal authentication flows.

Missing User Warnings

Medium
Confidence
90% confidence
Finding
The skill encourages automated uploading and scheduled posting to external social media accounts without any explicit warning, confirmation, or guardrails around irreversible external actions. In an agent setting, this can cause unauthorized posting, spammy behavior, account enforcement actions, or reputational damage if the automation is triggered unexpectedly or with the wrong content.

Natural-Language Policy Violations

Medium
Confidence
95% confidence
Finding
The configuration sets `defaults.language` to `id`, which imposes a specific language choice by default. Under the policy, locale constraints should either be user-selectable or clearly justified as region-specific; this file provides neither.

Natural-Language Policy Violations

Medium
Confidence
96% confidence
Finding
The generated default caption uses Indonesian words and currency formatting such as "Produk", "Jangan sampai kehabisan!", and "Rp" with no option for the user to select language or locale. This is a natural-language policy issue because the skill imposes a specific language/locale in user-facing content without opt-in or justification.

Natural-Language Policy Violations

Medium
Confidence
94% confidence
Finding
The default caption template includes fixed Indonesian phrases such as "Jangan sampai kehabisan! Order sekarang:". This imposes a specific language on generated output with no user opt-in or documented region-specific justification, which matches the language/locale policy violation category.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
]

    print(f"   Running: {' '.join(cmd)}")
    result = subprocess.run(cmd, capture_output=True, text=True)

    if result.returncode != 0:
        print(f"❌ Generation failed:")
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
]

    print(f"   Running: {' '.join(cmd)}")
    result = subprocess.run(cmd, capture_output=True, text=True)

    if result.returncode != 0:
        print(f"❌ Generation failed:")
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Natural-Language Policy Violations

Medium
Confidence
89% confidence
Finding
The function signature sets `language="id"` as the default, which imposes a specific language choice unless the user explicitly overrides it. This is a natural-language locale policy concern because the skill defaults to one language rather than remaining neutral or prompting for preference.

External Transmission

Medium
Category
Data Exfiltration
Content
List of generated video paths
    """
    api_key = POSTAI_API.get("api_key")
    endpoint = POSTAI_API.get("endpoint", "https://api.postai.com/v1")

    if not api_key:
        print("❌ POST AI API key not configured in config.json")
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
List of generated video paths
    """
    api_key = POSTAI_API.get("api_key")
    endpoint = POSTAI_API.get("endpoint", "https://api.postai.com/v1")

    if not api_key:
        print("❌ POST AI API key not configured in config.json")
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
List of generated video paths
    """
    api_key = POSTAI_API.get("api_key")
    endpoint = POSTAI_API.get("endpoint", "https://api.postai.com/v1")

    if not api_key:
        print("❌ POST AI API key not configured in config.json")
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
List of generated video paths
    """
    api_key = POSTAI_API.get("api_key")
    endpoint = POSTAI_API.get("endpoint", "https://api.postai.com/v1")

    if not api_key:
        print("❌ POST AI API key not configured in config.json")
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
# Placeholder: Replace with actual POST AI API call
        # Example:
        # response = requests.post(
        #     f"{endpoint}/generate",
        #     headers={"Authorization": f"Bearer {api_key}"},
        #     json={
Confidence
80% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Natural-Language Policy Violations

Medium
Confidence
94% confidence
Finding
The command-line argument declares `--language` with `default="id"`, causing the tool to generate voice-over in Indonesian unless the user changes it. That is a language/locale default that is not presented as opt-in or justified as region-specific.

Natural-Language Policy Violations

Low
Confidence
92% confidence
Finding
The configuration example sets `"language": "id"` as the default language, which implies a specific locale preference. The README does not state that this is optional, offer alternatives, or justify the locale as a region-specific requirement.

Natural-Language Policy Violations

Low
Confidence
90% confidence
Finding
This CSV contains user-facing natural-language content such as product names and caption templates written only in Indonesian. Under the language/locale policy, forcing a specific language without opt-in can be a policy violation when no alternative locale or user choice is provided.