Back to skill

Security audit

Social Autopilot

Security checks for vulnerabilities and agentic risk

Overview

This skill openly automates social posting, but it needs careful review because it can publish to multiple public accounts from a short command and handles account credentials unsafely.

Install only if you are comfortable giving the skill real posting rights on the connected accounts. Use test accounts first, limit API scopes, review generated content before publishing, avoid the generic all-platform trigger, pin installer and dependency versions, store YouTube tokens outside the project with restrictive permissions, and run video rendering in an isolated environment without production credentials.

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
scripts/yt_auth.py:52
Finding
Unsafe Pickle Deserialization Enables Arbitrary Local Code Execution<![CDATA[ ## Vulnerability Details **File Location**: `scripts/yt_auth.py`, lines 52-59 **Vulnerability Type**: Unsafe deserialization **Risk Level**: High ### Vulnerable Code ```python # Load saved token if it exists (supports both pickle and JSON) if TOKEN_FILE.exists(): logger.info("Loading saved credentials from %s", TOKEN_FILE) try: # Try pickle format first (local development) with open(TOKEN_FILE, "rb") as token: credentials = pickle.load(token) except (pickle.UnpicklingError, EOFError): ``` ### Technical Analysis The application passes the contents of the predictable `youtube_token.json` file to `pickle.load()`. Python pickle data is executable serialization rather than a safe data-only format. During deserialization, an attacker-controlled object can invoke arbitrary functions through methods such as `__reduce__`. The `.json` extension is also misleading because the application first treats the file as a pickle. The JSON fallback does not mitigate this issue: a valid malicious pickle will execute before any fallback occurs. Exploitation requires the attacker to create or replace `youtube_token.json`. Potential sources include another process with project-directory write access, an untrusted archive or CI artifact, a compromised workspace, or a malicious repository contribution that introduces the token file. ### Attack Path 1. The attacker obtains write access to the project root or controls an artifact copied into it. 2. The attacker creates a malicious pickle payload and saves it as `youtube_token.json`. 3. The user invokes YouTube posting or another operation that calls `get_authenticated_service()`. 4. The function opens the file and passes it to `pickle.load()`. 5. The pickle reconstruction routine invokes the attacker-selected Python callable. 6. Arbitrary code executes with the operating-system privileges and environment access of the Skill process. ### Impact Assessment Successful exploitation pr ...[truncated 625 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Remove pickle support completely; OAuth credentials do not require executable object serialization. 2. Store credentials in JSON and load them with the Google authentication library: ```python token_data = json.loads(TOKEN_FILE.read_text(encoding="utf-8")) credentials = Credentials.from_authorized_user_info( token_data, scopes=YOUTUBE_SCOPES, ) ``` 3. Reject files that are not valid JSON rather than attempting another serialization format. 4. Validate that expected fields and OAuth endpoints are present before constructing credentials. 5. Store the token outside the repository in a dedicated user configuration or secret-storage directory. 6. Apply restrictive permissions and reject symbolic links before reading the token. 7. Delete and rotate existing token files after deployment because an attacker may already have replaced or copied them. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/yt_auth.py:89
Finding
YouTube OAuth Credentials Are Persisted Without Restrictive File Protections<![CDATA[ ## Vulnerability Details **File Location**: `scripts/yt_auth.py`, lines 89-92 **Vulnerability Type**: Insecure plaintext credential storage **Risk Level**: Medium ### Vulnerable Code ```python # Save credentials for next run with open(TOKEN_FILE, "wb") as token: pickle.dump(credentials, token) logger.info("Saved credentials to %s", TOKEN_FILE) ``` The destination is defined in `scripts/yt_config.py` as a predictable file in the project root: ```python BASE_DIR = Path(__file__).resolve().parent.parent CLIENT_SECRETS_FILE = BASE_DIR / "client_secrets.json" TOKEN_FILE = BASE_DIR / "youtube_token.json" ``` ### Technical Analysis The code persists reusable YouTube OAuth credentials to a predictable project-root file. It relies on the process's ambient umask and does not explicitly enforce owner-only permissions, reject symbolic links, encrypt the credential material, or use an operating-system secret store. The serialized credentials can include an access token, refresh token, client identifier, client secret, token endpoint, and authorized scopes. A refresh token may remain useful after the short-lived access token expires. Writing secrets inside the project directory also increases the chance that they will be included in source-control commits, CI artifacts, shared archives, backups, or container images. ### Attack Path 1. A local user, shared build process, backup collector, or CI artifact consumer gains read access to the project directory. 2. The party copies `youtube_token.json`. 3. The serialized credentials are decoded to recover the OAuth access and refresh token material. 4. The refresh token is exchanged for a new access token through Google's OAuth endpoint. 5. The attacker invokes YouTube APIs within the granted scopes until the authorization is revoked. A separate write-oriented attack is also possible if an attacker places a symbolic link at the token path before authentication. Because the code opens the path directly for ...[truncated 742 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Use an operating-system credential store or CI secret manager instead of a project-root token file. 2. If file storage is unavoidable, place the token in a private user configuration directory outside the repository. 3. Create the file atomically with owner-only permissions, such as mode `0600`, rather than relying on the ambient umask. 4. Refuse to follow symbolic links and verify that the destination is a regular file owned by the expected user. 5. Store credentials as JSON rather than pickle. 6. Add `youtube_token.json` and `client_secrets.json` to source-control, packaging, backup, and CI artifact exclusion rules. 7. Avoid logging credential contents or serialized objects. 8. Revoke and regenerate tokens suspected of having been exposed. 9. Request only the minimum OAuth scopes required for the implemented operations. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/html_video_generator.py:887
Finding
Chromium Security Sandbox Is Unconditionally Disabled During HTML Rendering<![CDATA[ ## Vulnerability Details **File Location**: `scripts/html_video_generator.py`, lines 887-903 **Vulnerability Type**: Browser process isolation disabled **Risk Level**: Medium ### Vulnerable Code ```python def _make_hti(output_dir: Path) -> Html2Image: """Create one Html2Image instance (launches Chrome once).""" import os, shutil is_ci = os.getenv("CI") == "true" or os.getenv("GITHUB_ACTIONS") == "true" flags = [ "--no-sandbox", "--disable-dev-shm-usage", "--disable-gpu", "--headless=new", "--disable-software-rasterizer", ] # Find chromium/chrome binary browser_path = None for candidate in ["chromium-browser", "chromium", "google-chrome", "google-chrome-stable"]: found = shutil.which(candidate) if found: browser_path = found break ``` ### Technical Analysis The renderer launches Chromium with `--no-sandbox` in every environment. Chromium's sandbox is a major defense-in-depth boundary intended to constrain a compromised rendering process. Disabling it means a successful browser-engine exploit can operate directly with the privileges of the Skill process. The code calculates `is_ci`, but that value is not used to restrict the unsafe flag to a specially isolated CI environment. Consequently, the sandbox is disabled during ordinary local execution as well. Most question fields are HTML-escaped before rendering, which reduces direct HTML or JavaScript injection opportunities. Therefore, practical exploitation would generally require another route to attacker-controlled browser content or a vulnerability in Chromium, `html2image`, fonts, images, or other rendered resources. The disabled sandbox materially increases the impact if such a rendering compromise occurs. ### Attack Path 1. An attacker supplies malformed content or a crafted rendering resource capable of triggering a vulnerability in the installed Chromium build or related rendering ...[truncated 969 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Remove `--no-sandbox` from the default Chromium flags. 2. Run Chromium as a non-root user in an environment where its native sandbox works. 3. If a specific CI environment cannot support the sandbox, require an explicit opt-in configuration rather than silently disabling it. 4. For unavoidable unsandboxed CI rendering, use a disposable container or virtual machine with: - No social-platform credentials available during rendering. - A read-only filesystem except for a dedicated output directory. - No host filesystem mounts. - Restricted outbound networking. - Dropped Linux capabilities and a restrictive seccomp/AppArmor profile. 5. Keep Chromium and `html2image` updated and version-pinned. 6. Continue HTML-escaping all untrusted fields and validate CSS values before interpolation. 7. Add tests that assert `--no-sandbox` is absent during normal local execution. ]]>

T08 · Insecure Dependencies

Warning
Location
README.md:27
Finding
Installation Instructions Execute an Unpinned Remote NPM Package<![CDATA[ ## Vulnerability Details **File Location**: `README.md`, line 27 **Vulnerability Type**: Unpinned executable dependency **Risk Level**: Medium ### Vulnerable Code ```text 1. Install: `npx clawhub install social-autopilot` ``` ### Technical Analysis The documented installation command invokes `npx` without pinning a version of the `clawhub` package or supplying an integrity value. If the package is not already installed locally, `npx` may retrieve and execute the current registry version. This makes the effective installer mutable after the Skill has been audited. A compromised maintainer account, malicious package release, registry compromise, or incompatible future update could cause users following the documented instructions to execute code that was not part of the reviewed project. The required Python packages listed in `SKILL.md` are also specified without versions or hashes, further reducing reproducibility, although the directly executable `npx` command is the primary confirmed location. ### Attack Path 1. An attacker compromises the upstream NPM package, its maintainer account, or the relevant distribution channel. 2. The attacker publishes a malicious or modified release under the expected package name. 3. A user follows the Quick Start command without specifying an audited version. 4. `npx` retrieves the current package release. 5. Package lifecycle or CLI code executes with the user's installation privileges. 6. The malicious installer can read local credentials, alter the installed Skill, or establish additional compromise before normal Skill execution begins. ### Impact Assessment The executed package receives the privileges of the user running the installation command. Depending on the environment, this may permit: - Modification of the installed Skill and project files. - Theft of environment variables and local configuration secrets. - Installation of additional packages or persistence mechanisms. - Access to developer creden ...[truncated 257 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Pin the CLI to a specific audited version, for example: ```text npx --yes clawhub@<audited-version> install social-autopilot@<audited-version> ``` 2. Commit and enforce a lockfile for NPM dependencies. 3. Verify package provenance, publisher identity, and registry integrity metadata before execution. 4. Prefer a locally installed, locked dependency invoked through package scripts rather than allowing `npx` to retrieve the latest release dynamically. 5. Pin all Python dependencies to reviewed versions and use a hash-locked requirements file, such as one generated with `pip-compile --generate-hashes`. 6. Run installation in a disposable, least-privileged environment without production credentials. 7. Establish a controlled update process that reviews release diffs before changing pinned versions. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • Taint TrackingDirect Taint Flow, Variable-Mediated Taint Flow, Credential Exfiltration Chain
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
Findings (27)

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

Critical
Category
Data Flow
Content
"""Make a POST request with retry logic. Returns the post/comment ID."""
    for attempt in range(1, MAX_RETRIES + 2):
        try:
            resp = requests.post(url, params=params, timeout=30)
            resp.raise_for_status()
            data = resp.json()
            post_id = str(data["id"])
Confidence
90% confidence
Finding
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Credential Access

High
Category
Privilege Escalation
Content
```
X_API_KEY=<your X/Twitter API key>
X_API_SECRET=<your X/Twitter API secret>
X_ACCESS_TOKEN=<your X/Twitter access token>
X_ACCESS_TOKEN_SECRET=<your X/Twitter access token secret>
```
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
```
X_API_KEY=<your X/Twitter API key>
X_API_SECRET=<your X/Twitter API secret>
X_ACCESS_TOKEN=<your X/Twitter access token>
X_ACCESS_TOKEN_SECRET=<your X/Twitter access token secret>
```
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
```
X_API_KEY=<your X/Twitter API key>
X_API_SECRET=<your X/Twitter API secret>
X_ACCESS_TOKEN=<your X/Twitter access token>
X_ACCESS_TOKEN_SECRET=<your X/Twitter access token secret>
```
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
```
X_API_KEY=<your X/Twitter API key>
X_API_SECRET=<your X/Twitter API secret>
X_ACCESS_TOKEN=<your X/Twitter access token>
X_ACCESS_TOKEN_SECRET=<your X/Twitter access token secret>
```
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
# ── Paths ─────────────────────────────────────────────────────────────────────
BASE_DIR = Path(__file__).resolve().parent.parent
CLIENT_SECRETS_FILE = BASE_DIR / "client_secrets.json"
TOKEN_FILE = BASE_DIR / "youtube_token.json"
OUTPUT_DIR = BASE_DIR / "output"
LOGS_DIR = BASE_DIR / "logs"
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
# ── Paths ─────────────────────────────────────────────────────────────────────
BASE_DIR = Path(__file__).resolve().parent.parent
CLIENT_SECRETS_FILE = BASE_DIR / "client_secrets.json"
TOKEN_FILE = BASE_DIR / "youtube_token.json"
OUTPUT_DIR = BASE_DIR / "output"
LOGS_DIR = BASE_DIR / "logs"
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
# ── Paths ─────────────────────────────────────────────────────────────────────
BASE_DIR = Path(__file__).resolve().parent.parent
CLIENT_SECRETS_FILE = BASE_DIR / "client_secrets.json"
TOKEN_FILE = BASE_DIR / "youtube_token.json"
OUTPUT_DIR = BASE_DIR / "output"
LOGS_DIR = BASE_DIR / "logs"
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Missing User Warnings

Medium
Confidence
94% confidence
Finding
The README prominently advertises fully automatic posting, scheduling, and multi-platform publishing but does not clearly warn users that the skill can take actions on external accounts using stored credentials. In this context, missing safety disclosure is especially dangerous because users may enable automation without understanding that posts, comments, and scheduled actions can be sent autonomously to public-facing accounts.

Rp1

Medium
Category
MCP Rug Pull
Confidence
92% confidence
Finding
Using `npx clawhub install social-autopilot` without a pinned version or integrity control allows whatever package is current at install time to be executed on the user's system. In a skill that automates posting and likely handles API keys for external accounts, a compromised or swapped package could steal credentials, alter behavior, or perform unauthorized actions during installation.

Vague Triggers

Medium
Confidence
96% confidence
Finding
The trigger phrase `Post now` is short, generic, and likely to occur in normal conversation, increasing the chance of accidental invocation. Because this skill performs external side effects by publishing content to social platforms, unintended activation could cause unauthorized or embarrassing posts across linked accounts.

Vague Triggers

Medium
Confidence
93% confidence
Finding
The invocation description allows a single ambiguous command to perform a high-impact action across X, Instagram, YouTube, and Meta without any documented confirmation or scoping. In context, this is more dangerous because the skill is fully autonomous and connected to multiple posting backends, so one mistaken invocation can create widespread public actions immediately.

Vague Triggers

Medium
Confidence
91% confidence
Finding
The invocation description allows a single ambiguous command to perform a high-impact action across X, Instagram, YouTube, and Meta without any documented confirmation or scoping. In context, this is more dangerous because the skill is fully autonomous and connected to multiple posting backends, so one mistaken invocation can create widespread public actions immediately.

Intent-Code Divergence

Medium
Confidence
98% confidence
Finding
The documentation claims the content database 'stays local' and is 'never uploaded anywhere,' but the skill explicitly uses CSV-derived content to generate posts, threads, captions, and videos that are then sent to X, Instagram, YouTube, Meta, and Cloudflare R2. This is a misleading data-handling statement that can cause operators to underestimate what information leaves the local environment and accidentally publish sensitive or proprietary dataset contents.

Missing User Warnings

Medium
Confidence
96% confidence
Finding
The manifest states that the skill automatically posts to X, Instagram, YouTube, and Facebook, but it does not present a user-facing warning that it can take real actions on connected third-party accounts or process account-linked data. This omission is dangerous because users may enable the skill without understanding that it can publish content externally, potentially causing account abuse, policy violations, data leakage, or reputational harm.

Vague Triggers

Medium
Confidence
94% confidence
Finding
The manifest advertises 'full autopilot' social media behavior and 'Complete social media automation' across multiple external platforms without defining trigger boundaries, approval requirements, or operational limits. In an agent ecosystem, this creates a real risk of unintended autonomous posting, spammy behavior, reputational damage, or misuse of linked accounts because users are not clearly warned about when and how actions will occur.

Natural-Language Policy Violations

Medium
Confidence
97% confidence
Finding
The top-level description states the formatter is "English only," which is a natural-language locale restriction. Under the policy, forcing a specific language without user opt-in or a documented region-specific justification is a violation.

Natural-Language Policy Violations

Medium
Confidence
93% confidence
Finding
This Python file embeds multiple user-facing strings such as "UPSC ka sabse bada pattern", "Practice karo", and similar Hindi/English mixed phrases that determine the generated video output. Because the skill does not offer any user opt-in or language selection, it effectively forces a specific language/locale style, which matches the natural-language locale policy violation criteria.

Intent-Code Divergence

Medium
Confidence
97% confidence
Finding
The docstring states the function builds a 3-phase timer challenge with a 5-second countdown and a 5-second answer reveal totaling about 12 seconds. In code, the function appends only a 7-second hook screen and a 3-second CTA screen, with no countdown frames and no answer reveal, so the documented intent materially contradicts the actual behavior.

Missing User Warnings

Medium
Confidence
88% confidence
Finding
This code performs external POST requests that create media containers, publish reels, and post comments to Instagram, which can affect the user's public account and transmit content and access tokens over the network. While the code logs status internally, there is no confirmation prompt, docstring, or comment warning the user that these account-modifying actions will be performed.

Cloud Storage Exfiltration

Medium
Category
Data Exfiltration
Content
for attempt in range(1, max_retries + 1):
        try:
            logger.info("Uploading %s to R2 (attempt %d/%d)", key, attempt, max_retries)
            client.upload_file(
                str(file_path),
                R2_BUCKET,
                key,
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.

Insecure deserialization: pickle.load()

Medium
Category
Dangerous Code Execution
Content
try:
            # Try pickle format first (local development)
            with open(TOKEN_FILE, "rb") as token:
                credentials = pickle.load(token)
        except (pickle.UnpicklingError, EOFError):
            # Fall back to JSON format (GitHub Actions)
            logger.info("Pickle failed, trying JSON format")
Confidence
97% confidence
Finding
The code deserializes TOKEN_FILE with pickle.load(), which can execute arbitrary code during unpickling if the token file is attacker-controlled or has been tampered with. In this context the file stores OAuth credentials, so compromise of the token path, CI workspace, or local filesystem could turn a simple credential load into code execution inside the agent environment.

Intent-Code Divergence

Medium
Confidence
91% confidence
Finding
The top-level documentation describes this file as an uploader for YouTube Shorts, which implies upload-only behavior. However, the module also defines comment_on_video and post_short logic that posts a top-level comment containing the answer/explanation, which is a materially different action affecting video interactions.

Intent-Code Divergence

Low
Confidence
98% confidence
Finding
The docstring for format_question_post states 'Zero hashtags' for formatted X/Twitter posts, but the function unconditionally calls _add_x_hashtags for non-quiz days, which appends '#UPSC' and sometimes a second hashtag. This is an active contradiction between the documented intent and runtime behavior.

Intent-Code Divergence

Low
Confidence
87% confidence
Finding
The docstring says the description includes a 'site link,' but the templates interpolate BRAND_URL, which is not defined anywhere in this file. As written, the function does not reliably implement the documented behavior of producing a real link-containing description.

Static analysis

No suspicious patterns detected.