Back to skill

Security audit

Nano Banana Image Skills

Security checks for vulnerabilities and agentic risk

Overview

This image-generation skill is purpose-aligned, but users should know prompts, input images, and refinement history may leave or persist outside the chat.

Install only if you are comfortable sending prompts, selected input images, and refinement context to Wisdom Gate/Gemini services. Avoid sensitive or regulated images unless approved, use a dedicated API key, install dependencies in a virtual environment, and keep conversation.json or custom history files out of shared folders, backups, and source control.

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

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/refine_image.py:28
Finding
Plaintext Persistence of Sensitive Conversation and Image Data<![CDATA[ ## Vulnerability Details **File Location**: `scripts/refine_image.py:28-36`, `scripts/refine_image.py:61-67`, and `scripts/refine_image.py:82-89` **Vulnerability Type**: Plaintext storage of potentially sensitive data **Risk Level**: Medium ### Vulnerable Code ```python def load_conversation(history_file): """Load conversation history from JSON file.""" if not os.path.exists(history_file): return [] with open(history_file, "r") as f: return json.load(f) def save_conversation(history_file, contents): """Save conversation history to JSON file.""" with open(history_file, "w") as f: json.dump(contents, f, indent=2) ``` The history receives the user prompt: ```python # Load existing conversation contents = load_conversation(history_file) # Add new user prompt contents.append({ "role": "user", "parts": [{"text": prompt}] }) ``` The complete model response is then persisted: ```python # Add model response to conversation contents.append({ "role": "model", "parts": model_parts }) # Save updated conversation save_conversation(history_file, contents) ``` ### Technical Analysis The refinement script stores the complete conversation in an unencrypted JSON file. This includes user prompts and the API's complete `model_parts` response. Because generated images may be returned as base64-encoded `inlineData`, the history can contain both textual information and full image content. The file is created using the process's default permissions and umask. The script does not explicitly restrict access to the owner, encrypt sensitive content, redact inline image data, enforce a retention policy, or limit history size. Consequently, the history may be readable by other local users in permissively configured environments or exposed through backups, synchronization services, shared directories, or accidental source-control commits. Repeated refinement also appends API responses to the history without a si ...[truncated 1363 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Create history files with owner-only permissions, such as mode `0600`, rather than relying solely on the process umask. 2. Use an atomic write procedure: create a protected temporary file in the destination directory, flush and synchronize it, and atomically replace the old history. 3. Reject symbolic-link destinations or otherwise ensure that the selected history path cannot redirect writes to an unintended file. 4. Do not store base64-encoded `inlineData` in conversation history unless it is strictly required. Store only the minimum metadata needed for refinement, or place image data in separately protected files. 5. Make persistent history opt-in and provide an ephemeral mode that keeps conversation data only in memory. 6. Add configurable retention, turn-count, and total-size limits. 7. Warn users that the history contains sensitive prompts and potentially complete images, and recommend excluding it from version control, backups, and shared directories. 8. If persistent sensitive histories are required, encrypt them at rest using a key managed separately from the history file. ]]>

T08 · Insecure Dependencies

Note
Location
README.md:17
Finding
Unpinned Third-Party Dependency Installation<![CDATA[ ## Vulnerability Details **File Location**: `README.md:13-18` **Vulnerability Type**: Unpinned and unverifiable dependency installation **Risk Level**: Low ### Vulnerable Code ```markdown ## Prerequisites - Python 3.8+ - `requests` library (`pip install requests`) - A valid [Wisdom Gate](https://wisgate.ai) API key ``` ### Technical Analysis The documented command installs an unconstrained version of the `requests` package from the user's configured Python package index. The project supplies no lock file, exact version constraint, or package hash. Although `requests` is a legitimate and conventional package name, this installation method is non-reproducible. Future releases can alter behavior or introduce incompatibilities, and a compromised or maliciously configured package index could provide an unintended artifact. Without hash verification, users cannot confirm that the downloaded package is the exact artifact reviewed by the project maintainers. No evidence was found that the project intentionally references a malicious or typosquatted dependency. The risk arises from the unsafe installation guidance rather than from a confirmed malicious package. ### Attack Path 1. A user follows the README and executes `pip install requests`. 2. `pip` resolves an unconstrained package version from the user's configured index or mirror. 3. If that index, mirror, DNS path, account, or upstream release is compromised, an attacker-controlled artifact may be selected. 4. Malicious installation behavior may execute during package installation, or malicious runtime behavior may execute when either script imports `requests`. Successful exploitation therefore depends on compromise or malicious configuration of the dependency supply chain. ### Impact Assessment A malicious dependency executes with the privileges of the user running `pip` or the image-generation scripts. It could access that user's files, environment variables—including `WISGATE_KEY`—and networ ...[truncated 204 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Add a reviewed dependency manifest containing an exact compatible version of `requests` and all required transitive dependencies. 2. Generate and publish cryptographic hashes for every permitted distribution artifact. 3. Instruct users to install dependencies with hash verification, for example: ```bash python -m pip install --require-hashes -r requirements.txt ``` 4. Use a lock-file generation process that records exact transitive versions and is reproducible in CI. 5. Periodically scan locked dependencies for known vulnerabilities and review updates before regenerating hashes. 6. Recommend installation inside a dedicated virtual environment without elevated privileges. 7. Document the expected package index and discourage untrusted mirrors or additional package sources. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Memory PoisoningPersistent Context Injection, Context Window Stuffing, Memory Manipulation
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • Taint TrackingDirect Taint Flow, Variable-Mediated Taint Flow, Credential Exfiltration Chain
Findings (24)

Tainted flow: 'headers' from os.getenv (line 79, credential/environment) → requests.post (network output)

Critical
Category
Data Flow
Content
print(f"Using model: {model}", file=sys.stderr)
    
    try:
        response = requests.post(api_url, headers=headers, json=payload, timeout=60)
        response.raise_for_status()
        result = response.json()
Confidence
90% confidence
Finding
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Tainted flow: 'headers' from os.getenv (line 57, credential/environment) → requests.post (network output)

Critical
Category
Data Flow
Content
print(f"Using model: {model}", file=sys.stderr)
    
    try:
        response = requests.post(api_url, headers=headers, json=payload, timeout=60)
        response.raise_for_status()
        result = response.json()
Confidence
90% confidence
Finding
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding
The declared behavior understates what the skill actually does: it sends prompts and images to a third-party API, may enable external search grounding, and supports behavior not reflected in the public description. This mismatch can mislead users and orchestrators about data exposure and execution behavior, undermining informed consent and safe routing decisions.

Memory Manipulation

High
Category
Memory Poisoning
Content
- `prompt` (required): Refinement instruction or initial prompt
- `--history`: Conversation history file (default: `conversation.json`)
- `--output`: Output file path (default: `refined_image.png`)
- `--reset`: Reset conversation history and start fresh
- `--model`: Force specific model - `nano-banana`, `nano-banana-2`, `nano-banana-pro` (auto-select if not specified)
- `--quality`: Prioritize quality over cost (uses Nano Banana Pro)
Confidence
80% confidence
Finding
Skill manipulates agent memory, state, or stored context. Memory corruption can alter personality, override safety rules, or cause unpredictable behavior.

Memory Manipulation

High
Category
Memory Poisoning
Content
- `prompt` (required): Refinement instruction or initial prompt
- `--history`: Conversation history file (default: `conversation.json`)
- `--output`: Output file path (default: `refined_image.png`)
- `--reset`: Reset conversation history and start fresh
- `--model`: Force specific model - `nano-banana`, `nano-banana-2`, `nano-banana-pro` (auto-select if not specified)
- `--quality`: Prioritize quality over cost (uses Nano Banana Pro)
Confidence
80% confidence
Finding
Skill manipulates agent memory, state, or stored context. Memory corruption can alter personality, override safety rules, or cause unpredictable behavior.

Memory Manipulation

High
Category
Memory Poisoning
Content
- `prompt` (required): Refinement instruction or initial prompt
- `--history`: Conversation history file (default: `conversation.json`)
- `--output`: Output file path (default: `refined_image.png`)
- `--reset`: Reset conversation history and start fresh
- `--model`: Force specific model - `nano-banana`, `nano-banana-2`, `nano-banana-pro` (auto-select if not specified)
- `--quality`: Prioritize quality over cost (uses Nano Banana Pro)
Confidence
80% confidence
Finding
Skill manipulates agent memory, state, or stored context. Memory corruption can alter personality, override safety rules, or cause unpredictable behavior.

Memory Manipulation

High
Category
Memory Poisoning
Content
args = parser.parse_args()
    
    # Reset history if requested
    if args.reset and os.path.exists(args.history):
        os.remove(args.history)
        print(f"Conversation history reset: {args.history}")
Confidence
80% confidence
Finding
Skill manipulates agent memory, state, or stored context. Memory corruption can alter personality, override safety rules, or cause unpredictable behavior.

Missing User Warnings

Medium
Confidence
96% confidence
Finding
The README states that prompts and images are processed via the Wisdom Gate API, but it does not explicitly warn users that their text and uploaded images are transmitted to a third-party service. In an image-generation skill, users may supply sensitive personal photos or confidential visual material, so lack of disclosure can lead to unintended data exposure and privacy/compliance issues.

External Transmission

Medium
Category
Data Exfiltration
Content
**Endpoint:**
```
https://api.wisgate.ai/v1beta/models/{model}:generateContent
```

**Authentication** (either header works):
Confidence
89% confidence
Finding
The README documents a hardcoded external API endpoint, confirming that skill data is transmitted off-host to a remote service. External transmission is expected for this skill's purpose, but it still carries security significance because prompts, images, and possibly derived metadata leave the local trust boundary.

Lp3

Medium
Category
MCP Least Privilege
Confidence
93% confidence
Finding
The skill documentation indicates capabilities that require environment access, file I/O, and outbound network use, but it does not declare any tool scope or permissions. This weakens reviewability and least-privilege controls, making it easier for a skill to access sensitive files, API keys, or transmit data externally without clear user or platform visibility.

Vague Triggers

Medium
Confidence
95% confidence
Finding
The trigger language is broad enough that the skill could activate on common image-related requests without the user realizing their content will be sent to an external service. In this context, unintended invocation matters because prompts and possibly local images may be transmitted off-platform, increasing privacy and data-handling risk.

Missing User Warnings

Medium
Confidence
97% confidence
Finding
The skill lacks a clear warning that user prompts and uploaded images are transmitted to an external API provider. This is dangerous because users may include sensitive personal, proprietary, or regulated content in prompts or source images without realizing that data leaves the local environment.

External Transmission

Medium
Category
Data Exfiltration
Content
**API Endpoint Format:**
```
https://api.wisgate.ai/v1beta/models/{model}:generateContent
```

**Authentication:**
Confidence
90% confidence
Finding
The skill explicitly uses an external endpoint, which means prompts, images, and associated metadata may be transmitted outside the local trust boundary. External transmission is expected for this skill's function, but it remains security-relevant because it creates privacy, confidentiality, and supply-chain exposure if not clearly declared and constrained.

Description-Behavior Mismatch

Medium
Confidence
94% confidence
Finding
For text-to-image requests, the code silently enables a Google search tool by adding `payload["tools"] = [{"google_search": {}}]`, but the skill metadata only describes image generation and editing. This creates an undisclosed data flow and capability expansion: user prompts may be used to trigger external search behavior that users and integrators would not reasonably expect from the manifest.

Context-Inappropriate Capability

Medium
Confidence
90% confidence
Finding
An external search capability is enabled in an image-generation skill without any demonstrated need in the implementation or description. Because prompts may contain sensitive user content, enabling search broadens third-party processing and retrieval behavior beyond the minimum required for image generation, increasing privacy and policy risk.

Missing User Warnings

Medium
Confidence
92% confidence
Finding
The script transmits user prompts and optional input images to a remote API, but there is no explicit warning, consent, or disclosure in the skill description about that data leaving the local environment. In the context of an image-editing skill, users may upload private images, making undisclosed third-party transmission materially more sensitive.

External Transmission

Medium
Category
Data Exfiltration
Content
if not model:
        model = MODELS["nano-banana-pro"]["id"] if quality_priority else MODELS["nano-banana-2"]["id"]
    
    api_url = f"https://api.wisgate.ai/v1beta/models/{model}:generateContent"
    
    headers = {
        "x-goog-api-key": api_key,
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

External Transmission

Medium
Category
Data Exfiltration
Content
if not model:
        model = MODELS["nano-banana-pro"]["id"] if quality_priority else MODELS["nano-banana-2"]["id"]
    
    api_url = f"https://api.wisgate.ai/v1beta/models/{model}:generateContent"
    
    headers = {
        "x-goog-api-key": api_key,
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

External Transmission

Medium
Category
Data Exfiltration
Content
print(f"Using model: {model}", file=sys.stderr)
    
    try:
        response = requests.post(api_url, headers=headers, json=payload, timeout=60)
        response.raise_for_status()
        result = response.json()
Confidence
80% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

External Transmission

Medium
Category
Data Exfiltration
Content
print(f"Using model: {model}", file=sys.stderr)
    
    try:
        response = requests.post(api_url, headers=headers, json=payload, timeout=60)
        response.raise_for_status()
        result = response.json()
Confidence
80% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Missing User Warnings

Medium
Confidence
93% confidence
Finding
The script sends the full conversation history, including prior user prompts and model responses, to a remote API on every refinement request without any explicit user warning, consent flow, or minimization. In an agent setting, prior turns may contain sensitive text, file-derived content, or operational context that users may not expect to be retransmitted to a third party.

Missing User Warnings

Low
Confidence
91% confidence
Finding
The README notes that conversation history is saved to JSON and can be resumed, but it does not warn that prompt history may persist locally on disk. This can expose sensitive prompts or workflow context to other local users, backups, source-control mistakes, or forensic recovery if operators assume the data is ephemeral.

Intent-Code Divergence

Low
Confidence
90% confidence
Finding
The documentation states the required environment variable is `WISGATE_KEY` at L062, but later the workflow says to check `WISDOM_GATE_KEY` at L139. This is an active documentation contradiction about how the skill is configured, which can cause users to provide credentials in the wrong place and fail to use the skill as described.

Intent-Code Divergence

Low
Confidence
88% confidence
Finding
The module and CLI documentation say the tool generates images via Gemini models, but omit that some requests also enable Google search. While this is primarily a transparency issue rather than direct code execution risk, the omission can mislead users about where their prompts may go and what external capabilities are in use.

Static analysis

No suspicious patterns detected.