fix(webdav): reject PROPPATCH on protected DAV:/oc:/nc:/ocs: props
DeadPropertyStore let PROPPATCH set any namespace/name verbatim, incl. names the server itself emits as live state (DAV: entirely, plus oc:/nc:/ocs: names used by write_file_response / write_folder_response). That either forges a live prop or stores dead rows nothing ever reads. is_protected_property() denylists them; both PROPPATCH handlers (native + NC) now return per-property 403 instead of storing. oc:favorite stays writable via its existing special-case, which runs before the protection check.
This commit is contained in:
@@ -18,7 +18,7 @@ use quick_xml::Writer;
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::application::adapters::webdav_adapter::{
|
||||
LockInfo, PropFindRequest, PropPatchOp, QualifiedName, WebDavAdapter,
|
||||
LockInfo, PropFindRequest, PropPatchOp, QualifiedName, WebDavAdapter, is_protected_property,
|
||||
};
|
||||
use crate::application::dtos::file_dto::FileDto;
|
||||
use crate::application::dtos::folder_dto::FolderDto;
|
||||
@@ -807,6 +807,12 @@ async fn handle_proppatch(
|
||||
let mut results: Vec<(&QualifiedName, bool)> = Vec::new();
|
||||
for op in &ops {
|
||||
match op {
|
||||
PropPatchOp::Set(pv) if is_protected_property(&pv.name) => {
|
||||
results.push((&pv.name, false));
|
||||
}
|
||||
PropPatchOp::Remove(name) if is_protected_property(name) => {
|
||||
results.push((name, false));
|
||||
}
|
||||
PropPatchOp::Set(pv) => {
|
||||
dead_props
|
||||
.set(resource_ref, pv.name.clone(), pv.value.clone())
|
||||
|
||||
Reference in New Issue
Block a user