Back to skill

Security audit

Twitter Post AIsa

Security checks for vulnerabilities and agentic risk

Overview

This skill does what it says for Twitter/X posting and engagement, but it unnecessarily exposes the AIsa API key in command output and has weak boundaries around public actions and media uploads.

Review this skill before installing. It requires an AIsa API key and can publish posts, replies, likes, follows, unfollows, and upload media through a remote AIsa service. Avoid using it where command output is logged unless the API-key output issue is fixed, and only pass media paths you explicitly intend to upload.

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

T09 · Insecure Skill Coding Practices

Error
Location
scripts/twitter_oauth_client.py:338
Finding

AISA API Key Disclosed in OAuth and Publishing Command Output

Content
View full analysis
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/twitter_oauth_client.py:377
Finding

AISA API Key Redundantly Transmitted in Request Bodies

Content
View full analysis
Dict[str, Any]: payload: Dict[str, Any] = { "aisa_api_key": config["aisa_api_key"], } ``` The same request is also authenticated using an authorization header: ```python def build_auth_headers(aisa_api_key: str, extra_headers: Optional[Dict[str, str]] = None) -> Dict[str, str]: headers = { "Authorization": f"Bearer {aisa_api_key}", "User-Agent": DEFAULT_CHROME_USER_AGENT, } if extra_headers: headers.update(extra_headers) return headers ``` The authorization request also duplicates the credential: ```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"], ) ``` Engagement requests use the same pattern: ```python payload = { "aisa_api_key": config["aisa_api_key"], "tweet_id": tweet_id.strip(), } relay_result = relay_action(config, endpoint, payload) ``` ```python payload = { "aisa_api_key": config["aisa_api_key"], "target_user_id": target_user_id.strip(), } relay_result = relay_action(config, endpoint, payload) ``` ## ...[truncated 2027 chars]
Remediation
View remediation
``` 2. Update posting payload construction to begin with an empty dictionary: ```python payload: Dict[str, Any] = {} ``` 3. Change engagement payloads to contain only the target: ```python payload = {"tweet_id": tweet_id.strip()} ``` or: ```python payload = {"target_user_id": target_user_id.strip()} ``` 4. Change the authorization payload to an empty object unless non-secret request fields are needed. 5. If the AIsa backend currently requires the body field, update the backend contract. As an interim measure, use a short-lived, single-purpose token instead of the primary API key. 6. Configure proxy, application, and observability layers to redact both authorization headers and known secret fields in request bodies. 7. Add transport-level tests that inspect generated requests and verify that the API key occurs only in the authorization header. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/twitter_oauth_client.py:410
Finding

Media Upload Can Read and Transmit Arbitrary Accessible Local Files

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, } ) ``` ### Technical Analysis The Skill documentation states that only local files explicitly attached by the user should be uploaded. The implementation does not enforce this boundary. It accepts arbitrary relative paths, absolute paths, and home-directory paths through `os.path.expanduser() ...[truncated 2302 chars]
Remediation
View remediation
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 (18)

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding

There is a material description-behavior mismatch. The declaration emphasizes explicit engagement workflows and approved posting through AIsa, including likes, follows, replies, and OAuth-gated posting. The supplied code chunk only implements read APIs and a CLI for querying Twitter/X data. Its request helper only sends POST generically, but no action-oriented methods or CLI commands use POST, and all exposed endpoints are read endpoints. The code does support the 'read context' portion of the description, but the key declared engagement and posting capabilities are absent, making the declared purpose substantially broader and materially different from the actual behavior.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding

The code substantially matches part of the description: it supports read context (list-tweets) and engagement actions for likes/follows, including unlike/unfollow variants, all routed through AIsa relay endpoints. However, the declared purpose overstates capabilities by claiming support for replies and OAuth-gated posting/approved posting, neither of which appear anywhere in this code. The code exposes only /like_twitter, /unlike_twitter, /follow_twitter, and /unfollow_twitter endpoints plus tweet listing and status inspection. There is also no explicit campaign workflow logic. Because key declared capabilities are absent from the implementation, the description does not accurately represent what this code chunk actually does.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding

The declared description promises a broader Twitter/X engagement skill: likes, follows, replies, read context, and approved posting. The supplied code only implements three commands: authorize, post, and status. Posting supports replies and quote-style chaining, plus media upload and local tweet-length splitting, but there is no code for liking tweets, following accounts, or reading Twitter context. OAuth-gated posting is represented, but the overall declared capability set materially exceeds the actual behavior. Therefore this is a description-behavior mismatch.

Content

No source excerpt is available for this finding.

Missing User Warnings

High
Category
Not specified by scanner
Confidence
99% confidence
Finding

The client includes the raw AISA API key in printed JSON outputs for post/authorization-related results, which can expose the credential to terminal logs, shell history capture, CI logs, screen recordings, or downstream tooling that stores stdout. Because this key authorizes API actions, disclosure can enable unauthorized use of the linked AIsa account and abuse of posting capabilities.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
94% confidence
Finding

The skill declares access to an API key and documents use of a fixed remote endpoint, but it does not declare any explicit tool scope or permissions boundary for environment and network use. In an agent setting, that omission reduces transparency and can allow broader-than-expected secret access and outbound requests, which is a real security concern even if the functionality is intended.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
91% confidence
Finding

The skill explicitly sends API-key-authenticated requests and possibly user-approved media to a third-party endpoint outside the local environment. External transmission is expected for this type of integration, but it still creates real confidentiality and data-handling risk because agent users may not fully appreciate that prompts, account targets, media, and metadata are being sent to a remote service.

Content

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

md
- `AISA_API_KEY` is required for AIsa-backed API access.
- Use repo-relative `scripts/` paths from the shipped package.
- Twitter/X reads, OAuth requests, and user-approved media uploads use the fixed AIsa API endpoint `https://api.aisa.one/apis/v1/twitter`.
- Provide only `AISA_API_KEY`; do not use passwords, cookies, or browser credential export.

## Example Requests

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/engage_twitter.md (reported line 27)May include surrounding context.

md
When the user asks to like, unlike, follow, or unfollow on X/Twitter:

1. Do not ask the user to manually paste tweet links or IDs.
2. If the user first asked to query tweets, keep the returned `tweets[]` structure in the current agent context.
3. Map ordinal follow-up requests to remembered tweet or author context.
4. If multiple user candidates match a natural-language name, stop and ask the user to confirm the account.

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
When the user asks to like, unlike, follow, or unfollow on X/Twitter:

1. Do not ask the user to manually paste tweet links or IDs.
2. If the user first asked to query tweets, keep the returned `tweets[]` structure in the current agent context.
3. Map ordinal follow-up requests to remembered tweet or author context.
4. If multiple user candidates match a natural-language name, stop and ask the user to confirm the account.

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The module docstring and implemented CLI expose only read-oriented Twitter/X operations such as user info, search, followers, replies, trends, lists, communities, and spaces. There is no code for likes, follows, replies-as-actions, or posting, so the actual behavior of this file is materially narrower than the skill's manifest description.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
88% confidence
Finding

This Python code file makes outbound network requests to a third-party endpoint and includes user-provided parameters such as usernames, queries, tweet IDs, and community IDs in those requests. Although the module docstring mentions read APIs and bearer authentication, there is no clear user-facing warning at execution time or explicit disclosure that these inputs are sent to an external service.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The command surface created in main() consists entirely of read and discovery commands, and the parser description explicitly says 'Twitter/X read APIs'. That conflicts with the broader manifest promise of likes, follows, replies, and approved posting workflows through AIsa.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
89% confidence
Finding

The like/unlike paths send tweet engagement requests directly to the remote relay once a tweet ID or latest tweet is resolved, without any explicit warning or confirmation in the client. Because this skill is specifically designed to perform OAuth-gated engagement actions, accidental invocation or malicious upstream instructions can produce unintended public interactions and reputational harm on the user's account.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

The follow/unfollow commands trigger real remote social-account actions immediately after argument parsing and user resolution, with no interactive confirmation, dry-run mode, or explicit consent check in this client. In an agent-integrated workflow, ambiguous prompting, tool misuse, or prompt injection in upstream context could cause unintended account actions that affect the user's social graph and reputation.

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 · 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
91% confidence
Finding

The post command transmits tweet text, media IDs, and possibly uploaded file contents to the remote AIsa API endpoint. Although network transmission is inherent to posting, this code does not provide a visible runtime disclosure that local text and media files will be sent to an external service, beyond terse argument help strings.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
88% confidence
Finding

The authorize command constructs a payload containing the AISA API key and posts it to the AIsa authorization endpoint. The file lacks an explicit warning in comments, docstrings, or user-facing output that credentials are being transmitted over the network during authorization.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Low
Category
Not specified by scanner
Confidence
97% confidence
Finding

The module docstring and CLI argument help indicate an authorize flow with an optional --open-browser behavior, suggesting the client can open the authorization URL locally. However, command_authorize only prints a message that browser auto-open is disabled and never calls webbrowser.open, so the documented behavior contradicts the code.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.