Back to skill

Security audit

Filtrix Image Generation

Security checks for vulnerabilities and agentic risk

Overview

This image generation and editing skill mostly matches its stated purpose, but it needs review because it disables a fal.ai safety check and downloads provider-returned URLs without validation.

Review before installing. Use this only with API keys you intend to spend from, avoid sending private images or sensitive prompts unless you trust the selected provider, and be aware that fal.ai editing disables a provider safety filter by default. The URL download handling should be hardened before use in sensitive or network-restricted environments.

Vulnerability Patterns
  • 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
  • Embedded Malicious CodeShips malicious scripts inside the skill and executes them locally
Findings (2)

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/edit.py:124
Finding

Unvalidated Provider-Controlled URL Retrieval

Content
View full analysis
Remediation
View remediation

other

Note
Location
scripts/edit.py:221
Finding

fal.ai Image Editing Explicitly Disables the Provider Safety Checker

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
  • Taint TrackingDirect Taint Flow, Variable-Mediated Taint Flow, Credential Exfiltration Chain
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (21)

Tainted flow: 'req' from os.environ.get (line 237, 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/edit.py (reported line 118)May include surrounding context.

python
)

    try:
        with urllib.request.urlopen(req) as resp:
            result = json.loads(resp.read())
    except urllib.error.HTTPError as e:
        err_body = e.read().decode()

Tainted flow: 'req' from os.environ.get (line 237, 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/edit.py (reported line 179)May include surrounding context.

python
)

    try:
        with urllib.request.urlopen(req) as resp:
            result = json.loads(resp.read())
    except urllib.error.HTTPError as e:
        err_body = e.read().decode()

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

Critical
Category
Data Flow
Confidence
90% confidence
Finding

The code follows a URL returned by the OpenAI API and fetches it without validating the scheme, host, or IP range. If the upstream response is compromised or altered, this creates an SSRF-style outbound fetch primitive that could access internal services or unexpected hosts from the runtime environment.

Content

Scanner excerpt · scripts/edit.py (reported line 128)May include surrounding context.

python
if not b64:
        url = result.get("data", [{}])[0].get("url")
        if url:
            with urllib.request.urlopen(url) as img_resp:
                return img_resp.read()
        raise RuntimeError("No image data in OpenAI edit response")

Tainted flow: 'req' from os.environ.get (line 237, 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/edit.py (reported line 246)May include surrounding context.

python
)

    try:
        with urllib.request.urlopen(req, timeout=180) as resp:
            body = json.loads(resp.read())
    except urllib.error.HTTPError as e:
        err_body = e.read().decode()

Tainted flow: 'req' from os.environ.get (line 229, 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/generate.py (reported line 81)May include surrounding context.

python
)

    try:
        with urllib.request.urlopen(req) as resp:
            body = json.loads(resp.read())
    except urllib.error.HTTPError as e:
        err_body = e.read().decode()

Tainted flow: 'req' from os.environ.get (line 229, 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/generate.py (reported line 129)May include surrounding context.

python
)

    try:
        with urllib.request.urlopen(req) as resp:
            body = json.loads(resp.read())
    except urllib.error.HTTPError as e:
        err_body = e.read().decode()

Tainted flow: 'req' from os.environ.get (line 229, 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/generate.py (reported line 225)May include surrounding context.

python
)

    try:
        with urllib.request.urlopen(req) as resp:
            body = json.loads(resp.read())
    except urllib.error.HTTPError as e:
        err_body = e.read().decode()

Tainted flow: 'req' from os.environ.get (line 229, 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/generate.py (reported line 230)May include surrounding context.

python
)

    try:
        with urllib.request.urlopen(req) as resp:
            body = json.loads(resp.read())
    except urllib.error.HTTPError as e:
        err_body = e.read().decode()

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

Critical
Category
Data Flow
Confidence
90% confidence
Finding

The code blindly fetches an image from a URL returned by the OpenAI API without validating the scheme, host, or content type. If that upstream response were compromised or unexpected, the skill could be induced to make arbitrary outbound requests, creating an SSRF-style primitive and downloading untrusted content to disk.

Content

Scanner excerpt · scripts/generate.py (reported line 92)May include surrounding context.

python
# Try URL fallback
        url = body.get("data", [{}])[0].get("url")
        if url:
            with urllib.request.urlopen(url) as img_resp:
                return img_resp.read()
        raise RuntimeError("No image data in OpenAI response")

Tainted flow: 'req' from os.environ.get (line 229, 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/generate.py (reported line 198)May include surrounding context.

python
)

    try:
        with urllib.request.urlopen(req, timeout=180) as resp:
            body = json.loads(resp.read())
    except urllib.error.HTTPError as e:
        err_body = e.read().decode()

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding

The declared purpose emphasizes image generation, but the skill also supports image editing, masking, and processing of local input image files with output written to disk. That mismatch matters because users and policy systems may authorize a generation-only skill while overlooking that it can ingest local files and transform existing content, which broadens the data-handling and privacy exposure.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding

The declared purpose emphasizes image generation, but the skill also supports image editing, masking, and processing of local input image files with output written to disk. That mismatch matters because users and policy systems may authorize a generation-only skill while overlooking that it can ingest local files and transform existing content, which broadens the data-handling and privacy exposure.

Content

No source excerpt is available for this finding.

External Model or Provider Selection

High
Category
Excessive Agency
Confidence
90% confidence
Finding

Skill selects an external model or provider that may use a different account or billing plan than the operator expects. Undisclosed model switches can cause unexpected cost or quota consumption.

Content

Scanner excerpt · scripts/generate.py (reported line 108)May include surrounding context.

python
if not api_key:
        raise RuntimeError("GOOGLE_API_KEY not set")

    # Default to Flash (cheaper). Use --model gemini-3-pro-image-preview for higher quality.
    model = model or "gemini-2.5-flash-image"
    aspect = GEMINI_ASPECT_MAP.get(size, "1:1")

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
91% confidence
Finding

The skill documentation advertises behavior that uses environment variables, local file reads/writes, and network access, but it does not declare any explicit tool scope or permissions. This creates a least-privilege and transparency problem: an agent or reviewer cannot easily constrain the skill to only the capabilities it genuinely needs, increasing the chance of unintended credential access, filesystem interaction, or outbound requests.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The trigger description is very broad and can capture many ordinary image-related requests, causing the skill to activate in situations where users may not expect external API calls, file creation, or provider selection based on available keys. Over-broad invocation increases the chance of unnecessary data disclosure to third-party providers and accidental use of local or environmental resources.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The manifest says the skill is for generating images when a user asks to create or make an image, but this file is explicitly an image editor and requires an existing input image to transform. The docstring, provider functions, and CLI all implement image-to-image editing, which is a materially different user-facing behavior from pure image generation.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

If an arbitrary model string is supplied, the code uses it directly to construct the fal.run path instead of restricting requests to the declared supported models. This expands the reachable external capability surface and can bypass intended policy controls, cost controls, or safety expectations tied to the documented provider/model scope.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The fal provider explicitly disables the safety checker, which increases the chance of generating disallowed, harmful, or policy-violating content without any user warning. In a skill exposed to end users, disabling provider safeguards materially raises misuse risk and can undermine platform safety guarantees.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
60% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · references/openai.md (reported line 5)May include surrounding context.

md
}).encode()

    req = urllib.request.Request(
        "https://api.openai.com/v1/images/generations",
        data=payload,
        headers={
            "Authorization": f"Bearer {api_key}",

External Transmission

Medium
Category
Data Exfiltration
Confidence
60% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · scripts/edit.py (reported line 109)May include surrounding context.

python
}).encode()

    req = urllib.request.Request(
        "https://api.openai.com/v1/images/generations",
        data=payload,
        headers={
            "Authorization": f"Bearer {api_key}",

External Transmission

Medium
Category
Data Exfiltration
Confidence
60% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · scripts/generate.py (reported line 72)May include surrounding context.

python
}).encode()

    req = urllib.request.Request(
        "https://api.openai.com/v1/images/generations",
        data=payload,
        headers={
            "Authorization": f"Bearer {api_key}",

Static analysis

No suspicious patterns detected.