I found this issue while reviewing Docmost, an open-source collaborative wiki and documentation platform, with a very simple question in mind:
If a page is intentionally hidden from a public share viewer, does every public feature respect that same restriction boundary?
In this case, the answer was no.
A restricted child page could stay hidden in the public share tree while still leaking through the public share search endpoint.
The issue was accepted and assigned CVE-2026-33146.
Docmost: Docmost on GitHub
CVE: CVE-2026-33146
Docmost’s official site presents it as an enterprise-ready on-premises wiki with 3M+ downloads, and says it is trusted by teams at organizations including Vilnius City, Bechtle, the Australian Government, the Red Cross, and ETS Quebec.
public parent share with subpages enabled → restricted descendant omitted from public tree → attacker queries public share search → restricted child title and snippet leak
Docmost is a collaborative wiki and documentation platform.
It provides:
That means its public-sharing model is a real security boundary.
The important question here was not whether Docmost can share pages publicly.
The real question was:
When Docmost decides a descendant page is restricted and should not appear to a public share visitor, does that restriction hold everywhere in the public share flow?
In this case, it did not.
A lot of security reviews stop too early once they see a page hidden in the UI.
That is not enough.
The stronger question is this:
Does every backend path enforce the same visibility decision?
That matters because security boundaries are not defined by what the interface looks like.
They are defined by what the server actually returns.
Here, the public tree endpoint behaved safely:
But the public share search path behaved differently:
That made this a real authorization and information disclosure issue, not just a presentation inconsistency.
I did not approach Docmost by randomly fuzzing routes and hoping something interesting appeared.
The stronger path was to choose a trust boundary first.
For applications that support:
one of the best questions to ask is:
Does the search layer enforce the exact same authorization boundary as the browse layer?
That question becomes especially valuable when:
That is exactly where this issue showed up.
The bug was not that Docmost failed to hide the restricted page in the normal public tree.
The bug was that public search did not honor that same restriction logic.
From source review, the public tree flow used restricted-aware descendant traversal.
Relevant area:
apps/server/src/core/share/share.service.tsThat path intentionally excluded restricted descendants by using:
getPageAndDescendantsExcludingRestricted(...)But the public share search flow followed a different path.
Relevant areas:
apps/server/src/core/search/search.controller.tsapps/server/src/core/search/search.service.tsThere, the code collected descendants using:
getPageAndDescendants(...)That meant restricted descendants remained in scope for search.
In the public-share context, that matters a lot because the search branch runs without a normal authenticated user permission context. So once restricted descendants were included in the searchable page set, their metadata could leak through the response.
Because the attacker does not need an authenticated account.
They only need:
Once that condition exists, a public visitor can query the share-search endpoint and recover:
That is enough to create a confidentiality leak, even if the full page body is not returned.
The important distinction is that the application already signals the intended security model clearly.
The public tree endpoint hides restricted descendants.
So the real question is not:
“Does search happen to return a broader result set?”
The real question is:
“Is search violating an authorization decision already enforced elsewhere for the same public share boundary?”
In Docmost, it was.
That turns this from:
into:
That is why this is a real vulnerability.
I validated the issue by comparing the two relevant public endpoints side by side.
First, I tested the normal public tree endpoint using the public share key.
Example request:
The response returned only the public child page in the page tree.
Representative result:
That established the expected product behavior:
I then queried the public share search endpoint using a term that appeared inside the restricted descendant.
Example request:
The response included the restricted child anyway:
That proved the core claim:
The strongest part of this issue is not the second request by itself.
It is the contrast between the two endpoints.
It shows the product already has an intended restriction model for public shares.
The restricted descendant is not supposed to be visible to the public visitor.
It proves the search path breaks that exact same boundary.
That makes the issue harder to dismiss as expected search behavior or a documentation gap.
The application itself establishes the rule through the tree response, then violates it through the search response.
That is powerful evidence.
This issue does not expose arbitrary content across the whole workspace.
Its scope is narrower than that.
But within the affected public share subtree, it still gives an attacker useful unauthorized knowledge:
Even short snippets can matter.
A title like:
already creates security value for an attacker.
So while this was ultimately classified as Moderate, it is still a valid confidentiality issue with a clean and defensible boundary break.
This issue was assigned:
The advisory severity was:
CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:U/C:L/I:N/A:NThat scoring reflects a narrower confidentiality leak rather than full unauthorized document access.
The important point is that the issue is still valid.
The claim here is not:
The claim is:
That is a real authorization-related information disclosure.
Some people dismiss metadata leaks too quickly.
That is a mistake.
The real question is whether the leaked data crosses an intended boundary.
Here, it did.
If the application says:
but a public endpoint still reveals:
then the confidentiality model has failed, even if the impact is limited.
That makes it worth reporting.
Clean, scoped, and reproducible bugs like this are exactly the kind of issues that help demonstrate strong security review judgment.
The safest fix direction is to make public search use the same restriction-aware descendant logic as the public tree flow.
In practice, that means the share-search branch should not enumerate descendants with:
It should instead align with the safer public-share traversal and use:
An alternative fix would be to keep the broader enumeration and then explicitly filter restricted descendants out before the search query returns results.
But the cleaner design is simple:
the search boundary should match the browse boundary
That is the security property that failed.
This issue was reported privately through GitHub’s security reporting flow.
The report showed:
/api/shares/tree/api/search/share-searchThe issue was accepted and assigned:
CVE-2026-33146
The final advisory severity was Moderate, which fits the narrower leak scope better than a broader criticality claim would have.
That does not weaken the validity of the finding.
It just defines its impact more precisely.
The key lesson here is simple:
hiding something in one public endpoint is not enough if another public endpoint still reveals it.
A lot of developers think about authorization only in the obvious rendering path:
But the real boundary is broader.
You also have to ask:
In Docmost, the answer was no.
That is the real takeaway.
This vulnerability was not about flashy payloads or complex exploit chains.
It was about asking a very practical trust-boundary question.
Docmost hid the restricted page in one place.
Then leaked it in another.
That is why this became CVE-2026-33146.