Skip to content

Frozen/archived file guard ignores writes made through a share #4649

Open

Description

@WilcoLouwerse

Found during a Strict review of #4580 (development → beta promotion). Present on development; introduced in #4549.

Impact

Files of a frozen or archived object can still be edited, renamed or deleted by any user they are shared with, because the guard only recognises the OpenRegister app user's own path. Fires on the happy path for shared files.

The chain

refusalFor() returns null unless $node->getPath() starts with /openregister/files/. A node reached through a share (the way a person gets at an object's files: FileSharingHandler shares the file or folder to users with permissions 31, which include update and delete) has the path of the sharee's own tree, e.g. /alice/files/<share name>/x.pdf, because Nextcloud's HookConnector::getNodeForPath() builds the node from the acting user's view ($info->getPath()). So a WebDAV PUT, DELETE or MOVE by alice into a frozen or archived object's shared folder is never matched and goes through, which is the scenario REQ-OAS-007 names ("a user overwrites that file through WebDAV"). For a share of a single file there is no UUID-named ancestor at all, so even with the prefix test relaxed the ancestor walk would find nothing. Only writes made as the openregister account itself (internal callers) are covered. Introduced in #4549.

lib/Listener/FrozenNodeWriteListener.php:172 — verified against b9e5d8df40.

Reproduction

Verified by reading the code at b9e5d8df40; not executed (no vendor tree, no rig in the review).
Verification: grep -n "str_starts_with(\$path, \$home)" lib/Listener/FrozenNodeWriteListener.php; grep -n "TYPE_USER" lib/Service/File/FileSharingHandler.php; grep -n "getPath()" /home/wilco/nextcloud-docker-dev/workspace/server/lib/private/Files/Node/HookConnector.php → prefix test on the node path; files are shared with TYPE_USER; hook nodes are built from $info->getPath() of the acting user's view

Suggested direction

resolve the owner by file id instead of by path: take $node->getId() (for a node that does not exist yet, its parent's id), look the node up in rootFolder->getUserFolder(FileOwnershipHandler::APP_USER)->getById($id), and run the existing UUID-ancestor + getFolder() proof on that node. Keep the cheap early exit by testing $node->getOwner()?->getUID() or the mount instead of the path.

Done when: a unit test feeds BeforeNodeWrittenEvent, BeforeNodeDeletedEvent and BeforeNodeRenamedEvent with a node whose getPath() is /alice/files/<share>/x.pdf (owner openregister, same file id as the frozen object's folder child) and the listener aborts all three (seam: FrozenNodeWriteListenerTest::testAWriteThroughAShareeMountIsAborted); a Newman PUT as the sharee against a frozen object's file answers 4xx.

Review thread

#4580 (comment)

Fix

Proposed in #4655 (not merged yet).

Activity

  1. added
    bugSomething isn't working
    securityA user can read or write what they must not
    on Oct 15, 2026
  2. github-actions commented on Oct 15, 2026

    @github-actions
    Contributor

    Thank you for opening this issue! It has been assigned to @rubenvdlinde for review.

    What happens next:

    1. A maintainer will review this issue and determine the appropriate action
    2. If accepted, it may be converted into an OpenSpec change for structured development
    3. You will be notified of any status changes

    Triage labels:

    • triage:accept-feature — accepted as a new feature
    • triage:accept-bugfix — accepted as a bug fix
    • triage:needs-info — more information needed
    • triage:wontfix — will not be addressed
    • triage:duplicate — duplicate of another issue
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

bugSomething isn't workingsecurityA user can read or write what they must nottriageAwaiting triage

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions