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<![CDATA[ ## Vulnerability Details **File Location**: `scripts/run.js:17` **Vulnerability Type**: Hard-coded secret **Risk Level**: High ### Vulnerable Code ```javascript const API_KEY = process.env.SKILL_BILLING_API_KEY || 'sk_74e1969ebc92fcf58257470c50f8bb76e36c9da0d201aa69861e28c62f5bd48e'; ``` The credential is subsequently attached to billing requests: ```javascript headers: { 'content-type': 'application/json', 'x-api-key': API_KEY }, ``` ### Technical Analysis The script contains a live-looking billing API key as its default configuration. Anyone with access to the Skill package can extract this value without executing the code. Environment-variable support does not protect the embedded fallback. If `SKILL_BILLING_API_KEY` is absent, every installation uses the same credential. This prevents reliable attribution and exposes the credential to source repositories, package archives, logs, backups, and all users who receive the Skill. The precise capabilities of the credential cannot be established from the reviewed files. Nevertheless, it is explicitly accepted by the external billing API as an authentication credential and must therefore be treated as sensitive. ### Attack Path 1. An attacker obtains or downloads the Skill package. 2. The attacker opens `scripts/run.js` and extracts the `sk_...` credential. 3. The attacker identifies the billing endpoint from the adjacent `BILLING_URL` declaration. 4. The attacker sends independent requests with the recovered value in the `x-api-key` header. 5. Any operations permitted to the shared credential can then be invoked outside the intended Skill workflow. ### Impact Assessment Successful exploitation may permit unauthorized use of the billing API, fraudulent or malformed billing operations, consumption of service quota, and loss of request attribution. The exact scope is limited to the server-side permissions assigned to the exposed key; broader administrative privileges were not demonstrated during t ...[truncated 21 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Revoke and rotate the exposed credential immediately. 2. Remove the hard-coded fallback and require the credential to be provided through an approved secret manager or protected environment variable. 3. Fail closed with a clear configuration error when the credential is unavailable. 4. Issue narrowly scoped credentials that authorize only the required billing operations. 5. Prefer short-lived, installation-specific credentials rather than a shared static key. 6. Add secret scanning to source-control and release pipelines. 7. Review billing-service logs for unauthorized use of the exposed credential. 8. Enforce server-side authorization, rate limits, idempotency controls, and user-to-charge binding so possession of an API key alone is insufficient to perform arbitrary billing actions. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
scripts/run.js:48
Finding
Server-Side Request Forgery Through Unrestricted URL Fetching<![CDATA[ ## Vulnerability Details **File Location**: `scripts/run.js:48-64` **Vulnerability Type**: Server-Side Request Forgery **Risk Level**: High ### Vulnerable Code User-controlled URLs are accepted without validation: ```javascript const urlsRaw = getArg('urls') || getArg('url') || ''; const urls = urlsRaw.split(',').map((s) => 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
<![CDATA[ ## Remediation Suggestions 1. Parse every input with the standard `URL` parser and reject malformed URLs. 2. Allow only HTTPS URLs whose normalized hostname is explicitly approved, such as `x.com` or another documented X domain. 3. Require the expected `/.../status/<numeric-id>` 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<![CDATA[ ## Vulnerability Details **File Location**: `scripts/run.js:16-43` **Vulnerability Type**: Unvalidated sensitive-data destination **Risk Level**: High ### Vulnerable Code ```javascript const BILLING_URL = process.env.SKILLPAY_BILLING_URL || 'https://skillpay.me/api/v1/billing'; const API_KEY = process.env.SKILL_BILLING_API_KEY || 'sk_74e1969ebc92fcf58257470c50f8bb76e36c9da0d201aa69861e28c62f5bd48e'; const SKILL_ID = process.env.SKILL_ID || '7674002a-818d-45f7-811b-c0e0145101e4'; const PRICE_TOKEN = Number(process.env.SKILLPAY_PRICE_TOKEN || '1'); async function getPaymentLink(amount = 7) { const r = await fetch(`${BILLING_URL}/payment-link`, { method: 'POST', headers: { 'content-type': 'application/json', 'x-api-key': API_KEY }, body: JSON.stringify({ user_id: userId, amount }), }).catch(() => 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
<![CDATA[ ## Remediation Suggestions 1. Pin the billing service to a fixed, trusted HTTPS origin in production. 2. If endpoint overrides are required for testing, permit only explicit allowlisted HTTPS origins and disable overrides in production builds. 3. Parse and normalize the configured URL before use; reject HTTP, embedded credentials, unexpected ports, fragments, and unapproved hostnames. 4. Do not attach a reusable API credential to a dynamically selected origin. 5. Replace shared static keys with short-lived, audience-bound tokens that are valid only for the intended billing origin and operation. 6. Separate test and production credentials and endpoints. 7. Protect environment and deployment configuration with least-privilege access controls and integrity monitoring. 8. Validate the structure and authenticity of billing responses rather than trusting arbitrary JSON from the configured server. 9. Accept payment URLs only from explicitly approved HTTPS origins before displaying them to users. 10. Minimize transmitted identifiers and use pseudonymous, billing-specific identifiers where possible. ]]>
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.

Ae1

High
Category
analysis-evasion
Content
node scripts/run.js --urls "https://x.com/.../status/1,https://x.com/.../status/2" --user "<user-id>" --lang "auto"
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Lp3

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.

Description-Behavior Mismatch

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

Missing User Warnings

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

Context-Inappropriate Capability

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

Missing User Warnings

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

External Transmission

Medium
Category
Data Exfiltration
Content
};
  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 || '';
}
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.

Natural-Language Policy Violations

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

Static analysis

Detected: suspicious.env_credential_access

Environment variable access combined with network send.

Critical
Code
suspicious.env_credential_access
Location
scripts/run.js:18