Back to skill

Security audit

Nano banana korean rendering

Security checks for vulnerabilities and agentic risk

Overview

This skill is a disclosed Gemini-and-Canvas image text rendering helper, with some privacy and supply-chain cautions but no evidence of deception, persistence, or malicious behavior.

Install only if you are comfortable sending prompts, rendered text images, and any --ref images you provide to Google Gemini. Prefer running setup in a minimal environment without unnecessary secrets, and review or pin dependencies and downloaded fonts if reproducibility matters.

Vulnerability Patterns
  • Insecure DependenciesIntroduces malicious components through unsafe dependency sources
  • 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)

T08 · Insecure Dependencies

Warning
Location
setup.mjs:145
Finding
Unpinned Dependencies and Unverified Remote Font Downloads<![CDATA[ ## Vulnerability Details **File Location**: `package.json:11-12`; `setup.mjs:81`; `setup.mjs:145-174` **Vulnerability Type**: Supply-chain integrity weakness **Risk Level**: Medium ### Vulnerable Code `package.json:11-12`: ```json "canvas": "^3.1.0", "@google/generative-ai": "^0.21.0" ``` `setup.mjs:81`: ```js execSync('npm install', { cwd: __dirname, stdio: 'inherit' }); ``` `setup.mjs:145-174`: ```js async function downloadFonts() { // 동일 URL → 한 번만 다운로드, 여러 파일명에 복사 const urlCache = new Map(); // url → Buffer for (const fontFile of REQUIRED_FONTS) { const dst = path.join(FONTS_DIR, fontFile); if (fs.existsSync(dst)) continue; const url = FONT_DOWNLOAD_URLS[fontFile]; if (!url) { logWarn(`다운로드 URL 없음: ${fontFile} (수동 다운로드 필요)`); log(` Google Fonts에서 다운로드: https://fonts.google.com/noto`); log(` 저장 위치: ${dst}`); continue; } // 이미 같은 URL에서 다운로드 했으면 캐시에서 복사 if (urlCache.has(url)) { fs.writeFileSync(dst, urlCache.get(url)); logOk(`캐시에서 복사: ${fontFile}`); continue; } log(`다운로드 중: ${fontFile}...`); try { const response = await fetch(url, { redirect: 'follow' }); if (!response.ok) throw new Error(`HTTP ${response.status}`); const buffer = Buffer.from(await response.arrayBuffer()); fs.writeFileSync(dst, buffer); urlCache.set(url, buffer); logOk(`다운로드 완료: ${fontFile} (${(buffer.length / 1024 / 1024).toFixed(1)}MB)`); } catch (error) { logWarn(`다운로드 실패: ${fontFile} - ${error.message}`); log(` 수동으로 다운로드해주세요: ${url}`); log(` 저장 위치: ${dst}`); } } } ``` ### Technical Analysis The project does not include a dependency lockfile, while its dependencies use caret version ranges. Running `npm install` can therefore resolve package versions that differ from those reviewed during the audit. The command also permits dependency lifecycle scripts to execute unless separately disabled. The setup process addit ...[truncated 2149 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Pin dependencies to exact reviewed versions rather than caret ranges. 2. Generate and commit a lockfile, and use `npm ci` for reproducible installation. 3. Use `npm ci --ignore-scripts` where possible. If lifecycle scripts are required, explicitly review and allow only the necessary packages and scripts. 4. Pin font URLs to immutable commit hashes or versioned release assets rather than mutable branch paths. 5. Maintain expected SHA-256 hashes for every downloaded font and verify each artifact before writing it to disk. 6. Validate the final redirect destination against an explicit host and path allowlist. 7. Reject responses with unexpected content types, excessive sizes, or invalid font signatures. 8. Download to a temporary file, validate it, and then atomically move it into the font directory. 9. Run setup and rendering under a minimally privileged account without unnecessary secrets in the process environment. 10. Add automated dependency and artifact-integrity checks to the release process. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • System Prompt LeakageDirect Leakage, Indirect Extraction, Tool-Based Exfiltration
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
Findings (10)

Description-Behavior Mismatch

High
Confidence
98% confidence
Finding
The skill metadata and description frame this as a text-preserving rendering utility, but the code also exposes full Gemini-powered image generation through generate/pipeline flows. This hidden expansion of capability broadens data handling, outbound network use, and model-side prompt/media processing beyond what a caller would reasonably expect, which is a real security and trust-boundary issue for agent skills.

Lp3

Medium
Category
MCP Least Privilege
Confidence
95% confidence
Finding
The skill explicitly depends on environment variables and external Gemini API calls, but it does not declare any tool scope or allowed-tools restrictions. This creates a capability/permission mismatch where an agent may invoke network and env access without clear least-privilege boundaries, increasing the chance of unintended secret exposure or unauthorized outbound requests.

Missing User Warnings

Medium
Confidence
96% confidence
Finding
The skill instructs the agent to send user prompts, rendered text images, and optional reference images to Gemini, but it does not provide a clear privacy notice or external-transfer warning at the point of use. This is risky because users may unknowingly submit sensitive text or images to a third-party service, and the broad triggers make such transmission more likely to happen unexpectedly.

Vague Triggers

Medium
Confidence
95% confidence
Finding
The auto-activation triggers are very broad, covering common words like 'text', 'title', and any non-Latin script, so the skill may run for many ordinary image requests without clear user intent. In this skill's context, unintended activation matters because it can cause prompts and possibly images to be routed into an external analysis/generation pipeline, expanding data exposure and unexpected API usage.

Natural-Language Policy Violations

Medium
Confidence
95% confidence
Finding
The analysis prompt is entirely written in Korean and instructs the LLM to operate in that locale, which can force a specific language behavior regardless of user preference. Under the policy, locale constraints should either be user-selectable or clearly justified as region-specific, neither of which is present here.

Context-Inappropriate Capability

Medium
Confidence
94% confidence
Finding
The generate/pipeline paths accept arbitrary --ref image paths and upload their contents to Gemini without restricting file origin, purpose, or type beyond a simple extension-based MIME guess. In an agent setting, this can cause unintended exfiltration of local images or sensitive visual data to a third-party API, which exceeds the stated text-preservation function and increases privacy risk.

Natural-Language Policy Violations

Low
Confidence
82% confidence
Finding
The natural-language description is written entirely in Korean and presents the skill as such without indicating any language choice or opt-in. Under the policy for language/locale constraints, this can be a policy concern when a skill appears to assume a specific language without documenting user choice or a justified regional scope.

Unpinned Dependencies

Low
Category
Supply Chain
Content
"render": "node render.mjs render"
  },
  "dependencies": {
    "canvas": "^3.1.0",
    "@google/generative-ai": "^0.21.0"
  }
}
Confidence
91% confidence
Finding
Using a caret range for the canvas dependency allows newer package versions to be installed without explicit review, which weakens build reproducibility and can silently introduce vulnerable or malicious upstream changes. In this skill context, canvas is a native/image-processing dependency and has had security advisories before, so drift in resolved versions is more risky than for a purely cosmetic package.

Unverifiable Dependency: canvas has 2 known advisory(ies) (CVE-2020-8215 (Buffer overflow in canvas); GHSA-vpq5-4rc8-c222 (Denial of Service in canvas)), but the manifest does not pin a version, so it is unknown whether the installed release is affected

Low
Category
Supply Chain
Confidence
86% confidence
Finding
The manifest references canvas with a non-exact version while known advisories exist for that package, making it impossible to verify from this file alone whether the installed version is safe. In an image-rendering skill that processes text and graphics, a vulnerable native parsing/rendering library can increase exposure to crashes, denial of service, or memory-safety issues depending on the deployed version.

Unpinned Dependencies

Low
Category
Supply Chain
Content
},
  "dependencies": {
    "canvas": "^3.1.0",
    "@google/generative-ai": "^0.21.0"
  }
}
Confidence
88% confidence
Finding
Using a caret range for @google/generative-ai permits automatic adoption of future releases, reducing supply-chain control and reproducibility. Because this library handles AI-service interactions and potentially sensitive prompts or generated content, unexpected upstream changes could affect security posture, data handling, or integrity of outputs.

Static analysis

Detected: suspicious.dangerous_exec, suspicious.env_credential_access

Shell command execution detected (child_process).

Critical
Code
suspicious.dangerous_exec
Location
setup.mjs:81

Environment variable access combined with network send.

Critical
Code
suspicious.env_credential_access
Location
setup.mjs:216