Back to skill

Security audit

Creatok Generate Image

Security checks for vulnerabilities and agentic risk

Overview

The skill mostly does what it claims, but it has an unsanitized run ID that can write result files outside its intended artifact folder.

Review before installing. The CreatOK API behavior is disclosed and purpose-aligned, but only use this skill in an environment where run IDs are trusted or sandboxed, because malformed run_id values can write outside the intended .artifacts folder. Also expect prompts, selected reference images, and generation metadata to be sent to CreatOK and result details to be stored locally.

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

T09 · Insecure Skill Coding Practices

Warning
Location
lib/artifacts.js:34
Finding
Path Traversal Through Unsanitized Run ID Allows Writes Outside the Artifact Directory<![CDATA[ ## Vulnerability Details **File Location**: `scripts/run.js:15-37, 126`; `lib/artifacts.js:8-26, 30-36`; `lib/generate-image.js:106-125, 232` **Vulnerability Type**: Path traversal leading to unintended directory creation and file overwrite **Risk Level**: Medium ### Vulnerable Code The command-line parser accepts `--run_id` without validation: ```js for (let i = 2; i < argv.length; i += 1) { const key = argv[i]; if (key === '--yes') { args.yes = true; continue; } if (key === '--wait') { args.wait = true; continue; } const value = argv[i + 1]; if (key === '--prompt') args.prompt = value; if (key === '--task_id') args.taskId = value; if (key === '--model') args.model = value; if (key === '--resolution') args.resolution = value; if (key === '--n') args.n = Number(value); if (key === '--aspect_ratio') args.aspectRatio = value; if (key === '--reference_images') args.referenceImages = value.split(',').map((s) => s.trim()); if (key === '--run_id') args.runId = value; if (key === '--timeout_sec') args.timeoutSec = Number(value); if (key === '--poll_interval') args.pollInterval = Number(value); if (key.startsWith('--')) { i += 1; } } ``` The untrusted value is passed into the generation workflow: ```js const result = await runGenerateImage({ prompt: resolvedArgs.prompt, runId: resolvedArgs.runId, skillDir: SKILL_ROOT, model: resolvedArgs.model, resolution: resolvedArgs.resolution, n: resolvedArgs.n, aspectRatio: resolvedArgs.aspectRatio || null, referenceImages: resolvedArgs.referenceImages, timeoutSec: resolvedArgs.timeoutSec, pollInterval: resolvedArgs.pollInterval, }); ``` It is then joined directly to the artifact root: ```js function artifactsRootForSkill(skillDir) { return path.join(skillDir, '.artifacts'); } function artifactsForRun(skillDir, runId) { return new Artifacts(path.join(artifactsRootForSkill(skillDir), runId)); } ``` The resulting path is used for recu ...[truncated 4392 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. **Restrict run IDs to opaque identifiers** Accept only a narrow character set and a reasonable maximum length: ```js function validateRunId(runId) { if (typeof runId !== 'string' || !/^[A-Za-z0-9_-]{1,128}$/.test(runId)) { throw new Error('Invalid run_id'); } return runId; } ``` 2. **Enforce canonical path containment** Resolve both the artifact root and candidate run directory, then verify that the candidate remains a descendant: ```js function artifactsForRun(skillDir, runId) { const safeRunId = validateRunId(runId); const artifactsRoot = path.resolve(skillDir, '.artifacts'); const candidate = path.resolve(artifactsRoot, safeRunId); const relative = path.relative(artifactsRoot, candidate); if ( relative === '' || relative.startsWith(`..${path.sep}`) || relative === '..' || path.isAbsolute(relative) ) { throw new Error('run_id escapes the artifact directory'); } return new Artifacts(candidate); } ``` 3. **Reject path syntax explicitly** Reject absolute paths, `.` and `..` components, forward slashes, backslashes, null bytes, and platform-specific path separators before performing filesystem operations. 4. **Address symlink traversal** Where the execution environment is not fully trusted, check existing path components with `lstat`, reject symbolic links, and use filesystem APIs or deployment permissions that prevent symlink-based redirection. 5. **Reduce overwrite risk** If overwriting existing run artifacts is unnecessary, use exclusive creation such as the `wx` flag or create each run directory with exclusive semantics. Alternatively, generate run IDs internally using a UUID rather than accepting arbitrary caller-provided values. 6. **Apply least-privilege filesystem permissions** Run the Skill under an account that can write only to its designated artifact directory. Thi ...[truncated 346 chars]
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
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
Findings (13)

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding
The declared purpose centers on image generation and recovery of interrupted image-generation tasks through an external API. The supplied code chunk does not implement or directly support those behaviors in any image-specific way; instead, it provides generic artifact management utilities that create directories and write files locally. Because the actual code's primary behavior is filesystem artifact handling rather than image generation or task continuation, this is a material mismatch for the provided chunk.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
The declared purpose is specifically about image generation and continuation of interrupted image generation flows. The supplied code only implements a generic authenticated JSON request helper and a `getCapabilities()` method that fetches capability information from `/api/open/skills/capabilities`. There is no code for submitting image prompts, creating product images, polling image jobs, or resuming tasks by ID. While HTTP helper logic is supporting infrastructure, the only concrete behavior shown is capabilities discovery, which is materially different from the declared image-generation purpose.

Lp3

Medium
Category
MCP Least Privilege
Confidence
94% confidence
Finding
The skill declares network access and use of an API key via metadata, but it does not define an explicit tool scope such as allowed tools or permissions. That increases the attack surface because a host may grant broader-than-necessary capabilities, making unintended network or environment access easier if the skill is invoked or extended unsafely.

Vague Triggers

Medium
Confidence
95% confidence
Finding
The trigger phrases are very broad and match common image-related requests across multiple languages, increasing the chance of the skill being auto-invoked in situations the user did not intend. In this skill, that matters because the workflow can lead to paid API usage and external data transmission once the conversation enters the flow.

Autonomous Decision Making

Medium
Category
Excessive Agency
Content
## Existing Task Recovery

- If the user already has a `task_id`, continue from that task instead of starting a new one.
- In recovery mode, do not ask the user to restate the prompt if the task id is already available.
- The model can either check status once or keep polling if the user wants to wait.
- If the task succeeded, return the final image URLs verbatim.
- If the task is still queued or running, explain clearly and offer to keep checking.
Confidence
80% confidence
Finding
Skill enables autonomous high-impact decisions without human-in-the-loop verification. Critical operations (destructive commands, financial transactions, data deletion) should require explicit user confirmation.

Missing User Warnings

Medium
Confidence
92% confidence
Finding
This code creates directories and writes JSON/text artifacts to disk via fs.mkdirSync and fs.writeFileSync, but it contains no confirmation prompt, logging, print statement, or explanatory comment/docstring disclosing that user or run data will be persisted. Because this is a code file and the operations write files under a hidden .artifacts directory, the lack of visible disclosure matches the missing user warning criteria.

Description-Behavior Mismatch

Medium
Confidence
92% confidence
Finding
The manifest says this skill generates images via CreatOK's image generation API and can continue existing image generation tasks. This file instead calls a broad '/api/open/skills/capabilities' endpoint and returns capability metadata, which is not image generation or task recovery behavior and exceeds the described operational scope.

Missing User Warnings

Medium
Confidence
81% confidence
Finding
The function reads a user-supplied local file and sends its name, type, and size to a remote upload-initialization endpoint. This is a data-transmission step involving local user data, but the file contains no user-facing disclosure, confirmation, or explanatory comment/docstring about that behavior.

Missing User Warnings

Medium
Confidence
89% confidence
Finding
This code performs a PUT request that transmits the contents of a local image file to a presigned upload URL. While the operation is functional and expected for uploading, there is no confirmation prompt, user-facing log/print, or inline comment/docstring in this file disclosing that local file data will be sent to a remote service.

Missing User Warnings

Medium
Confidence
91% confidence
Finding
This code uploads each path in referenceImages to a remote service via client.uploadImageFile, which can transmit local file contents off-host. In an agent skill context, file paths may originate from user or workflow state, so absent an explicit consent/disclosure boundary and path restrictions, sensitive local data could be exfiltrated unintentionally.

Missing User Warnings

Medium
Confidence
86% confidence
Finding
The skill submits prompt, model/resolution metadata, and optional reference image keys to a third-party image-generation API with no in-file indication of disclosure, minimization, or consent handling. Prompts can contain secrets, proprietary product plans, or personal data, so sending them externally may violate user expectations or data-handling requirements.

Vague Triggers

Medium
Confidence
93% confidence
Finding
The rule encourages handoff based on very broad, everyday phrases without requiring a strong intent check or confirmation. In an agent-skill system, this can cause unintended skill transitions, which may trigger the wrong capability, send user context to another skill, or perform actions the user did not clearly request.

Missing User Warnings

Low
Confidence
78% confidence
Finding
The skill writes raw API responses, task identifiers, error messages, and image URLs/object keys to local artifacts. While lower severity than direct exfiltration, this can create secondary exposure if artifact storage is shared, retained too long, or accessible to other users/processes.

Static analysis

No suspicious patterns detected.