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:
@@ -422,13 +422,17 @@ pub async fn handle_search(
|
||||
|
||||
let mut entries: Vec<serde_json::Value> = Vec::new();
|
||||
|
||||
// Map file results
|
||||
// TODO(D1): drop the hardcoded "Personal/" prefix and read the
|
||||
// caller's default-drive root folder name from `drives.root_folder_id`
|
||||
// instead. Correct for D0-provisioned default drives; secondary
|
||||
// drives keep their original root name.
|
||||
// Map file results.
|
||||
//
|
||||
// `strip_drive_root_segment` handles both default and secondary
|
||||
// drives — post-D0 the first path segment is the drive's root
|
||||
// folder name (`"Personal"` for D0-provisioned defaults, the
|
||||
// original sibling-root name for M2 backfilled secondaries).
|
||||
// Read-scope is upstream in `state.applications.search_service`;
|
||||
// this handler only formats display paths.
|
||||
for file in &results.files {
|
||||
let display_path = file.path.strip_prefix("Personal/").unwrap_or(&file.path);
|
||||
let display_path =
|
||||
crate::interfaces::nextcloud::webdav_handler::strip_drive_root_segment(&file.path);
|
||||
let display_path = format!("/{}", display_path);
|
||||
|
||||
let numeric_id = file_id_map.get(&file.id).copied();
|
||||
@@ -452,12 +456,10 @@ pub async fn handle_search(
|
||||
}));
|
||||
}
|
||||
|
||||
// Map folder results — same TODO(D1) as above.
|
||||
// Map folder results — same drive-agnostic strip as above.
|
||||
for folder in &results.folders {
|
||||
let display_path = folder
|
||||
.path
|
||||
.strip_prefix("Personal/")
|
||||
.unwrap_or(&folder.path);
|
||||
let display_path =
|
||||
crate::interfaces::nextcloud::webdav_handler::strip_drive_root_segment(&folder.path);
|
||||
let display_path = format!("/{}", display_path);
|
||||
|
||||
entries.push(json!({
|
||||
|
||||
Reference in New Issue
Block a user