fix(security): reject cross-module document-access grants (CE0148 guard) - #15
Merged
Conversation
Granting a page/microflow/nanoflow/published OData|REST service access to a module role from a DIFFERENT module than the document passed `mxcli check` and `exec`, then failed the Mendix build with an opaque CE0148 "reselect roles". Document access (AllowedModuleRoles) may only reference the document's OWN module roles — Studio Pro's role picker only offers those — so a cross-module reference is invalid even though it is well-formed. The grant handlers wrote `role.Module + "." + role.Name` verbatim, guarded only by validateModuleRole (which checks the role exists in ITS module, not that it matches the document's module). The MOVE path already enforced the same-module rule by remapping (remapDocumentAccessRoles); GRANT had no equivalent. Add checkDocumentAccessRolesSameModule and wire it into all five grant handlers (page, microflow, nanoflow, OData service, published REST service). It rejects a cross-module role with an actionable message that names the document's module and suggests the own-module role — turning an opaque deploy-time failure into an immediate, clear error. Reject (not silently remap): a GRANT is explicit, so a wrong role or wrong document must not be substituted. Tests: the pure guard (both branches) plus page/microflow/nanoflow handler tests via MockBackend (cross-module rejected, no write; same-module still works). Adds mdl-examples/bug-tests/ce0148-cross-module-grant.mdl and a fix-issue.md symptom-table entry. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JXnEgoc2NQP1Y2TWMCMXC4
ako
pushed a commit
that referenced
this pull request
Jul 29, 2026
Mendix requires widget names to be unique per page and rejects duplicates with CE0495 "Duplicate name" — but `mxcli check --references` passed a page with, e.g., a container and a listview both named `ruTop`, and the failure only surfaced at MxBuild. Added checkDuplicateWidgetNames to the page context validator: it walks the widget tree, counts names, and reports each name used more than once (once per name, first-seen order). Runs at check time under --references, before the build. Verified end to end: a page with two `ruTop` widgets now reports the CE0495-class error; unique-named pages pass; full executor suite (no false positives on existing test pages). Unit + parsed-page tests added. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JXnEgoc2NQP1Y2TWMCMXC4
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.
Fixes the P1 CE0148 bug surfaced by the Atlas design-system testing (see
PROPOSAL_atlas_design_system.md).The bug
grant view on page A.Page to B.Role— whereB.Rolebelongs to a different module than the document — passedmxcli checkandexec, then failed the Mendix build with an opaque CE0148 "reselect roles." The own-module grant works, so it looked like a serialization bug.Root cause: document access (
AllowedModuleRoleson a page / microflow / nanoflow / published OData|REST service) may only reference the document's own module roles — Studio Pro's role picker only offers those. The grant handlers wroterole.Module + "." + role.Nameverbatim, guarded only byvalidateModuleRole(which checks the role exists in its module, not that it matches the document's module), so a cross-module reference reached the model. The MOVE path already enforced this rule by remapping (remapDocumentAccessRoles); GRANT had no equivalent.The fix
Add
checkDocumentAccessRolesSameModule(incmd_security_defaults.go, next to the documented rule) and wire it into all five grant handlers — page, microflow, nanoflow, OData service, published REST service. It rejects a cross-module role before any write, with an actionable message that names the document's module and suggests the own-module role:Reject, not silently remap: unlike MOVE (which relocates a document and remaps to same-named target roles), a GRANT is an explicit statement, so substituting a different module's role could mask a wrong role or wrong document. This turns an opaque deploy-time failure into an immediate, clear error.
Tests & docs
cmd_security_grant_crossmodule_test.go: the pure guard (both branches) + page/microflow/nanoflow handler tests viaMockBackend— cross-module rejected with no write; same-module still works (regression guard).mdl-examples/bug-tests/ce0148-cross-module-grant.mdl(passescheck).fix-issue.mdsymptom-table entry.build/vet/gofmtclean. Verified no existing example or test grants cross-module, so nothing valid is newly rejected.🤖 Generated with Claude Code
https://claude.ai/code/session_01JXnEgoc2NQP1Y2TWMCMXC4
Generated by Claude Code