BLOCKER: recheck file-drop Sidecar authorization at final installation #801

Open
opened 2026-10-02 13:12:25 +00:00 by kayg · 2 comments
Owner

Source finding from the sec-fs audit requested on #663. No live-server reproduction was run.

Context and evidence

Source: c4a61e8cf0 (origin/dev). The same boundary remains in round 7a at 2f4482ded0.

DESIGN §22 states that a file drop can add files, but cannot add an XMP Sidecar to content that already exists. A Sidecar changes the content item's tags or rating, so that operation requires edit access.

  • crates/plugins/files/src/public.rs:1920–1950: public_upload_create checks for a matching existing parent when accepting an XMP filename. This check runs at creation, outside the final install lock.
  • crates/plugins/files/src/uploads.rs:1229–1275: finish holds the mutation lock, rechecks the link identity and upload permission, and excludes replacement.
  • crates/plugins/files/src/uploads.rs:1385–1534: finalization checks scope and destination absence and then installs the staged file. It never checks whether the final XMP name now describes existing content. The finalization module contains no Sidecar guard.
  • The upload is resumable and the namespace may change between creation and final install. Its final parent is resolved by item identity, so the final check must use that parent and the final collision-policy filename.

Reasoned impact

The creation-time result is not an authorization guarantee for a later namespace state. A file-drop installation can publish metadata for content that exists at finalization, despite having only upload permission. This permits a metadata edit outside the file drop's grant. No resulting tag/rating change is claimed as reproduced.

Concrete fix

Extract the existing parent/Sidecar rule into one reused check. Recheck it under the mutation lock immediately before installation, against the actual final destination after parent-identity resolution and collision naming. Retain the early check for a fast response. Fail closed if the parent listing fails. Include any Sidecar format that the metadata indexer uses to modify a content item's human metadata.

Defensive tests

Add authorization tests that use small inert content and Sidecar files. Assert that an upload-only grant never publishes a Sidecar for content present at finalization, that rejection releases staging and quota reservations, and that an ordinary new content file can still finish. Cover both full-filename and Lightroom Sidecar naming and changed parent identity/path. Run the standard local authorization checks after the fix; do not add an autonomous attack tool.

Duplicate search: all-state sidecar search. #420 covers moving/hiding paired Sidecars, #12 builds file drops, and #663 is the audit parent. No existing issue covers finalization-time Sidecar authorization.

Source finding from the sec-fs audit requested on #663. No live-server reproduction was run. ## Context and evidence Source: c4a61e8cf090170f35b1bed3350d9de20c83ecd5 (`origin/dev`). The same boundary remains in round 7a at 2f4482ded066d9c5d9c59130377907f7fd2916c9. DESIGN §22 states that a file drop can add files, but cannot add an XMP Sidecar to content that already exists. A Sidecar changes the content item's tags or rating, so that operation requires edit access. - `crates/plugins/files/src/public.rs:1920–1950`: `public_upload_create` checks for a matching existing parent when accepting an XMP filename. This check runs at creation, outside the final install lock. - `crates/plugins/files/src/uploads.rs:1229–1275`: `finish` holds the mutation lock, rechecks the link identity and upload permission, and excludes replacement. - `crates/plugins/files/src/uploads.rs:1385–1534`: finalization checks scope and destination absence and then installs the staged file. It never checks whether the final XMP name now describes existing content. The finalization module contains no Sidecar guard. - The upload is resumable and the namespace may change between creation and final install. Its final parent is resolved by item identity, so the final check must use that parent and the final collision-policy filename. ## Reasoned impact The creation-time result is not an authorization guarantee for a later namespace state. A file-drop installation can publish metadata for content that exists at finalization, despite having only upload permission. This permits a metadata edit outside the file drop's grant. No resulting tag/rating change is claimed as reproduced. ## Concrete fix Extract the existing parent/Sidecar rule into one reused check. Recheck it under the mutation lock immediately before installation, against the actual final destination after parent-identity resolution and collision naming. Retain the early check for a fast response. Fail closed if the parent listing fails. Include any Sidecar format that the metadata indexer uses to modify a content item's human metadata. ## Defensive tests Add authorization tests that use small inert content and Sidecar files. Assert that an upload-only grant never publishes a Sidecar for content present at finalization, that rejection releases staging and quota reservations, and that an ordinary new content file can still finish. Cover both full-filename and Lightroom Sidecar naming and changed parent identity/path. Run the standard local authorization checks after the fix; do not add an autonomous attack tool. Duplicate search: all-state sidecar search. #420 covers moving/hiding paired Sidecars, #12 builds file drops, and #663 is the audit parent. No existing issue covers finalization-time Sidecar authorization.
Author
Owner

#801 implementation: creation and completion now share one additive-file-drop guard. The guard uses calternal-fs Sidecar naming, including Apple AAE, keeps the existing case-insensitive XMP rule, and denies listing errors. Completion runs it under the existing mutation lock after stable parent identity and collision-name resolution, before install intent or bytes are published. Rejection uses the existing staging/row/reservation cleanup.

The regression covers a parent file added after upload creation, a renamed parent folder, and a collision that selects a different Sidecar filename. Existing assertions are unchanged. The corrected old-code regression build is still running on the loaded host; final gates are queued per crate.

#801 implementation: creation and completion now share one additive-file-drop guard. The guard uses calternal-fs Sidecar naming, including Apple AAE, keeps the existing case-insensitive XMP rule, and denies listing errors. Completion runs it under the existing mutation lock after stable parent identity and collision-name resolution, before install intent or bytes are published. Rejection uses the existing staging/row/reservation cleanup. The regression covers a parent file added after upload creation, a renamed parent folder, and a collision that selects a different Sidecar filename. Existing assertions are unchanged. The corrected old-code regression build is still running on the loaded host; final gates are queued per crate.
Author
Owner

The final-install Sidecar permission check and five regression cases are committed. The corrected fixture refreshes the folder Index after raw writes. Its final test and a valid old-code negative run remain unverified; initial old-code attempts failed due fixture URI/token errors. Head: bd6e97e09b. Full handoff and verbatim output are on #779. No push or deploy.

The final-install Sidecar permission check and five regression cases are committed. The corrected fixture refreshes the folder Index after raw writes. Its final test and a valid old-code negative run remain unverified; initial old-code attempts failed due fixture URI/token errors. Head: bd6e97e09b56c8df6ae770f5bb440aaf2d9d8f36. Full handoff and verbatim output are on #779. No push or deploy.
Sign in to join this conversation.
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
kayg/calternal#801
No description provided.