Skip to main content

EUVDEUVD-2026-70529

| CVE-2026-50554 MEDIUM
Information Exposure (CWE-200)
2026-07-09 https://github.com/enchant97/note-mark GHSA-588f-fvcv-xhvf
5.3
CVSS 3.1 · Vendor: https://github.com/enchant97/note-mark
Share

Severity by source

Vendor (https://github.com/enchant97/note-mark) PRIMARY
5.3 MEDIUM
AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:N
vuln.today AI
5.3 MEDIUM

Unauthenticated network endpoint, no complexity; confidentiality impact is Low because only note metadata - not body content - is disclosed.

3.1 AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:N
4.0 AV:N/AC:L/AT:N/PR:N/UI:N/VC:L/VI:N/VA:N/SC:N/SI:N/SA:N
SUSE
MEDIUM
qualitative

Primary rating from Vendor (https://github.com/enchant97/note-mark).

CVSS VectorVendor: https://github.com/enchant97/note-mark

Attack Vector
Network
Attack Complexity
Low
Privileges Required
None
User Interaction
None
Scope
Unchanged
Confidentiality
Low
Integrity
None
Availability
None

Lifecycle Timeline

3
Source Code Evidence Fetched
Jul 27, 2026 - 10:50 vuln.today
Analysis Generated
Jul 09, 2026 - 14:35 vuln.today
CVE Published
Jul 09, 2026 - 13:41 github-advisory
MEDIUM 5.3

DescriptionCVE.org

Summary

GET /api/books/{bookID}/notes is an unauthenticated endpoint that accepts a "deleted" query parameter. When the request is ?deleted=true, the service runs the query with Unscoped() (bypassing GORM's soft-delete scope) but keeps the read-authorization clause as "owner_id = ? OR is_public = ?". As a result, any unauthenticated caller can enumerate the metadata of soft-deleted ("trashed") notes belonging to any public book - notes the owner explicitly deleted and expected to be removed from public view.

Affected component (code-verified)

backend/services/notes.go - GetNotesByBookID (lines 72-89):

func (s NotesService) GetNotesByBookID(currentUserID *uuid.UUID, bookID uuid.UUID, deleted bool) ([]db.Note, error) { tx := db.DB if deleted { tx = tx.Unscoped() // <-- bypasses soft-delete scope } tx = tx. Preload("Book"). Joins("JOIN books ON books.id = notes.book_id"). Where( db.DB.Where("books.id = ?", bookID), db.DB.Where("owner_id = ? OR is_public = ?", currentUserID, true), // <-- is_public still honored for trash ) if deleted { tx = tx.Where("notes.deleted_at IS NOT NULL") } var notes []db.Note return notes, dbErrorToServiceError(tx.Find(&notes).Error) }

Route registration confirms the endpoint has no AuthRequiredMiddleware (backend/handlers/notes.go:37), and the deleted flag is attacker-controlled (backend/handlers/notes.go:86 - Deleted bool with query:"deleted").

Proof of concept

  1. A victim owns a public book (is_public = true), creates a note, then soft-deletes it (moves it to trash). The note still exists in the DB with

deleted_at set.

  1. An unauthenticated attacker who knows (or enumerates) the book UUID requests: GET /api/books/<bookID>/notes?deleted=true
  2. The response lists the soft-deleted note(s) - id, title, slug, timestamps - even though the attacker is not authenticated and the owner

intended the note to be deleted.

Impact

Exposure of soft-deleted note metadata (title, slug, timestamps) of public books to unauthenticated actors. The note body is not exposed - the content endpoint (GetNoteContent) does not use Unscoped(), so its count query returns 0 for soft-deleted notes and yields 404. Impact is therefore limited to metadata disclosure and the bypass of the intended "delete" semantics on public books.

Remediation

Restrict trash (soft-deleted) listings to the book owner only - never honor the is_public branch when deleted=true:

func (s NotesService) GetNotesByBookID(currentUserID *uuid.UUID, bookID uuid.UUID, deleted bool) ([]db.Note, error) { tx := db.DB if deleted { tx = tx.Unscoped() } + // Soft-deleted ("trash") notes must only ever be listed to the book owner. + authz := db.DB.Where("owner_id = ? OR is_public = ?", currentUserID, true) + if deleted { + authz = db.DB.Where("owner_id = ?", currentUserID) + } tx = tx. Preload("Book"). Joins("JOIN books ON books.id = notes.book_id"). Where( db.DB.Where("books.id = ?", bookID),

  • db.DB.Where("owner_id = ? OR is_public = ?", currentUserID, true),

+ authz, ) if deleted { tx = tx.Where("notes.deleted_at IS NOT NULL") } var notes []db.Note return notes, dbErrorToServiceError(tx.Find(&notes).Error) }

With this change, when currentUserID is nil (unauthenticated) and deleted=true, the clause becomes owner_id = NULL, which matches nothing - so trash is never exposed to anonymous callers.

Coordinated disclosure / CVE request

We have reported this privately and are happy to assist with any further validation or testing you need. If you agree this qualifies as a security vulnerability, we would be grateful if you could request a CVE ID for it - GitHub lets maintainers request a CVE directly from this advisory page once it is accepted. Thank you for your time and for maintaining note-mark.

References

  • CWE-200: Exposure of Sensitive Information to an Unauthorized Actor
  • CWE-285: Improper Authorization
  • Prior note-mark authorization fix (CVE-2026-40265) established that read paths must scope by owner_id OR is_public; this report covers the trash

path that the scope did not fully cover.

---

AnalysisAI

Unauthenticated metadata disclosure in note-mark backend allows remote attackers to enumerate soft-deleted notes' titles, slugs, and timestamps from any public book. The vulnerability stems from an authorization flaw that honors the 'is_public' flag even when the trash endpoint is queried, bypassing the intended delete semantics. A publicly available proof-of-concept demonstrates the attack, but no active exploitation has been confirmed.

Vendor StatusVendor

SUSE

Severity: Moderate
Product Status
SUSE Linux Enterprise Server 16.1 Affected
SUSE Linux Enterprise Server for SAP applications 16.1 Affected
SUSE Linux Enterprise Module for Package Hub 15 SP5 Affected
SUSE Linux Enterprise Module for Package Hub 15 SP6 Affected
openSUSE Leap 15.5 Affected

Share

EUVD-2026-70529 vulnerability details – vuln.today

This site uses cookies essential for authentication and security. No tracking or analytics cookies are used. Privacy Policy