Several API endpoints have missing or insufficient authorization checks, allowing authenticated users to access or modify resources belonging to other users or groups.
Finding 1: Shared Node Access — Scope Mismatch + Missing Ownership Check
File: papermerge/core/features/shared_nodes/router.py
The PATCH /shared-nodes/access/{node_id} endpoint (line 103) has two issues:
Scope mismatch (line 109): The endpoint requires scopes.SHARED_NODE_VIEW but should require scopes.SHARED_NODE_UPDATE (as stated in the docstring at line 104). Users with only VIEW permissions can modify shared node access.
Scope mismatch (line 109): The endpoint requires scopes.SHARED_NODE_VIEW but should require scopes.SHARED_NODE_UPDATE (as stated in the docstring at line 104). Users with only VIEW permissions can modify shared node access.
Missing ownership check (lines 103-123): Neither the PATCH nor the GET /shared-nodes/access/{node_id} verifies that the requesting user owns the node. Any authenticated user with SHARED_NODE_VIEW scope can view who has access to any node and modify sharing permissions on any node.
Missing ownership check (lines 103-123): Neither the PATCH nor the GET /shared-nodes/access/{node_id} verifies that the requesting user owns the node. Any authenticated user with SHARED_NODE_VIEW scope can view who has access to any node and modify sharing permissions on any node.
Compare with the nodes router which correctly uses user_can_access_resource() checks.
Finding 2: Tags /all — Missing Group Membership Check
File: papermerge/core/features/tags/router.py lines 43-72
The GET /tags/all endpoint accepts a group_id parameter. The docstring (lines 50-55) explicitly states a 403 should be raised if the user doesn't belong to the group, but the implementation skips this check.
Compare with the correct implementation in custom_fields/router.py lines 59-63:
Finding 3: Document Types /all — Missing Group Membership Check
File: papermerge/core/features/document_types/router.py lines 39-66
Same pattern as tags. The GET /document-types/all accepts group_id with docstring promising a 403 check, but the code directly queries without verifying group membership.
shared_nodes/router.py:109 — Change scopes.SHARED_NODE_VIEW to scopes.SHARED_NODE_UPDATE
shared_nodes/router.py:82-123 — Add ownership verification via ownership_api.user_can_access_resource()
tags/router.py:59-72 — Add user_dbapi.user_belongs_to() check (match custom_fields pattern)
document_types/router.py:55-62 — Add user_dbapi.user_belongs_to() check (match custom_fields pattern)
ciur self-assigned this on Feb 28, 2026
ciur added security on Feb 28, 2026
marfoerst added high confirmed on Sep 19, 2026
Jalst closed this as completed in #1324 on Sep 24, 2026
Jalst added a commit that references this issue on Sep 24, 2026 fix: authorization checks in shared nodes, tags, and document types ( #… 0fd4691
fix: authorization checks in shared nodes, tags, and document types ( #…
The full story
This article is one source in a clustered incident — the cluster page carries the summary, timeline and every other outlet covering it.
