Back to skill

Security audit

X Hourly Brief

Security checks for vulnerabilities and agentic risk

Overview

This paid X brief skill should be reviewed before install because it auto-charges users, embeds a billing credential, and can fetch arbitrary URLs from the runtime environment.

Install only after the publisher removes and rotates the embedded billing key, validates URLs to the intended X domains, documents all outbound services, and adds clear user control around charging. Avoid running it in environments that can reach private networks or sensitive internal services.

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/run.js:17
Finding

Hard-Coded Billing API Credential Exposed in Source Code

Content
View full analysis
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Error
Location
scripts/run.js:48
Finding

Server-Side Request Forgery Through Unrestricted URL Fetching

Content
View full analysis
s.trim()).filter(Boolean); ``` The unrestricted URL is eventually fetched directly: ```javascript async function fetchText(u) { const bad = (t) => !t || t.length < 40 || ['JavaScript is not available', 'Please enable JavaScript', '.css-175oi2r'].some(x => t.includes(x)); const tryFetch = async (url) => { const r = await fetch(url).catch(() => null); if (!r || !r.ok) return null; const t = await r.text(); return bad(t) ? null : t; }; let t = await tryFetch(toJina(u)); const id = sid(u); if (!t && id) t = await tryFetch(`https://api.fxtwitter.com/i/status/${id}`) || await tryFetch(`https://api.fxtwitter.com/status/${id}`); if (!t) t = await tryFetch(u); return t || ''; } ``` ### Technical Analysis The declared purpose is to summarize X post URLs, but the implementation does not enforce an X hostname, HTTPS, a valid status path, or an approved URL scheme. It also does not validate redirect destinations or reject addresses resolving to localhost, private networks, link-local networks, or cloud metadata services. The final fallback passes the user-controlled value directly to `fetch`. This causes the request to originate from the environment running the Skill, potentially giving an attacker access to network locations that are not directly reachable from the attacker's own system. If a response is at least 40 characters and does not contain one of three blocked strings, its text is processed and included in the JSON output. Consequently, the flaw is not limited to blind SSRF: readable internal responses may be partially disclosed through the generated bullets and takeaway. The simplistic HTML-tag ...[truncated 1479 chars]
Remediation
View remediation
` path structure. 4. Reject embedded credentials, nonstandard schemes, unexpected ports, fragments, and hostname-suffix tricks. 5. Resolve the hostname and reject loopback, private, link-local, multicast, reserved, and cloud metadata address ranges for both IPv4 and IPv6. 6. Disable redirects or validate every redirect destination using the same hostname and resolved-address controls. 7. Prefer extracting only the numeric status ID and sending it to one fixed, trusted API rather than fetching arbitrary user-provided URLs. 8. Apply outbound firewall or sandbox restrictions so the Skill cannot reach localhost, metadata endpoints, or private networks. 9. Validate all inputs before charging the user. 10. Add tests covering localhost, private IPv4 and IPv6 addresses, encoded IP forms, DNS rebinding, user-info syntax, malicious redirects, and hostname-suffix bypasses. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
scripts/run.js:16
Finding

Sensitive Billing Data Can Be Redirected to an Attacker-Controlled Endpoint

Content
View full analysis
null); if (!r) return null; const d = await r.json().catch(() => ({})); return d.payment_url || null; } async function charge() { const r = await fetch(`${BILLING_URL}/charge`, { method: 'POST', headers: { 'content-type': 'application/json', 'x-api-key': API_KEY }, body: JSON.stringify({ user_id: userId, skill_id: SKILL_ID, amount: PRICE_TOKEN }), }).catch(() => null); if (!r) return { ok: false, reason: 'network_error' }; const d = await r.json().catch(() => ({})); if (d.success) return { ok: true, data: d }; if (!d.payment_url) d.payment_url = await getPaymentLink(7); return { ok: false, reason: 'insufficient_balance', data: d }; } ``` ### Technical Analysis Transmitting a user identifier and billing fields to the documented billing provider is necessary for the Skill's charge-first functionality. However, `SKILLPAY_BILLING_URL` can replace the entire destination without any scheme or origin validation. Both billing functions attach the API key to requests derived from this configurable base URL. A process supervisor, compromised launch script, malicious deployment configuration, or other actor able to infl ...[truncated 1876 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 (9)

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding

The documented purpose materially understates the actual behavior: the skill performs external billing and fetches arbitrary user-supplied URLs and third-party mirrors while presenting itself as a simple brief generator. This mismatch is dangerous because operators and users cannot give informed consent, and an attacker or careless deployment could use the undeclared network and charging behavior to trigger unwanted charges, route data to third parties, or process untrusted content outside expected boundaries.

Content

No source excerpt is available for this finding.

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
node scripts/run.js --urls "https://x.com/.../status/1,https://x.com/.../status/2" --user "<user-id>" --lang "auto"

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
90% confidence
Finding

The skill advertises executable behavior with access to environment variables and network resources but does not declare any tool scope or permissions boundary in the skill manifest. This makes the capability set opaque to reviewers and host systems, increasing the risk of unexpected outbound requests, secret exposure from env access, and policy bypass through under-declared behavior.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
97% confidence
Finding

The script performs a charge attempt before fetching or generating the brief, which creates a charge-first workflow inconsistent with a simple content summarization utility. This is risky because users can be billed before any value is delivered, and if downstream fetching fails or returns no usable content, the user may still have been charged.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
88% confidence
Finding

The code loads billing credentials and uses them to perform real charge operations without any in-script disclosure or explicit runtime consent gate. While reading secrets from environment variables is standard, using them to initiate billing automatically makes the behavior security-relevant because it can silently monetize execution.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The skill includes active payment processing and charging capability, which is sensitive functionality beyond ordinary brief generation. In this context, embedding charging logic directly in the execution path increases the risk of unauthorized or unexpected billing, especially because execution automatically attempts a charge without an interactive confirmation step.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

The script sends the user identifier to the billing service and sends requested URLs to third-party fetch endpoints, which exposes user-linked activity and requested content to external parties. Without clear disclosure, minimization, or consent controls, this creates privacy and data-handling risk, especially since third-party proxies may log requests and correlate them with users.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
86% confidence
Finding

The script transmits X/Twitter status identifiers and potentially full requested URLs to external services such as api.fxtwitter.com and r.jina.ai to retrieve content. This is dangerous because third-party services can observe, log, or manipulate fetched content, creating privacy, supply-chain, and content-integrity risks for users of the skill.

Content

Scanner excerpt · scripts/run.js (reported line 60)May include surrounding context.

js
};
  let t = await tryFetch(toJina(u));
  const id = sid(u);
  if (!t && id) t = await tryFetch(`https://api.fxtwitter.com/i/status/${id}`) || await tryFetch(`https://api.fxtwitter.com/status/${id}`);
  if (!t) t = await tryFetch(u);
  return t || '';
}

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
73% confidence
Finding

The usage string and summarization logic restrict language handling to zh|en|auto, and the script emits Chinese fallback text for zh mode and English otherwise. This creates a built-in locale limitation without any documented opt-in justification or support for broader language choice.

Content

No source excerpt is available for this finding.