Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
f86b8b0
fix(catalog): save the cache atomically so parallel processes do not …
ako Oct 3, 2026
54997ca
fix(lint): keep failing rules out of the report score (#952)
ako Oct 3, 2026
70cb9b1
feat(init): stamp the tooling version, guard against older binaries, …
ako Oct 3, 2026
aeaf080
fix(docker): run docker check on a temporary copy of the project (#951)
ako Oct 3, 2026
d94fcfb
fix(docker): refuse a missing project and never copy the check copy i…
ako Oct 3, 2026
8964f28
fix(check): MDL-BUTTON01 skips a grid nested in a data container
ako Oct 3, 2026
a423d46
fix(lint): MPR012 and MDL-WIDGET40 stay off native pages
ako Oct 3, 2026
c4a14bb
fix(lint): MPR002 does not call a return-only flow empty
ako Oct 3, 2026
6763b4a
fix(lint): CONV006 reports once per entity
ako Oct 3, 2026
0977b5a
feat(report): --modules scores only the selected modules
ako Oct 3, 2026
7481a07
fix: delete close page keeps ClosePage on write (#950)
ako Oct 3, 2026
b6efd01
fix: describe adds $ only to a bare returned variable (#950)
ako Oct 3, 2026
9254eda
fix: navigation-list items describe and re-execute (#950)
ako Oct 3, 2026
8c64d16
test: round-trip harness covers the three #950 describe outputs
ako Oct 3, 2026
190be49
fix(check): a void Java/JavaScript action call declares no variable; …
ako Oct 3, 2026
4d53f68
fix(check): unknown call parameters are no longer hidden behind other…
ako Oct 3, 2026
3e85395
Merge remote-tracking branch 'origin/feat/952-tooling-version-guard' …
ako Oct 3, 2026
d41abe6
Merge remote-tracking branch 'origin/fix/953-void-call-duplicates' in…
ako Oct 3, 2026
119622b
style: gofmt validate_void_code_calls_test.go
ako Oct 3, 2026
05b6313
Merge remote-tracking branch 'origin/fix/953-lint-false-positives' in…
ako Oct 3, 2026
bc4cab5
test(report): --modules scoping keeps rule failures listed
ako Oct 3, 2026
6dc5a30
Merge remote-tracking branch 'origin/fix/950-describe-roundtrip' into…
ako Oct 3, 2026
d388835
Merge pull request #956 from ako/fix/951-check-readonly-cache-race
ako Oct 3, 2026
6e8a2dd
Merge pull request #955 from ako/feat/952-tooling-version-guard
ako Oct 3, 2026
9b61904
Merge pull request #958 from ako/fix/953-void-call-duplicates
ako Oct 3, 2026
7cd25fb
Merge pull request #957 from ako/fix/953-lint-false-positives
ako Oct 3, 2026
fc2406f
Merge pull request #959 from ako/fix/950-describe-roundtrip
ako Oct 3, 2026
6555636
Merge commit '8b5fbc224' into sync/mendixlabs-merge-8b5fbc224
ako Oct 3, 2026
52e74a3
Merge pull request #960 from ako/sync/mendixlabs-merge-8b5fbc224
ako Oct 3, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 31 additions & 14 deletions .claude/lint-rules/conv006_no_create_delete_rights.star
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@
# create and delete operations should go through microflows that enforce
# business logic. Only READ and WRITE should be granted via access rules.
#
# One finding per entity, naming every role per right: the advice is the same
# for each role, and one row per entity x role x right buried the report (111
# findings on a mid-sized app, ako/mxcli#953).
#
# Requires FULL catalog (REFRESH CATALOG FULL).

RULE_ID = "CONV006"
Expand All @@ -12,27 +16,40 @@ DESCRIPTION = "Entity access rules should not grant CREATE or DELETE directly; u
CATEGORY = "security"
SEVERITY = "warning"

RIGHTS = ("CREATE", "DELETE")

def check():
violations = []

for entity in entities():
if entity.entity_type != "Persistent" or entity.is_external:
continue

# right -> sorted, de-duplicated roles (a role can hold several rules)
roles = {}
for perm in permissions_for(entity.qualified_name):
if perm.access_type in ("CREATE", "DELETE"):
violations.append(violation(
message="Entity '{}' grants {} to role '{}'. Use a microflow to enforce business logic.".format(
entity.qualified_name, perm.access_type, perm.module_role_name
),
location=location(
module=entity.module_name,
document_type="Entity",
document_name=entity.qualified_name,
),
suggestion="Remove the {} right and implement a microflow (ACT_) with security checks".format(
perm.access_type
),
))
if perm.access_type in RIGHTS:
held = roles.setdefault(perm.access_type, [])
if perm.module_role_name not in held:
held.append(perm.module_role_name)

granted = [r for r in RIGHTS if r in roles]
if not granted:
continue

parts = ["{} ({})".format(r, ", ".join(sorted(roles[r]))) for r in granted]
violations.append(violation(
message="Entity '{}' grants {}. Use a microflow to enforce business logic.".format(
entity.qualified_name, "; ".join(parts)
),
location=location(
module=entity.module_name,
document_type="Entity",
document_name=entity.qualified_name,
),
suggestion="Remove the {} right{} and implement a microflow (ACT_) with security checks".format(
" and ".join(granted), "s" if len(granted) > 1 else ""
),
))

return violations
4 changes: 4 additions & 0 deletions .claude/skills/fix-issue/findings/cmd-mxcli.jsonl
Original file line number Diff line number Diff line change
Expand Up @@ -152,3 +152,7 @@
{"date": "2026-10-03", "area": "cmd/mxcli/check", "symptom": "`mxcli check -p` prints MDL067 (bare commit now WITH events) for a create-or-modify flow already stored with events, which `exec -p` no longer prints", "cause": "cmd_check ran ValidateProgram without the DropSettledCommitNotes filter exec_preflight applies; the project was only connected later, for the reference tier", "fix": "check connects to the project before the semantic report when -p is given, applies DropSettledCommitNotes and StoredTaskClaimViolations, and reuses that connection for the reference tier", "insight": "Two gates over one rule set drift whenever a post-filter lives in only one of them; grep for every caller of ValidateProgram when adding a filter. Control: a flow stored without events still notes", "issue": "ako/mxcli#943", "file": "cmd/mxcli/cmd_check.go", "test": "cmd/mxcli/check_stored_semantics_test.go"}
{"date": "2026-10-03", "area": "cmd/mxcli/test", "symptom": "every `mxcli test` run prints 2x MDL-DEPR001 and 2x MDL-V1-SLASH about a script the user never wrote", "cause": "GenerateEndpointMDL emitted a headerless mdl 0 script with `create or replace` and `/` terminators; the test-flow generators had already moved to the version-aware writeScriptHeader/createFlow/writeFlowEnd", "fix": "GenerateEndpointMDL writes mdl 1 through the same helpers (header, create or modify, `;` only); endpoint script is independent of the suite's version", "insight": "A generated script is checked like a user's one; pin it with a test that parses it and asserts ValidateProgram returns nothing. Verified end to end with `mxcli test --local` on a fresh 11.13 app", "issue": "ako/mxcli#943", "file": "cmd/mxcli/testrunner/endpoint.go", "test": "cmd/mxcli/testrunner/endpoint_clean_test.go"}
{"date": "2026-10-03", "area": "cmd/mxcli/theme", "symptom": "`theme create acme --from design.css` with `--mxt-font: \"Inter\", system-ui, sans-serif` prints nothing about Inter; the theme ships no woff2 and no @font-face for it and renders in the fallback font wherever Inter is not installed", "cause": "planFonts only decided which VENDORED families to drop; a seeded family outside the vendored set was never looked at, so the silent outcome was the default", "fix": "unvendoredSeededFamilies takes the primary (first) family of each seeded font stack, skips generic families and var() and the families the base partial loads, and CreateResult.UnvendoredFonts carries them to cmd_theme.go, which prints a note per family naming mxcli-fonts/ and the partial", "insight": "Only the first family of a stack is the design's choice; flagging the fallbacks (Helvetica, Arial) would make the note noise. The controls are a vendored family (IBM Plex Mono) and a generic stack, which must stay silent", "issue": "ako/mxcli#944", "file": "cmd/mxcli/theme/create_seeded.go (unvendoredSeededFamilies, planFonts); cmd/mxcli/cmd_theme.go", "test": "cmd/mxcli/theme/create_seeded_test.go (TestCreate_NamesSeededFontsItDoesNotVendor)"}
{"area": "cmd/mxcli/docker", "date": "2026-10-03", "symptom": "`mxcli docker check` (a check) rewrote an MPRv1 project's .mpr permanently, and on v1 and v2 alike rewrote theme-cache/web/theme.compiled.css(.map) and created deployment/sass/main.scss \u2014 with or without --no-update-widgets", "cause": "update-widgets ran on the user's project, protected only by a snapshot/restore of the v2 storage (.mpr + mprcontents/), so v1 had no protection; and `mx check` itself compiles the theme into theme-cache/ and writes deployment/sass/, which nothing guarded", "file": "`cmd/mxcli/docker/check.go` (`Check`), `cmd/mxcli/docker/check_copy.go` (`copyProjectForCheck`)", "insight": "A snapshot of the files you expect a tool to touch protects only those files; measure with a whole-tree hash+mtime diff before/after, which is what showed that plain `mx check` writes too. The fix is to run both mx steps on a temporary copy (skipping deployment/, releases/, theme-cache/, .git, node_modules) and rewrite the copy's path back in mx output. Control: a CE0117 microflow is still reported on both formats with the tree unchanged. `docker build` keeps runUpdateWidgets because it is expected to write deployment/ \u2014 but it still rewrites a v1 .mpr", "refs": ["ako/mxcli#951", "ako/mxcli#568", "ako/mxcli#646"]}
{"area": "cmd/mxcli", "date": "2026-10-03", "symptom": "A project whose CLAUDE.md, skills and lint rules were written by a newer mxcli is served by an older binary (v0.24.0 on PATH) with no warning: 'mdl 1;' is a parse error and shipped lint rules crash, all reading as project defects", "cause": "Nothing recorded which mxcli wrote the tooling; .claude/bootstrap-mxcli.sh linked whatever mxcli was on PATH; init --sync-skills refreshed only .ai-context/skills, never .claude/lint-rules or CLAUDE.md/AGENTS.md", "fix": "init and every sync write .ai-context/mxcli-tooling.json; root PersistentPreRun warns once on stderr when the binary is provably older (release by number, nightly by tag date, mixed by build date, dev never); sync refuses from an older binary; the bootstrap script carries a POSIX-sh copy of the ordering and neither links an older PATH binary nor keeps an older ./mxcli, downloading via a temp file + mv; sync also refreshes bundled lint rules by name and the CLAUDE.md/AGENTS.md section between mxcli:begin/end markers", "insight": "A binary cannot warn about a stamp it predates, so the guard for already-shipped binaries must live in the generated script, which the newer mxcli regenerates. The sh and Go comparisons share one test table so they cannot drift. curl -o ./mxcli on a symlinked ./mxcli would overwrite the PATH binary — always download to a temp name and rename", "issue": "ako/mxcli#952", "file": "cmd/mxcli/tooling_stamp.go; cmd/mxcli/init_tooling_sync.go; cmd/mxcli/init_hook.go (bootstrapScriptTemplate); cmd/mxcli/main.go; cmd/mxcli/init.go", "test": "cmd/mxcli/tooling_stamp_test.go; cmd/mxcli/init_hook_version_test.go; cmd/mxcli/init_tooling_sync_test.go"}
{"area": "cmd/mxcli", "date": "2026-10-03", "symptom": "CONV006 emits one finding per entity x role x CREATE/DELETE (111 on a mid-sized app), the same advice repeated per role, and the per-finding Security score is driven by role count rather than by entities", "cause": "The Starlark rule appended a violation inside the permissions_for() loop", "file": "`.claude/lint-rules/conv006_no_create_delete_rights.star` (synced to `cmd/mxcli/lint-rules/`)", "insight": "Group per entity and per right with de-duplicated sorted roles (a role can hold several access rules on one entity). Test both rule copies (.claude and the embedded one) like SEC008's test does", "refs": ["ako/mxcli#953"]}
{"area": "cmd/mxcli", "date": "2026-10-03", "symptom": "`mxcli report` could not score a project's own modules: `lint` has --modules, `report` had only --exclude", "cause": "Feature gap; and the LintContext module filter alone would not make the score exact, because project-level findings (CONV008 role mappings, project security) carry no module and are reported regardless", "file": "`cmd/mxcli/cmd_report.go`, `mdl/linter/report.go` (`ScopeToModules`, Report.Modules)", "insight": "Filter the scored violations to those located in a selected module, and print the selection in every format so a module score is not mistaken for the project's", "refs": ["ako/mxcli#953"]}
Loading
Loading