
Una condivisione pubblica sembrava pulita nell'albero delle pagine, ma l'endpoint di ricerca raccontava una storia diversa. In Docmost, le pagine figlie limitate nascoste agli spettatori della condivisione pubblica potevano comunque trapelare attraverso i risultati di ricerca della condivisione pubblica.
Una condivisione pubblica sembrava pulita nell'albero delle pagine, ma l'endpoint di ricerca raccontava un'altra storia. In Docmost, le pagine figlie con restrizioni nascoste ai visitatori della condivisione pubblica potevano comunque trapelare attraverso i risultati di ricerca della condivisione pubblica.
Ho trovato questo problema durante la revisione di Docmost, una piattaforma wiki e documentazione collaborativa open-source, partendo da una domanda molto semplice:
Se una pagina è intenzionalmente nascosta a un visitatore della condivisione pubblica, ogni funzionalità pubblica rispetta lo stesso confine di restrizione?
In questo caso, la risposta è stata no.
Una pagina figlia con restrizione poteva rimanere nascosta nell'albero della condivisione pubblica mentre trapelava comunque attraverso l'endpoint di ricerca della condivisione pubblica.
Il problema è stato accettato e gli è stato assegnato CVE-2026-33146.
Docmost: Docmost su GitHub
CVE: CVE-2026-33146
Il sito ufficiale di Docmost lo presenta come un wiki on-premise pronto per le aziende con 3M+ download, e afferma che è utilizzato da team in organizzazioni tra cui Vilnius City, Bechtle, il Governo Australiano, la Croce Rossa e ETS Quebec.
condivisione pubblica genitore con sottopagine abilitate → discendente con restrizione omesso dall'albero pubblico → attore malintenzionato interroga la ricerca della condivisione pubblica → titolo e snippet del figlio con restrizione trapelano
Docmost è una piattaforma wiki e documentazione collaborativa.
Fornisce:
Ciò significa che il suo modello di condivisione pubblica è un vero confine di sicurezza.
La domanda importante qui non era se Docmost può condividere pagine pubblicamente.
La vera domanda era:
Quando Docmost decide che una pagina discendente è limitata e non deve apparire a un visitatore della condivisione pubblica, quella restrizione è valida ovunque nel flusso di condivisione pubblica?
In questo caso, non lo era.
Molte revisioni di sicurezza si fermano troppo presto quando vedono una pagina nascosta nell'interfaccia utente.
Questo non basta.
La domanda più forte è questa:
Ogni percorso backend impone la stessa decisione di visibilità?
Questo è importante perché i confini di sicurezza non sono definiti dall'aspetto dell'interfaccia.
Sono definiti da ciò che il server restituisce effettivamente.
Qui, l'endpoint dell'albero pubblico si è comportato in modo sicuro:
Ma il percorso di ricerca della condivisione pubblica si è comportato diversamente:
Questo ha reso il problema una reale violazione dell'autorizzazione e della divulgazione di informazioni, non solo un'incoerenza di presentazione.
Non ho affrontato Docmost facendo fuzzing casuale degli endpoint sperando che apparisse qualcosa di interessante.
La strada più solida era scegliere prima un confine di fiducia.
Per le applicazioni che supportano:
una delle migliori domande da porsi è:
Il livello di ricerca impone esattamente lo stesso confine di autorizzazione del livello di navigazione?
Questa domanda diventa particolarmente preziosa quando:
È esattamente qui che si è manifestato questo problema.
Il bug non era che Docmost non riusciva a nascondere la pagina con restrizione nell'albero pubblico normale.
Il bug era che la ricerca pubblica non onorava la stessa logica di restrizione.
Dalla revisione del codice sorgente, il flusso dell'albero pubblico utilizzava la traversata dei discendenti consapevole delle restrizioni.
Area rilevante:
apps/server/src/core/share/share.service.tsQuel percorso escludeva intenzionalmente i discendenti con restrizione utilizzando:
getPageAndDescendantsExcludingRestricted(...)Ma il flusso di ricerca della condivisione pubblica seguiva un percorso diverso.
Aree rilevanti:
apps/server/src/core/search/search.controller.tsapps/server/src/core/search/search.service.tsLì, il codice raccoglieva i discendenti utilizzando:
getPageAndDescendants(...)Ciò significa che i discendenti con restrizione rimanevano nel campo d'azione per la ricerca.
Nel contesto della condivisione pubblica, questo è molto importante perché il ramo di ricerca viene eseguito senza un contesto di autorizzazione utente autenticato normale. Quindi, una volta che i discendenti con restrizione erano inclusi nel set di pagine ricercabili, i loro metadati potevano trapelare attraverso la risposta.
Perché l'attaccante non ha bisogno di un account autenticato.
Ha solo bisogno di:
Una volta che questa condizione esiste, un visitatore pubblico può interrogare l'endpoint di ricerca della condivisione e recuperare:
Questo è sufficiente per creare una fuga di informazioni, anche se l'intero corpo della pagina non viene restituito.
La distinzione importante è che l'applicazione segnala già chiaramente il modello di sicurezza previsto.
L'endpoint dell'albero pubblico nasconde i discendenti con restrizione.
Quindi la vera domanda non è:
"La ricerca restituisce per caso un insieme di risultati più ampio?"
La vera domanda è:
"La ricerca sta violando una decisione di autorizzazione già applicata altrove per lo stesso confine di condivisione pubblica?"
In Docmost, sì.
Questo trasforma il problema da:
a:
Ecco perché è una vulnerabilità reale.
Ho validato il problema confrontando i due endpoint pubblici rilevanti fianco a fianco.
Prima, ho testato l'endpoint normale dell'albero pubblico utilizzando la chiave di condivisione pubblica.
Richiesta di esempio:
POST /api/shares/tree HTTP/1.1
Host: 127.0.0.1:6752
Content-Type: application/json
{
"shareId": "public-share-key"
}
La risposta restituiva solo la pagina figlia pubblica nell'albero delle pagine.
Risultato rappresentativo:
{
"pageTree": [
{
"id": "public-child",
"title": "Public roadmap"
}
]
}
Questo ha stabilito il comportamento atteso del prodotto:
Ho quindi interrogato l'endpoint di ricerca della condivisione pubblica utilizzando un termine che appariva all'interno del discendente con restrizione.
Richiesta di esempio:
POST /api/search/share-search HTTP/1.1
Host: 127.0.0.1:6752
Content-Type: application/json
{
"shareId": "public-share-key",
"query": "salary"
}
La risposta includeva comunque il figlio con restrizione:
{
"items": [
{
"id": "public-child",
"title": "Public roadmap",
"highlight": "release plan and milestones"
},
{
"id": "restricted-child",
"title": "Payroll Q4",
"highlight": "salary bands and bonus targets"
}
]
}
Questo ha provato l'affermazione centrale:
La parte più forte di questo problema non è la seconda richiesta da sola.
È il contrasto tra i due endpoint.
Mostra che il prodotto ha già un modello di restrizione previsto per le condivisioni pubbliche.
Il discendente con restrizione non dovrebbe essere visibile al visitatore pubblico.
Dimostra che il percorso di ricerca rompe esattamente lo stesso confine.
Questo rende il problema più difficile da liquidare come comportamento di ricerca previsto o come lacuna nella documentazione.
L'applicazione stessa stabilisce la regola attraverso la risposta dell'albero, poi la viola attraverso la risposta della ricerca.
Questa è una prova potente.
Questo problema non espone contenuti arbitrari in tutto lo spazio di lavoro.
Il suo ambito è più ristretto.
Ma all'interno del sottoalbero della condivisione pubblica interessato, fornisce comunque all'attaccante una conoscenza non autorizzata utile:
Anche snippet brevi possono essere importanti.
Un titolo come:
ha già valore di sicurezza per un attaccante.
Quindi, anche se alla fine è stato classificato come Moderato, rimane un problema di riservatezza valido con una violazione del confine chiara e difendibile.
A questo problema è stato assegnato:
La gravità dell'avviso era:
CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:U/C:L/I:N/A:NQuesto punteggio riflette una fuga di informazioni più ristretta piuttosto che un accesso completo non autorizzato ai documenti.
Il punto importante è che il problema è comunque valido.
L'affermazione qui non è:
L'affermazione è:
Questa è una reale divulgazione di informazioni legata all'autorizzazione.
Alcune persone liquidano troppo rapidamente le fughe di metadati.
Questo è un errore.
La vera domanda è se i dati trapelati oltrepassano un confine previsto.
Qui, l'hanno fatto.
Se l'applicazione dice:
ma un endpoint pubblico rivela comunque:
allora il modello di riservatezza è fallito, anche se l'impatto è limitato.
Questo lo rende degno di segnalazione.
Bug puliti, circoscritti e riproducibili come questo sono esattamente il tipo di problemi che aiutano a dimostrare un buon giudizio nella revisione della sicurezza.
La direzione più sicura per la correzione è fare in modo che la ricerca pubblica utilizzi la stessa logica dei discendenti consapevole delle restrizioni del flusso dell'albero pubblico.
In pratica, ciò significa che il ramo di ricerca della condivisione non dovrebbe enumerare i discendenti con:
getPageAndDescendants(...)
Dovrebbe invece allinearsi con la traversata più sicura della condivisione pubblica e utilizzare:
getPageAndDescendantsExcludingRestricted(...)
Una correzione alternativa sarebbe mantenere l'enumerazione più ampia e poi filtrare esplicitamente i discendenti con restrizione prima che la query di ricerca restituisca i risultati.
Ma il design più pulito è semplice:
il confine di ricerca dovrebbe corrispondere al confine di navigazione
Questa è la proprietà di sicurezza che è venuta meno.
Questo problema è stato segnalato privatamente attraverso il flusso di segnalazione della sicurezza di GitHub.
Il rapporto mostrava:
/api/shares/tree/api/search/share-searchIl problema è stato accettato e gli è stato assegnato:
CVE-2026-33146
La gravità finale dell'avviso era Moderata, che si adatta meglio all'ambito di fuga di informazioni più ristretto rispetto a un'affermazione di criticità più ampia.
Ciò non indebolisce la validità del reperto.
Ne definisce solo l'impatto in modo più preciso.
La lezione chiave qui è semplice:
nascondere qualcosa in un endpoint pubblico non è sufficiente se un altro endpoint pubblico lo rivela ancora.
Molti sviluppatori pensano all'autorizzazione solo nel percorso di rendering ovvio:
Ma il confine reale è più ampio.
Bisogna anche chiedersi:
In Docmost, la risposta è stata no.
Questo è il vero insegnamento.
Questa vulnerabilità non riguardava payload appariscenti o complesse catene di sfruttamento.
Riguardava il porsi una domanda pratica sul confine di fiducia.
Docmost nascondeva la pagina con restrizione in un posto.
Poi la faceva trapelare in un altro.
Ecco perché è diventata CVE-2026-33146.