Repository navigation
[Security] nano-banana-pro follows output symlinks and overwrites files outside the working directory #46
Description
Activity
- addedP2Normal priority bug or improvement with limited blast radius.Normal priority bug or improvement with limited blast radius.clawsweeper:needs-security-reviewClawSweeper marked this issue as needing security-sensitive review.ClawSweeper marked this issue as needing security-sensitive review.clawsweeper:no-new-fix-prClawSweeper does not recommend queueing a new automated fix PR for this issue.ClawSweeper does not recommend queueing a new automated fix PR for this issue.clawsweeper:source-reproClawSweeper found a high-confidence source-level issue reproduction.ClawSweeper found a high-confidence source-level issue reproduction.impact:data-lossThis issue is about lost, corrupted, or silently dropped user/session/config data.This issue is about lost, corrupted, or silently dropped user/session/config data.impact:securityThis issue is about security boundaries, credentials, authz, sandboxing, or sensitive data.This issue is about security boundaries, credentials, authz, sandboxing, or sensitive data.issue-rating: 🦞 diamond lobsterVery strong issue quality with high-confidence source-level or clear reproduction.Very strong issue quality with high-confidence source-level or clear reproduction.
on Oct 7, 2026 Codex review: this still needs some work. Reviewed October 6, 2026, 11:19 PM ET / October 7, 2026, 03:19 UTC.
Summary
The reported overwrite mechanism remains reachable on current main and in release 0.12.0. This is an owned security defect; no open fixing PR was found.Reproducibility: yes. from source: a relative output symlink reaches Pillow's truncating filename writer when Gemini returns an image. No filesystem-writing reproduction or live API call was executed during this read-only review.
Next step
Security-sensitive filesystem hardening needs owner-led repair and adversarial validation before automated implementation intake.Security
Needs attention: Current source confirms an attacker-controlled output symlink can redirect a destructive image write.Review details
Best possible solution:
Use a race-resistant output writer that cannot follow attacker-controlled links, preserves explicitly selected output directories, and verifies that rejected destinations leave outside files untouched.
Do we have a high-confidence way to reproduce the issue?
Yes, from source: a relative output symlink reaches Pillow's truncating filename writer when Gemini returns an image. No filesystem-writing reproduction or live API call was executed during this read-only review.
Is this the best way to solve the issue?
Partly: rejecting redirected outputs addresses the defect, but standalone path checks are race-prone. Blanket working-directory confinement also conflicts with documented explicit output paths; safe file creation should enforce the intended destination at the write boundary.
AGENTS.md: found and applied where relevant.
Remaining risk / open question:
- A pre-save symlink or resolve check alone would remain vulnerable to path replacement races and symlinked parent directories.
Codex review notes: model internal, reasoning medium; reviewed against 79150cfac4a6.
Label changes
Label changes:
- add
P2: This is a concrete security defect in an optional image-generation skill, requiring an attacker-controlled destination and a successful image-generation run. - add
impact:data-loss: The filename-based writer truncates writable symlink targets and replaces their contents with image data. - add
impact:security: Repository-controlled symlinks can redirect a write to a destination outside the user's intended output location. - add
issue-rating: 🦞 diamond lobster: Current issue advisory state selects this label. - add
clawsweeper:source-repro: Current issue advisory state selects this label. - add
clawsweeper:no-new-fix-pr: Current issue advisory state selects this label. - add
clawsweeper:needs-security-review: Current issue advisory state selects this label.
Label justifications:
P2: This is a concrete security defect in an optional image-generation skill, requiring an attacker-controlled destination and a successful image-generation run.impact:security: Repository-controlled symlinks can redirect a write to a destination outside the user's intended output location.impact:data-loss: The filename-based writer truncates writable symlink targets and replaces their contents with image data.
Evidence reviewed
Security concerns:
- [high] Output symlinks redirect truncating writes —
skills/nano-banana-pro/scripts/generate_image.py:163
The image-save branches pass an unchecked filename to Pillow, which opens it for truncating writes. An attacker-controlled symlink can consequently overwrite any target writable by the invoking process.
Confidence: 0.99
What I checked:
- Current output path lacks protection: The script constructs the requested path and creates its parent directories without rejecting symlinks. All three image-mode branches pass that path directly to Pillow; resolve() runs only after saving. (
skills/nano-banana-pro/scripts/generate_image.py:92, 79150cfac4a6) - Dependency confirms destructive filename semantics: The target imports PIL.Image and calls its save method with a filename, establishing an affirmative dependency contract. Pillow's Image.save opens filename outputs with builtins.open(filename, "w+b"), which follows filesystem symlinks and truncates existing targets. It also supports caller-opened binary file objects, allowing the target to control safe file creation. (
src/PIL/Image.py:2706, 68a3fe957d8a) - Existing output-directory contract: The skill instructs agents to run from the user's working directory, recommends a new filename each run, and explicitly permits a specified output path containing a directory. Mandatory working-directory confinement would therefore change an existing documented capability. (
skills/nano-banana-pro/SKILL.md:118, 79150cfac4a6) - Latest release retains the unsafe writer: The 0.12.0 tag contains the same three filename-based save calls and post-save resolve operation. The supplied latest-release identity matches tag commit b7f7269; no fixed release is established. (
skills/nano-banana-pro/scripts/generate_image.py:161, b7f726924927) - Related-work check: GitHub REST listings returned this issue as the only open item and no open PR. The supplied merged reference, docs(codex-first): cover ChatGPT-app-bundled Codex on PATH #29, concerns launching an app-bundled Codex binary through symlinks, not image-output writes, and does not fix this report.
- Area history and inspection limitation: Available local history and GitHub commit metadata connect steipete to the skill's model update and earlier versioning work. The inspected model-update patch leaves the output writer unchanged. Local blame and deeper history searches failed because a required historical object could not be fetched (HTTP 403); vulnerability introduction was not established. (
skills/nano-banana-pro/scripts/generate_image.py:130, 27b549436127)
Likely related people:
- steipete: Suggested for follow-up; no historical authorship or introduction is verified. (role: unverified routing candidate; confidence: low)
How this review workflow works
- ClawSweeper keeps one durable marker-backed review comment per issue or PR.
- Re-runs edit this comment so the latest verdict, findings, and automation markers stay together instead of adding duplicate bot comments.
- A fresh review can be triggered by eligible
@clawsweeper re-reviewcomments, exact-item GitHub events, scheduled/background review runs, or manual workflow dispatch. - PR/issue authors and users with repository write access can comment
@clawsweeper re-reviewor@clawsweeper re-runon an open PR or issue to request a fresh review only. - Maintainers can also comment
@clawsweeper reviewto request a fresh review only. - Fresh-review commands do not start repair, autofix, rebase, CI repair, or automerge.
- Maintainer-only repair and merge flows require explicit commands such as
@clawsweeper autofix,@clawsweeper automerge,@clawsweeper fix ci, or@clawsweeper address review. - Maintainers can comment
@clawsweeper explainto ask for more context, or@clawsweeper stopto stop active automation.
- added a commit that references this issue
on Oct 10, 2026 Fixed in #47, merged as d9f70f4. Thanks for the clear report.
Reproduced with real Pillow writes and synthetic Gemini responses: the original script followed the output symlink into an outside sentinel file. The writer now creates outputs exclusively through pinned, no-follow directory handles, so existing files, hard links, dangling symlinks, and symlinked parent paths are rejected. Explicit output directories remain supported.
All ten offline regression tests pass, including leaf/parent replacement races and image conversion:
python3 -m unittest discover -s skills/nano-banana-pro/scripts -p 'test_*.py'Independent review found no actionable P0–P2 findings. Both PR CI and post-merge main CI passed. This fix is on main; no release was published in this round.
Hi, I found a symlink-following issue in the nano-banana-pro skill that can overwrite files outside the working directory.
GitHub: https://github.com/steipete/agent-scripts/tree/main/skills/nano-banana-pro
ClawHub: https://clawhub.ai/steipete/skills/nano-banana-pro
Summary
generate_image.pysaves the generated image to the path supplied with--filenamewithout rejecting symlinks or checking that the resolved path stays inside the working directory.If an untrusted repository contains
result.pngas a symlink to a file outside the repository, running the skill from that repository with--filename result.pngfollows the link and overwrites the target with PNG data. This can corrupt or destroy files that the OpenClaw process can write.Steps to reproduce
Create an isolated test directory:
From the workspace, run the skill with a valid Gemini API key available through
GEMINI_API_KEY:Check the target:
Expected behavior
The script should reject symlink outputs and any resolved output path outside the working directory.
Actual behavior
The script follows
result.pngand writes the generated PNG to/tmp/nbp-test/outside/victim.txt.Impact
An attacker who can provide or modify repository contents can place the output symlink in the repository. If a user or agent runs image generation from that repository using the symlink name, files outside the repository may be overwritten or corrupted, subject to the OpenClaw process's file permissions.
Affected versions
Confirmed in
nano-banana-pro1.0.0 and 1.0.1.Suggested fix
Reject symlink outputs and verify the resolved destination remains within the intended output directory before writing. Consider requiring an explicit overwrite option when the destination already exists.