Public Studio: the raw plugin registry is refused too, so the gated bundle's name stays private

In ACCESS=public mode the guard refused a gated bundle's source and its /__leaf/ pages, but not
/__plugin-gallery/registry.json. That file is the raw discovery list for the smoke tools, and it still
names key-cities, its tag and its two profiles to any visitor. No data, but a gated plugin must not reach a
public surface. The Studio reads only manifest.json, which already leaves gated bundles out, so the
registry is now refused in public mode as well. Dev mode is unchanged, and the smoke tools that read the
registry run against a dev server.
Measured with the cloud config run locally:
- before: public mode served registry.json (200) naming key-cities;
- after: public mode answers 404, including with a query string and a percent-encoded name;
- manifest.json and / still answer 200 in public mode, and dev mode still serves registry.json.
This commit is contained in:
Doxa Maps 2026-09-29 13:35:58 +00:00
parent 004d1d3f51
commit c9caf68e44

View file

@ -45,7 +45,9 @@ const rudderStarting = {
// - The backend's paths go to admin.mjs, whose gates decide: /cdn/ and /api-prod/api/ public, /pb/ records
// by PocketBase's own rules, and /pb/_/, _superusers and /admin/ for the admin password.
// - GATED bundles (gatedProfiles, e.g. key-cities) must never reach a public surface. The Studio already leaves
// them out of its list; here their source and dev pages are refused too, so a typed URL can't load them.
// them out of its list; here their source and dev pages are refused too, so a typed URL can't load them. So is
// /__plugin-gallery/registry.json, the raw discovery list for smoke tools: it still names the gated bundles and
// their profiles, and the Studio itself reads only manifest.json.
// - /__open-in-editor (Vite's own) would start an editor process on this server for a visitor: refused.
const PUBLIC = env.DX_PUBLIC_STUDIO === '1'
const publicGuard = {
@ -58,7 +60,7 @@ const publicGuard = {
let p = (req.url || '').split('?')[0]
try { p = decodeURIComponent(p) } catch { /* keep it raw */ }
const gated = GATED_PROFILES.some((g) => p.includes(`/plugin-designs/${g}/`) || p === `/__leaf/${g}` || p.startsWith(`/__leaf/${g}/`))
if (!gated && !p.startsWith('/__open-in-editor')) return next()
if (!gated && !p.startsWith('/__open-in-editor') && p !== '/__plugin-gallery/registry.json') return next()
res.statusCode = 404
res.setHeader('content-type', 'text/plain; charset=utf-8')
res.end('not found\n')