Back to skill

Security audit

FlowCutPro

Security checks for vulnerabilities and agentic risk

Overview

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.

Vulnerability Patterns
  • Insecure DependenciesIntroduces malicious components through unsafe dependency sources
  • Insecure Skill Coding PracticesFinds exploitable flaws such as hardcoded secrets or command injection
  • Skill Instruction HijackingAlters the agent's session goals or safety constraints when the skill loads
  • Agent Memory PoisoningWrites attacker-controlled rules into memory that affect later sessions
  • Remote Payload Retrieval and ExecutionFetches external code whose behavior can change after review
Findings (5)

T09 · Insecure Skill Coding Practices

Error
Location
scripts/flowcutpro.py:18
Finding

Hardcoded Google Veo API Key in Main Script

Content
View full analysis
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Error
Location
examples/hotel-commercial.py:20
Finding

Hardcoded Google Veo API Key Duplicated in Example Script

Content
View full analysis
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/flowcutpro.py:170
Finding

API Key Forwarded to an Unvalidated Download URI in Main Script

Content
View full analysis
bool: """Download a Veo output video to disk.""" # Handle URL parameter correctly sep = "&" if "?" in uri else "?" url = f"{uri}{sep}key={API_KEY}" try: req = urllib.request.Request(url) with urllib.request.urlopen(req, timeout=120) as r: ``` The URI originates from the remote operation response: ```python uri = result["response"]["generateVideoResponse"]["generatedSamples"][0]["video"]["uri"] if download_clip(uri, out): ``` ### Technical Analysis The script takes a download URI from a remote API response and appends the Google API key as a query parameter without validating the URI's scheme or hostname. It then sends the request using `urllib.request.urlopen`. If the response contains an attacker-controlled URI, the key will be transmitted to that host. Redirect behavior can create an additional exposure path if an initially accepted host redirects to an unrelated destination. Appending credentials to URLs also increases the chance of disclosure through proxy, server, or diagnostic logs. Downloading generated content is necessary for the declared functionality. Forwarding the credential to any hostname supplied by the response exceeds the minimum privileges required. ### Attack Path 1. An attacker compromises or successfully manipulates the API response path, operation response, or upstream provider. 2. The returned `video.uri` points to an attacker-controlled HTTPS endpoint. 3. `download_clip` appends `?key=` or `&key=` to that URI. 4. `urlopen` sends the request to the attacker-controlled endpoint. 5. The attacker extracts the key from the request URL and reuses it against permitted Google APIs. ### Impact Assessment Successful exploitation disc ...[truncated 427 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
examples/hotel-commercial.py:165
Finding

API Key Forwarded to an Unvalidated Download URI in Example Script

Content
View full analysis
Remediation
View remediation

T08 · Insecure Dependencies

Note
Location
SKILL.md:50
Finding

Unpinned and Unused Python Dependencies in Installation Instructions

Content
View full analysis
Remediation
View remediation
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
  • Taint TrackingDirect Taint Flow, Variable-Mediated Taint Flow, Credential Exfiltration Chain
Findings (24)

Tainted flow: 'req' from os.environ.get (line 158, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Confidence
90% confidence
Finding

Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Content

Scanner excerpt · examples/hotel-commercial.py (reported line 161)May include surrounding context.

python
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:

Tainted flow: 'url' from os.environ.get (line 193, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Confidence
90% confidence
Finding

Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Content

Scanner excerpt · examples/hotel-commercial.py (reported line 180)May include surrounding context.

python
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

Tainted flow: 'url' from os.environ.get (line 193, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Confidence
95% confidence
Finding

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.

Content

Scanner excerpt · examples/hotel-commercial.py (reported line 195)May include surrounding context.

python
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)")

Tainted flow: 'req' from os.environ.get (line 156, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Confidence
90% confidence
Finding

Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Content

Scanner excerpt · scripts/flowcutpro.py (reported line 135)May include surrounding context.

python
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:

Tainted flow: 'req' from os.environ.get (line 156, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Confidence
90% confidence
Finding

Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Content

Scanner excerpt · scripts/flowcutpro.py (reported line 157)May include surrounding context.

python
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

Tainted flow: 'req' from os.environ.get (line 156, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Confidence
90% confidence
Finding

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.

Content

Scanner excerpt · scripts/flowcutpro.py (reported line 177)May include surrounding context.

python
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)")

Tp4

High
Category
MCP Tool Poisoning
Confidence
93% confidence
Finding

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.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding

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.

Content

No source excerpt is available for this finding.

Missing User Warnings

High
Category
Not specified by scanner
Confidence
98% confidence
Finding

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.

Content

No source excerpt is available for this finding.

Missing User Warnings

High
Category
Not specified by scanner
Confidence
98% confidence
Finding

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.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
91% confidence
Finding

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.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

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.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

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.

Content

No source excerpt is available for this finding.

Dynamic import via __import__()

Medium
Category
Dangerous Code Execution
Confidence
75% confidence
Finding

Dynamic import() can load arbitrary modules at runtime, bypassing static analysis and potentially importing malicious code.

Content

Scanner excerpt · examples/hotel-commercial.py (reported line 13)May include surrounding context.

python
"""

import sys
sys.path.insert(0, str(__import__("pathlib").Path(__file__).parent.parent / "scripts"))

import json, time, subprocess, argparse
from pathlib import Path

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
99% confidence
Finding

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.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
88% confidence
Finding

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.

Content

No source excerpt is available for this finding.

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
70% confidence
Finding

subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Content

Scanner excerpt · examples/hotel-commercial.py (reported line 206)May include surrounding context.

python
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 call

Medium
Category
Dangerous Code Execution
Confidence
70% confidence
Finding

subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Content

Scanner excerpt · examples/hotel-commercial.py (reported line 229)May include surrounding context.

python
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

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
99% confidence
Finding

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.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Medium
Category
Not specified by scanner
Confidence
97% confidence
Finding

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.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

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.

Content

No source excerpt is available for this finding.

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
70% confidence
Finding

subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Content

Scanner excerpt · scripts/flowcutpro.py (reported line 219)May include surrounding context.

python
# ─── 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 call

Medium
Category
Dangerous Code Execution
Confidence
70% confidence
Finding

subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Content

Scanner excerpt · scripts/flowcutpro.py (reported line 259)May include surrounding context.

python
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 call

Medium
Category
Dangerous Code Execution
Confidence
70% confidence
Finding

subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Content

Scanner excerpt · scripts/flowcutpro.py (reported line 279)May include surrounding context.

python
"-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

Static analysis

No suspicious patterns detected.