Back to skill

Security audit

Nano Banana Pro Enhanced

Security checks for vulnerabilities and agentic risk

Overview

This image-generation skill appears legitimate, but its batch mode stores prompts and job data in a predictable local file and can later write results to paths from that stored data.

Install only if you are comfortable with prompts, filenames, and batch job details being stored locally until cleanup. Prefer running it in a restricted workspace, avoid sensitive prompts in batch mode, use explicit safe output directories, and review or clear pending job and HEARTBEAT entries after use.

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)

T08 · Insecure Dependencies

Warning
Location
scripts/generate_image.py:2
Finding
Unpinned Runtime Dependencies Create a Supply-Chain Risk## Vulnerability Details **File Location**: `scripts/generate_image.py:2-7` **Vulnerability Type**: Unpinned third-party runtime dependencies **Risk Level**: Medium ### Vulnerable Code ```python # /// script # requires-python = ">=3.10" # dependencies = [ # "google-genai>=1.0.0", # "pillow>=10.0.0", # ] # /// ``` ### Technical Analysis The inline dependency metadata specifies only lower version bounds for `google-genai` and `pillow`. The documented execution method uses `uv run`, which may resolve and install any current or future package release satisfying these constraints. Consequently, the code reviewed during the audit is not guaranteed to execute with the same dependency code on subsequent runs. A compromised upstream release, maliciously modified distribution artifact, or newly introduced vulnerability in a later compatible version could be incorporated automatically. The script imports and executes these dependencies: ```python from google import genai from google.genai import types from PIL import Image as PILImage ``` Dependency code executes with the same operating-system privileges as the user invoking the Skill. It may consequently access the Gemini API key, prompts, input images, output files, and other files available to that user. This finding does not establish that the currently published dependency versions are malicious. The vulnerability is the absence of immutable, reviewed dependency resolution. ### Attack Path 1. An upstream dependency account or package distribution channel is compromised, or a vulnerable future release is published. 2. The malicious or vulnerable release retains a version compatible with `google-genai>=1.0.0` or `pillow>=10.0.0`. 3. A user invokes the documented `uv run` command without an existing immutable lock state. 4. `uv` resolves and installs the affected release. 5. The package code executes when imported or used by the image-generati ...[truncated 822 chars]
Remediation
## Remediation Suggestions 1. Pin dependencies to exact, reviewed versions rather than open-ended lower bounds: ```python # dependencies = [ # "google-genai==REVIEWED_VERSION", # "pillow==REVIEWED_VERSION", # ] ``` 2. Generate and commit an immutable `uv.lock` file. 3. Require locked execution and fail if dependency resolution would modify the lockfile. 4. Verify package hashes where supported so altered distribution artifacts are rejected. 5. Use an automated dependency update process that performs security scanning, compatibility testing, and human review before changing pinned versions. 6. Run the Skill in a restricted environment with only the required filesystem and network access. 7. Avoid passing the API key through `--api-key` where it may be exposed in process listings or shell history; prefer a protected environment or secret-injection facility.

T09 · Insecure Skill Coding Practices

Error
Location
scripts/generate_image.py:29
Finding
Predictable Shared Temporary State Allows Batch Metadata Disclosure and Tampering## Vulnerability Details **File Location**: `scripts/generate_image.py:29, 34-45, 48-65, 76-83, 317-324` **Vulnerability Type**: Unsafe predictable temporary-state file and insufficient path validation **Risk Level**: High ### Vulnerable Code The state path is calculated by traversing four parents from the script file. In the audited project layout, this resolves to `/tmp/memory/pending-batch-jobs.json`. ```python MODEL = "gemini-3-pro-image-preview" PENDING_JOBS_PATH = Path(__file__).resolve().parent.parent.parent.parent / "memory" / "pending-batch-jobs.json" ``` Pending state is loaded without validating file ownership, permissions, symlink status, or record structure: ```python def _load_pending_jobs() -> list[dict]: """Load pending batch jobs list from memory.""" if PENDING_JOBS_PATH.exists(): try: return json.loads(PENDING_JOBS_PATH.read_text()) except (json.JSONDecodeError, OSError): return [] return [] ``` The state is written directly to the predictable location without exclusive creation, atomic replacement, restrictive permissions, symlink protection, or locking: ```python def _save_pending_jobs(jobs: list[dict]) -> None: """Save pending batch jobs list to memory.""" PENDING_JOBS_PATH.parent.mkdir(parents=True, exist_ok=True) PENDING_JOBS_PATH.write_text(json.dumps(jobs, indent=2, ensure_ascii=False) + "\n") ``` User prompts and output filenames are persisted in that file: ```python def add_pending_job(job_name: str, filenames: str | list[str], prompt: str | None = None) -> None: """Register a batch job as pending. filenames can be single string or list of strings.""" jobs = _load_pending_jobs() # Avoid duplicates if any(j["job_name"] == job_name for j in jobs): return # Normalize filenames to list if isinstance(filenames, str): filenames = [filenames] jobs.app ...[truncated 4671 chars]
Remediation
## Remediation Suggestions 1. Do not derive persistent state by traversing to a shared temporary directory. Use an application-specific user data directory, such as a protected directory under the user's configured state location. 2. If temporary storage is required, create a unique private directory using `tempfile.mkdtemp()` or an equivalent secure API. 3. Create the state directory with mode `0700` and the state file with mode `0600`. 4. Verify that the directory and file: - Are owned by the current user. - Are not symbolic links. - Are regular files or directories of the expected type. - Do not have group or world write permissions. 5. Use no-follow and exclusive-creation semantics where supported, such as `O_NOFOLLOW`, `O_CREAT`, and `O_EXCL`. 6. Write updates to a securely created temporary file in the same directory, flush and synchronize it, then atomically replace the state file. 7. Add inter-process locking around read-modify-write operations. 8. Validate the complete JSON schema before using records. Require `job_name` to be a string and `filenames` to be a bounded list of strings. 9. Treat persisted filenames as untrusted. Resolve each output path against an explicitly approved output directory and reject: - Absolute paths. - `..` traversal. - Paths escaping the approved directory. - Symbolic-link destinations. 10. Do not persist full prompts unless necessary. If they must be retained, document the retention behavior, minimize retention time, and protect the records as sensitive data. 11. Remove pending records only after result processing succeeds; the current removal before processing can lose state when output handling fails.
Vulnerability Patterns
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
Findings (4)

Credential Access

High
Category
Privilege Escalation
Content
def get_api_key(provided_key: str | None) -> str | None:
    """Get API key from argument first, then environment."""
    if provided_key:
        return provided_key
    return os.environ.get("GEMINI_API_KEY")
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Lp3

Medium
Category
MCP Least Privilege
Confidence
89% confidence
Finding
The skill invokes a bundled Python script that uses environment variables, reads and writes local files, and makes networked API calls, yet the manifest does not declare any tool scope or allowed-tools boundary. This weakens least-privilege controls and makes it harder for the host agent or reviewer to understand and constrain what the skill is permitted to access.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The batch workflow instructs the agent to copy the user's request context and intent into HEARTBEAT.md, which can persist sensitive prompts, personal details, or confidential business context beyond the immediate task. Because the skill description does not warn users about this retention, users may unknowingly disclose private information into a general tracking file that could be read by other skills, sessions, or operators.

Missing User Warnings

Medium
Confidence
93% confidence
Finding
The skill documents persistent storage of pending batch job metadata in memory/pending-batch-jobs.json, including job name, filename, prompt, and creation time, without warning the user that their prompts are retained locally. Prompts for image generation often contain sensitive or proprietary information, so undisclosed retention increases privacy and data leakage risk if the workstation, repo, or agent memory is later accessed.

Static analysis

No suspicious patterns detected.