Back to skill

Security audit

Edison Autopilot Post X

Security checks for vulnerabilities and agentic risk

Overview

The skill is a coherent auto-posting tool, but it deserves review because it can repeatedly publish to a live X account and can send local Markdown scan content to OpenAI when SCAN_DIR is set.

Review carefully before installing. Use a dedicated X account and narrowly scoped API keys, run dry-run first, avoid enabling cron until outputs are acceptable, and do not set SCAN_DIR to any directory containing private or untrusted Markdown. If Telegram is enabled, assume tweet text and some failure details may be sent there.

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

T09 · Insecure Skill Coding Practices

Warning
Location
auto_tweet.py:98
Finding
Unrestricted Research Scan Content Is Transmitted to OpenAI and Can Influence Automated X Posts<![CDATA[ ## Vulnerability Details **File Location**: `auto_tweet.py:39, 98-106, 124-170` **Vulnerability Type**: `T09: Insecure Skill Coding Practices` **Risk Level**: Medium ### Vulnerable Code ```python # Optional: research scan directory for context-aware tweets SCAN_DIR = os.environ.get("SCAN_DIR", "") ``` ```python def get_latest_scan(): """Read the most recent research scan report (optional).""" if not SCAN_DIR or not os.path.isdir(SCAN_DIR): return "" files = sorted(os.listdir(SCAN_DIR), reverse=True) for f in files: if f.endswith(".md"): with open(os.path.join(SCAN_DIR, f)) as fh: return fh.read()[:3000] return "" ``` ```python def generate_tweet(): """Use OpenAI to generate a tweet.""" scan = get_latest_scan() recent = get_recent_tweets() topic = random.choice(TOPICS) banned = ", ".join(f'"{p}"' for p in BANNED_PHRASES) prompt = f"""{PERSONA} Write ONE tweet (max 220 chars) about: {topic} Context — today's research scan (use as inspiration, don't copy verbatim): {scan[:1500] if scan else "(no scan available today)"} Recent tweets (DO NOT repeat similar topics or phrasing): {recent[:800] if recent else "(none yet)"} CRITICAL RULES: - MUST be under 220 characters (count carefully — spaces, emoji, @mentions all count) - MUST include at least one @mention - NEVER FABRICATE DATA. Do NOT invent numbers, stats, percentages, or metrics. - NO generic filler. BANNED phrases: {banned} - NEVER end with a generic motivational statement. - Shorter is ALWAYS better. 140 chars > 220 chars. - One idea per tweet. - Output ONLY the tweet text, nothing else. No quotes around it. VARIETY (critical — never repeat the same format): - Mix formats: bold claims, disagreements, questions, one-liners, mini-stories - Mix tone: funny, dead serious, provocative, vulnerable - NEVER start two tweets the same way. - BE BOLD. Safe tweets get zero engagement. """ resp = requests.post( ...[truncated 3500 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Require explicit opt-in before reading or transmitting research scans, and clearly document that their contents are sent to OpenAI. 2. Restrict `SCAN_DIR` to a dedicated directory rather than accepting an arbitrary readable path. 3. Resolve paths with `os.path.realpath()` and reject files that escape the approved directory. 4. Reject symbolic links and verify that selected files are regular files with expected ownership and restrictive permissions. 5. Add secret and sensitive-data detection or redaction before including scan content in an external request. 6. Delimit scan content as untrusted quoted data and add explicit instructions that the model must not follow commands found inside that content. 7. Use structured messages that separate system instructions from untrusted contextual data. 8. Validate generated output against explicit content and disclosure policies instead of checking only its character length. 9. Require human approval before publishing content derived from untrusted or externally populated scans. 10. Run the scheduled task under a dedicated low-privilege operating-system account with read access only to its dedicated scan and log directories. ]]>

T08 · Insecure Dependencies

Note
Location
README.md:21
Finding
Unpinned Third-Party Dependencies Create Supply-Chain and Reproducibility Risk<![CDATA[ ## Vulnerability Details **File Location**: `README.md:21-24`; duplicated in `SKILL.md:21-24` and `auto_tweet.py:4-7` **Vulnerability Type**: `T08: Insecure Dependencies` **Risk Level**: Low ### Vulnerable Code ```bash pip install tweepy requests ``` ### Technical Analysis The installation instructions install `tweepy` and `requests` without version constraints, a lock file, or package hashes. Installation therefore resolves whichever compatible releases are available from the configured Python package index at installation time. No malicious or typosquatted dependency is present in the reviewed project: both package names correspond to the libraries imported by the script. The risk arises because the effective dependency code can change after this project has been audited, making deployments non-reproducible and allowing a future compromised or defective release to enter the runtime environment without project-level review. These dependencies execute in the same Python process as the Skill. They can therefore access inherited environment variables, including the OpenAI, X, and optional Telegram credentials, as well as files and network permissions available to the account running the scheduled task. ### Attack Path 1. A user follows the documented `pip install tweepy requests` command. 2. Pip resolves mutable package versions from the user's configured package index. 3. A future compromised release, compromised package index, or unsafe index configuration supplies altered dependency code. 4. Python executes that code when `requests` or `tweepy` is imported or used. 5. The altered dependency inherits the script's operating-system permissions and environment. 6. It could read API credentials, alter network requests, manipulate tweet content, or access files available to the scheduled-task account. This is a contingent supply-chain path. The audit found no evidence that the currently named packages are malicious. ### Impact Assessment If the d ...[truncated 664 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Add a reviewed `requirements.txt` or lock file containing exact versions of `requests`, `tweepy`, and their transitive dependencies. 2. Generate and verify cryptographic hashes, for example by using a hash-locked requirements workflow and `pip install --require-hashes`. 3. Install dependencies in a dedicated virtual environment rather than into a shared or system Python environment. 4. Use only trusted package indexes and explicitly control index configuration in deployment documentation. 5. Review dependency updates before changing pinned versions and use automated vulnerability scanning. 6. Run the application under a dedicated non-root account with only the filesystem and network access required for posting. 7. Avoid exposing unrelated secrets to the scheduled process; provide only the API credentials necessary for this workflow. ]]>
Vulnerability Patterns
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Rogue AgentSelf-Modification, Session Persistence
  • Taint TrackingDirect Taint Flow, Variable-Mediated Taint Flow, Credential Exfiltration Chain
Findings (20)

YARA rule 'agent_skill_credential_exfiltration_webhook': AI agent skill credential harvesting followed by webhook or external exfiltration [agent_skills]

Critical
Category
YARA Match
Content
# post a tweet
  4. python auto_tweet.py --dry-run # preview without posting

Cron example (5x daily at 8am, 11am, 2pm, 5pm, 9pm PST):
  0 8,11,14,17,21 * * * cd /path/to/repo && python auto_tweet.py
"""

import json
import os
import random
import sys
from datetime import datetime, timezone

import requests
import tweepy

# --- Config (set these as environment variables) ---
OPENAI_KEY = os.environ["OPENAI_API_KEY"]
X_CONSUMER_KEY = os.environ["X_CONSUMER_KEY"]
X_CONSUMER_SECRET = os.environ["X_CONSUMER_SECRET"]
X_ACCESS_TOKEN = os.environ["X_ACCESS_TOKEN"]
X_ACCESS_TOKEN_SECRET = os.environ["X_ACCESS_TOKEN_SECRET"]

# Optional: Telegram notifications
TG_BOT_TOKEN = os.environ.get("TWEET_BOT_TOKEN", "")
TG_CHAT_ID = os.environ.get("TWEET_BOT_CHAT_ID", "")

# Where to log posted tweets (for dedup)
LOG_DIR = os.path.expanduser("~/autopilot-post-x/logs")

# Optional: research scan directory for context-aware tweets
SCAN_DIR = os.environ.get("SCAN_DIR", "")

# --- Persona Prompt -
Confidence
85% confidence
Finding
YARA rule matched a known malware signature (reverse shell, backdoor, ransomware, C2 framework, or info stealer).

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

Critical
Category
Data Flow
Content
- BE BOLD. Safe tweets get zero engagement.
"""

    resp = requests.post(
        "https://api.openai.com/v1/chat/completions",
        headers={"Authorization": f"Bearer {OPENAI_KEY}", "Content-Type": "application/json"},
        json={
Confidence
90% confidence
Finding
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

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

Critical
Category
Data Flow
Content
for _ in range(3):
        if len(text) <= 280:
            break
        resp = requests.post(
            "https://api.openai.com/v1/chat/completions",
            headers={"Authorization": f"Bearer {OPENAI_KEY}", "Content-Type": "application/json"},
            json={
Confidence
90% confidence
Finding
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

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

Critical
Category
Data Flow
Content
f"Error: {error_msg}"
        )
    try:
        requests.post(
            f"https://api.telegram.org/bot{TG_BOT_TOKEN}/sendMessage",
            data={"chat_id": TG_CHAT_ID, "text": msg, "disable_web_page_preview": True},
            timeout=10,
Confidence
90% confidence
Finding
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Missing User Warnings

Medium
Confidence
88% confidence
Finding
The README clearly promotes fully automated posting to a real X account and optional Telegram notifications, but it does not prominently warn that running the tool will transmit generated content and metadata to external services and can perform irreversible real-account actions. This creates a meaningful safety and operational risk because users may test or schedule it without appreciating that it can post publicly and send notifications off-host.

Session Persistence

Medium
Category
Rogue Agent
Content
### 6. Schedule with cron

```bash
crontab -e
```

Add (5x daily at 8am, 11am, 2pm, 5pm, 9pm):
Confidence
85% 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
89% confidence
Finding
The skill explicitly instructs users to run a script that will post to a live X account, but it does not prominently warn that this causes real external actions, may consume paid API quota, and may damage the user's account reputation if misconfigured. In an agent-skill context, missing safety framing around irreversible network actions increases the chance of unintended posting or misuse.

Session Persistence

Medium
Category
Rogue Agent
Content
### 5. Schedule with cron (5x daily)

```bash
crontab -e
# Add: 0 8,11,14,17,21 * * * cd /path/to/repo && python auto_tweet.py
```
Confidence
85% confidence
Finding
The cron instruction creates persistent automated execution, causing repeated external posting without per-run user review. In a skill that publishes content to a public platform, this persistence amplifies the impact of prompt mistakes, compromised dependencies, or later code changes because actions continue unattended.

Context-Inappropriate Capability

Medium
Confidence
87% confidence
Finding
Telegram notification capability adds a second outbound communication channel unrelated to the core requirement of generating and posting tweets. Extra channels increase the chance of silent data leakage, especially because this code sends tweet content and exception details off-platform.

Context-Inappropriate Capability

Medium
Confidence
93% confidence
Finding
The skill reads arbitrary markdown files from a configurable local directory and injects their contents into prompts sent to OpenAI. If SCAN_DIR points to sensitive reports or internal documents, the script can disclose local proprietary or confidential data to a third-party API without validation or consent.

Natural-Language Policy Violations

Medium
Confidence
96% confidence
Finding
The natural-language instruction 'Always in English' imposes a fixed language policy on generated content. For a general-purpose auto-posting skill, this is a locale/language restriction without user choice or an explicit region-specific justification, which matches the policy-violation criteria.

External Transmission

Medium
Category
Data Exfiltration
Content
- BE BOLD. Safe tweets get zero engagement.
"""

    resp = requests.post(
        "https://api.openai.com/v1/chat/completions",
        headers={"Authorization": f"Bearer {OPENAI_KEY}", "Content-Type": "application/json"},
        json={
Confidence
91% confidence
Finding
This duplicate external-transmission finding corresponds to the same primary OpenAI request. In context, it is dangerous because local context from SCAN_DIR and recent logs can be exported to a third party without strong boundary controls.

External Transmission

Medium
Category
Data Exfiltration
Content
- BE BOLD. Safe tweets get zero engagement.
"""

    resp = requests.post(
        "https://api.openai.com/v1/chat/completions",
        headers={"Authorization": f"Bearer {OPENAI_KEY}", "Content-Type": "application/json"},
        json={
Confidence
91% confidence
Finding
This duplicate external-transmission finding corresponds to the same primary OpenAI request. In context, it is dangerous because local context from SCAN_DIR and recent logs can be exported to a third party without strong boundary controls.

External Transmission

Medium
Category
Data Exfiltration
Content
"""

    resp = requests.post(
        "https://api.openai.com/v1/chat/completions",
        headers={"Authorization": f"Bearer {OPENAI_KEY}", "Content-Type": "application/json"},
        json={
            "model": "gpt-5.1",
Confidence
91% confidence
Finding
Use of the OpenAI endpoint is an external data transfer to a third party. Given this script reads local scan files and recent tweet logs, the context makes the transfer materially sensitive unless the transmitted content is constrained and reviewed.

External Transmission

Medium
Category
Data Exfiltration
Content
for _ in range(3):
        if len(text) <= 280:
            break
        resp = requests.post(
            "https://api.openai.com/v1/chat/completions",
            headers={"Authorization": f"Bearer {OPENAI_KEY}", "Content-Type": "application/json"},
            json={
Confidence
84% confidence
Finding
This duplicate finding reflects the same shortening request to OpenAI. The concern is secondary exposure of already-ingested content, which matters if sensitive local information entered the first prompt or model output.

External Transmission

Medium
Category
Data Exfiltration
Content
for _ in range(3):
        if len(text) <= 280:
            break
        resp = requests.post(
            "https://api.openai.com/v1/chat/completions",
            headers={"Authorization": f"Bearer {OPENAI_KEY}", "Content-Type": "application/json"},
            json={
Confidence
84% confidence
Finding
This duplicate finding reflects the same shortening request to OpenAI. The concern is secondary exposure of already-ingested content, which matters if sensitive local information entered the first prompt or model output.

Tainted flow: 'text' from requests.post (line 187, network input) → requests.post (network output)

Medium
Category
Data Flow
Content
for _ in range(3):
        if len(text) <= 280:
            break
        resp = requests.post(
            "https://api.openai.com/v1/chat/completions",
            headers={"Authorization": f"Bearer {OPENAI_KEY}", "Content-Type": "application/json"},
            json={
Confidence
65% confidence
Finding
Data from a source is assigned to a variable that is later passed to a sink, creating a variable-mediated taint flow.

External Transmission

Medium
Category
Data Exfiltration
Content
if len(text) <= 280:
            break
        resp = requests.post(
            "https://api.openai.com/v1/chat/completions",
            headers={"Authorization": f"Bearer {OPENAI_KEY}", "Content-Type": "application/json"},
            json={
                "model": "gpt-5.1",
Confidence
84% confidence
Finding
The shortening request is another external transmission to OpenAI. Its risk derives from repeated exposure of generated text that may encode sensitive prompt material from local sources.

Tainted flow: 'msg' from requests.post (line 231, network input) → requests.post (network output)

Medium
Category
Data Flow
Content
f"Error: {error_msg}"
        )
    try:
        requests.post(
            f"https://api.telegram.org/bot{TG_BOT_TOKEN}/sendMessage",
            data={"chat_id": TG_CHAT_ID, "text": msg, "disable_web_page_preview": True},
            timeout=10,
Confidence
86% confidence
Finding
The Telegram notification forwards tweet text and raw error messages to an external messaging service. Because error strings or generated content can include sensitive local or third-party data, this creates an unintended data disclosure channel outside the stated core tweet-posting function.

External Transmission

Medium
Category
Data Exfiltration
Content
)
    try:
        requests.post(
            f"https://api.telegram.org/bot{TG_BOT_TOKEN}/sendMessage",
            data={"chat_id": TG_CHAT_ID, "text": msg, "disable_web_page_preview": True},
            timeout=10,
        )
Confidence
87% confidence
Finding
This Telegram request sends operational messages, tweet content, and raw failure details to a separate third-party service. In the context of an automation skill, that creates an unnecessary external disclosure path and broadens the blast radius of prompt leakage or exception-based secret exposure.

Static analysis

No suspicious patterns detected.