Back to skill

Security audit

X Expert

Security checks for vulnerabilities and agentic risk

Overview

This skill is a real X/Twitter publishing helper, but it can directly post, upload media, and delete tweets using live account credentials without code-enforced confirmation.

Install only if you are comfortable giving the skill live X account credentials. Use least-privilege X tokens, test with a non-production account first, review every final post/media path manually, and avoid invoking delete-tweet.js or direct publishing unless you intentionally want immediate irreversible account changes.

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

Warning
Location
scripts/post-media.js:23
Finding
Unrestricted Local File Selection for Remote Media Upload<![CDATA[ ## Vulnerability Details **File Location**: `scripts/post-media.js:23-38` **Vulnerability Type**: Arbitrary local file upload through an unrestricted path **Risk Level**: Medium ### Vulnerable Code ```js // Check if media file exists if (!fs.existsSync(mediaPath)) { console.error(`Error: Media file not found: ${mediaPath}`); process.exit(1); } const client = new TwitterApi({ appKey: apiKey, appSecret: apiSecret, accessToken: accessToken, accessSecret: accessSecret, }); try { // Upload media first console.log('Uploading media...'); const mediaId = await client.v1.uploadMedia(mediaPath); ``` The value of `mediaPath` is taken directly from the command line: ```js const text = process.argv[2]; const mediaPath = process.argv[3]; if (!text || !mediaPath) { console.error('Usage: node post-media.js "Your tweet text" "/path/to/image.jpg"'); process.exit(1); } postTweetWithMedia(text, mediaPath); ``` ### Technical Analysis The script accepts an arbitrary filesystem path and verifies only that the path exists. It does not: - Restrict files to a dedicated workspace or approved media directory. - Resolve and validate the canonical path. - reject symbolic links. - Verify that the path refers to a regular file. - Validate the file's MIME type or extension. - Enforce a file-size limit. - Require the user to confirm the canonical path before transmission. The supplied path is passed directly to `twitter-api-v2`, which reads the local file and uploads it to X. This exceeds the minimum filesystem access required for publishing a user-approved attachment because the script can attempt to access any path readable by its process. Successful disclosure depends on the selected file being accepted by the X media API. Nevertheless, private local images, videos, or other accepted media files could be exposed. ### Attack Path 1. An attacker-controlled prompt, untrusted workflow input, or mistaken Agent decision supplies a sensitive local path ...[truncated 1101 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Create a dedicated media directory and reject paths outside it. 2. Resolve the canonical path with `fs.realpathSync()` and verify that it remains under the approved directory. 3. Use `fs.lstatSync()` and `fs.statSync()` to reject symbolic links and non-regular files. 4. Allowlist supported media MIME types and verify content using file signatures rather than relying only on extensions. 5. Enforce explicit file-size and dimension limits before upload. 6. Display the canonical path, detected media type, size, and a preview to the user. 7. Require explicit approval tied to the exact file hash and tweet content. 8. Run the Skill with filesystem permissions limited to its workspace and approved media directory. Example boundary validation: ```js const approvedRoot = fs.realpathSync(process.env.X_MEDIA_ROOT); const canonicalPath = fs.realpathSync(mediaPath); const relativePath = path.relative(approvedRoot, canonicalPath); if ( relativePath.startsWith('..') || path.isAbsolute(relativePath) || !fs.statSync(canonicalPath).isFile() ) { throw new Error('Media path is outside the approved media directory'); } ``` ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/post-tweet.js:27
Finding
Publishing and Deletion Operations Execute Without Enforced User Confirmation<![CDATA[ ## Vulnerability Details **File Locations**: - `scripts/post-tweet.js:27` - `scripts/post-thread.js:34-38` - `scripts/post-media.js:37-44` - `scripts/delete-tweet.js:31-34` **Vulnerability Type**: Missing authorization and confirmation gate for state-changing operations **Risk Level**: Medium ### Vulnerable Code Single-tweet publication executes immediately: ```js try { const tweet = await client.v2.tweet(content); console.log('Tweet posted successfully!'); console.log('Tweet ID:', tweet.data.id); console.log('Tweet URL:', `https://twitter.com/user/status/${tweet.data.id}`); return tweet.data; } ``` Thread publication executes each mutation sequentially: ```js const tweet = await client.v2.tweet({ text: tweets[i], ...(replyTo && { reply: { in_reply_to_tweet_id: replyTo } }), }); tweetIds.push(tweet.data.id); console.log(`Tweet ${i + 1}/${tweets.length} posted:`, tweet.data.id); ``` Media upload and publication also execute immediately: ```js // Upload media first console.log('Uploading media...'); const mediaId = await client.v1.uploadMedia(mediaPath); console.log('Media uploaded, ID:', mediaId); // Post tweet with media const tweet = await client.v2.tweet({ text: text, media: { media_ids: [mediaId] }, }); ``` Deletion executes immediately after receiving a tweet identifier: ```js try { await client.v2.deleteTweet(tweetId); console.log('Tweet deleted successfully!'); console.log('Deleted Tweet ID:', tweetId); } ``` ### Technical Analysis `SKILL.md` describes a final preview and user-confirmation step before publishing and states that confirmation is required by default. The executable scripts do not enforce that control. There is no: - Dry-run or preview default. - Interactive confirmation. - Approval token tied to the exact requested action. - Verification of the intended X account. - Confirmation flag for destructive deletion. - Integrity binding between reviewed text/media and submitted text/media. - Transacti ...[truncated 2112 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Make all state-changing scripts operate in preview or dry-run mode by default. 2. Require a short-lived, single-use approval token generated only after displaying the final action to the user. 3. Cryptographically bind the approval token to: - The action type. - The exact tweet text or complete thread. - The selected account identifier. - The canonical media path and file hash. - The target tweet identifier for deletion. - An expiration timestamp. 4. Validate that token inside each script immediately before performing the X API request. 5. Require an explicit destructive-action flag and fresh confirmation for deletion. 6. Query and display the authenticated account identity before approval. 7. For threads, validate every post before starting and report partial completion precisely. 8. Consider recording posted tweet identifiers and offering an explicitly confirmed cleanup operation if a thread fails midway. 9. Avoid treating conversational confirmation alone as an authorization boundary. A safer command pattern would be: ```bash node scripts/post-tweet.js \ --dry-run \ --content "Reviewed content" ``` After preview, execution should require an exact approval artifact: ```bash node scripts/post-tweet.js \ --content "Reviewed content" \ --approval-token "$SHORT_LIVED_APPROVAL" ``` Deletion should require a separate explicit option, such as: ```bash node scripts/delete-tweet.js \ --tweet-id "1234567890" \ --confirm-delete \ --approval-token "$SHORT_LIVED_APPROVAL" ``` ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • 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 (45)

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
The skill advertises an assistant-style workflow but appears to include direct posting capability with no explicit declared permissions. In a skill that uses X API credentials, undeclared write actions are dangerous because they can publish content on behalf of the user without sufficiently transparent authorization boundaries.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
The skill advertises an assistant-style workflow but appears to include direct posting capability with no explicit declared permissions. In a skill that uses X API credentials, undeclared write actions are dangerous because they can publish content on behalf of the user without sufficiently transparent authorization boundaries.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
The skill advertises an assistant-style workflow but appears to include direct posting capability with no explicit declared permissions. In a skill that uses X API credentials, undeclared write actions are dangerous because they can publish content on behalf of the user without sufficiently transparent authorization boundaries.

Tp4

High
Category
MCP Tool Poisoning
Confidence
93% confidence
Finding
The skill advertises an assistant-style workflow but appears to include direct posting capability with no explicit declared permissions. In a skill that uses X API credentials, undeclared write actions are dangerous because they can publish content on behalf of the user without sufficiently transparent authorization boundaries.

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding
The skill advertises an assistant-style workflow but appears to include direct posting capability with no explicit declared permissions. In a skill that uses X API credentials, undeclared write actions are dangerous because they can publish content on behalf of the user without sufficiently transparent authorization boundaries.

Tp4

High
Category
MCP Tool Poisoning
Confidence
91% confidence
Finding
The skill advertises an assistant-style workflow but appears to include direct posting capability with no explicit declared permissions. In a skill that uses X API credentials, undeclared write actions are dangerous because they can publish content on behalf of the user without sufficiently transparent authorization boundaries.

Ae1

High
Category
analysis-evasion
Content
| `collect-info.js` | 使用 Exa/MiniMax/Brave Search 收集信息 |
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
| `generate-tweet.js` | 根据主题和风格生成推文内容 |
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
| `post-tweet.js` | 发布单条推文 |
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
| `post-thread.js` | 发布推文串 |
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
| `post-media.js` | 发布带媒体的推文 |
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Credential Access

High
Category
Privilege Escalation
Content
## 故障排除

- 如果收到 401 错误,检查 Access Token 是否有效
- 如果收到 403 错误,检查 App 权限
- 如果图片生成失败,检查 API 配置
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Rp1

Medium
Category
MCP Rug Pull
Confidence
92% confidence
Finding
The README instructs users to execute `npx clawhub@latest install x-expert`, which pulls and runs the latest package version at install time rather than a pinned, reviewed release. This creates a supply-chain risk: if the upstream package is compromised or a breaking/malicious update is published, users may execute attacker-controlled code during installation.

Description-Behavior Mismatch

Medium
Confidence
91% confidence
Finding
The README advertises a `delete-tweet.js` script even though the described skill scope is a create/plan/publish assistant. This scope expansion introduces a destructive capability that users may not expect, increasing the risk of accidental or hidden content deletion if the skill or surrounding agent invokes undocumented actions.

Missing User Warnings

Medium
Confidence
91% confidence
Finding
The README lists tweet deletion as a supported script without any warning that it is a destructive operation. In a conversational publishing skill, omission of a prominent warning and confirmation expectations makes accidental or socially engineered deletion more plausible, especially if users assume all listed actions are routine and safe.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The skill is explicitly designed to publish, schedule, and delete tweets using real X credentials, but the README does not warn users that these actions affect live accounts and may be public, automated, or difficult to fully undo. In this context, lack of an operational safety warning increases the chance of accidental posting, reputational damage, unintended deletions, or misuse of privileged social-media access.

Rp1

Medium
Category
MCP Rug Pull
Confidence
94% confidence
Finding
The README instructs users to run `npx clawhub@latest install x-expert`, which fetches and executes the latest CLI code without pinning a specific trusted version. If the upstream package is compromised or a breaking/malicious release is published, users could execute attacker-controlled code during installation.

Lp3

Medium
Category
MCP Least Privilege
Confidence
95% confidence
Finding
The skill declares access to environment variables and relies on outbound network activity, but it does not declare an explicit tool scope such as permissions or allowed-tools. That makes the skill's effective authority opaque to users and reviewers, increasing the chance of unintended credential use or external data transmission.

Natural-Language Policy Violations

Medium
Confidence
94% confidence
Finding
The manifest description forces a specific language/locale for the skill experience by presenting the core description entirely in Chinese. The file does not indicate that users can choose another language or that the Chinese-only behavior is limited to a justified region-specific use case.

Natural-Language Policy Violations

Medium
Confidence
91% confidence
Finding
The prescribed user prompts throughout the workflow are all fixed in Chinese, which creates a language policy issue when no alternative language path or opt-in is provided. Because these are the required interaction strings, the skill effectively enforces one locale for all users.

Intent-Code Divergence

Medium
Confidence
88% confidence
Finding
The documentation says publishing should default to user confirmation, but it also offers a 'direct publish' review mode. For a skill with posting authority to a social-media account, this weakens a key safety barrier and can lead to accidental or unauthorized publication.

Natural-Language Policy Violations

Medium
Confidence
93% confidence
Finding
The file presents its purpose and CLI usage entirely in Chinese, including the expected query example, which implies a language-specific interaction model. The policy allows locale constraints only when users are given a choice or the restriction is clearly documented and justified; neither is present here.

External Transmission

Medium
Category
Data Exfiltration
Content
throw new Error('EXA_API_KEY not set');
  }

  const response = await fetch('https://api.exa.ai/search', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
Confidence
70% 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
throw new Error('EXA_API_KEY not set');
  }

  const response = await fetch('https://api.exa.ai/search', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
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
throw new Error('MINIMAX_API_KEY not set');
  }

  const response = await fetch('https://api.minimax.chat/v1/web_search', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
Confidence
70% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Static analysis

Detected: suspicious.env_credential_access

Environment variable access combined with network send.

Critical
Code
suspicious.env_credential_access
Location
scripts/collect-info.js:13

Environment variable access combined with network send.

Critical
Code
suspicious.env_credential_access
Location
scripts/generate-image.js:37