Back to skill

Security audit

TwtAPI

Security checks for vulnerabilities and agentic risk

Overview

This skill is a coherent Twitter/X data client that uses a dedicated TwtAPI key, with a caution around third-party data handling and custom endpoint configuration.

Install only if you are comfortable sending Twitter/X search terms, account or tweet identifiers, and returned content through TwtAPI's hosted service using a dedicated skill key. Keep TWTAPI_SKILL_BASE_URL unset for normal use, or set it only to an HTTPS endpoint you trust; rotate the skill key if it was ever used with an HTTP or untrusted custom gateway.

Vulnerability Patterns
  • 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
  • Embedded Malicious CodeShips malicious scripts inside the skill and executes them locally
Findings (1)

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/twtapi.py:15
Finding

Custom gateway permits plaintext transmission of the TwtAPI skill key

Content
View full analysis

Vulnerability Details

File Location: scripts/twtapi.py:15-40 and scripts/twtapi.py:69-80
Vulnerability Type: Unvalidated URL scheme resulting in potential plaintext credential transmission
Risk Level: Medium

Vulnerable Code

python
BASE_URL = os.environ.get("TWTAPI_SKILL_BASE_URL") or DEFAULT_BASE_URL
TIMEOUT = 30
USER_AGENT = "curl/8.7.1"


def _call(tool: str, params: dict | None = None) -> dict:
    if not SKILL_KEY:
        print("Error: TWTAPI_SKILL_KEY is not set. Get it from your TwtAPI dashboard.", file=sys.stderr)
        sys.exit(1)

    url = f"{BASE_URL.rstrip('/')}/api/v1/agent/invoke"
    payload = json.dumps(
        {
            "tool": tool,
            "params": params or {},
        }
    ).encode("utf-8")
    req = urllib.request.Request(
        url,
        data=payload,
        method="POST",
        headers={
            "X-Skill-Key": SKILL_KEY,
            "Accept": "application/json",
            "Content-Type": "application/json",
            "User-Agent": USER_AGENT,
        },
    )

The same unsafe destination construction is used by the tool-discovery endpoint:

python
def _call_tools() -> dict:
    if not SKILL_KEY:
        print("Error: TWTAPI_SKILL_KEY is not set. Get it from your TwtAPI dashboard.", file=sys.stderr)
        sys.exit(1)

    url = f"{BASE_URL.rstrip('/')}/api/v1/agent/tools"
    req = urllib.request.Request(
        url,
        headers={
            "X-Skill-Key": SKILL_KEY,
            "Accept": "application/json",
            "User-Agent": USER_AGENT,
        },
    )

Technical Analysis

TWTAPI_SKILL_BASE_URL is accepted directly from the process environment and used as the destination for authenticated requests. The code does not parse the URL or require the https scheme before placing TWTAPI_SKILL_KEY in the X-Skill-Key header.

The default endpoint, `http ...[truncated 2187 chars]

Remediation
View remediation

Remediation Suggestions

  1. Parse TWTAPI_SKILL_BASE_URL with urllib.parse.urlparse before constructing a request.
  2. Require the https scheme for all remote endpoints.
  3. Reject URLs with missing hostnames, embedded user information, fragments, or unsupported schemes.
  4. If plaintext HTTP is required for local development, permit it only for explicit loopback destinations such as localhost, 127.0.0.1, or ::1, and require a separate opt-in environment variable.
  5. Perform validation before adding X-Skill-Key to any request.
  6. Clearly warn users that any custom gateway receives the dedicated skill key and request parameters.
  7. Recommend immediate key rotation if the credential has previously been sent to an HTTP or untrusted endpoint.

Example hardening logic:

python
import ipaddress
import urllib.parse

def validate_base_url(value: str) -> str:
    parsed = urllib.parse.urlparse(value)

    if not parsed.hostname or parsed.username or parsed.password:
        raise ValueError("The TwtAPI base URL is invalid.")

    is_loopback = parsed.hostname == "localhost"
    if not is_loopback:
        try:
            is_loopback = ipaddress.ip_address(parsed.hostname).is_loopback
        except ValueError:
            is_loopback = False

    if parsed.scheme != "https" and not (
        parsed.scheme == "http"
        and is_loopback
        and os.environ.get("TWTAPI_ALLOW_INSECURE_LOCALHOST") == "1"
    ):
        raise ValueError("The TwtAPI base URL must use HTTPS.")

    return value.rstrip("/")

BASE_URL = validate_base_url(
    os.environ.get("TWTAPI_SKILL_BASE_URL") or DEFAULT_BASE_URL
)
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • Taint TrackingDirect Taint Flow, Variable-Mediated Taint Flow, Credential Exfiltration Chain
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
Findings (5)

Tainted flow: 'req' from os.environ.get (line 78, credential/environment) → urllib.request.urlopen (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/twtapi.py (reported line 46)May include surrounding context.

python
)

    try:
        with urllib.request.urlopen(req, timeout=TIMEOUT) as resp:
            data = json.loads(resp.read())
            if isinstance(data, dict) and "data" in data:
                return data["data"]

Tainted flow: 'req' from os.environ.get (line 78, credential/environment) → urllib.request.urlopen (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/twtapi.py (reported line 88)May include surrounding context.

python
)

    try:
        with urllib.request.urlopen(req, timeout=TIMEOUT) as resp:
            data = json.loads(resp.read())
            if isinstance(data, dict) and "data" in data:
                return data["data"]

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
94% confidence
Finding

The skill requires environment variables, invokes a local Python interpreter, and communicates with a remote hosted gateway, but it does not declare any explicit tool scope or permission boundaries. This weakens least-privilege controls and can cause agents or runners to grant broader shell/network/env access than users expect, increasing the risk of secret exposure or unintended command execution if the script or endpoint behavior changes.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The manifest explicitly states that live Twitter/X data is accessed through a hosted public skill gateway, but it does not clearly disclose that user queries, identifiers, and retrieved social-media content will be sent to and processed by a third-party service. This creates a real transparency and privacy risk because users or orchestrators may invoke the skill without understanding that prompts and data leave the local environment.

Content

No source excerpt is available for this finding.

Vague Triggers

Low
Category
Not specified by scanner
Confidence
87% confidence
Finding

The skill description is broad and does not define clear invocation boundaries, approved use cases, or constraints for when the skill should be activated. In agent environments, vague activation scope can cause over-invocation of an external networked skill, increasing unnecessary data exposure and the chance that unrelated user input is transmitted to the third-party API.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.