T09 · Insecure Skill Coding Practices
- Location
examples/auth.ts:158- Finding
OAuth State Is Generated but Not Validated in the Documented Callback Flow
- Content
View full analysis
- Remediation
View remediation
Security audit
Security checks for vulnerabilities and agentic risk
This is a coherent Fanvue API skill, but it gives an agent broad access to post content, message subscribers, fetch private media links, and read earnings/spender data without clear approval or handling safeguards.
Review before installing. Use it only for Fanvue accounts where the agent is allowed to handle OAuth tokens, private media links, subscriber identities, spending/earnings data, posts, and messages. Require explicit confirmation before posting, deleting, sending any message, or mass messaging, and avoid logging or retaining signed media URLs, tokens, subscriber lists, or financial data.
examples/auth.ts:158OAuth State Is Generated but Not Validated in the Documented Callback Flow
examples/chat-operations.ts:159Unvalidated Identifiers Are Interpolated Directly into Authenticated API Paths
The supplied code is narrowly focused on authentication infrastructure for Fanvue's OAuth 2.0 flow. It generates PKCE parameters and CSRF state, constructs the authorization URL, exchanges an authorization code for tokens, and refreshes tokens. While this supports later API access, it does not itself implement the declared skill behavior of managing content, chats, subscribers, or earnings. This is a material description-to-behavior mismatch because the declared primary purpose is operational management on the platform, whereas the actual code only handles auth setup.
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
Access tokens expire. Use the refresh token to get new ones:
const response = await fetch('https://auth.fanvue.com/oauth2/token', {
Tool parameters are crafted to achieve unintended or unsafe behavior. Parameter abuse can bypass intended safety checks (e.g. shell=True, --force, dangerous glob patterns).
DELETE /chat-messages/:id
Scope: write:chat
Code or instructions that leak agent conversation context to external services, potentially exposing sensitive user interactions.
}
### Create Upload Session
Start a multipart upload.
Code or instructions that leak agent conversation context to external services, potentially exposing sensitive user interactions.
}
### Create Upload Session
Start a multipart upload.
Tool parameters are crafted to achieve unintended or unsafe behavior. Parameter abuse can bypass intended safety checks (e.g. shell=True, --force, dangerous glob patterns).
DELETE /tracking-links/:uuid
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
}
/**
* Refresh an expired access token
*/
async refreshToken(refreshToken: string): Promise<TokenResponse> {
const response = await fetch(FanvueAuth.TOKEN_URL, {
The skill documents use of environment variables and broad outbound network access, but the manifest declares no explicit tool/permission scope. That mismatch weakens least-privilege controls and can let an agent invoke sensitive capabilities without clear operator review. In a skill that handles OAuth credentials and subscriber data, undeclared env and network access is materially risky.
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
// 3. Exchange authorization code for tokens
const tokenResponse = await fetch('https://auth.fanvue.com/oauth2/token', {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: new URLSearchParams({
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
// 3. Exchange authorization code for tokens
const tokenResponse = await fetch('https://auth.fanvue.com/oauth2/token', {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: new URLSearchParams({
The skill encourages automated collection of signed media URLs, subscriber lists, top spenders, and earnings data without privacy guidance, consent boundaries, retention limits, or sensitivity warnings. Because these are highly sensitive creator and subscriber data flows, an agent could over-collect or misuse personal and monetization data in ways users may not expect.
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
// Step 1: List all media
const list = await fetch('https://api.fanvue.com/media', { headers });
const { data } = await list.json();
// Step 2: Get signed URLs for a specific media item
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
// Step 1: List all media
const list = await fetch('https://api.fanvue.com/media', { headers });
const { data } = await list.json();
// Step 2: Get signed URLs for a specific media item
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
// Step 1: List all media
const list = await fetch('https://api.fanvue.com/media', { headers });
const { data } = await list.json();
// Step 2: Get signed URLs for a specific media item
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
// Step 1: List all media
const list = await fetch('https://api.fanvue.com/media', { headers });
const { data } = await list.json();
// Step 2: Get signed URLs for a specific media item
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
// Step 1: List all media
const list = await fetch('https://api.fanvue.com/media', { headers });
const { data } = await list.json();
// Step 2: Get signed URLs for a specific media item
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
// Step 1: List all media
const list = await fetch('https://api.fanvue.com/media', { headers });
const { data } = await list.json();
// Step 2: Get signed URLs for a specific media item
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
// Step 1: List all media
const list = await fetch('https://api.fanvue.com/media', { headers });
const { data } = await list.json();
// Step 2: Get signed URLs for a specific media item
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
```typescript
// Step 1: List all media
const list = await fetch('https://api.fanvue.com/media', { headers });
const { data } = await list.json();
// Step 2: Get signed URLs for a specific media item
This call retrieves signed URLs for private media variants, which are sensitive access artifacts that can expose otherwise protected content if logged, cached, or forwarded. In an agent workflow, signed URLs increase the blast radius because anyone with the URL may access the content until expiry.
// Step 2: Get signed URLs for a specific media item
const media = await fetch(
`https://api.fanvue.com/media/${uuid}?variants=main,thumbnail,blurred`,
{ headers }
);
const { variants } = await media.json();
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
const mediaIds = ['media-uuid-1', 'media-uuid-2'];
// Step 2: Create post
const response = await fetch('https://api.fanvue.com/posts', {
method: 'POST',
headers,
body: JSON.stringify({
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
const mediaIds = ['media-uuid-1', 'media-uuid-2'];
// Step 2: Create post
const response = await fetch('https://api.fanvue.com/posts', {
method: 'POST',
headers,
body: JSON.stringify({
The skill includes direct-send and mass-messaging workflows that can affect real subscribers, but it provides no safeguard such as confirmation prompts, dry-run mode, audience review, rate limiting, or anti-abuse warning. In an agent context, this can easily lead to spam, reputational harm, accidental outreach, or abusive targeting of subscribers at scale.
Fetching subscriber lists exposes personal relationship/account data to the agent, and the skill offers no privacy notice or minimization guidance. In context, this can enable unnecessary collection, profiling, or downstream misuse of subscriber identifiers.
// Get subscriber list for decision making
const subs = await fetch('https://api.fanvue.com/creators/list-subscribers', { headers });
const { data: subscribers } = await subs.json();
// Get top spenders for VIP targeting
Retrieving top spenders creates a sensitive financial-profile dataset that can be used for targeting or discriminatory treatment. Without privacy warnings and purpose limitation, the skill normalizes high-risk profiling of subscribers based on spending behavior.
const { data: subscribers } = await subs.json();
// Get top spenders for VIP targeting
const vips = await fetch('https://api.fanvue.com/insights/get-top-spenders', { headers });
const { data: topSpenders } = await vips.json();
// Send personalized message with media
Detected: suspicious.env_credential_access, suspicious.exposed_secret_literal