Back to skill

Security audit

AIsa Twitter API Command Center

Security checks for vulnerabilities and agentic risk

Overview

This skill mostly does what it says, but it exposes the AISA API key in command output and has weak controls around public posting and media uploads.

Review before installing. Use this only with an AISA_API_KEY you are comfortable granting Twitter/X relay access, avoid running it where stdout is logged, and rotate the key if it has already been used because the client can print it in plaintext. Only pass media files you intentionally want uploaded and posted publicly.

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

T09 · Insecure Skill Coding Practices

Error
Location
scripts/twitter_oauth_client.py:342
Finding

Plaintext API Key Included in Request Bodies and Command Output

Content
View full analysis

Vulnerability Details

File Location: scripts/twitter_oauth_client.py:342-381, 458-478
Vulnerability Type: Sensitive credential exposure
Risk Level: High

Vulnerable Code

python
if result.get("ok") is False or result.get("code") != 200:
    return {
        "ok": False,
        "aisa_api_key": config["aisa_api_key"],
        "is_thread": should_thread,
        "total_chunks": len(chunks),
        "failed_at_chunk": index + 1,
        "results": publish_results,
    }

latest_tweet_id = extract_tweet_id(result)
if not latest_tweet_id:
    return {
        "ok": False,
        "aisa_api_key": config["aisa_api_key"],
        "is_thread": should_thread,
        "total_chunks": len(chunks),
        "failed_at_chunk": index + 1,
        "error": "Missing tweet_id in relay response.",
        "results": publish_results,
    }

return {
    "ok": True,
    "aisa_api_key": config["aisa_api_key"],
    "is_thread": should_thread,
    "total_chunks": len(chunks),
    "results": publish_results,
}
python
payload: Dict[str, Any] = {
    "aisa_api_key": config["aisa_api_key"],
}
python
def command_authorize(args: argparse.Namespace) -> None:
    config = load_config(args)
    payload = {"aisa_api_key": config["aisa_api_key"]}
    result = send_json_request(
        f"{config['base_url']}/twitter/auth_twitter",
        payload,
        timeout=config["timeout"],
        aisa_api_key=config["aisa_api_key"],
    )

    if result.get("ok") is False:
        print(json.dumps(result, indent=2, ensure_ascii=False))
        sys.exit(1)

    auth_url = (result.get("data") or {}).get("auth_url")
    output = {
        "ok": result.get("code") == 200 and bool(auth_url),
        "aisa_api_key": config["aisa_api_key"],
        "authorization_url": auth_url,
        "raw_response": result,
    }
    print(json.dumps(output, indent=2, e
...[truncated 2340 chars]
Remediation
View remediation

Remediation Suggestions

  1. Remove aisa_api_key from every returned result and CLI output structure.
  2. Remove the key from JSON and multipart request bodies and authenticate exclusively with the HTTPS Authorization: Bearer header.
  3. If the relay currently requires body-based authentication, update the client and server protocol together so the body field can be eliminated.
  4. Add centralized output sanitization that recursively redacts API keys, authorization headers, tokens, and similarly sensitive fields before serialization.
  5. Ensure error responses cannot reflect credentials received from the relay.
  6. Add automated tests asserting that command output and serialized payloads never contain the configured key.
  7. Review logs and transcripts generated by previous executions, remove exposed values where possible, and rotate any key that may already have been recorded.

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/twitter_oauth_client.py:412
Finding

Local Media Upload Accepts Unrestricted Readable File Paths

Content
View full analysis

Vulnerability Details

File Location: scripts/twitter_oauth_client.py:412-448
Vulnerability Type: Unrestricted local file read and upload
Risk Level: Medium

Vulnerable Code

python
def load_media_files(paths: Optional[list[str]]) -> list[Dict[str, Any]]:
    if not paths:
        return []

    media_files: list[Dict[str, Any]] = []
    media_kinds: set[str] = set()
    seen_paths: set[str] = set()

    for raw_path in paths:
        resolved_path = os.path.abspath(os.path.expanduser(raw_path))
        normalized_path = os.path.normcase(resolved_path)
        if normalized_path in seen_paths:
            continue
        seen_paths.add(normalized_path)

        if not os.path.exists(resolved_path):
            raise RelayConfigError(f"Media file does not exist: {raw_path}")
        if not os.path.isfile(resolved_path):
            raise RelayConfigError(f"Media path is not a file: {raw_path}")

        mime_type = mimetypes.guess_type(resolved_path)[0] or "application/octet-stream"
        media_kind = mime_type.split("/", 1)[0]
        if media_kind not in {"image", "video"}:
            raise RelayConfigError(
                f"Unsupported media type for {raw_path}: {mime_type}. Only image and video files are supported."
            )
        media_kinds.add(media_kind)

        with open(resolved_path, "rb") as file_handle:
            content = file_handle.read()

        media_files.append(
            {
                "field_name": "media_files",
                "filename": os.path.basename(resolved_path),
                "content_type": mime_type,
                "content": content,
            }
        )

Technical Analysis

The --media-file option accepts an arbitrary path accessible to the process. The implementation expands home-directory notation and converts the input to an absolute path, but it does not constrain the resulting targe ...[truncated 2241 chars]

Remediation
View remediation

Remediation Suggestions

  1. Require a trusted workspace or attachment root and reject paths outside that directory.
  2. Canonicalize both the trusted root and requested target using os.path.realpath(), then validate containment with os.path.commonpath().
  3. Reject symbolic links, or securely open files without following links where the platform supports that behavior.
  4. Validate media using file signatures or a trusted content-inspection library rather than filename extensions alone.
  5. Enforce explicit limits on individual file size, total upload size, attachment count, and supported media formats.
  6. Stream approved files where practical instead of loading each complete file into memory.
  7. Display or log a safely quoted canonical path and require explicit user approval for every file before transmitting it.
  8. Add tests covering parent-directory traversal, home-directory expansion, absolute paths outside the workspace, symbolic links, misleading extensions, and oversized files.
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
Findings (14)

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding

The code’s primary behavior is a read-only Twitter/X API client using bearer-token authentication to query user, tweet, trend, list, community, and space data from AIsa endpoints. This aligns with the research/search/trend-tracking portion of the description, but materially does not support several declared flagship capabilities: there are no POST write endpoints for publishing tweets, no OAuth handling for gated posting, and no explicit watchlist features or monitoring automation beyond basic fetch/search operations. Therefore the description overstates the implemented functionality in meaningful ways.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding

The description presents a broad Twitter/X research and monitoring skill with search, watchlists, competitor tracking, and approved posting. The supplied code does not implement research, search, monitoring, trend tracking, or watchlist management. Instead, it is narrowly a CLI client for AIsa Twitter authorization and posting. It can obtain an auth URL, report status, upload image/video media, and publish tweets/replies/quote-style chains with local Twitter-length splitting. That means the declared primary purpose materially overstates and misrepresents the implemented behavior. The posting/OAuth portion is consistent with part of the description, but the major advertised monitoring/research/watchlist capabilities are absent, while posting-specific implementation details like media uploads and threading are present but undeclared.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

High
Category
Not specified by scanner
Confidence
99% confidence
Finding

The script includes the raw AISA API key in JSON output from both posting flows and the status/authorization-related outputs, unnecessarily disclosing a bearer credential to anyone who can read terminal logs, shell history captures, CI job logs, or downstream tool output. Because this key authorizes actions against the AIsa API, exposure can enable unauthorized use of the account and abuse of Twitter posting capabilities.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
83% confidence
Finding

The skill declares required environment access and a fixed external API endpoint, but does not define any explicit tool scope such as allowed-tools or permissions. In an agent environment, that omission weakens policy enforcement and can allow broader-than-expected network or secret access when the skill is invoked.

Content

No source excerpt is available for this finding.

Autonomous Decision Making

Medium
Category
Excessive Agency
Confidence
80% confidence
Finding

Skill enables autonomous high-impact decisions without human-in-the-loop verification. Critical operations (destructive commands, financial transactions, data deletion) should require explicit user confirmation.

Content

Scanner excerpt · references/post_twitter.md (reported line 86)May include surrounding context.

md
## Guardrails

- Do not ask the user for their Twitter password.
- Do not use cookie-based login or proxy-based login unless the user explicitly asks for legacy behavior.
- Do not default to `--open-browser`; return the authorization link unless the user explicitly wants local browser launch.
- Do not invent remote URLs for attachments; always use the provided local workspace file path with `--media-file`.

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The module docstring says this is for "Twitter/X read APIs" and the usage block only describes read operations. However, the shared _request method explicitly supports POST bodies, enabling write-capable calls if used with additional endpoints, which exceeds the read-only behavior claimed in the file documentation.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
60% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · SKILL.md (reported line 64)May include surrounding context.

md
DEFAULT_TIMEOUT = 30
DEFAULT_BASE_URL = "https://api.aisa.one/apis/v1"
DEFAULT_CHROME_USER_AGENT = (
    "Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
    "AppleWebKit/537.36 (KHTML, like Gecko) "

External Transmission

Medium
Category
Data Exfiltration
Confidence
60% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · scripts/twitter_client.py (reported line 35)May include surrounding context.

python
DEFAULT_TIMEOUT = 30
DEFAULT_BASE_URL = "https://api.aisa.one/apis/v1"
DEFAULT_CHROME_USER_AGENT = (
    "Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
    "AppleWebKit/537.36 (KHTML, like Gecko) "

External Transmission

Medium
Category
Data Exfiltration
Confidence
60% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · scripts/twitter_oauth_client.py (reported line 29)May include surrounding context.

python
DEFAULT_TIMEOUT = 30
DEFAULT_BASE_URL = "https://api.aisa.one/apis/v1"
DEFAULT_CHROME_USER_AGENT = (
    "Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
    "AppleWebKit/537.36 (KHTML, like Gecko) "

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
89% confidence
Finding

The code reads arbitrary local image/video files from disk and includes their raw contents in a multipart HTTP request. Although uploading media is part of the command purpose, there is no explicit disclosure that selected local files will be transmitted to the external AIsa endpoint.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
86% confidence
Finding

This code sends the AISA API key to remote endpoints and, in the post flow, also transmits tweet text and media to the AIsa service. While the script's purpose implies posting, there is no explicit disclosure in the execution path or module docstring warning that local content and credentials are sent to an external service.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Low
Category
Not specified by scanner
Confidence
80% confidence
Finding

Line L87 implies the skill may support a cookie-based or proxy-based legacy login path when explicitly requested. The rest of the document consistently describes only OAuth-based posting through the AISA relay and explicitly states that the workflow does not use passwords, browser cookies, cache sync, or home-directory persistence. This creates a documentation-level contradiction about supported authentication behavior.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Low
Category
Not specified by scanner
Confidence
88% confidence
Finding

The module docstring and CLI flags present authorize --open-browser as a supported workflow, which implies the client can launch the authorization URL locally. However, command_authorize explicitly disables browser auto-open and instructs the user to open the URL manually, making the declared capability misleading relative to the implemented behavior.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Low
Category
Not specified by scanner
Confidence
91% confidence
Finding

The file-level Commands section and parser help text describe authorize [--open-browser] and "Open the authorization URL in the default browser". In contrast, the implementation at runtime prints that browser auto-open is disabled, which is an active contradiction between documentation and behavior rather than mere incompleteness.

Content

No source excerpt is available for this finding.