T09 · Insecure Skill Coding Practices
- Location
scripts/run.js:17- Finding
Hard-Coded Billing API Credential Exposed in Source Code
- Content
View full analysis
- Remediation
View remediation
Security audit
Security checks for vulnerabilities and agentic risk
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.
scripts/run.js:17Hard-Coded Billing API Credential Exposed in Source Code
scripts/run.js:48Server-Side Request Forgery Through Unrestricted URL Fetching
scripts/run.js:16Sensitive Billing Data Can Be Redirected to an Attacker-Controlled Endpoint
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.
Referenced artifact was not completely inspected
node scripts/run.js --urls "https://x.com/.../status/1,https://x.com/.../status/2" --user "<user-id>" --lang "auto"
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.
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.
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.
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.
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.
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.
};
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 || '';
}
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.