T09 · Insecure Skill Coding Practices
- Location
create_videos.py:23- Finding
Hard-Coded ElevenLabs API Credential
- Content
View full analysis
- Remediation
View remediation
Security audit
Security checks for vulnerabilities and agentic risk
The skill mostly matches its video-generation purpose, but it ships a hard-coded ElevenLabs credential and has under-scoped third-party/API behavior users should review before installing.
Review this skill before installing. Use isolated credentials with spending limits, rotate the exposed ElevenLabs key if you own it, do not rely on --test to prevent live API calls, and only enable any TikTok/Postiz posting after confirming there is an explicit approval step. Run it in a virtual environment and expect content to be sent to external media-generation services.
create_videos.py:23Hard-Coded ElevenLabs API Credential
SKILL.md:32Unpinned Third-Party Dependency Installation
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.
print(f"🎤 Generating voiceover ({len(text)} chars)...")
try:
response = requests.post(
f"{ELEVENLABS_URL}/{voice_id}",
json=payload,
headers=headers
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.
try:
# Submit render job
response = requests.post(
f"{SHOTSTACK_URL}/render",
json=timeline,
headers=headers
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.
# Poll for completion
while True:
status_response = requests.get(
f"{SHOTSTACK_URL}/render/{render_id}",
headers=headers
)
A live-looking ElevenLabs API key is hardcoded directly in source code, exposing a reusable credential to anyone with file access or repository visibility. This can lead to unauthorized API usage, billing abuse, account compromise, and downstream exposure if the same secret handling practice is used elsewhere.
The listing advertises automatic posting to TikTok but does not disclose that the skill can perform actions on a user's social media account or describe the privacy and account-security implications. This can mislead users about the level of access required, increase the chance of overbroad authorization, and cause unexpected publication of content from a connected account.
The skill description states that it generates content with fal.ai, ElevenLabs, and Shotstack and can post to TikTok via Postiz, but it does not prominently warn users that recipe data, prompts, media, and possibly account-linked posting actions will be sent to third-party services. This can lead to users invoking the skill without understanding external data disclosure or automated social-media actions, increasing privacy, compliance, and reputational risk.
The ffmpeg subprocess is invoked with unescaped text derived from food_name inside a filter expression, which can break ffmpeg parsing or enable unexpected behavior if that value later becomes user-controlled. The broad bare except also suppresses all errors, making misuse or failures invisible and reducing detectability of abuse.
]
try:
subprocess.run(cmd, capture_output=True, timeout=10)
except:
pass
This code performs external transmission to ElevenLabs, sending script text and authentication data over the network. External transmission is expected for TTS functionality, but in a skill context it is still security-relevant because content leaves the local environment and may expose sensitive information.
"xi-api-key": ELEVENLABS_API_KEY
}
response = requests.post(
f"https://api.elevenlabs.io/v1/text-to-speech/{voice_id}",
json={
"text": script[:2000],
The script sends generated text content to a third-party TTS provider without any user notice, consent flow, or data classification controls. If the script content later contains sensitive, proprietary, or personal data, this causes unintended external disclosure.
subprocess module calls execute external commands. Without careful input validation, this enables command injection.
str(video_path)
]
result = subprocess.run(cmd, capture_output=True, timeout=120)
if result.returncode != 0:
log(f" ⚠️ Video failed: {result.stderr[:100]}")
Dynamic import() can load arbitrary modules at runtime, bypassing static analysis and potentially importing malicious code.
print("="*60)
print("🏋️ DAILY FITNESS RECIPE GENERATION")
print("="*60)
print(f"📅 Date: {__import__('datetime').datetime.now().strftime('%Y-%m-%d')}")
print(f"🎬 Videos: {VIDEOS_PER_DAY}")
print("="*60)
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
from pathlib import Path
ELEVENLABS_API_KEY = os.getenv("ELEVENLABS_API_KEY", "")
ELEVENLABS_URL = "https://api.elevenlabs.io/v1/text-to-speech"
# Voice IDs for fitness content (energetic male voice)
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
from pathlib import Path
ELEVENLABS_API_KEY = os.getenv("ELEVENLABS_API_KEY", "")
ELEVENLABS_URL = "https://api.elevenlabs.io/v1/text-to-speech"
# Voice IDs for fitness content (energetic male voice)
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
print(f"🎤 Generating voiceover ({len(text)} chars)...")
try:
response = requests.post(
f"{ELEVENLABS_URL}/{voice_id}",
json=payload,
headers=headers
No manifest is available, so there is no declared purpose that would justify accessing environment-based credentials. The code directly reads FAL_API_KEY from the process environment, which is a privileged capability beyond simple local image formatting or placeholder generation.
The function transmits user-provided prompt data to an external third-party service, which is a real data-exposure risk if prompts contain sensitive or proprietary information. In this skill context, external transmission is expected for image generation, but the code does not document the network transfer, set timeouts, or constrain follow-on downloads from returned URLs, making the behavior less transparent and somewhat riskier.
print(f"🎨 Generating: {prompt}")
try:
response = requests.post(FAL_API_URL, json=payload, headers=headers)
response.raise_for_status()
data = response.json()
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
"""Generate food image using fal.ai"""
print(f"🎨 Generating image: {prompt}")
# TODO: Implement fal.ai API call
# response = requests.post(
# "https://fal.run/fal-ai/flux",
# headers={"Authorization": f"Key {CONFIG['api_keys']['fal_ai']}"},
# json={"prompt": prompt}
The CLI exposes a --test flag that implies safe placeholder behavior, but the code never uses args.test and always executes the live generation pipeline. This can mislead operators into triggering real API calls, costs, external data transfers, or side effects when they expected a non-destructive test run.
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
from pathlib import Path
SHOTSTACK_API_KEY = os.getenv("SHOTSTACK_API_KEY", "")
SHOTSTACK_URL = "https://api.shotstack.io/v1"
def create_video_timeline(
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
try:
# Submit render job
response = requests.post(
f"{SHOTSTACK_URL}/render",
json=timeline,
headers=headers
Data from a source is assigned to a variable that is later passed to a sink, creating a variable-mediated taint flow.
# Poll for completion
while True:
status_response = requests.get(
f"{SHOTSTACK_URL}/render/{render_id}",
headers=headers
)
The code downloads a URL taken directly from a prior API response without validating the hostname, scheme, or content type. If the upstream service, API response, or network path were compromised, this could enable server-side request forgery or retrieval of unexpected internal or malicious content, which is then written to disk.
output_dir.mkdir(parents=True, exist_ok=True)
output_path = str(output_dir / f"video_{render_id}.mp4")
video_response = requests.get(video_url)
with open(output_path, "wb") as f:
f.write(video_response.content)
Data from a source is assigned to a variable that is later passed to a sink, creating a variable-mediated taint flow.
output_path = str(output_dir / f"video_{render_id}.mp4")
video_response = requests.get(video_url)
with open(output_path, "wb") as f:
f.write(video_response.content)
print(f"💾 Saved: {output_path}")
This manifest declares sensitive environment variables required by the skill, including multiple API keys, but the visible description does not warn users that they must provide credentials or that external services will be used. For manifest/markdown-facing skill metadata, the lack of disclosure can leave users unaware of privacy and account-impact implications.
The module docstring states the tool uses 'Free food images from online sources'. In practice, get_food_image does not fetch online images; it creates a local placeholder image with ffmpeg and even comments 'For now, use a colored placeholder', which contradicts the documented behavior.
No suspicious patterns detected.