Back to skill

Security audit

Image Gen Compare

Security checks for vulnerabilities and agentic risk

Overview

This image-comparison skill mostly does what it says, but it can silently save generated images to a Proton Drive-synced folder and uses a 1Password service-token fallback that is broader than the user-facing instructions make clear.

Review before installing. Use it only with prompts and generated images you are comfortable sending to OpenAI and potentially storing in a synced Proton Drive folder. Prefer setting OPENAI_API_KEY directly, avoid running it in an environment full of unrelated secrets, and inspect or change the output directory before using confidential creative work.

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 (3)

other

Warning
Location
scripts/image_gen_compare.py:40
Finding

Generated Images Are Silently Written to Cloud-Synchronized Storage

Content
View full analysis
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/image_gen_compare.py:61
Finding

Security-Sensitive Credential Helper Inherits the Entire Process Environment

Content
View full analysis
str: key = os.environ.get("OPENAI_API_KEY") if key: return key try: token_path = Path.home() / ".config/openclaw/.op-service-token" env = os.environ.copy() env["OP_SERVICE_ACCOUNT_TOKEN"] = token_path.read_text().strip() result = subprocess.run( ["op", "read", "op://OpenClaw/OpenAI API Key/credential"], capture_output=True, text=True, env=env, timeout=10, ) if result.returncode == 0: return result.stdout.strip() except Exception as exc: print(f"Warning: 1Password fetch failed: {exc}", file=sys.stderr) raise RuntimeError("No OPENAI_API_KEY found") ``` ### Technical Analysis When `OPENAI_API_KEY` is absent, the script reads a persistent 1Password service-account token from the user's home directory. It then copies the entire parent environment, adds the service token, and invokes `op` by a bare executable name. Two security weaknesses are combined: - The child receives every environment variable inherited by the script, including unrelated credentials that may be present. - The executable is resolved through `PATH`, so the code does not verify that the invoked program is the intended 1Password CLI binary. A legitimate 1Password CLI needs the service token for this operation, but it does not need unrelated environment secrets. Passing the complete environment violates least-privilege principles for a subprocess handling security-sensitive credentials. The script itself does not transmit the token to OpenAI, print it, or otherwise directly exfiltrate it. Exploitation requires control over executable resolution or compromise of the invoked `op` program. ### Attack Path 1. `OPENAI_API_K ...[truncated 1217 chars]
Remediation
View remediation

T08 · Insecure Dependencies

Warning
Location
scripts/image_gen_compare.py:145
Finding

Unpinned Python Dependencies and Mutable Runtime Model Downloads

Content
View full analysis
dict: """Generate an image with FLUX.1-schnell via mflux (local, free).""" try: # mflux v0.16+ uses explicit module paths from mflux.models.flux.variants.txt2img.flux import Flux1 from mflux.models.common.config.model_config import ModelConfig except ImportError: raise RuntimeError("mflux not installed. Run: pip install mflux") print(f"\n🖥️ FLUX.1-schnell (local, {LINKEDIN_W}×{LINKEDIN_H}, {steps} steps, free)...") print(" (First run: downloads ~9GB model from HuggingFace — grab a coffee)") start = time.monotonic() # Load model (downloads from HuggingFace on first run) flux = Flux1( model_config=ModelConfig.schnell(), quantize=8, ) ``` ```python def generate_sdxl(prompt: str, steps: int = 30, seed: int | None = None) -> dict: """Generate with SDXL base via diffusers on Apple MPS. Free, ungated, ~7GB model.""" try: import torch from diffusers import StableDiffusionXLPipeline except ImportError: raise RuntimeError("diffusers not installed. Run: pip install diffusers accelerate") if not torch.backends.mps.is_available(): raise RuntimeError("MPS (Apple Metal) not available — is this Apple Silicon?") print(f"\n🍎 SDXL base (MPS, {SDXL_W}×{SDXL_H}, {steps} steps, free)...") print(" (First run: downloads ~7GB model from HuggingFace)") start = time.monotonic() pipe = StableDiffusionXLPipeline.from_pretrained( "stabilityai/stable-diffusion-xl-base-1.0", torch_dtype=torch.float32, use_safetensors=True, ) ``` ### Technical Analysis The error ...[truncated 2353 chars]
Remediation
View remediation
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
  • Taint TrackingDirect Taint Flow, Variable-Mediated Taint Flow, Credential Exfiltration Chain
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (15)

Tainted flow: 'key' from os.environ.get (line 61, credential/environment) → requests.post (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/image_gen_compare.py (reported line 91)May include surrounding context.

python
print(f"\n🎨 DALL-E 3 ({quality}, {DALLE_W}×{DALLE_H}, ~${cost:.2f})...")
    start = time.monotonic()

    resp = requests.post(
        "https://api.openai.com/v1/images/generations",
        headers={"Authorization": f"Bearer {key}", "Content-Type": "application/json"},
        json={

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding

The documented behavior does not fully match the skill's effective capabilities: it can access a 1Password service-account token to retrieve secrets and may write outputs into a synced storage location, neither of which are clearly disclosed in the primary description. This creates a trust and review gap where users may authorize a seemingly simple image-comparison skill without realizing it can handle secrets and persist generated content to broader storage destinations.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

High
Category
Not specified by scanner
Confidence
97% confidence
Finding

The code intentionally writes generated images to a Proton Drive-synced directory when present, causing automatic replication of user prompts' outputs outside the local workspace. In the context of a model-comparison utility, this is an unnecessary externalization channel that can leak sensitive or proprietary generated content.

Content

No source excerpt is available for this finding.

Env Variable Harvesting

High
Category
Data Exfiltration
Confidence
60% confidence
Finding

Code enumerates, copies, or searches environment variables for secrets. Bulk environment access can collect credentials unrelated to the skill's stated purpose.

Content

Scanner excerpt · scripts/image_gen_compare.py (reported line 66)May include surrounding context.

python
return key
    try:
        token_path = Path.home() / ".config/openclaw/.op-service-token"
        env = os.environ.copy()
        env["OP_SERVICE_ACCOUNT_TOKEN"] = token_path.read_text().strip()
        result = subprocess.run(
            ["op", "read", "op://OpenClaw/OpenAI API Key/credential"],

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
93% confidence
Finding

The skill declares significant capabilities in metadata and usage patterns—environment variable access, network access, shell execution, and file read/write—but does not define an explicit tool scope such as permissions or allowed-tools. This weakens the security boundary by making it harder for reviewers and enforcement systems to verify that the skill only uses the minimum necessary privileges.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The script documentation says outputs are saved under the workspace, but the code may instead write generated images to a Proton Drive-synced path outside the workspace. This hidden data egress/storage behavior increases the risk of unintentionally exposing prompts and generated artifacts to synced cloud storage.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
84% confidence
Finding

The skill reaches beyond simple image generation by reading a local service-account token file and invoking the 1Password CLI to retrieve a secret. That secret access pattern expands the skill's privilege footprint and is not obvious from the high-level description, increasing the risk of unintended credential use in automated environments.

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/image_gen_compare.py (reported line 68)May include surrounding context.

python
token_path = Path.home() / ".config/openclaw/.op-service-token"
        env = os.environ.copy()
        env["OP_SERVICE_ACCOUNT_TOKEN"] = token_path.read_text().strip()
        result = subprocess.run(
            ["op", "read", "op://OpenClaw/OpenAI API Key/credential"],
            capture_output=True, text=True, env=env, timeout=10,
        )

External Transmission

Medium
Category
Data Exfiltration
Confidence
70% 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/image_gen_compare.py (reported line 91)May include surrounding context.

python
print(f"\n🎨 DALL-E 3 ({quality}, {DALLE_W}×{DALLE_H}, ~${cost:.2f})...")
    start = time.monotonic()

    resp = requests.post(
        "https://api.openai.com/v1/images/generations",
        headers={"Authorization": f"Bearer {key}", "Content-Type": "application/json"},
        json={

External Transmission

Medium
Category
Data Exfiltration
Confidence
80% 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/image_gen_compare.py (reported line 91)May include surrounding context.

python
print(f"\n🎨 DALL-E 3 ({quality}, {DALLE_W}×{DALLE_H}, ~${cost:.2f})...")
    start = time.monotonic()

    resp = requests.post(
        "https://api.openai.com/v1/images/generations",
        headers={"Authorization": f"Bearer {key}", "Content-Type": "application/json"},
        json={

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

User-supplied prompt text is sent to OpenAI, but the script does not provide an explicit runtime warning or consent mechanism about external data sharing. In a comparison tool that may be used with proprietary creative briefs or internal prompts, this omission increases the risk of accidental disclosure.

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 · scripts/image_gen_compare.py (reported line 92)May include surrounding context.

python
start = time.monotonic()

    resp = requests.post(
        "https://api.openai.com/v1/images/generations",
        headers={"Authorization": f"Bearer {key}", "Content-Type": "application/json"},
        json={
            "model": "dall-e-3",

Tainted flow: 'image_url' from requests.post (line 108, network input) → requests.get (network output)

Medium
Category
Data Flow
Confidence
87% confidence
Finding

The code performs a second network fetch against a URL returned by a remote service without validating the hostname, scheme, or content type. If the upstream response were compromised or unexpectedly changed, this could enable unintended outbound requests (SSRF-like behavior) or download of untrusted content into local/cloud-synced storage.

Content

Scanner excerpt · scripts/image_gen_compare.py (reported line 116)May include surrounding context.

python
filename = f"dalle3_{quality}_{ts}.png"
    dest = OUTPUT_DIR / filename

    img_resp = requests.get(image_url, timeout=30)
    dest.write_bytes(img_resp.content)

    result = {

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The manifest description frames the skill around comparing specific paid and local models, explicitly naming DALL-E 3, FLUX.1-schnell, Gemini Imagen, and others. In the code, an additional SDXL generator is implemented, while no Gemini Imagen integration exists anywhere in the file, so the actual supported model set diverges from the stated description.

Content

No source excerpt is available for this finding.

Missing User Warnings

Low
Category
Not specified by scanner
Confidence
90% confidence
Finding

The skill description says it logs metadata and stores run history, but does not clearly warn users that generated images and associated metadata will be retained. In this context, prompts and outputs may contain sensitive or proprietary material, so undisclosed persistence increases privacy and data-handling risk.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.