CVE-2026-48052 Overview
CVE-2026-48052 is a broken access control vulnerability in Papra, an open-source minimalistic document management and archiving platform. Prior to version 26.5.0, any authenticated user who belongs to at least one organization can rename or delete tags owned by a different organization. The flaw exists because the route handler validates the caller's membership only against the :organizationId in the URL, while the repository layer filters writes on tag.id alone. The organization scope never propagates to the database query. The issue is tracked under CWE-639: Authorization Bypass Through User-Controlled Key.
Critical Impact
Any authenticated Papra user with a valid organization membership can modify or delete tags in unrelated organizations if the tag ID is known or guessed.
Affected Products
- Papra document management platform, all versions prior to 26.5.0
- Papra self-hosted deployments (papra-server)
- Papra multi-tenant instances hosting more than one organization
Discovery Timeline
- 2026-07-27 - CVE-2026-48052 published to NVD
- 2026-07-30 - Last updated in NVD database
Technical Details for CVE-2026-48052
Vulnerability Analysis
The vulnerability is an Insecure Direct Object Reference (IDOR) in the tags module of papra-server. The affected endpoints handle tag updates and deletions inside an organization-scoped route. The route handler calls ensureUserIsInOrganization to confirm the caller belongs to the organization named in the URL path. It then invokes tagsRepository.updateTag or the equivalent delete function using only tagId as the identifier.
Because the repository query does not include the organization in its WHERE clause, any tag row matching the supplied ID is modified. An attacker who is a member of organization A can send a request to their own organization's route while supplying a tagId that belongs to organization B. The membership check passes, and the write succeeds against the foreign tag.
Root Cause
The root cause is a missing tenancy filter in the data access layer. Authorization state established at the route level is not carried into the persistence layer. The route trusts the URL parameter, and the repository trusts the caller. Neither enforces that the tag actually belongs to the organization named in the URL.
Attack Vector
Exploitation requires authentication and membership in any organization. The attacker enumerates or otherwise obtains a target tag's ID, then issues an authenticated PATCH or DELETE request to a tag route scoped to their own organization while passing the foreign tagId. Impact is limited to integrity and availability of tag metadata; document contents and organization boundaries elsewhere are not directly affected.
// Patch in apps/papra-server/src/modules/tags/tags.routes.ts
// Source: https://github.com/papra-hq/papra/commit/47d44e0681bf59da0638b140d1c5ef5b970f6b67
await ensureUserIsInOrganization({ userId, organizationId, organizationsRepository });
- const { tag } = await tagsRepository.updateTag({ tagId, name, color, description });
+ const { tag } = await tagsRepository.updateTag({ tagId, organizationId, name, color, description });
+
+ if (!tag) {
+ throw createTagNotFoundError();
+ }
return context.json({
tag,
The fix propagates organizationId into the repository call so the update is scoped to the caller's organization. A missing result now returns a TagNotFoundError instead of silently succeeding.
Detection Methods for CVE-2026-48052
Indicators of Compromise
- Unexpected tag deletions or renames reported by organization owners who did not initiate the change.
- Application logs showing tag update or delete requests where the tagId in the request body or path does not match tags owned by the caller's organization.
- Repeated tag modification requests from a single authenticated user session across many tagId values, indicating enumeration.
Detection Strategies
- Correlate application audit logs of tag UPDATE and DELETE operations with each tag's organization_id in the database, and flag mismatches against the acting user's memberships.
- Add server-side logging that records both the URL-level organizationId and the resolved tag's organizationId for every write to the tagsTable.
- Baseline normal tag modification rates per user and alert on statistical outliers.
Monitoring Recommendations
- Enable request-level logging for the /api/organizations/:organizationId/tags/* endpoints, including request body and authenticated user ID.
- Ship application logs to a centralized SIEM or data lake and build a rule that joins tag write events against tag ownership metadata.
- Monitor error rates for the newly introduced TagNotFoundError after upgrading, as spikes may indicate probing attempts.
How to Mitigate CVE-2026-48052
Immediate Actions Required
- Upgrade all Papra deployments to version 26.5.0 or later, which is the patched release.
- Audit the tagsTable and tag audit logs for unauthorized modifications since the deployment date.
- Notify organization administrators of the risk and request confirmation of current tag inventories.
Patch Information
The fix is delivered in Papra 26.5.0 via Pull Request #1080 and commit 47d44e0. Full disclosure is documented in GitHub Security Advisory GHSA-wrx4-3vff-jm94. The patch scopes updateTag and deleteTag to organizationId and returns TagNotFoundError when no matching tag exists in the caller's organization.
Workarounds
- If upgrading is not immediately possible, restrict access to the Papra API to trusted users only and disable new user registrations.
- Deploy a reverse-proxy rule that inspects tag write requests and rejects them when the caller cannot be confirmed as a member of the tag's owning organization.
- Temporarily revoke tag write permissions at the application role level for non-administrative users until the patch is applied.
# Upgrade Papra to the patched release
docker pull ghcr.io/papra-hq/papra:26.5.0
docker stop papra && docker rm papra
docker run -d --name papra \
-p 1221:1221 \
-v papra-data:/app/data \
ghcr.io/papra-hq/papra:26.5.0
# Verify the running version
curl -s http://localhost:1221/api/config | jq '.version'
Disclaimer: This content was generated using AI. While we strive for accuracy, please verify critical information with official sources.

