Our research
CVE-2026-110787: How the pREST MCP Endpoint Bypassed the expose Settings
CVE-2026-110787 is an access control weakness (CWE-284) in the open source project pREST: the MCP endpoint /_mcp ignored the [expose] settings and listed databases, schemas, tables and column names even when the operator had switched those listings off. GitHub advisory GHSA-x62p-38px-pp73 rates it CVSS 5.3 (medium) and lists the Go module github.com/prest/prest/v2 up to and including version 2.4.0 as affected; the fix shipped in 2.4.1 on 29 July 2026. Only deployments that actively used [expose] to hide their catalog were affected, and what leaked were names, not table contents beyond what the access rules already allowed.
- The
expose.databases,expose.schemasandexpose.tablessettings only applied to the three REST routes/databases,/schemasand/tables, not to the MCP endpoint that was added later. - According to the project documentation,
[expose]controls discovery, not data access. Whether data may be read is governed by the separate[access]section, and that applied to/_mcpjust as it did to REST. - Rating per the advisory: CVSS 3.1 base score 5.3 under the documented default configuration without authentication, 4.3 with authentication enabled.
- What operators should do: upgrade to 2.4.1 or later, and protect sensitive data with
[access]and authentication rather than with[expose].
Background
What pREST is and what [expose] controls.
The vulnerability
The mechanism in plain words and in detail.
Go to sectionWho was affected
Versions and configurations.
Go to sectionThe fix
What changed and what operators should do.
Go to sectionTimeline
Publicly documented dates only.
Go to sectionThe underlying pattern
A new MCP surface next to an old control.
Go to sectionKey facts
| Item | Detail |
|---|---|
| Identifiers | CVE-2026-110787 (assigned), GHSA-x62p-38px-pp73 |
| Product | pREST (prest/prest), Go module github.com/prest/prest/v2 |
| Class | Access control bypass; CWE-284 “Improper Access Control” |
| Severity | CVSS 3.1: 5.3, medium (CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:N); 4.3 with authentication enabled according to the advisory (PR:L) |
| Affected | all versions up to and including 2.4.0 |
| Fixed | 2.4.1, released 29 July 2026 |
| Published | GitHub advisory on 29 July 2026; the CVE record was not yet published in the CVE List as of 28 October 2026 |
| Credit | Reported by CyberSec42 (GitHub: lwebmedia) |
Background: what pREST is and what [expose] controls
pREST is an open source project written in Go and released under the MIT licence. It puts a REST API in front of an existing PostgreSQL database, and since version 2.5 also MySQL, without any backend code of your own: reads, writes, filtering, custom SQL routes, authentication and access rules. Since version 2.1.0, released on 9 July 2026, the same server also offers a Model Context Protocol (MCP) endpoint at /_mcp. AI agents can use it to explore the database catalog, describe tables and read rows. The documentation describes the endpoint as read-only by design and says it shares the authentication and access rules of the REST API.
Two configuration sections matter here, and pREST deliberately keeps them apart:
[access]controls whether data may be read or written, optionally per table, field and user. Withaccess.restrict = true, only explicitly listed tables are reachable.[expose], according to the documentation, controls access to discovery: whether a caller can list the names of databases, schemas and tables. Withexpose.enabled = trueanddatabases,schemasortablesset tofalse, the operator hides those listings. Tables whose names a caller already knows remain queryable within the limits of[access].
Both sections are open by default: expose.enabled is false, so everything can be listed, and access.restrict is false as well. Authentication (auth.enabled) is also off in the default configuration. Anyone running pREST in public therefore switches these layers on deliberately. For the general picture of hardening MCP servers, see our guide to securing MCP servers; this article covers the specific case.
The vulnerability
In plain words
Picture an office building that has taken down the tenant directory in its lobby so visitors cannot see which companies are inside. Who may enter which office is still decided by the access card; the directory has nothing to do with that. Then the building opens a second entrance, and the full directory is still hanging on the wall there, floor plan included.
That is how pREST behaved up to version 2.4.0. The removed directory is the [expose] setting for the REST routes, the second entrance is /_mcp, and the access card is [access].
In detail
pREST enforces [expose] in exactly one place, the ExposureMiddleware. It joins the global middleware stack whenever expose.enabled is on and compares each request path against three fixed prefixes: /databases, /schemas and /tables. If a prefix matches and the corresponding setting is false, the server answers with HTTP 401 (“unauthorized listing”). Every other path is passed through unchecked.
/_mcp matches none of those prefixes. The MCP tools prest.list_databases, prest.list_schemas and prest.list_tables returned the same catalog as the REST routes but never consulted the [expose] configuration. On top of that came the discovery response to a plain GET /_mcp: it enumerates the available tools, including one read tool per table whose name carries the database, schema and table and whose description lists the column names. A caller correctly refused at GET /databases therefore saw the full catalog through /_mcp without calling a single tool.
This is important for the assessment: the read tools on /_mcp remained subject to [access]. With access.restrict = true, pREST also filtered the schema and table listings by those rules, though according to the advisory not the database listing. What leaked was metadata, not the contents of tables the caller would not have been allowed to read anyway.
Reading the vector: PR:N reflects the documented default configuration, in which auth.enabled is off and /_mcp requires no login. With authentication enabled, the caller needs a valid account with any privileges; the advisory gives PR:L and a base score of 4.3 for that case. C:L stands for a limited loss of confidentiality, while integrity and availability are not affected. We recalculated both base scores using the formula in the CVSS 3.1 specification.
Schematic flow
/_mcp passed through unchecked. From 2.4.1 the MCP tools check the [expose] settings too.- The operator sets
expose.enabled = trueand switches off the database, schema or table listing. - A caller who can reach the pREST HTTP endpoint requests
/databasesand is correctly refused. - The same caller requests
/_mcp. The middleware does not recognise the path as a catalog route and passes the request on. - The discovery response and the listing tools of the MCP endpoint return database, schema, table and column names.
- Which table contents the caller can read afterwards is still decided by
[access], exactly as for a direct REST request.
We deliberately do not publish exploit code. The description is enough to assess your own installation and to follow the fix.
Who was affected
The advisory lists the Go module github.com/prest/prest/v2 in all versions up to and including 2.4.0 as affected. According to the release notes and the documentation, the /_mcp endpoint has only existed since version 2.1.0; it is not registered in the 2.0.0 source code. In practice the issue only mattered to operators who actually used [expose] to hide their catalog.
| Configuration | Assessment up to 2.4.0 |
|---|---|
| Version before 2.1.0 | No MCP endpoint, so the path described here does not exist |
expose.enabled = false (default) | The catalog can be listed via REST and MCP alike, as configured. The vulnerability changes nothing here |
expose.enabled = true with listings switched off, authentication off | Affected. Anyone who could reach the HTTP endpoint saw the catalog through /_mcp (CVSS 5.3) |
expose.enabled = true with listings switched off, authentication on | Affected for every logged-in account, regardless of its privileges (CVSS 4.3) |
additionally access.restrict = true | Schema and table listings on /_mcp were limited to what the access rules allow the caller; the database listing was not |
How much an exposed catalog gives away depends on the names. Tables such as orders say little, schemas named after customers or projects say more. It also depends on who could reach the HTTP endpoint: only the local machine, an internal network or the internet.
The fix and what operators should do now
What the 2.4.1 fix changes
The fix (pull request #1016) defines in one central place what [expose] permits: three predicate functions for database, schema and table listings. The REST middleware and the MCP tools now both consult the same functions. A code comment states the reason explicitly: a new discovery endpoint should no longer be able to escape the control silently.
On /_mcp this means, according to the documentation: if a listing is switched off, the matching prest.list_* tool disappears from the discovery response and a direct call fails with “unauthorized listing”. If any listing is switched off, the discovery response also stops enumerating the per-table read tools, because their names and descriptions would reveal the catalog. The generic tools prest.describe_table and prest.select_table remain available within the limits of [access] for callers who already know their target. The fix adds tests for exactly these cases and extends the comment in the sample configuration to cover the MCP endpoint. The same commit also contains an unrelated hardening of parameter checks for scripts, which is not part of this advisory.
Recommendations for operators
The following steps are our recommendations, not requirements from the project.
| Step | Action |
|---|---|
| 1. Check your version | Upgrade to 2.4.1 or later. The most recent published release as of 28 October 2026 is 2.5.2. |
| 2. Verify the effect | If you use [expose]: after the upgrade, check on your own installation that the /_mcp discovery response no longer lists table and column names. |
3. Protect data with [access] | Do not protect sensitive tables by hiding them. Use access.restrict = true with permissions per table, field and user. |
| 4. Turn on authentication | If pREST is reachable beyond the local network, enable auth.enabled. /_mcp uses the same authentication as the REST routes. |
| 5. Block an unused endpoint | If you do not use MCP, you can block /_mcp at the reverse proxy. The advisory names this as an interim measure as well. |
| 6. Review your logs | If your reverse proxy logs requests: look for calls to /_mcp from unexpected sources. |
Timeline
Only dates that can be backed by public sources are listed.
| Date | Event | Source |
|---|---|---|
| 9 Jul 2026 | Release 2.1.0 introduces the /_mcp endpoint | GitHub release v2.1.0, documentation |
| 27 Jul 2026 | Release 2.4.0, the last affected version; according to the advisory, the review covered the source code at this release | GitHub release v2.4.0, advisory |
| 27 Jul 2026 | Advisory created in the repository | GitHub API for the advisory |
| 29 Jul 2026 | Fix merged (pull request #1016), GHSA-x62p-38px-pp73 published, release 2.4.1 | GitHub |
| 28 Oct 2026 | CVE-2026-110787 is recorded in the advisory; the CVE record is not yet published in the CVE List | Advisory, MITRE CVE API |
The fixed version was therefore available two days after the advisory was created. When the CVE number was assigned is not publicly documented, so it is not listed.
The underlying pattern: a new MCP surface bypasses an existing control
CVE-2026-110787 reflects a pattern that is likely to become more common as MCP spreads. A mature product has an access control tied to specific routes. Later, an MCP endpoint is added that serves the same data through a new path. The old control does not know about the new path, and nobody notices, because the old routes still refuse correctly. To the operator, the setting looks effective.
In our explanation of the OWASP MCP Top 10, this maps to class 8, authorization applied unevenly across the protocol surface, with a touch of class 4, a control that promises more than it covers. Developers who add an MCP surface to an existing application can take four rules from it:
- Bind the control to the function, not the path. A check keyed on URL prefixes only covers the paths that existed when it was written. A central predicate that every piece of code returning catalog data has to call makes new paths checkable too. That is the direction the pREST fix takes.
- New surface, new inventory. Before adding an MCP endpoint, list every existing setting that restricts data or metadata and check each one against the new path.
- Descriptions are output. Tool names and tool descriptions in a discovery response can carry exactly the information a control is meant to hide.
- Test the negative case on every surface. A test such as “listing disabled, REST and MCP must both refuse” catches this class for good. The fix adds such tests.
For operators, the flip side applies: an MCP endpoint in an existing application is one more interface to your data. Check whether every setting you rely on also takes effect there. More documented findings of this kind are on our research page.
Thanks to the pREST maintainers
We thank the pREST maintainers for the fast and constructive collaboration under coordinated disclosure. The report was submitted confidentially as a security advisory in the repository. Two days after the advisory was created, the fix, tests, updated documentation and release 2.4.1 were out, and the advisory credits CyberSec42 as the reporter. The fix does more than close the reported path: it consolidates [expose] into one central definition that future discovery endpoints can build on.
On our method: we reviewed only the project's public source code. According to the advisory, no running pREST instance, database or third-party system was contacted, all example values in the report are fabricated, and no issues, pull requests, comments or branches were opened or changed on GitHub.
Do your access rules hold on the MCP endpoint too?
We test MCP servers and AI agents within a scope agreed in writing: whether existing controls apply equally on every surface, what discovery responses give away, how authentication and access rules interact and what a tool call can actually reach. Every finding comes with reproducible evidence and a concrete fix. Learn more about AI pentesting & MCP security.
Request an AI pentestCyberSec42 is an independent technical security assessment, not an accredited certification body, and does not provide legal advice. CyberSec42 is not affiliated with the pREST project.
Frequently asked questions
What is CVE-2026-110787?
An access control weakness in pREST (CWE-284). The MCP endpoint /_mcp listed databases, schemas, tables and column names even though the operator had switched those listings off via [expose]. GitHub advisory GHSA-x62p-38px-pp73 rates it CVSS 5.3 (medium). The CVE number is assigned; the record was not yet published in the CVE List as of 28 October 2026.
Which pREST versions are affected?
According to the advisory, all versions of the Go module github.com/prest/prest/v2 up to and including 2.4.0. The MCP endpoint has existed since 2.1.0. The issue is fixed in 2.4.1, released on 29 July 2026. Upgrade to 2.4.1 or later.
Could attackers read table contents this way?
Not beyond what the access rules already allowed. According to the documentation, [expose] only controls discovery, while data access is governed by [access], and that applied to /_mcp just as it did to the REST routes. What became visible were the names of databases, schemas, tables and columns. If you protected sensitive tables only by hiding them, configure [access].
Am I affected if I do not use [expose]?
No, not in the sense of this vulnerability. With the default expose.enabled = false, the catalog can be listed via REST and MCP alike, as configured. You should still upgrade, since your configuration may change later.
Was the vulnerability actively exploited?
The public sources, meaning the advisory, release notes and documentation, make no statement about it. We only reviewed source code and did not examine any running installations.
Further reading
- Research: our publicly documented findings
- The OWASP MCP Top 10, Explained in Practice
- Securing MCP Servers: A Practical Guide
- Service: AI pentest & MCP security
Sources
- GitHub security advisory: GHSA-x62p-38px-pp73 (prest/prest)
- GitHub: pREST release v2.4.1
- GitHub: pREST pull request #1016
- GitHub: changes between v2.4.0 and v2.4.1
- GitHub: pREST release v2.1.0 (MCP over HTTP)
- pREST documentation: Configuring pREST (Expose Data)
- pREST documentation: MCP over HTTP
- MITRE: CWE-284
- FIRST: CVSS v3.1 specification