fix(security): stop auto-exposing generic MasterData CRUD as server functions#332
Open
0xcucumbersalad wants to merge 1 commit into
Open
fix(security): stop auto-exposing generic MasterData CRUD as server functions#3320xcucumbersalad wants to merge 1 commit into
0xcucumbersalad wants to merge 1 commit into
Conversation
…unctions
Generic VTEX MasterData CRUD (createDocument, getDocument, patchDocument,
searchDocuments, uploadAttachment) was listed in @decocms/apps-vtex's
`invoke.vtex.actions`. The blocks-cli generator emits one top-level
`createServerFn` per entry, which TanStack Start compiles into a public,
unauthenticated `/_serverFn/<hash>` endpoint. These handlers take a
client-supplied `entity` and proxy to the VTEX MasterData API with the app's
admin appKey/appToken and no auth or entity allow-list — so any caller can
`searchDocuments({ entity: "CL", ... })` to enumerate customer PII, or
create/patch arbitrary entities. Broken access control.
Authentication alone can't make a generic (entity, filter) proxy safe — it has
no room for per-entity/per-record authorization — so remove it from the
auto-exposed surface rather than guard it:
- apps-vtex/invoke.ts: drop the 5 MasterData entries from invoke.vtex.actions
(and their imports). The functions stay exported from actions/masterData for
server-side use; sites needing MasterData define a narrow, entity-pinned,
authenticated action in their own src/server/invoke.ts.
- generate-invoke.ts: add a PRIVILEGED_ACTIONS denylist that skips + warns on
these names even if re-added to the contract, so the exclusion can't silently
regress. Also covers the apps-start legacy source path.
- generate-invoke.test.ts: lock the guard — privileged actions are never
emitted as consts / vtexActions entries / imports, safe actions still are.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
hugo-ccabral
approved these changes
Jul 10, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
@decocms/apps-vtexlists five generic VTEX MasterData CRUD actions in itsinvoke.vtex.actionscontract:searchDocuments({ entity, filter })createDocument({ entity, data })getDocument({ entity, documentId })patchDocument({ entity, documentId, data })uploadAttachment(...)@decocms/blocks-cli'sgenerate-invoke.tsemits one top-levelcreateServerFnper entry into each site'ssrc/server/invoke.gen.ts. TanStack Start compiles every top-levelcreateServerFn().handler()into a public, unauthenticated/_serverFn/<hash>endpoint — addressable over HTTP regardless of whether any UI code references it.These handlers take a client-supplied
entityand proxy straight to the VTEX MasterData API using the app's adminappKey/appToken, with no auth middleware and no entity allow-list. So an anonymous caller can:searchDocuments({ entity: "CL", filter: "..." })→ enumerate the Clients (customer PII) tablecreateDocument/patchDocument→ write arbitrary entitiesThis is broken access control / mass data exposure.
Why not just add an auth check
Even an authenticated customer must not be able to run
searchDocuments({ entity: "CL" })and read other customers' records under the app's admin token. A generic(entity, filter)proxy structurally has no room for per-entity/per-record authorization — the generic, admin-credentialed, client-parameterized shape is the vulnerability. So remove it from the auto-exposed surface rather than guard it.Changes
apps-vtex/src/invoke.ts— drop the 5 MasterData entries (and their imports) frominvoke.vtex.actions. The functions stay exported fromactions/masterDatafor server-side use.blocks-cli/scripts/generate-invoke.ts— add aPRIVILEGED_ACTIONSdenylist that skips + warns on these names even if re-added to the contract, so the exclusion can't silently regress. Also covers theapps-startlegacy source path the generator can fall back to.generate-invoke.test.ts— lock the guard: privileged actions are never emitted as consts /vtexActionsentries / imports; safe actions still are; a warning is printed per skip.How a site adds MasterData back
The
masterData.tsfunctions remain exported, so a site that legitimately needs MasterData defines a narrow, entity-pinned, authenticatedcreateServerFnin its ownsrc/server/invoke.ts(same mechanism sites already use for_vtexAuth/_logout):Rules: pin
entityserver-side (never from the client), validate input, and for user-scoped data derive identity from the session cookie — not a client-supplied id. There is deliberately no generic re-enable switch; re-emitting the generic handler would recreate an unguardable endpoint.Verification
vitest run packages/blocks-cli/scripts/generate-invoke.test.ts→ 15 passed (9 existing + 6 new).typecheckclean for@decocms/apps-vtexand@decocms/blocks-cli.actions/masterDataimport; cart/session/newsletter actions intact.Follow-ups (out of scope)
@decocms/apps-vtex+ re-rungenerate:invoke+ redeploy to close the hole on already-generatedinvoke.gen.ts.@decocms/apps(apps-start) repo carries the same source list; the generator guard here already protects it, but an optional consistency PR could mirror the data change there.userId;tableMasterdata.tsarbitrary clienttableId) are separate.🤖 Generated with Claude Code
Summary by cubic
Stopped auto-exposing generic VTEX MasterData CRUD as public
/_serverFnendpoints in generated code. This closes a broken access control issue where anonymous callers could read or write MasterData using admin credentials.Bug Fixes
@decocms/apps-vtex: removed generic MasterData actions frominvoke.vtex.actions; functions remain for server-side use only.@decocms/blocks-cli: addedPRIVILEGED_ACTIONSdenylist ingenerate-invoke.tsto skip and warn on these actions (covers legacy path too).Migration
@decocms/apps-vtexand@decocms/blocks-cli.generate:invoketo regeneratesrc/server/invoke.gen.ts.Written for commit 61cf768. Summary will update on new commits.