Repository navigation
check -p --references misses CE1406 for a database retrieve or a create of a Hidden entity of a Protected module #1652
Description
Description
Follow-up on #1498 (closed). Since that fix, check -p --references reports CE1406 for a call of a Hidden microflow, a parameter of a Hidden entity, and a Hidden attribute or association used through a typed variable. But the entity of a retrieve from the database or of a create is never checked: a flow outside the Protected module that retrieves or creates a Hidden entity passes check and exec, and mxbuild then refuses it with CE1406.
(The related gap for a Hidden attribute on a call microflow result has its own issue, #1654.)
Steps to reproduce
-
Create a module
AddOnand switch it to export level Protected (module type Add-on) in the module settings:mdl 1; create module AddOn; create persistent entity AddOn.Request (Code: String(50), Secret: String(50)); create persistent entity AddOn.Internal (Note: String(50)); alter entity AddOn.Request set export level api; alter entity AddOn.Request modify attribute Code: String(50) export level api;
AddOn.Internalis Hidden,AddOn.Request.Secretis Hidden.show modulesshowsAddOn | Protected AddOn. -
Run
cases.mdlagainst it (ordinary module):mdl 1; -- Control: a Hidden attribute on a parameter of a Usable entity. Reported today. create microflow MyFirstModule.Case0_Control ($Request: AddOn.Request) returns String as $Secret begin declare $Secret String = $Request/Secret; return $Secret; end; -- 1: database retrieve of a Hidden entity. create microflow MyFirstModule.Case1_RetrieveHidden () returns Boolean as $Found begin retrieve $Internals from AddOn.Internal; $Count = count($Internals); declare $Found Boolean = $Count > 0; return $Found; end; -- 2: a bare create of a Hidden entity. create microflow MyFirstModule.Case2_CreateHidden () returns Boolean as $Created begin $Internal = create AddOn.Internal; declare $Created Boolean = $Internal != empty; return $Created; end;
-
mxcli check cases.mdl -p App.mpr --references, then (without the control)mxcli execandmx check App.mpr.
Expected behavior
check -p --references reports CE1406 for Case1 and Case2, like it does for the control, so exec refuses the script before it writes something the build rejects.
Actual behavior
Only the control is reported:
$ mxcli check cases.mdl -p App.mpr --references
statement 1: CE1406: declare $Secret is not allowed to use attribute 'AddOn.Request.Secret' because app modules
cannot reference hidden elements in add-on modules — ...
✗ 1 reference error(s) found
Without the control: ✓ All references valid, exec writes both microflows, and then:
$ mx check App.mpr
[error] [CE1406] "Retrieve object(s) activity 'Retrieve list of Internal from database' is not allowed to use entity 'AddOn.Internal' because app modules cannot reference hidden elements in add-on modules."
[error] [CE1406] "Create object activity 'Create Internal' is not allowed to use entity 'AddOn.Internal' because app modules cannot reference hidden elements in add-on modules."
Environment
- mxcli v0.28.0 (2026-10-23), macOS arm64
mx check: Mendix 11.12.4 (fresh app)checkalso reproduced on the repository'stestdata/testapp(Mendix 11.14.0, cleanHOME; check only, no mxbuild 11.14 here)- The relevant source on
mainis unchanged since 2026-10-21, so Nightly 2026-10-25 behaves the same
Root cause
checkFlow (mdl/executor/validate_protected_module_refs.go) calls entityRef only for parameter and return types; the body walk (flowRefCollector) collects pages and microflows. checkFlowMembers (validate_protected_module_members.go) checks the members of a CreateObjectStmt and of a RetrieveStmt's XPath, but not s.EntityType / s.Source. A bare create or a retrieve without a where therefore reaches no check at all.
Proposed fix
In the walkTypedFlow visitor of checkFlowMembers (which already runs only for flows outside the Protected module), check the entity of a create and of a database retrieve:
case *ast.CreateObjectStmt:
out = appendNonEmpty(out, r.entityRef("create object activity '"+s.Variable+"'", s.EntityType, "app modules"))
// ... existing member checks
case *ast.RetrieveStmt:
if s.StartVariable == "" {
out = appendNonEmpty(out, r.entityRef("retrieve $"+s.Variable, s.Source, "app modules"))
}
// ... existing member checksentityRef already resolves the stated / stored / script-created level and is silent for a Usable entity, an ordinary module and an entity it cannot find.
Tests in TestValidateProtectedModuleRefs (the fixture already has a Hidden MyAddOn.Request and a Usable MyAddOn.Pub):
{"retrieve of hidden entity",
"create microflow MyFirstModule.R () begin retrieve $l from MyAddOn.Request; end;",
[]string{"CE1406", "entity 'MyAddOn.Request'"}},
{"retrieve of usable entity", "create microflow MyFirstModule.R () begin retrieve $l from MyAddOn.Pub; end;", nil},
{"bare create of hidden entity",
"create microflow MyFirstModule.C () begin $r = create MyAddOn.Request; end;",
[]string{"CE1406", "entity 'MyAddOn.Request'"}},and the same in an ordinary module (Open) as the control that stays clean.
I have a fix with tests ready and will open a PR referencing this issue shortly.
Activity
[-]`check -p --references` misses three CE1406 shapes into a Protected module: a database retrieve, a create object, and a Hidden member of a call result[/-][+]`check -p --references` misses CE1406 for a database retrieve or a create of a Hidden entity of a Protected module[/+]