Skip to content

fix(security): validate --output-dir against path traversal (#30) - #34

Open
seojcarlos wants to merge 1 commit into
AgriciDaniel:mainfrom
seojcarlos:fix/issue-30-output-dir-traversal
Open

fix(security): validate --output-dir against path traversal (#30)#34
seojcarlos wants to merge 1 commit into
AgriciDaniel:mainfrom
seojcarlos:fix/issue-30-output-dir-traversal

Conversation

@seojcarlos

@seojcarlos seojcarlos commented Apr 27, 2026

Copy link
Copy Markdown

Summary

run_batch() strips path components from per-job
filenames but does not validate --output-dir itself,
so a path that resolves outside CWD (e.g.
/tmp/../../etc/cron.d) gets created and written into.

This is defense in depth, not RCE — exploitation
requires the script to be invoked by an orchestrator
forwarding user-influenced arguments (e.g. an agent or
wrapper). A direct shell user can already write wherever
their account allows.

Type of Change

  • Bug fix

What this PR does

  • Adds _validate_output_dir(output_dir) that resolves
    the path and rejects anything outside the working
    directory.
  • CLAUDE_ADS_OUTPUT_BASE env var as the escape hatch
    when an absolute path outside CWD is intentional.
  • Uses Path.resolve() + relative_to() (matches your
    option 2 in the issue).
  • run_batch swaps Path(output_dir) for the validated
    path; nothing else in the function is touched.

Tests

7 regression tests in
tests/test_output_dir_validation.py using stdlib
unittest — no new deps. Covers the reporter's exact
reproduction, relative traversal, absolute paths outside
CWD, dot-path acceptance, the env override, and the env
override not allowing escape above the configured base.

CI doesn't run tests today (only py_compile /
pip-audit). Happy to wire python -m unittest discover
into CI in a follow-up.

Checklist

  • No credentials or API keys in the diff
  • Backward compatible (existing relative output dirs
    keep working)
  • [N/A] SKILL.md / reference files (none touched)
  • [N/A] Shell scripts (none touched)
  • [N/A] Tested against a sample ad account (no platform
    code touched)

Reproducer (pre-fix)

  python scripts/generate_image.py --batch

prompts.json --output-dir /tmp/../../etc/cron.d
# Pre-fix: creates /etc/cron.d and writes images
there.
# Post-fix: errors out with clear message and exits
1.

Closes #30

…niel#30)

run_batch() already strips path components from per-job filenames but did
not validate the --output-dir argument itself, so a path that resolves
outside CWD (e.g. "/tmp/../../etc/cron.d") would be created and written
into.

This is defense in depth — exploitation requires the script to be invoked
by an orchestrator that forwards user-influenced arguments (e.g. an
agent), not a direct shell user who already has write access.

Adds _validate_output_dir() that resolves the path and rejects anything
outside the working directory. Set CLAUDE_ADS_OUTPUT_BASE to override the
base when an absolute path outside CWD is intentional.

Includes regression tests using stdlib unittest (no new dependencies).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Security: Potential path traversal in batch mode output_dir (generate_image.py)

1 participant