security(nextcloud): chroot-aware display paths + recent race fix

strip_chroot_prefix replaces the hardcoded "Personal/" strip
    in NC trashbin PROPFIND, OCS unified search, and REPORT
    (favorites + search). Handles composed chroots, drops
    cross-chroot items instead of surfacing malformed paths, and
    fixes the leading-slash mismatch (FolderDto path has '/', DB
    paths don't) that silently dropped every NC trashbin item
    post-D3. OCS keeps a first-segment fallback (results
    legitimately span drives, no single chroot).

    uploads_handler switches to nc_to_internal_path(chroot, …)
    for the two remaining hardcoded "Personal/" sites, closing
    the D1 TODO markers.

    RecentService::record_item_access is split from a new
    record_item_access_internal (no authz) used by
    RecentRecordingHook. Round 1's authz.require widened the
    tokio::spawn race past tests/api/recent.hurl step 7; the
    internal path skips the redundant Read gate — upstream
    _with_perms already enforced it.

    Tests: 8 unit tests pin strip_chroot_prefix (leading slash,
    composed chroots, sibling-leak rejection, partial-prefix,
    empty-chroot). drives_membership.hurl step 21b/22b cover
    Editor upload → 201 / Viewer upload → 404 fresh + overwrite
    with fixture cleanup at 30c. test_nc_move_copy_delete_trash
    K1 pins the actual original-location value.
This commit is contained in:
Edouard Vanbelle
2026-07-05 22:52:26 +02:00
parent 0342bae300
commit 1786fe4111
8 changed files with 446 additions and 54 deletions
+14 -1
View File
@@ -323,7 +323,20 @@ grep -q 'g8-doomed' <<< "$BODY" \
|| fail "K1: g8-doomed.txt not in trashbin PROPFIND"
grep -q '<nc:trashbin-original-location>' <<< "$BODY" \
|| fail "K1: trashbin response missing <nc:trashbin-original-location>"
pass "K1: trashbin shows g8-doomed.txt with original-location"
# Post-D3 (secondary/shared drive support): the `original-location`
# value is drive-relative — the emitter strips the drive-root segment
# from the internal `storage.folders.path` (`"Personal/g8-doomed.txt"`
# for a file at the default drive root) so NC clients see
# `"g8-doomed.txt"` regardless of what the drive's root is named.
# Regression guard: the pre-D3 code hardcoded `strip_prefix("Personal/")`
# — a bug that would silently break secondary drives. Assert the
# stripped shape (no leading `Personal/`, no leading `/`, no drive
# segment).
grep -q '<nc:trashbin-original-location>g8-doomed\.txt</nc:trashbin-original-location>' <<< "$BODY" \
|| fail "K1: original-location not drive-relative (expected 'g8-doomed.txt', got: $(grep -o '<nc:trashbin-original-location>[^<]*</nc:trashbin-original-location>' <<< "$BODY"))"
pass "K1: trashbin shows g8-doomed.txt with drive-relative original-location"
# Extract the trashed item id (last segment of the href).
# Trashbin hrefs are `/remote.php/dav/trashbin/{user}/trash/{uuid}`