Back to skill

Security audit

twitter-aisa-api

Security checks for vulnerabilities and agentic risk

Overview

The skill is a coherent Twitter/X relay client, but it exposes the relay API key in command output and can upload arbitrary readable local media paths if invoked that way.

Install only if you trust the AISA relay and are comfortable with Twitter/X content, search terms, OAuth state, and selected media files being sent there. Treat AISA_API_KEY as sensitive: this version can print it in command output, so avoid running it in logged environments and rotate the key if it has already been exposed. Use media uploads only with explicit user-selected workspace files.

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

Error
Location
scripts/twitter_oauth_client.py:458
Finding

AISA API Key Exposed in Command Output and Redundantly Included in Request Bodies

Content
View full analysis
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, ensure_ascii=False)) ``` The posting path also includes the credential in returned result structures: ```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, } ``` ### Technical Analysis The client reads `AISA_API_KEY` from the environment and correctly uses it as a Bearer credential: ```python headers = { "Authorization": f"Bearer {aisa_api_key}", "User-Agent": DE ...[truncated 2664 chars]
Remediation
View remediation
``` 3. If backend compatibility temporarily requires body authentication, update the backend first and then remove the duplicate field from the client. 4. Add centralized recursive output redaction before serializing responses. At minimum, redact fields named: - `aisa_api_key` - `authorization` - `token` - `access_token` - `refresh_token` 5. Do not return complete raw relay responses when they may contain credentials or OAuth state. Construct a minimal allowlisted response containing only required fields such as status, authorization URL, tweet ID, and error message. 6. Add automated tests asserting that a sentinel API key never appears in: - Standard output. - Standard error. - Returned result dictionaries. - JSON request bodies. - Multipart form fields. 7. Rotate any API key that has already been processed by this version if command output may have been logged or retained. ]]>

T05 · Unauthorized Access and Privilege Escalation

Warning
Location
scripts/twitter_oauth_client.py:413
Finding

Unrestricted Local File Paths Can Be Read and Uploaded as Twitter Media

Content
View full analysis
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, } ) if len(media_kinds) > 1: raise RelayConfigError("Do not mix image and video files in a single post request.") return media_files ``` ### Technical Analysis The Skill documentation states that `--media-file` should be used only for user-provided workspace files. The ...[truncated 3264 chars]
Remediation
View remediation
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
Findings (8)

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
- The release bundle is runtime-only: it keeps `SKILL.md`, `scripts/`, and the posting reference, while omitting non-runtime files such as `README.md` and `_met

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

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 54)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
92% confidence
Finding

The authorization flow transmits the AISA_API_KEY to the remote relay and also includes the key in printed JSON output fields, creating unnecessary exposure of a credential. This is dangerous because API keys can be captured from terminal logs, CI logs, shell history wrappers, or agent transcripts, enabling unauthorized use of the relay account.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The post command sends user-supplied tweet text, media IDs, and the full contents of local media files to a third-party relay service, but the CLI does not present a clear, explicit warning at the point of use that this data leaves the local machine. In a security-sensitive agent context, users may assume local processing while the tool actually exfiltrates potentially sensitive content and files to a remote endpoint.

Content

No source excerpt is available for this finding.

Missing User Warnings

Low
Category
Not specified by scanner
Confidence
89% confidence
Finding

This code file performs network calls to https://api.aisa.one and sends user-provided parameters such as usernames, queries, tweet IDs, and community IDs, but the runtime path contains no confirmation prompt or user-facing notice when those requests are made. Although the module docstring states it is a Twitter/X API client, it does not clearly warn that supplied lookup data will be transmitted to an external service.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.