T09 · Insecure Skill Coding Practices
- Location
rules/queue.md:25- Finding
API Credential Disclosure Through Unvalidated Queue URLs
- Content
View full analysis
- Remediation
View remediation
Security audit
Security checks for vulnerabilities and agentic risk
The skill matches its stated video-generation purpose, but it can publish user media to a third-party CDN and uses queue-provided URLs with an API key without enough safeguards.
Install only if you are comfortable sending the selected photo, audio, script, and generated video to fal.ai/VEED infrastructure, with local media becoming reachable through CDN URLs. Use a limited-scope fal.ai key if possible, avoid sensitive or non-consented face and voice data, and prefer a pinned install source or reviewed release.
rules/queue.md:25API Credential Disclosure Through Unvalidated Queue URLs
README.md:7Unpinned npx Installer and Mutable Skill Source
The skill instructs uploading local files to a publicly accessible CDN but does not clearly warn the user that their local image or audio will become publicly reachable via a URL. This creates a real privacy and data exposure risk, especially because users may provide sensitive headshots, voice recordings, or arbitrary local files without understanding they are being published externally.
npx commands without a version suffix (e.g. @1.0.0) create a rug-pull risk if the upstream server is compromised and publishes a malicious update.
npx commands without a version suffix (e.g. @1.0.0) create a rug-pull risk if the upstream server is compromised and publishes a malicious update.
The README explicitly states that local files or URLs are uploaded to the fal.ai CDN, but it does not warn users that photos, audio, and potentially sensitive content will be transmitted to a third party. In this skill's context, the inputs are likely personal headshots and voice data, so omission of a clear privacy/data-handling warning creates a meaningful risk of unintentional disclosure.
This step sends file metadata and authentication material to an external service to obtain a presigned upload URL. In context, the transmission is expected for functionality, but it still represents a genuine data-flow boundary to a third party and becomes dangerous because the skill normalizes external publication of user-supplied local content without explicit consent messaging.
CONTENT_TYPE=$(file --mime-type -b "/path/to/local/file.png")
FILE_NAME=$(basename "/path/to/local/file.png")
UPLOAD_RESPONSE=$(curl -s -X POST "https://rest.fal.ai/storage/upload/initiate?storage_type=fal-cdn-v3" \
-H "Authorization: Key $FAL_KEY" \
-H "Content-Type: application/json" \
-d "{\"content_type\": \"$CONTENT_TYPE\", \"file_name\": \"$FILE_NAME\"}")
This command uploads the full contents of a local file to an externally hosted presigned URL, making it available through a public CDN URL. That is a direct exfiltration path for local data, and in this skill's context it is more dangerous because the instructions broadly define any non-URL path as uploadable without requiring strong user awareness or narrowing what files may be sent.
FILE_URL=$(echo "$UPLOAD_RESPONSE" | jq -r '.file_url') UPLOAD_URL=$(echo "$UPLOAD_RESPONSE" | jq -r '.upload_url')
curl -s -X PUT "$UPLOAD_URL"
-H "Content-Type: $CONTENT_TYPE"
--data-binary @"/path/to/local/file.png"
The example demonstrates uploading a local headshot to a remote presigned URL and then using the resulting public URL downstream. Even as sample code, it operationalizes external transmission of local user media and may encourage silent publication of personally identifiable content such as facial images.
IMAGE_URL=$(echo "$UPLOAD_RESPONSE" | jq -r '.file_url')
UPLOAD_URL=$(echo "$UPLOAD_RESPONSE" | jq -r '.upload_url')
curl -s -X PUT "$UPLOAD_URL" \
-H "Content-Type: $CONTENT_TYPE" \
--data-binary @"./headshot.png"
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
Both image_url and audio_url MUST be publicly accessible URLs. If the user provides local file paths, upload them first — see ./file-upload.md.
RESPONSE=$(curl -s -X POST "https://queue.fal.run/veed/fabric-1.0" \
-H "Authorization: Key $FAL_KEY" \
-H "Content-Type: application/json" \
-d '{
The skill explicitly instructs sending user-provided image and script data to a third-party service and may require making a local image publicly accessible, but it does not warn the user about that disclosure or obtain informed consent. This creates a privacy and data-handling risk, especially because face images and spoken scripts may contain sensitive personal or business information.
This workflow transmits user-supplied content to an external endpoint, including a face image and script text, and the surrounding instructions require the image URL to be publicly accessible. In context this is part of the feature's intended operation, but it is still security-relevant because it can expose sensitive media and text to third-party processing and broader access if the upload mechanism is public.
The image_url MUST be a publicly accessible URL. If the user provides a local file path, upload it first — see ./file-upload.md.
RESPONSE=$(curl -s -X POST "https://queue.fal.run/veed/fabric-1.0/text" \
-H "Authorization: Key $FAL_KEY" \
-H "Content-Type: application/json" \
-d '{
The spec states that local files will be uploaded to fal.ai storage to obtain public URLs, but it does not prominently warn users that private headshots and audio may become externally accessible. In this skill's context, the inputs are likely sensitive personal media, so lack of explicit disclosure creates a meaningful privacy risk and could cause unintentional data exposure.
Output size or generation rate is not bounded. Unbounded output enables denial-of-service through resource exhaustion, log flooding, or context-window stuffing.
## Out of Scope
- **Script writing/editing**: The skill does not help draft or refine scripts. User provides exact text.
- **Video editing/post-processing**: No trimming, adding music, subtitles, or effects after generation.
- **Batch processing**: One video at a time. No bulk generation from multiple scripts.
- **Image generation**: User must provide their own photo. No AI headshot generation.
- **Audio generation outside Fabric**: No integration with external TTS services. Only Fabric's built-in `/text` endpoint.
The skill documentation says it downloads the finished video to ./output/, which is a local file write affecting the user's filesystem. The README mentions the behavior, but does not frame it as a warning or disclosure about where files will be created and overwritten/accumulated.
The file presents a complete while true bash polling loop as an example, then immediately states that polling "MUST NOT" be run as a single bash command and should instead be done manually with user updates between polls. This is an active contradiction between the documentation example and the stated intended usage.
This markdown file includes a cancellation endpoint call that can terminate a running generation, which is an irreversible action for the current request. While the section explains how to cancel, it does not explicitly warn the user that cancellation stops the job and may discard in-progress work.
The spec says the generated MP4 will be written to ./output/ in the current working directory, but it does not clearly warn users about local artifact creation. This is a lower-severity issue than public upload exposure, but it can still leak sensitive generated media into shared repositories, synced folders, or multi-user workspaces unexpectedly.