T09 · Insecure Skill Coding Practices
- Location
scripts/flowcutpro.py:18- Finding
Hardcoded Google Veo API Key in Main Script
- Content
View full analysis
- Remediation
View remediation
Security audit
Security checks for vulnerabilities and agentic risk
This video-generation skill is mostly coherent, but it embeds a real-looking Google API key and handles user/API keys in ways that could leak them.
Review this carefully before installing. Do not run it with a personal or production Google API key unless the hardcoded fallback key is removed, the exposed key is rotated, download URLs are validated against trusted Google hosts, and the direct Anthropic prompt flow is clearly disclosed or replaced with the platform-managed LLM path.
scripts/flowcutpro.py:18Hardcoded Google Veo API Key in Main Script
examples/hotel-commercial.py:20Hardcoded Google Veo API Key Duplicated in Example Script
scripts/flowcutpro.py:170API Key Forwarded to an Unvalidated Download URI in Main Script
examples/hotel-commercial.py:165API Key Forwarded to an Unvalidated Download URI in Example Script
SKILL.md:50Unpinned and Unused Python Dependencies in Installation Instructions
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.
req = urllib.request.Request(url, data=data, method="POST",
headers={"Content-Type": "application/json"})
try:
with urllib.request.urlopen(req, timeout=30) as r:
resp = json.loads(r.read())
op = resp.get("name", "")
if op:
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.
start = time.time()
while time.time() - start < timeout:
try:
with urllib.request.urlopen(urllib.request.Request(url), timeout=15) as r:
resp = json.loads(r.read())
if resp.get("done"):
return resp
The script appends the API key as a query parameter to a download URI returned by the remote service, without verifying the URI's host or origin. If that URI is malicious or unexpectedly points to a third-party domain, the key will be disclosed to that external endpoint and the script may download untrusted content from an arbitrary server.
sep = "&" if "?" in uri else "?"
url = f"{uri}{sep}key={API_KEY}"
try:
with urllib.request.urlopen(urllib.request.Request(url), timeout=120) as r:
out.write_bytes(r.read())
mb = out.stat().st_size / 1_048_576
print(f" ✅ {out.name} ({mb:.1f}MB)")
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.
headers={"Content-Type": "application/json"}
)
try:
with urllib.request.urlopen(req, timeout=30) as r:
resp = json.loads(r.read())
op = resp.get("name", "")
if op:
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.
while time.time() - start < timeout:
try:
req = urllib.request.Request(url)
with urllib.request.urlopen(req, timeout=15) as r:
resp = json.loads(r.read())
if resp.get("done"):
return resp
This download uses a URI returned by an external service without validating its host or scheme, then appends the API key to that URI. If the upstream response is compromised or redirected to an attacker-controlled endpoint, the tool could disclose the API key and fetch arbitrary content, creating an SSRF-style credential leakage path.
url = f"{uri}{sep}key={API_KEY}"
try:
req = urllib.request.Request(url)
with urllib.request.urlopen(req, timeout=120) as r:
out.write_bytes(r.read())
mb = out.stat().st_size / 1_048_576
print(f" ✅ {out.name} ({mb:.1f}MB)")
If the skill uses a hardcoded Google API key and an Anthropic model for planning without declaring those resources, that creates both secret-handling risk and undisclosed outbound data transfer. Undeclared credentialed access is especially risky in an agent skill because prompts and user content may be sent to external providers outside the user's expectations or policy controls.
If the skill uses a hardcoded Google API key and an Anthropic model for planning without declaring those resources, that creates both secret-handling risk and undisclosed outbound data transfer. Undeclared credentialed access is especially risky in an agent skill because prompts and user content may be sent to external providers outside the user's expectations or policy controls.
The script silently falls back to a hardcoded API key and automatically sends prompts and requests to an external Google API without explicit user consent or warning. In a skill context that may process user-supplied creative concepts, this creates a meaningful transparency and data-handling risk in addition to the exposed credential problem.
The code silently uses either a hardcoded API key or an environment credential without explicit user warning or consent. In a skill that sends prompts and downloads generated assets from external providers, hidden credential use increases the chance of unintended billing, unauthorized shared-key usage, and accidental exposure of organizational secrets.
The skill advertises and documents capabilities that require environment-variable access, local file writes, network access, and shell execution, but it declares no explicit tool scope or permissions. That omission weakens reviewability and informed consent: a host may invoke the skill without clear disclosure that it will contact external APIs, write media files locally, and run ffmpeg via shell.
The trigger phrases are broad enough to match many ordinary media-related requests, increasing the chance of unexpected activation. In context, unexpected activation matters because this skill can cause external network calls, local file creation, and shell execution, so overly loose routing expands the attack surface and reduces user intent fidelity.
The description does not clearly warn that user prompts and related creative content are transmitted to external services and that output files are written to local storage. This is a transparency and privacy risk: users may unknowingly send sensitive ideas, brand materials, or personal content to third parties and leave artifacts on disk.
Dynamic import() can load arbitrary modules at runtime, bypassing static analysis and potentially importing malicious code.
"""
import sys
sys.path.insert(0, str(__import__("pathlib").Path(__file__).parent.parent / "scripts"))
import json, time, subprocess, argparse
from pathlib import Path
The file contains a hardcoded default Google API key, which is a real secret-management vulnerability. Anyone with access to the code can extract and abuse the key for unauthorized API usage, causing billing exposure, service abuse, or rapid key revocation that breaks legitimate use.
The code creates an output directory automatically and later writes video files to it, including an ffmpeg invocation with '-y' that overwrites existing output without prompting. While these writes are part of the script's function, the file lacks an explicit disclosure in comments, docstring, or user-facing messaging that running in LIVE mode will create and overwrite local media files.
subprocess module calls execute external commands. Without careful input validation, this enables command injection.
def get_duration(path):
r = subprocess.run(
["ffprobe", "-v", "quiet", "-show_entries", "format=duration", "-of", "csv=p=0", str(path)],
capture_output=True, text=True)
return float(r.stdout.strip())
subprocess module calls execute external commands. Without careful input validation, this enables command injection.
cmd = ["ffmpeg", "-y", *inputs, "-filter_complex", ";".join(parts),
"-map", "[vout]", "-c:v", "libx264", "-crf", "18", "-preset", "slow",
"-pix_fmt", "yuv420p", "-movflags", "+faststart", str(final)]
r = subprocess.run(cmd, capture_output=True, text=True)
if r.returncode != 0:
print(" ❌ ffmpeg:", r.stderr[-300:])
return None
The script embeds a hardcoded Google API key directly in source as a fallback credential. Hardcoded secrets are easily leaked through source sharing, logs, backups, or repository history, and any user of the skill may unknowingly operate with a shared credential they do not control.
The function documentation says it uses OpenClaw's configured LLM, implying the platform-managed model selection and integration. In reality, the implementation explicitly imports the Anthropics SDK and calls a fixed Claude model, which materially contradicts the stated intent of using the configured LLM.
The skill sends the user's concept and generated shot prompts to external LLM and video-generation services without explicit disclosure at the point of use. For a creative-production tool, such prompts may contain confidential campaign plans, unreleased product concepts, or personal data, so undisclosed transmission creates privacy and data-governance risk.
subprocess module calls execute external commands. Without careful input validation, this enables command injection.
# ─── FFMPEG STITCH ────────────────────────────────────────────────────────────
def get_duration(path: Path) -> float:
r = subprocess.run(
["ffprobe", "-v", "quiet", "-show_entries", "format=duration",
"-of", "csv=p=0", str(path)],
capture_output=True, text=True
subprocess module calls execute external commands. Without careful input validation, this enables command injection.
aspect = clips[sorted(clips.keys())[0]].stem
# Detect aspect from first clip
try:
r = subprocess.run(
["ffprobe", "-v", "quiet", "-select_streams", "v:0",
"-show_entries", "stream=width,height", "-of", "csv=p=0",
str(ordered[0])],
subprocess module calls execute external commands. Without careful input validation, this enables command injection.
"-pix_fmt", "yuv420p", "-movflags", "+faststart",
str(final)
]
result = subprocess.run(cmd, capture_output=True, text=True)
if result.returncode != 0:
print(" ❌ ffmpeg error:", result.stderr[-400:])
return None
No suspicious patterns detected.