Skip to content

GRANT … WHERE keeps a double-quoted name verbatim for most rule shapes — XPath string literal, the access rule silently matches nothing (check / exec / mx check all pass) #1243

Description

@MohamedElNady

Summary

A double-quoted attribute name inside GRANT … WHERE '<xpath>' is stored verbatim for most rule shapes. In Mendix XPath "Status" is a string literal, so "Status" = 'Accepted' compares two strings, is always false, and the access rule silently grants no rows. For one rule shape mxcli rewrites the XPath (multi-line canonical form) and the quotes disappear, so the same habit is sometimes repaired and sometimes not.

mxcli check --references, mxcli lint, mx check and mx check -w all pass: the XPath is valid, just always false. In our app it was found only by a per-user runtime test (executives saw 0 rows of an entity their rule should have granted).

This matters because the generated CLAUDE.md / AGENTS.md says "Quote every identifier in MDL … Quotes are stripped, so it is always safe", so an agent reflexively quotes names inside the XPath string too (same trigger as #827). The same quoted name is stripped in a microflow retrieve … where.

Minimal repro (blank 11.14 project)

CREATE MODULE Rp;
CREATE OR MODIFY ENUMERATION Rp.St (Draft 'Draft', Accepted 'Accepted');
CREATE OR MODIFY PERSISTENT ENTITY Rp.Parent ("Name": String(50));
CREATE OR MODIFY PERSISTENT ENTITY Rp.Item ("Status": Enumeration(Rp.St), Kind: String(20));
CREATE OR MODIFY ASSOCIATION Rp.Item_Parent FROM Rp.Item TO Rp.Parent TYPE Reference;
CREATE OR MODIFY MODULE ROLE Rp.R1;
CREATE OR MODIFY MODULE ROLE Rp.R2;
CREATE OR MODIFY MODULE ROLE Rp.R3;
CREATE OR MODIFY MODULE ROLE Rp.R4;
CREATE OR MODIFY MODULE ROLE Rp.R5;
GRANT Rp.R1 ON Rp.Item (READ *) WHERE '[Status = ''Accepted'']';
GRANT Rp.R2 ON Rp.Item (READ *) WHERE '["Status" = ''Accepted'']';
GRANT Rp.R3 ON Rp.Item (READ *) WHERE '[Kind = ''a'' and "Status" = ''Accepted'']';
GRANT Rp.R4 ON Rp.Item (READ *) WHERE '["Status" = ''Accepted'' and Rp.Item_Parent/Rp.Parent["Name" = ''x'']]';
GRANT Rp.R5 ON Rp.Item (READ *) WHERE '[Kind = ''a'' and "Status" = ''Accepted'' and Rp.Item_Parent/Rp.Parent["Name" = ''x'']]';

mxcli check g49.mdl -p Repro.mpr --references → Check passed!; mxcli exec → OK.

Actual (show access on entity Rp.Item, confirmed in the stored XPathConstraint)

Rule Written Stored Effect
R1 [Status = 'Accepted'] same correct
R2 ["Status" = 'Accepted'] verbatim, quotes kept never matches
R3 [Kind = 'a' and "Status" = 'Accepted'] verbatim never matches
R4 ["Status" = 'Accepted' and Rp.Item_Parent/Rp.Parent["Name" = 'x']] verbatim never matches
R5 [Kind = 'a' and "Status" = 'Accepted' and Rp.Item_Parent/Rp.Parent["Name" = 'x']] rewritten: [Kind = 'a' and Status = 'Accepted' and Rp.Item_Parent/Rp.Parent[Name = 'x']] correct (by accident)

mx check Repro.mpr → The app contains: 0 errors.; mxcli lint → only SEC001 for Rp.Parent.

For contrast, in a microflow the quotes are stripped:

CREATE OR MODIFY MICROFLOW Rp.MF_Q () RETURNS Integer
BEGIN
  retrieve $L from Rp.Item where "Status" = 'Accepted';
  $N = count($L);
  return $N;
END;

→ describe microflow Rp.MF_Q shows where Status = Rp.St.Accepted;

The bracket form that the skills describe as quote-safe for XPath (where [ … ]) is not accepted by GRANT:
GRANT Rp.R6 ON Rp.Item (READ *) WHERE ["Status" = 'Accepted']; → mismatched input '[' expecting STRING_LITERAL.

Expected

One consistent behaviour for GRANT … WHERE, in order of preference:

  1. strip identifier quotes in access-rule XPath as retrieve … where already does (and as R5 happens to get); or
  2. reject / warn in check: "double-quoted name in XPath is a string literal — the rule never matches".

Suggestions

  • Apply the same identifier-quote normalisation to every XPath string property (access rules, view-entity rules, page data-source XPath strings), not only when the multi-line formatter (Feature request: preserve or emit line breaks in XPath constraints #979) rewrites the constraint.
  • Add a lint rule for a comparison whose left side is a string literal in any stored XPath constraint (it also catches rules made in Studio Pro).
  • Qualify the "quotes are stripped, so it is always safe" line in the generated CLAUDE.md / AGENTS.md: not inside XPath strings.

Workaround we use

Bare names in every XPath string; a text scan of MDL scripts before exec, and a post-exec scan of every stored XPathConstraint for "name" = / != / < / >; an as-user test for each access rule.

Session log (isolated HOME, this repro only)
{"time":"2026-09-30T21:49:10.3327987+03:00","level":"INFO","msg":"session_start","version":"v0.24.0","go":"go1.26.6","os":"windows","arch":"amd64","mode":"mxcli check","args":["C:\\Tools\\mxcli.exe","check","D:/Mohamed/Mendix/_mxrepro/scripts/g49.mdl","-p","Repro.mpr","--references"],"pid":49832,"parent_pid":""}
{"time":"2026-09-30T21:49:12.1945076+03:00","level":"INFO","msg":"connect","mpr_path":"Repro.mpr","mendix_version":"11.14.0","mpr_format":2}
{"time":"2026-09-30T21:49:12.1945076+03:00","level":"INFO","msg":"execute","stmt_type":"ConnectStmt","stmt_summary":"connect local 'Repro.mpr'","duration_ms":1}
{"time":"2026-09-30T21:49:12.7124484+03:00","level":"INFO","msg":"session_end","commands_executed":1,"errors_count":0,"duration_s":2,"pid":49832}
{"time":"2026-09-30T21:49:13.2549944+03:00","level":"INFO","msg":"session_start","version":"v0.24.0","go":"go1.26.6","os":"windows","arch":"amd64","mode":"mxcli exec","args":["C:\\Tools\\mxcli.exe","exec","D:/Mohamed/Mendix/_mxrepro/scripts/g49.mdl","-p","Repro.mpr"],"pid":48000,"parent_pid":""}
{"time":"2026-09-30T21:49:13.2892795+03:00","level":"INFO","msg":"connect","mpr_path":"Repro.mpr","mendix_version":"11.14.0","mpr_format":2}
{"time":"2026-09-30T21:49:13.2892795+03:00","level":"INFO","msg":"execute","stmt_type":"ConnectStmt","stmt_summary":"connect local 'Repro.mpr'","duration_ms":1}
{"time":"2026-09-30T21:49:13.7964319+03:00","level":"INFO","msg":"execute","stmt_type":"CreateModuleStmt","stmt_summary":"create module Rp","duration_ms":120}
{"time":"2026-09-30T21:49:13.9801527+03:00","level":"INFO","msg":"execute","stmt_type":"CreateEnumerationStmt","stmt_summary":"create enumeration Rp.St","duration_ms":183}
{"time":"2026-09-30T21:49:14.1423535+03:00","level":"INFO","msg":"execute","stmt_type":"CreateEntityStmt","stmt_summary":"create entity Rp.Parent","duration_ms":162}
{"time":"2026-09-30T21:49:14.3868236+03:00","level":"INFO","msg":"execute","stmt_type":"CreateEntityStmt","stmt_summary":"create entity Rp.Item","duration_ms":244}
{"time":"2026-09-30T21:49:14.6340449+03:00","level":"INFO","msg":"execute","stmt_type":"CreateAssociationStmt","stmt_summary":"create association Rp.Item_Parent","duration_ms":246}
{"time":"2026-09-30T21:49:14.6678397+03:00","level":"INFO","msg":"execute","stmt_type":"CreateModuleRoleStmt","stmt_summary":"create module role Rp.R1","duration_ms":33}
{"time":"2026-09-30T21:49:14.7773523+03:00","level":"INFO","msg":"execute","stmt_type":"CreateModuleRoleStmt","stmt_summary":"create module role Rp.R2","duration_ms":109}
{"time":"2026-09-30T21:49:14.8816998+03:00","level":"INFO","msg":"execute","stmt_type":"CreateModuleRoleStmt","stmt_summary":"create module role Rp.R3","duration_ms":104}
{"time":"2026-09-30T21:49:14.9892728+03:00","level":"INFO","msg":"execute","stmt_type":"CreateModuleRoleStmt","stmt_summary":"create module role Rp.R4","duration_ms":107}
{"time":"2026-09-30T21:49:15.0983088+03:00","level":"INFO","msg":"execute","stmt_type":"CreateModuleRoleStmt","stmt_summary":"create module role Rp.R5","duration_ms":109}
{"time":"2026-09-30T21:49:15.3957126+03:00","level":"INFO","msg":"execute","stmt_type":"GrantEntityAccessStmt","stmt_summary":"grant on entity Rp.Item","duration_ms":297}
{"time":"2026-09-30T21:49:15.5135647+03:00","level":"INFO","msg":"execute","stmt_type":"GrantEntityAccessStmt","stmt_summary":"grant on entity Rp.Item","duration_ms":117}
{"time":"2026-09-30T21:49:15.6305585+03:00","level":"INFO","msg":"execute","stmt_type":"GrantEntityAccessStmt","stmt_summary":"grant on entity Rp.Item","duration_ms":116}
{"time":"2026-09-30T21:49:15.7519818+03:00","level":"INFO","msg":"execute","stmt_type":"GrantEntityAccessStmt","stmt_summary":"grant on entity Rp.Item","duration_ms":121}
{"time":"2026-09-30T21:49:15.889849+03:00","level":"INFO","msg":"execute","stmt_type":"GrantEntityAccessStmt","stmt_summary":"grant on entity Rp.Item","duration_ms":137}
{"time":"2026-09-30T21:49:15.8959679+03:00","level":"INFO","msg":"session_end","commands_executed":16,"errors_count":0,"duration_s":2,"pid":48000}
{"time":"2026-09-30T21:49:16.4405609+03:00","level":"INFO","msg":"session_start","version":"v0.24.0","go":"go1.26.6","os":"windows","arch":"amd64","mode":"batch","args":["C:\\Tools\\mxcli.exe","-p","Repro.mpr","-c","show access on entity Rp.Item"],"pid":40480,"parent_pid":""}
{"time":"2026-09-30T21:49:16.4935548+03:00","level":"INFO","msg":"connect","mpr_path":"Repro.mpr","mendix_version":"11.14.0","mpr_format":2}
{"time":"2026-09-30T21:49:16.4935548+03:00","level":"INFO","msg":"execute","stmt_type":"ConnectStmt","stmt_summary":"connect local 'Repro.mpr'","duration_ms":2}
{"time":"2026-09-30T21:49:16.5733963+03:00","level":"INFO","msg":"execute","stmt_type":"ShowStmt","stmt_summary":"show ACCESS ON ENTITY Rp.Item","duration_ms":79}
{"time":"2026-09-30T21:49:16.5737403+03:00","level":"INFO","msg":"session_end","commands_executed":2,"errors_count":0,"duration_s":0,"pid":40480}
{"time":"2026-09-30T21:49:26.6633269+03:00","level":"INFO","msg":"session_start","version":"v0.24.0","go":"go1.26.6","os":"windows","arch":"amd64","mode":"mxcli lint","args":["C:\\Tools\\mxcli.exe","lint","-p","Repro.mpr"],"pid":13824,"parent_pid":""}
{"time":"2026-09-30T21:49:26.7004913+03:00","level":"INFO","msg":"connect","mpr_path":"Repro.mpr","mendix_version":"11.14.0","mpr_format":2}
{"time":"2026-09-30T21:49:26.7004913+03:00","level":"INFO","msg":"execute","stmt_type":"ConnectStmt","stmt_summary":"connect local 'Repro.mpr'","duration_ms":2}
{"time":"2026-09-30T21:49:28.748318+03:00","level":"INFO","msg":"execute","stmt_type":"RefreshCatalogStmt","stmt_summary":"refresh catalog","duration_ms":2046}
{"time":"2026-09-30T21:49:28.8200773+03:00","level":"INFO","msg":"session_end","commands_executed":2,"errors_count":0,"duration_s":2,"pid":13824}
{"time":"2026-09-30T21:49:29.3874928+03:00","level":"INFO","msg":"session_start","version":"v0.24.0","go":"go1.26.6","os":"windows","arch":"amd64","mode":"mxcli exec","args":["C:\\Tools\\mxcli.exe","exec","D:/Mohamed/Mendix/_mxrepro/scripts/g49_retrieve.mdl","-p","Repro.mpr"],"pid":10584,"parent_pid":""}
{"time":"2026-09-30T21:49:29.4243662+03:00","level":"INFO","msg":"connect","mpr_path":"Repro.mpr","mendix_version":"11.14.0","mpr_format":2}
{"time":"2026-09-30T21:49:29.4243662+03:00","level":"INFO","msg":"execute","stmt_type":"ConnectStmt","stmt_summary":"connect local 'Repro.mpr'","duration_ms":2}
{"time":"2026-09-30T21:49:29.8757364+03:00","level":"INFO","msg":"execute","stmt_type":"CreateMicroflowStmt","stmt_summary":"create microflow Rp.MF_Q","duration_ms":38}
{"time":"2026-09-30T21:49:29.8784214+03:00","level":"INFO","msg":"session_end","commands_executed":2,"errors_count":0,"duration_s":0,"pid":10584}
{"time":"2026-09-30T21:49:30.4818638+03:00","level":"INFO","msg":"session_start","version":"v0.24.0","go":"go1.26.6","os":"windows","arch":"amd64","mode":"batch","args":["C:\\Tools\\mxcli.exe","-p","Repro.mpr","-c","describe microflow Rp.MF_Q"],"pid":49288,"parent_pid":""}
{"time":"2026-09-30T21:49:30.5310811+03:00","level":"INFO","msg":"connect","mpr_path":"Repro.mpr","mendix_version":"11.14.0","mpr_format":2}
{"time":"2026-09-30T21:49:30.5310811+03:00","level":"INFO","msg":"execute","stmt_type":"ConnectStmt","stmt_summary":"connect local 'Repro.mpr'","duration_ms":2}
{"time":"2026-09-30T21:49:30.713686+03:00","level":"INFO","msg":"execute","stmt_type":"DescribeStmt","stmt_summary":"describe MICROFLOW Rp.MF_Q","duration_ms":182}
{"time":"2026-09-30T21:49:30.7142077+03:00","level":"INFO","msg":"session_end","commands_executed":2,"errors_count":0,"duration_s":0,"pid":49288}
{"time":"2026-09-30T21:49:31.2529343+03:00","level":"INFO","msg":"session_start","version":"v0.24.0","go":"go1.26.6","os":"windows","arch":"amd64","mode":"mxcli check","args":["C:\\Tools\\mxcli.exe","check","D:/Mohamed/Mendix/_mxrepro/scripts/g49_bracket.mdl","-p","Repro.mpr"],"pid":43128,"parent_pid":""}

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions