Skip to content

fix(security): reject cross-module document-access grants (CE0148 guard) - #15

Merged
ako merged 1 commit into
mainfrom
fix/cross-module-grant-ce0148
Jul 24, 2026
Merged

fix(security): reject cross-module document-access grants (CE0148 guard)#15
ako merged 1 commit into
mainfrom
fix/cross-module-grant-ce0148

Conversation

@ako

@ako ako commented Jul 24, 2026

Copy link
Copy Markdown
Owner

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 — where B.Role belongs to a different module than the document — passed mxcli check and exec, 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 (AllowedModuleRoles on 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 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), 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 (in cmd_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:

cannot grant page Alpha.Overview access to Beta.Manager: a page can only reference module roles from its own module "Alpha" (Mendix rejects a cross-module document role with CE0148 "reselect roles"). Grant an Alpha module role instead — e.g. Alpha.Manager if it exists — and map the user role to it.

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 via MockBackend — cross-module rejected with no write; same-module still works (regression guard).
  • mdl-examples/bug-tests/ce0148-cross-module-grant.mdl (passes check).
  • A fix-issue.md symptom-table entry.
  • Full executor suite green; build/vet/gofmt clean. 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

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
ako merged commit 40538a7 into main Jul 24, 2026
3 checks passed
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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants