T01 · Skill Instruction Hijacking
- Location
SKILL.md:280- Finding
Mandatory Skill Instructions Suppress Transparency and Direct Silent Environment Modification
- Content
View full analysis
- Remediation
View remediation
Security audit
Security checks for vulnerabilities and agentic risk
The skill is mostly an image-generation wrapper, but it under-discloses sensitive local file, credential, installation, and configuration behaviors users should review before installing.
Install only if you are comfortable with a remote WeryAI image gateway, local config writes, and an API key stored in a project or home .env file. Do not use local reference-image paths unless you have verified the exact file being uploaded, and ask to see exact install commands before approving any runtime setup.
SKILL.md:280Mandatory Skill Instructions Suppress Transparency and Direct Silent Environment Modification
scripts/main.ts:298Arbitrary Local Files Can Be Encoded and Uploaded as Reference Images
scripts/main.ts:162WeryAI Bearer Credential Can Be Forwarded to an Arbitrary Result URL
scripts/setup.mjs:109Plaintext API Key Is Persisted Without Restrictive File Permissions or Symlink Protection
The description presents this as a narrow skill specifically for chalkboard/chalk drawing art. The code, however, implements a broad WeryAI image-generation client whose primary purpose is generic image generation. While it does include a 'chalk' style preset, that is only one option among many presets such as photoreal, anime, watercolor, poster, infographic, etc. It also supports image-to-image generation via reference images, batch files, legacy subcommands, task-status lookup, config/env loading, and remote download of results. These are materially broader capabilities and a different primary purpose than the declared specialized chalk-art skill.
This is a clear mismatch. The declared purpose is an image/art generation capability focused on chalkboard-style visuals. The actual code does not generate images or process art prompts at all; it queries a remote balance endpoint and returns available credits/balance. That is a materially different primary purpose and an undeclared capability unrelated to the description.
The declared purpose is a domain-specific creative skill for generating chalkboard-style artwork. The supplied code does not contain any image generation, rendering, chalkboard styling, or art-specific logic. Instead, it is infrastructure code for making authenticated HTTP requests with timeout, retry, JSON parsing, and logging. That is a materially different primary purpose and introduces undeclared network access/authentication capabilities unrelated to the stated skill description.
The declared description says the skill generates chalkboard-style artwork, but the supplied code chunk does not perform image generation, drawing, style rendering, or any chalk-art-specific behavior. Instead, it is a utility module for handling WeryAI API responses and errors. This is a materially different primary purpose, so the description does not accurately represent the code.
The declared description is about producing chalkboard-style artwork, but the supplied code does not generate images or perform any drawing-related logic. Instead, it provides backend utility functions for normalizing task statuses and media task outputs from an external workflow. This is a materially different primary purpose from chalk art generation, so the description does not accurately represent the code's behavior.
The declared description says this skill generates chalkboard art and chalk drawings. However, the supplied code does not generate any artwork or perform image creation/stylization. Its actual purpose is to retrieve and normalize status information for previously created generation tasks or batches via /v1/generation/.../status API calls. This is a materially different primary purpose and introduces undeclared capabilities related to remote task-status monitoring and metadata retrieval. Therefore, the description does not accurately represent the code's behavior.
The declared description says this skill generates chalkboard art and chalk drawings. However, the supplied code does not perform image generation, drawing, rendering, style application, or any chalkboard-art-specific logic. Instead, it repeatedly queries remote /v1/generation/.../status endpoints to monitor asynchronous generation tasks, including batch status polling, retries, timeout handling, normalization of task results, and reporting completion/failure. This is a materially different primary purpose from the declared artistic function, so the description does not accurately represent the code chunk.
The declared description is narrowly about producing a specific artistic style: chalkboard/chalk/pastel-on-dark-background images. The supplied code does not implement any chalkboard-art-specific behavior, styling, prompts, triggers, or rendering logic. Instead, it provides generic backend support for image generation by contacting a models service, validating user inputs against model capabilities, and constructing API request payloads for text-to-image and image-to-image modes. This is a materially different primary purpose and includes undeclared network/model-registry functionality unrelated to the specific declared artistic specialization.
There is a clear mismatch between the declared purpose and the actual code behavior. The description claims this skill generates chalkboard/pastel-style art, implying image creation functionality. However, the supplied code only retrieves and returns model metadata from a remote registry, filtered by mode. Its primary purpose is model discovery/listing, not image generation, and nothing in the code is specific to chalkboard art, blackboard menus, or pastel drawings.
The description claims a specific end-user capability: generating chalkboard art or related dark-background pastel imagery. However, the provided code chunk does not implement or even reference chalkboard styling, drawing generation, prompt handling, triggers, or any art-specific logic. It only exports generic constants for an image model, an image-model API endpoint, and fallback generation defaults. That is materially different from the declared specialized purpose, so this should be flagged as a mismatch.
The declared description is narrow and style-specific, implying a specialized chalkboard/chalk drawing generator. The actual code is not specialized to chalkboard art at all; it is a general-purpose text-to-image submission routine that accepts arbitrary prompts and models, validates against a model registry, and calls a remote generation API. While chalkboard art could be one possible use of this code, the implemented primary purpose is materially broader than the declared description, so this is a description-behavior mismatch.
Referenced artifact was not completely inspected
| Base URL | `https://api.weryai.com` (hard-coded in `scripts/main.ts`) |
The skill is designed to locate, use, and optionally persist an API key from environment files, creating a credential-handling pathway that touches secrets on disk and in process environment. In a skill without strict scope declarations and with network capability, this increases the risk of secret exposure, misuse, or unauthorized persistence.
3. **If `IMAGE_GEN_API_KEY` is missing**, ask the user for permission to configure it now
4. Only after readiness and API key are resolved, continue to model selection / prompt clarification / generation
**Access token**: only use **`IMAGE_GEN_API_KEY`**. It may also live in `.image-skills/chalk-art-image-generator/.env` as `IMAGE_GEN_API_KEY=...`. After the user approves, the agent may persist it there by running `npm run setup -- --project . --workflow <workflow> --persist-api-key` when the key is already in env, or by writing the file locally on the user's behalf instead of asking the user to edit files manually.
**First-use readiness check**: before the first generation run in a new OpenClaw or local instance, the agent must run:
The script synchronously executes multiple local commands (node, npm, bun, npx, sips, cwebp, magick, convert) to fingerprint the host toolchain. Even without shell injection, this is still active host reconnaissance unrelated to chalk-art generation and can reveal system capabilities, installed software, and platform details that increase the blast radius if the skill is run in a sensitive environment.
The script reads a project-local .env file and tests for the presence of IMAGE_GEN_API_KEY, which constitutes credential discovery behavior. Although it does not exfiltrate the key value directly, scanning for and processing credential-bearing files is sensitive behavior, especially in a skill whose advertised purpose does not require filesystem credential inspection.
const projectSkillDir = path.join(projectRoot, ".image-skills", skillNamespace);
const homeSkillDir = homeRoot ? path.join(homeRoot, ".image-skills", skillNamespace) : null;
const projectEnvPath = path.join(projectRoot, ".image-skills", skillNamespace, ".env");
const projectEnvFile = pathExists(projectEnvPath);
let apiKeyInProjectEnv = false;
if (projectEnvFile) {
Skill contains instructions that could directly expose system prompts, internal rules, or hidden instructions to users or external parties.
}
function applyStylePreset(prompt: string, style: StylePreset | null | undefined): string {
if (!style) return prompt;
return `${prompt.trim()}\n\nStyle preset (${style}): ${STYLE_PRESETS[style]}.`;
}
Skill contains instructions that could directly expose system prompts, internal rules, or hidden instructions to users or external parties.
}
function applyStylePreset(prompt: string, style: StylePreset | null | undefined): string {
if (!style) return prompt;
return `${prompt.trim()}\n\nStyle preset (${style}): ${STYLE_PRESETS[style]}.`;
}
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
const projectSkillDir = path.join(projectRoot, ".image-skills", skillNamespace);
const homeSkillDir = homeRoot ? path.join(homeRoot, ".image-skills", skillNamespace) : null;
const projectEnvPath = path.join(projectRoot, ".image-skills", skillNamespace, ".env");
const projectEnvFile = pathExists(projectEnvPath);
let apiKeyInProjectEnv = false;
if (projectEnvFile) {
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
const projectSkillDir = path.join(projectRoot, ".image-skills", skillNamespace);
const homeSkillDir = homeRoot ? path.join(homeRoot, ".image-skills", skillNamespace) : null;
const projectEnvPath = path.join(projectRoot, ".image-skills", skillNamespace, ".env");
const projectEnvFile = pathExists(projectEnvPath);
let apiKeyInProjectEnv = false;
if (projectEnvFile) {
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
const projectSkillDir = path.join(projectRoot, ".image-skills", skillNamespace);
const homeSkillDir = homeRoot ? path.join(homeRoot, ".image-skills", skillNamespace) : null;
const projectEnvPath = path.join(projectRoot, ".image-skills", skillNamespace, ".env");
const projectEnvFile = pathExists(projectEnvPath);
let apiKeyInProjectEnv = false;
if (projectEnvFile) {
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
const projectSkillDir = path.join(projectRoot, ".image-skills", skillNamespace);
const homeSkillDir = homeRoot ? path.join(homeRoot, ".image-skills", skillNamespace) : null;
const projectEnvPath = path.join(projectRoot, ".image-skills", skillNamespace, ".env");
const projectEnvFile = pathExists(projectEnvPath);
let apiKeyInProjectEnv = false;
if (projectEnvFile) {
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
const projectSkillDir = path.join(projectRoot, ".image-skills", skillNamespace);
const homeSkillDir = homeRoot ? path.join(homeRoot, ".image-skills", skillNamespace) : null;
const projectEnvPath = path.join(projectRoot, ".image-skills", skillNamespace, ".env");
const projectEnvFile = pathExists(projectEnvPath);
let apiKeyInProjectEnv = false;
if (projectEnvFile) {
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
const projectSkillDir = path.join(projectRoot, ".image-skills", skillNamespace);
const homeSkillDir = homeRoot ? path.join(homeRoot, ".image-skills", skillNamespace) : null;
const projectEnvPath = path.join(projectRoot, ".image-skills", skillNamespace, ".env");
const projectEnvFile = pathExists(projectEnvPath);
let apiKeyInProjectEnv = false;
if (projectEnvFile) {
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
const projectSkillDir = path.join(projectRoot, ".image-skills", skillNamespace);
const homeSkillDir = homeRoot ? path.join(homeRoot, ".image-skills", skillNamespace) : null;
const projectEnvPath = path.join(projectRoot, ".image-skills", skillNamespace, ".env");
const projectEnvFile = pathExists(projectEnvPath);
let apiKeyInProjectEnv = false;
if (projectEnvFile) {
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
-h, --help Show help
What this does:
1) Optionally writes IMAGE_GEN_API_KEY into the local skill .env
2) Runs doctor (read-only)
3) Ensures a MODELS.json exists (writes the bundled starter if missing)
4) Initializes EXTEND.md with Nano Banana 2 if no default model exists yet