Back to skill

Security audit

Generate/edit images via Tuzi API (default), Google Gemini, OpenAI, DashScope, Replicate. Text-to-image + image-to-image editing; 1K/2K/4K resolution. Use for image create/modify/edit requests incl. --input-image.

Security checks for vulnerabilities and agentic risk

Overview

This image-generation skill is mostly purpose-aligned, but it contains unsafe shell execution paths that could let crafted inputs run local commands or expose API credentials.

Install only if you are comfortable reviewing and constraining it first. Run it in a restricted environment, avoid sensitive images/prompts, use narrowly scoped API keys, avoid proxy/custom-base-url settings unless trusted, and prefer a pinned/preinstalled Bun runtime. The unsafe shell paths should be fixed before using it with untrusted filenames, environment variables, or API-key inputs.

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

T09 · Insecure Skill Coding Practices

Error
Location
scripts/main.ts:113
Finding

OS Command Injection Through the Input Image Path

Content
View full analysis
{ if (explicitResolution !== "1K") return explicitResolution; try { const { execSync } = await import("node:child_process"); const result = execSync(`identify -format "%w %h" "${inputImagePath}" 2>/dev/null`, { encoding: "utf8" }).trim(); const [w, h] = result.split(" ").map(Number); ``` ### Technical Analysis The user-controlled `inputImagePath` is interpolated directly into a command executed through `execSync`. Because `execSync` receives a string, Node.js invokes a shell to interpret it. Wrapping the path in double quotes does not prevent injection. A filename containing a double quote can terminate the quoted argument and introduce shell operators or additional commands. The earlier `access(args.inputImage)` check does not make the value safe; an attacker can create a file whose name contains shell metacharacters and then supply that exact path. This shell invocation is not necessary for the declared image-generation functionality. Image dimensions should be obtained through a library or by invoking `identify` without a shell. ### Attack Path 1. The attacker creates an accessible image file with a crafted filename containing a quote, shell separator, command, and comment marker. 2. The attacker supplies that path using `--input-image`. 3. The `access()` check succeeds because the crafted file exists. 4. With the default `1K` resolution, `autoDetectResolution()` is called. 5. The crafted path terminates the quoted shell argument. 6. The shell executes the injected command with the same operating-system privileges as the Skill process. ### Impact Assessment Successful exploitation provi ...[truncated 370 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Error
Location
scripts/providers/google.ts:32
Finding

Command Injection and API-Key Exposure in Google Proxy Mode

Content
View full analysis
(pathname: string, body: unknown): Promise { const apiKey = getApiKey(); if (!apiKey) throw new Error("GOOGLE_API_KEY or GEMINI_API_KEY is required"); const url = buildUrl(pathname); const proxy = getHttpProxy(); if (proxy) { const bodyStr = JSON.stringify(body); const proxyArgs = `-x "${proxy}"`; const result = execSync( `curl -s --connect-timeout 30 --max-time 300 ${proxyArgs} "${url}" -H "Content-Type: application/json" -H "x-goog-api-key: ${apiKey}" -d @-`, { input: bodyStr, maxBuffer: 100 * 1024 * 1024, timeout: 310000 }, ); const parsed = JSON.parse(result.toString()) as any; if (parsed.error) throw new Error(`Google API error (${parsed.error.code}): ${parsed.error.message}`); return parsed as T; ``` ### Technical Analysis When a proxy environment variable is present, the provider constructs a shell command containing the proxy URL, Google base URL, model-derived pathname, and API key. These values are interpolated without shell-safe argument separation. The API key is directly user-controllable through `--api-key`; `main.ts` copies that value into `GEMINI_API_KEY`. A value containing quotes and shell operators can escape the `x-goog-api-key` header argument and execute another command. Proxy and base URL environment variables provide additional injection surfaces. The Google API key is also embedded in the command-line string passed to `curl`. Depending on the platform and timing, local users or monitoring tools may be able to observe it through process listings or process inspection interfaces. ### Attack Path 1. Proxy mode is enabled by setting one of `https_proxy`, `HTTPS_PROXY`, `http_proxy`, `HTTP_PROXY`, or `ALL_PROXY`. 2. The attacke ...[truncated 908 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/providers/replicate.ts:68
Finding

Replicate Bearer Token Forwarded to a Response-Controlled Polling URL

Content
View full analysis
setTimeout(r, POLL_INTERVAL_MS)); const pollRes = await fetch(prediction.urls!.get!, { headers: { Authorization: `Bearer ${apiToken}` } }); if (!pollRes.ok) throw new Error(`Replicate poll error: ${await pollRes.text()}`); prediction = (await pollRes.json()) as PredictionResponse; if (prediction.status === "succeeded") break; if (prediction.status === "failed" || prediction.status === "canceled") throw new Error(`Replicate ${prediction.status}: ${prediction.error}`); } ``` ### Technical Analysis The polling URL is taken from the initial API response and used without validating its scheme or origin. The code attaches the Replicate bearer token to every polling request. Authentication is required for legitimate Replicate polling, but the credential should only be sent to a trusted Replicate API origin. The current implementation permits the remote response to choose an arbitrary destination. A compromised endpoint, malicious custom `REPLICATE_BASE_URL`, or upstream response manipulation could therefore redirect the authorization header to an attacker-controlled server. ### Attack Path 1. The Skill submits a prediction to the configured Replicate endpoint. 2. A malicious or compromised endpoint returns a non-success status and sets `urls.get` to an attacker-controlled HTTPS URL. 3. The Skill enters the polling loop. 4. It sends `Authorization: Bearer ` to the unvalidated URL. 5. The attacker's server records the token. 6. The attacker reuses the token against Replicate ...[truncated 456 chars]
Remediation
View remediation

T08 · Insecure Dependencies

Warning
Location
SKILL.md:20
Finding

Unpinned Runtime Package Download and Execution

Content
View full analysis
Remediation
View remediation
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (22)

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding

The skill advertises image editing, --input-image support, and 2K/4K behavior that the implementation reportedly does not consistently provide for at least one provider. This mismatch can cause users or agents to send sensitive images/prompts to an external provider under false assumptions about functionality, routing, or output characteristics, undermining trust and safe decision-making.

Content

No source excerpt is available for this finding.

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · scripts/main.ts (reported line 82)May include surrounding context.

ts
}

async function loadEnv(): Promise<void> {
  const homeEnv = await loadEnvFile(path.join(homedir(), ".tuzi-skills", ".env"));
  const cwdEnv = await loadEnvFile(path.join(process.cwd(), ".tuzi-skills", ".env"));
  for (const [k, v] of Object.entries(homeEnv)) {
    if (!process.env[k]) process.env[k] = v;

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · scripts/main.ts (reported line 83)May include surrounding context.

ts
}

async function loadEnv(): Promise<void> {
  const homeEnv = await loadEnvFile(path.join(homedir(), ".tuzi-skills", ".env"));
  const cwdEnv = await loadEnvFile(path.join(process.cwd(), ".tuzi-skills", ".env"));
  for (const [k, v] of Object.entries(homeEnv)) {
    if (!process.env[k]) process.env[k] = v;

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
91% confidence
Finding

The skill invokes shell execution, accesses environment variables for API keys, and performs network calls to third-party providers, but it does not declare any explicit tool scope or permissions. This weakens reviewability and containment, making it easier for an agent or user to run the skill without understanding the breadth of its capabilities and the associated data exposure.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The skill description does not clearly warn that user prompts, input images, and possibly metadata will be transmitted to external third-party APIs. In an image-editing context this is especially sensitive because users may provide private images or proprietary content, leading to unintended data disclosure or policy violations.

Content

No source excerpt is available for this finding.

Rp1

Medium
Category
MCP Rug Pull
Confidence
96% confidence
Finding

Using npx -y bun without a pinned version allows retrieval and execution of whatever package/version is current at runtime. This creates a supply-chain risk where a compromised, replaced, or incompatible upstream package could execute arbitrary code on the host during skill invocation.

Content

No source excerpt is available for this finding.

Rp1

Medium
Category
MCP Rug Pull
Confidence
96% confidence
Finding

The command example again relies on unpinned npx -y bun, which means the runtime is downloaded/executed from the package ecosystem at invocation time. In a skill that processes user-controlled prompts and images and accesses API credentials, that supply-chain entry point materially increases compromise risk.

Content

No source excerpt is available for this finding.

Rp1

Medium
Category
MCP Rug Pull
Confidence
96% confidence
Finding

This workflow example repeats an unpinned npx -y bun invocation, preserving the same arbitrary-code/supply-chain exposure. Repetition in the primary workflow makes exploitation more likely because these are the commands users are most likely to copy verbatim.

Content

No source excerpt is available for this finding.

Rp1

Medium
Category
MCP Rug Pull
Confidence
96% confidence
Finding

The final 4K example still uses unversioned npx -y bun, so the same runtime supply-chain risk applies at every documented entry point. Because the skill also relies on network access and credentialed API usage, a malicious runtime could exfiltrate keys, prompts, or local file content.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
97% confidence
Finding

The code constructs a shell command with unsanitized user-controlled inputImagePath and executes it via execSync. Wrapping the path in double quotes is not sufficient because shell metacharacters such as command substitution can still be interpreted, enabling command execution if a crafted filename is supplied.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
84% confidence
Finding

This code accepts an API key, stores it in an environment variable, and then sends the user prompt and optional input image to an external provider via generateImage. Although it logs the provider/model in use, it does not disclose that credentials and user-supplied content may be transmitted to a third-party service.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

When a proxy is configured, the code builds a shell command string and passes untrusted environment-derived data (proxy) plus a secret (apiKey) into execSync. Because shell metacharacters in the proxy value can break out of the quoted argument, an attacker who can influence environment variables can achieve command injection and potentially exfiltrate the API key or run arbitrary commands.

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/providers/google.ts (reported line 43)May include surrounding context.

ts
const bodyStr = JSON.stringify(body);
    const proxyArgs = `-x "${proxy}"`;
    const result = execSync(
      `curl -s --connect-timeout 30 --max-time 300 ${proxyArgs} "${url}" -H "Content-Type: application/json" -H "x-goog-api-key: ${apiKey}" -d @-`,
      { input: bodyStr, maxBuffer: 100 * 1024 * 1024, timeout: 310000 },
    );
    const parsed = JSON.parse(result.toString()) as any;

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
87% confidence
Finding

This code sends the prompt and any optional input image to a remote Google API via HTTP, which can expose user-provided content off-host. While there is a progress log for image generation, there is no explicit disclosure that user content is being uploaded to an external service in this file.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
50% 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/providers/openai.ts (reported line 21)May include surrounding context.

ts
model: string,
  args: CliArgs
): Promise<Uint8Array> {
  const baseURL = process.env.OPENAI_BASE_URL || "https://api.openai.com/v1";
  const apiKey = process.env.OPENAI_API_KEY;
  if (!apiKey) throw new Error("OPENAI_API_KEY is required");

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
88% confidence
Finding

When args.inputImage is provided, the function reads a local image file and uploads it to the OpenAI images edit endpoint. The code performs this network transmission without any confirmation prompt, logging, or inline comment/docstring warning that local user content will be sent off-system.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
50% 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/providers/tuzi.ts (reported line 16)May include surrounding context.

ts
}

function getBaseUrl(): string {
  const base = process.env.TUZI_BASE_URL || "https://api.tu-zi.com/v1";
  return base.replace(/\/+$/g, "");
}

External Transmission

Medium
Category
Data Exfiltration
Confidence
50% 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/providers/tuzi.ts (reported line 56)May include surrounding context.

ts
}

function getBaseUrl(): string {
  const base = process.env.TUZI_BASE_URL || "https://api.tu-zi.com/v1";
  return base.replace(/\/+$/g, "");
}

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

When args.inputImage is set, the code reads the local image file, converts it to a base64 data URL, and includes it in the POST request to the external Tuzi service. Although there is a generic log that image generation is starting, there is no explicit disclosure that a local file's contents will be uploaded to a third-party API.

Content

No source excerpt is available for this finding.

Missing User Warnings

Low
Category
Not specified by scanner
Confidence
77% confidence
Finding

The code resolves the requested filename, creates parent directories, and writes image data directly to disk. While it logs the saved path after completion, there is no prior warning or confirmation that a file write will occur and may replace existing content at that path.

Content

No source excerpt is available for this finding.

Missing User Warnings

Low
Category
Not specified by scanner
Confidence
76% confidence
Finding

The function reads sensitive API credentials from environment variables and, when a proxy is configured, invokes curl through execSync to send the request. This file lacks any comment, docstring, or explicit user-facing disclosure explaining that credentials are used and that a subprocess may be spawned for network access through a proxy.

Content

No source excerpt is available for this finding.

Missing User Warnings

Low
Category
Not specified by scanner
Confidence
79% confidence
Finding

The code sends the user-supplied prompt to the OpenAI /images/generations endpoint, which is a network transmission of user data. There is no visible print/log statement, confirmation, or explanatory comment in this file to disclose that the prompt leaves the local system.

Content

No source excerpt is available for this finding.