Skip to content

Store sound uploads under a generated name (#699) - #982

Merged
pablodiegoss merged 4 commits into
developfrom
feature/699-uuid-media-filenames
Oct 26, 2026
Merged

pablodiegoss merged 4 commits into
developfrom
feature/699-uuid-media-filenames

Conversation

@vjpixel

@vjpixel vjpixel commented Sep 17, 2026 •

Copy link
Copy Markdown
Member

Description

@pablodiegoss's reason for keeping #699 open is the one that matters, and it's stronger than the S3-escaping framing the issue opens with: a user-supplied filename becomes part of a public URL, so nothing stops someone uploading a file whose name is NSFW, political or religious and having the platform serve it. That is the argument this PR is built on.

Checking what is actually stored today, most of the surface is already covered:

Field Stored as Status
Object.source objects/<pk>/source.<ext> safe (object_source_path)
Marker.source markers/<pk>/original.<ext> safe, with the caveat below
Sound.file sounds/<the user's filename> the remaining exposure

So this PR is narrow on purpose: Sound.file now routes through sound_file_path(), which writes sounds/<uuid>.<ext>. A uuid rather than the <pk>/<role> layout because the primary key does not exist yet when Django generates the upload path for a new row — and, as the issue notes, it also removes collisions.

Sound.file_name_original already existed and is already populated by SoundForm.save(), so the "keep the original name" half of the issue needed nothing.

Resolves (Issues)

Closes #699

General tasks performed

  • Added sound_file_path() next to the existing object_*_path helpers, lowercasing the extension and tolerating a name without one.
  • Pointed Sound.file at it; migration 0045_alter_sound_file_alter_soundevent_file covers the model and its pghistory event model.
  • Added src/core/tests/test_sound_upload_path.py — a hostile filename (Música #1 (versão final?) 100%.mp3) never reaching storage, the original name still recorded, two identical names not colliding, and the helper's extension handling.

A related gap, not fixed here

Markers are only eventually safe. The upload first lands under the user's own filename, and marker_utils.py:97-101 deletes it afterwards — inside a bare except Exception: pass. If that delete ever fails (S3 permissions, a transient error), the user-named object stays in public storage and nobody is told. Same privacy exposure, different code path; worth making that failure loud, but it is outside this PR's diff.

Deliberately not included

The backfill. The issue asks for "a migration to rename all the objects in the current database". That renames live S3 objects, and a half-applied run leaves rows pointing at files that no longer exist. It wants to be a resumable management command rather than a migration, and an ops decision about the window — so I left it out. Existing rows keep working untouched; only new uploads take the new path.

Verification: full suite passes, 277 passed. Reverting upload_to to the old string makes test_hostile_filename_never_reaches_storage fail, so the test does exercise the change. makemigrations --check --dry-run reports no changes; both CI lint steps are clean.

Have you confirmed the application builds locally without error? See here.

  • Yes

🤖 Generated with Claude Code

https://claude.ai/code/session_01HXUw7kyELDu8ycGQxtaFbb


Generated by Claude Code

Sound was the last media field writing the user's own filename straight to
storage: objects already land in objects/<pk>/source.<ext> and markers are
renamed to markers/<pk>/original.<ext> by generate_marker_variants.

Sound.file now routes through sound_file_path(), which writes
sounds/<uuid>.<ext>. A uuid rather than the <pk>/<role> layout because the
primary key does not exist yet when the path is generated for a new row.
The original name was already kept on Sound.file_name_original.

Closes #699

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HXUw7kyELDu8ycGQxtaFbb
The Linter workflow's `ruff format --diff src/` step failed on this file:
the SimpleUploadedFile call exceeded the line limit. No behaviour change.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HXUw7kyELDu8ycGQxtaFbb

vjpixel commented Oct 14, 2026

Copy link
Copy Markdown
Member Author

@pablodiegoss você pode revisar este PR quando tiver um tempo?


Generated by Claude Code

@pablodiegoss pablodiegoss left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@pablodiegoss
pablodiegoss merged commit c372b9b into develop Oct 26, 2026
8 checks passed
@pablodiegoss
pablodiegoss deleted the feature/699-uuid-media-filenames branch October 26, 2026 20:41
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.

Save media content with random names

3 participants