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## 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
## 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## 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
## 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.

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.

Context-Inappropriate Capability

High
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.

Lp3

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.

Autonomous Decision Making

Medium
Category
Excessive Agency
Content
## 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`.
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.

Description-Behavior Mismatch

Medium
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.

External Transmission

Medium
Category
Data Exfiltration
Content
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) "
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

External Transmission

Medium
Category
Data Exfiltration
Content
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) "
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

External Transmission

Medium
Category
Data Exfiltration
Content
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) "
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Missing User Warnings

Medium
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.

Missing User Warnings

Medium
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.

Intent-Code Divergence

Low
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.

Context-Inappropriate Capability

Low
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.

Intent-Code Divergence

Low
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.

Static analysis

No suspicious patterns detected.