Back to skill

Security audit

Twitter API Integration (Web Reversed )

Security checks for vulnerabilities and agentic risk

Overview

This Twitter/X automation skill needs Review because it handles raw session cookies and includes under-disclosed account-changing capabilities and committed session credentials.

Install only after reviewing the high-impact scope. Use a dedicated automation account, keep session cookies out of source control and logs, remove or rotate the committed credentials, pin dependencies, and disable or remove account-security, billing, DM, and bulk-mutation functions unless you explicitly need them with per-action confirmation.

Vulnerability Patterns
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
  • 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
Findings (4)

T09 · Insecure Skill Coding Practices

Error
Location
twitter_api/demo_langchain_tools.py:31
Finding
Hardcoded Twitter/X Session Credentials in an Executable Demo## Vulnerability Details **File Location**: `twitter_api/demo_langchain_tools.py:31-33, 151-156, 169-190` **Vulnerability Type**: Hardcoded session credentials **Risk Level**: Critical ### Vulnerable Code The credential values are redacted below to avoid further disclosure: ```python # Twitter credentials AUTH_TOKEN = "[REDACTED COMMITTED AUTH TOKEN]" CT0 = "[REDACTED COMMITTED CSRF TOKEN]" USERNAME = "Jordyn_Luv" ``` ```python # Get all Twitter tools tools = get_twitter_tools( auth_token=AUTH_TOKEN, ct0=CT0, config=tool_config ) ``` ```python try: # Post a new tweet tweet_id = await demo_post_tweet(post_tweet_tool) if tweet_id: # Like our own tweet await demo_like_tweet(like_tweet_tool, tweet_id) # Reply to our own tweet reply_id = await demo_reply_to_tweet(reply_to_tweet_tool, tweet_id) # Like the reply if reply_id: await demo_like_tweet(like_tweet_tool, reply_id) # Fetch mentions mention_ids = await demo_fetch_mentions(fetch_mentions_tool) # Like a mention if any exists if mention_ids: random_mention_id = mention_ids[0] await demo_like_tweet(like_tweet_tool, random_mention_id) ``` ### Technical Analysis The executable demo contains fixed values with the structure and context of real Twitter/X `auth_token` and `ct0` session credentials. These are not example placeholders. The credentials are passed directly to `get_twitter_tools()`, which constructs authenticated tools capable of posting tweets, replying, liking, retweeting, and reading account mentions. An `auth_token` is a bearer-equivalent browser session credential. The accompanying `ct0` value supplies the CSRF token expected by authenticated X web endpoints. Possession of both can enable session replay without knowing the account password, subject to session v ...[truncated 1500 chars]
Remediation
## Remediation Suggestions 1. Immediately revoke all Twitter/X sessions associated with the exposed credentials. 2. Rotate credentials and review the affected account for unauthorized activity. 3. Remove the values from the current source and purge them from all Git history, forks, build artifacts, logs, and published packages. 4. Load credentials only from protected environment variables or a dedicated secret manager: ```python import os AUTH_TOKEN = os.environ["TWITTER_AUTH_TOKEN"] CT0 = os.environ["TWITTER_CT0"] ``` 5. Ensure `.env` and other local secret files are excluded through `.gitignore`. 6. Replace executable demos with mocked credentials and mocked network calls by default. 7. Add pre-commit and CI secret scanning to reject session tokens before they enter repository history. 8. Require an explicit confirmation flag before any demo performs account mutations.

T09 · Insecure Skill Coding Practices

Error
Location
twitter_api/core/client.py:32
Finding
Twitter/X Authentication Cookies Can Be Sent to Arbitrary URLs## Vulnerability Details **File Location**: `twitter_api/core/client.py:32-43, 49-108` **Vulnerability Type**: Credential disclosure through unrestricted authenticated HTTP requests **Risk Level**: High ### Vulnerable Code ```python def __init__( self, auth_token: str, ct0: Optional[str] = None, headers: Optional[Dict[str, str]] = None, proxy_url: Optional[str] = None, ): self.auth_token = auth_token self.ct0 = ct0 or "" self.proxy_url = (proxy_url or "").strip() or None self.headers = dict(headers or PROFILE_HEADERS) self.headers["cookie"] = f"auth_token={auth_token}; ct0={self.ct0}" if self.ct0: self.headers["x-csrf-token"] = self.ct0 ``` ```python async def get( self, url: str, params: Optional[Dict[str, Any]] = None, data: Optional[Dict[str, Any]] = None, json_data: Optional[Dict[str, Any]] = None, ) -> Optional[Dict[str, Any]]: """GET request. json_data is serialized to query params (Twitter GraphQL style).""" qparams = dict(params) if params else {} if json_data: qparams.update(_build_params_from_json(json_data)) if data: qparams.update(data) async with aiohttp.ClientSession() as session: async with session.get( url, headers=self.headers, params=qparams or None, proxy=self.proxy_url ) as resp: if resp.status != 200: return None try: return await resp.json() except Exception: return None ``` ```python async def post( self, url: str, params: Optional[Dict[str, Any]] = None, data: Optional[Dict[str, Any]] = None, json_data: Optional[Dict[str, Any]] = None, ) -> Optional[Dict[str, Any]]: """POST request. json_data sent as JSON body; data as form.""" qparams = dict(params) if params else None async w ...[truncated 3213 chars]
Remediation
## Remediation Suggestions 1. Validate every destination before attaching authentication: - Require HTTPS. - Allowlist exact approved hosts, such as `x.com` and only the necessary Twitter/X upload host. - Reject user-info components, unexpected ports, deceptive suffixes, and nonstandard hostnames. 2. Disable automatic cross-origin redirects or validate the hostname after every redirect. 3. Do not expose unrestricted authenticated `get()` and `post()` methods. Prefer endpoint-specific methods that construct URLs internally. 4. Keep sensitive headers separate from generic headers and add them only after destination validation. 5. Use a hardened validation routine, for example: ```python from urllib.parse import urlparse ALLOWED_HOSTS = {"x.com", "upload.twitter.com"} def validate_twitter_url(url: str) -> None: parsed = urlparse(url) if parsed.scheme != "https" or parsed.hostname not in ALLOWED_HOSTS: raise ValueError("Untrusted Twitter API destination") ``` 6. Add tests proving that credentials are not sent to: - Subdomains not explicitly approved. - Lookalike domains. - HTTP URLs. - Redirect destinations outside the allowlist. 7. Avoid accepting caller-supplied proxy endpoints in high-trust contexts unless proxy use is explicitly approved and securely configured.

T05 · Unauthorized Access and Privilege Escalation

Error
Location
twitter_api/api/profile.py:252
Finding
Account-Security Mutations Exceed the Skill’s Minimum Required Privileges## Vulnerability Details **File Location**: `twitter_api/api/profile.py:252-277` **Vulnerability Type**: Excessive account-management capability **Risk Level**: High ### Vulnerable Code ```python async def change_password(self, current_password: str, new_password: str) -> Optional[Dict[str, Any]]: """ Change password. Args: current_password (str): Current password new_password (str): New password Returns: Optional[Dict[str, Any]]: Response from the API """ payload = { 'current_password': current_password, 'password': new_password, 'password_confirmation': new_password } return await self.client.post(Endpoints.CHANGE_PASSWORD, data=payload) async def delete_phone(self) -> Optional[Dict[str, Any]]: """ Delete phone number from account. Returns: Optional[Dict[str, Any]]: Response from the API """ return await self.client.post(Endpoints.DELETE_PHONE) ``` ### Technical Analysis The declared Skill functionality centers on timelines, notifications, posting, follow operations, and general social automation. The profile API additionally exposes password changes and removal of the account’s phone number. These operations are account-security and recovery mutations rather than ordinary social-posting functions. They are not required to fetch timelines, summarize notifications, post content, or follow an account. Making them available in the same authenticated package violates least-privilege design and increases the consequences of agent manipulation, accidental invocation, or credential compromise. Password changes require the current password argument, so session-cookie possession alone may not be sufficient to invoke that specific operation successfully. However, exposing a method that accepts and transmits the current and replacement passwords creates an additional s ...[truncated 1681 chars]
Remediation
## Remediation Suggestions 1. Remove password and phone-management operations from this Skill unless they are an explicit, documented requirement. 2. Split account-security operations into a separate package that is not loaded by ordinary social-automation agents. 3. Expose narrowly scoped interfaces, such as read-only timeline access or posting-only tools, rather than the complete authenticated client. 4. Require explicit, out-of-band user confirmation immediately before any password, phone, subscription, DM, or profile-security mutation. 5. Never allow an autonomous LLM decision alone to authorize account-recovery or credential changes. 6. Do not log, persist, or include password arguments in callback traces, debug output, exceptions, or agent memory. 7. Add policy controls that deny sensitive methods by default and permit them only through an explicit capability grant. 8. Document every mutating operation and its required privilege so operators can make an informed decision before enabling it.

T08 · Insecure Dependencies

Note
Location
requirements.txt:1
Finding
Unbounded Dependency Version Reduces Supply-Chain Reproducibility## Vulnerability Details **File Location**: `requirements.txt:1` **Vulnerability Type**: Unbounded third-party dependency **Risk Level**: Low ### Vulnerable Code ```text aiohttp>=3.9 ``` ### Technical Analysis The dependency declaration permits any future `aiohttp` version at or above 3.9. As a result, separate installations can resolve to different package versions, and a future major release can be selected without review. This does not establish that `aiohttp` is malicious or currently vulnerable. The risk is that an environment handling high-value Twitter/X session cookies depends on an unbounded package resolution process. A compromised, incompatible, or unexpectedly changed future release could therefore enter the execution environment without a corresponding source change in this project. The nested `twitter_api/requirements.txt` also uses a minimum-only constraint, indicating that dependency resolution is not reproducibly locked. ### Attack Path 1. A user follows the setup instructions and runs `pip install -r requirements.txt`. 2. The package index resolves the newest release satisfying the minimum constraint. 3. A future release contains a compromise, exploitable regression, or incompatible behavior. 4. That release is installed into the Skill environment without an explicit project review or lock-file update. 5. The dependency executes in the same process that handles Twitter/X session cookies and network traffic. ### Impact Assessment Potential impact includes: - Non-reproducible installations and inconsistent security behavior. - Unexpected breaking changes in HTTP, TLS, redirect, cookie, or proxy handling. - Exposure of account credentials if a future dependency release is compromised. - Reduced ability to audit and attest to the exact deployed software set. This is a preventive supply-chain finding rather than evidence of a currently malicious dependency.
Remediation
## Remediation Suggestions 1. Pin `aiohttp` to an audited version or tightly reviewed compatible range. 2. Generate and commit a lock file containing exact transitive dependency versions. 3. Use package hashes, such as pip `--require-hashes`, to verify downloaded artifacts. 4. Update dependencies through a controlled review process with automated tests and vulnerability scanning. 5. Keep the root and nested requirements files synchronized or consolidate them into one authoritative dependency definition. 6. Run dependency auditing in CI and review changes to HTTP, redirect, cookie, TLS, and proxy behavior before upgrades.
Vulnerability Patterns
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • YARA SignaturesMalware Match, Webshell Match, Cryptominer Match
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
Findings (87)

Credential Access

High
Category
Privilege Escalation
Content
cd twitter-agent-skill
python -m venv .venv && .venv\Scripts\activate
pip install -r requirements.txt
copy .env.example .env  # fill in your auth_token + ct0 values

# timeline + notification summaries
python scripts/timeline_summary.py
Confidence
92% confidence
Finding
The README instructs users to place live Twitter/X session cookies (`auth_token` and `ct0`) into a local `.env` file to enable automation. These are high-value session credentials; if the file is exposed through source control, logs, backups, or other local compromise, an attacker can hijack the associated accounts and perform actions as the user without needing official API keys.

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding
Even where some declared functions appear partial or absent, the documentation still fails to match the actual capability set, including undeclared engagement features. In agent ecosystems, inaccurate manifests are dangerous because trust and approval decisions are made from metadata, not deep code review every time.

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding
Even where some declared functions appear partial or absent, the documentation still fails to match the actual capability set, including undeclared engagement features. In agent ecosystems, inaccurate manifests are dangerous because trust and approval decisions are made from metadata, not deep code review every time.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
Even where some declared functions appear partial or absent, the documentation still fails to match the actual capability set, including undeclared engagement features. In agent ecosystems, inaccurate manifests are dangerous because trust and approval decisions are made from metadata, not deep code review every time.

Tp4

High
Category
MCP Tool Poisoning
Confidence
94% confidence
Finding
Even where some declared functions appear partial or absent, the documentation still fails to match the actual capability set, including undeclared engagement features. In agent ecosystems, inaccurate manifests are dangerous because trust and approval decisions are made from metadata, not deep code review every time.

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding
Even where some declared functions appear partial or absent, the documentation still fails to match the actual capability set, including undeclared engagement features. In agent ecosystems, inaccurate manifests are dangerous because trust and approval decisions are made from metadata, not deep code review every time.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
Even where some declared functions appear partial or absent, the documentation still fails to match the actual capability set, including undeclared engagement features. In agent ecosystems, inaccurate manifests are dangerous because trust and approval decisions are made from metadata, not deep code review every time.

Tp4

High
Category
MCP Tool Poisoning
Confidence
90% confidence
Finding
Even where some declared functions appear partial or absent, the documentation still fails to match the actual capability set, including undeclared engagement features. In agent ecosystems, inaccurate manifests are dangerous because trust and approval decisions are made from metadata, not deep code review every time.

Credential Access

High
Category
Privilege Escalation
Content
from twitter_api.twitter import Twitter

BASE = Path(__file__).parent
ENV_PATH = BASE / '.env'
OUT_PATH = BASE / 'notifications_raw.json'

def load_env(path: Path) -> None:
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
from twitter_api.twitter import Twitter

BASE = Path(__file__).parent
ENV_PATH = BASE / '.env'
OUT_PATH = BASE / 'notifications_raw.json'

def load_env(path: Path) -> None:
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Context-Inappropriate Capability

High
Confidence
98% confidence
Finding
The password change capability is highly sensitive and is not justified by the stated purpose of a Twitter/X automation toolkit focused on timeline, notifications, posting, and follow operations. If invoked by a compromised agent, malicious workflow, or confused deputy scenario, it can lock out the legitimate user and convert cookie access into durable account takeover.

Context-Inappropriate Capability

High
Confidence
96% confidence
Finding
Deleting the account phone number is a destructive account-recovery change unrelated to normal social-media automation. An attacker or misbehaving agent could remove a recovery factor, making takeover harder to reverse and weakening the user's ability to secure or recover the account.

Context-Inappropriate Capability

High
Confidence
92% confidence
Finding
The module can create paid subscriptions using a supplied payment_method_id, a billing-affecting action that is not justified by the stated Twitter automation purpose. In an agent skill, hidden or weakly disclosed purchase functionality materially increases abuse risk because an agent with account cookies could trigger unwanted charges on behalf of the user.

Context-Inappropriate Capability

High
Confidence
99% confidence
Finding
The file hard-codes what appear to be live Twitter/X session credentials (AUTH_TOKEN and CT0) and a specific username directly in source code. Anyone with access to the repository, logs, or distributed package could reuse these secrets to control the associated account, making this a direct account-compromise risk rather than a harmless demo artifact.

YARA rule 'agent_skill_mcp_tool_poisoning_metadata': MCP/tool metadata poisoning indicators in tool schemas or skill manifests [agent_skills]

High
Category
YARA Match
Content
,
    ReplyToTweetTool,
    FetchMentionsTool,
    LikeTweetTool,
    RetweetTool
)

# Set up logging
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
    handlers=[logging.StreamHandler(sys.stdout)]
)
logger = logging.getLogger("twitter_tools_demo")

# Twitter credentials
AUTH_TOKEN = "f06ec149475390a01262510a1cc1b59c9760a318"
CT0 = "37318663228df008399ba56501e3512d4b1b1d30eb852fc958561da0888027014e91199162dbc93f745a296fe0189018d1025f12b5dd065bb1d14798103016f3a0662487bebcf132aaff91db01812e0d"
USERNAME = "Jordyn_Luv"

async def demo_post_tweet(tool: PostTweetTool):
    """Demo posting a tweet."""
    logger.info("Testing PostTweetTool...")
    
    # Post a tweet
    current_time = asyncio.get_event_loop().time()
    tweet_text = f"Testing Twitter API with LangChain tools! This is an automated test post at time index: {current_time:.0f}"
    
    logger.info(f"Posting tweet: {tweet_text}")
    result = await tool._arun(twee
Confidence
80% confidence
Finding
YARA rule matched a hack tool or exploit indicator (offensive tools, reconnaissance, privilege escalation, or exploit frameworks).

Credential Access

High
Category
Privilege Escalation
Content
def _load_dotenv() -> None:
    cwd = os.path.abspath(os.getcwd())
    for path in [os.path.join(cwd, ".env"), os.path.join(cwd, "social_ops", ".env")]:
        if os.path.isfile(path):
            with open(path, "r", encoding="utf-8") as f:
                for line in f:
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
def _load_dotenv() -> None:
    cwd = os.path.abspath(os.getcwd())
    for path in [os.path.join(cwd, ".env"), os.path.join(cwd, "social_ops", ".env")]:
        if os.path.isfile(path):
            with open(path, "r", encoding="utf-8") as f:
                for line in f:
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
def _load_dotenv() -> None:
    cwd = os.path.abspath(os.getcwd())
    for path in [os.path.join(cwd, ".env"), os.path.join(cwd, "social_ops", ".env")]:
        if os.path.isfile(path):
            with open(path, "r", encoding="utf-8") as f:
                for line in f:
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
def _load_dotenv() -> None:
    cwd = os.path.abspath(os.getcwd())
    for path in [os.path.join(cwd, ".env"), os.path.join(cwd, "social_ops", ".env")]:
        if os.path.isfile(path):
            with open(path, "r", encoding="utf-8") as f:
                for line in f:
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
def _load_dotenv() -> None:
    cwd = os.path.abspath(os.getcwd())
    for path in [os.path.join(cwd, ".env"), os.path.join(cwd, "social_ops", ".env")]:
        if os.path.isfile(path):
            with open(path, "r", encoding="utf-8") as f:
                for line in f:
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
def _load_dotenv() -> None:
    cwd = os.path.abspath(os.getcwd())
    for path in [os.path.join(cwd, ".env"), os.path.join(cwd, "social_ops", ".env")]:
        if os.path.isfile(path):
            with open(path, "r", encoding="utf-8") as f:
                for line in f:
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
def _load_dotenv() -> None:
    cwd = os.path.abspath(os.getcwd())
    for path in [os.path.join(cwd, ".env"), os.path.join(cwd, "social_ops", ".env")]:
        if os.path.isfile(path):
            with open(path, "r", encoding="utf-8") as f:
                for line in f:
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
def _load_dotenv() -> None:
    cwd = os.path.abspath(os.getcwd())
    for path in [os.path.join(cwd, ".env"), os.path.join(cwd, "social_ops", ".env")]:
        if os.path.isfile(path):
            with open(path, "r", encoding="utf-8") as f:
                for line in f:
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
def _load_dotenv() -> None:
    cwd = os.path.abspath(os.getcwd())
    for path in [os.path.join(cwd, ".env"), os.path.join(cwd, "social_ops", ".env")]:
        if os.path.isfile(path):
            with open(path, "r", encoding="utf-8") as f:
                for line in f:
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
def _load_dotenv() -> None:
    cwd = os.path.abspath(os.getcwd())
    for path in [os.path.join(cwd, ".env"), os.path.join(cwd, "social_ops", ".env")]:
        if os.path.isfile(path):
            with open(path, "r", encoding="utf-8") as f:
                for line in f:
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Static analysis

No suspicious patterns detected.