Skip to content

Add 24 ACR-derived rules and fix pre-existing rule tests#29

Open
xiwenc wants to merge 9 commits into
mainfrom
claude/acr-rules-mxlint-75msgc
Open

Add 24 ACR-derived rules and fix pre-existing rule tests#29
xiwenc wants to merge 9 commits into
mainfrom
claude/acr-rules-mxlint-75msgc

Conversation

@xiwenc

@xiwenc xiwenc commented Jul 11, 2026

Copy link
Copy Markdown
Member

Summary

Implements a batch of rules derived from the CLEVR ACR (Application Consistency Rules) catalog (https://sdf-docs.clevr.com/?docs=acr-rules), mapped onto the Mendix model export formats that mxlint already parses. Also fixes four pre-existing rule tests and a CI-script bug that caused the test step to pass without running.

24 new rules, each with a passing _test.yaml, spanning all six ACR categories.

New rules

Project settings (001)

  • 001_0010 SecurityLevelCheckEverything — require SecurityLevel = CheckEverything
  • 001_0011 StrictPageUrlCheck — require strict page URL check enabled
  • 001_0012 AllowOneSessionPerUser — discourage multiple sessions per user
  • 001_0013 UnassignedModuleRole (cross-file JS) — module roles not assigned to any user role

Domain model (002)

  • 002_0010 AssociationOwnerBoth — discourage many-to-many owned by Both
  • 002_0011 EntityDocumentation — large entities should be documented
  • 002_0012 MultiLevelInheritance (cross-file JS) — inheritance limited to 2 levels
  • 002_0013 AttributeNaming — attributes should be PascalCase
  • 002_0014 AmountAccessRules — limit access rules per entity

Modules (003)

  • 003_0002 MinimumMendixVersion — flag unsupported Mendix major versions

Pages (004)

  • 004_0005 PageDocumentation — pages should be documented

Microflows (005)

  • 005_0006 MicroflowCallsSelf — detect direct recursion
  • 005_0007 LoopInLoop — detect nested loops
  • 005_0008 CommitWithoutEvents — commit/change without events
  • 005_0009 MicroflowDocumentation — complex microflows need documentation
  • 005_0010 CreateSystemEntity — don't instantiate System entities directly
  • 005_0011 TodoAnnotations — flag leftover TODO/FIXME annotations
  • 005_0012 ParameterAmount — limit microflow parameters
  • 005_0013 SplitCaption — splits should have a caption
  • 005_0014 NoContinueErrorHandling — don't silently continue on errors
  • 005_0015 LoopHeavyAction — heavy actions (retrieve, microflow call, etc.) inside a loop

Security (006)

  • 006_0002 ModuleRolesAmount — limit module roles per module
  • 006_0003 MicroflowEntityAccess — exposed microflows doing DB ops should apply entity access

Enumerations (007, new category)

  • 007_0001 EnumerationPrefix — enumerations should have a recognizable prefix

Cross-file rules

Three rules resolve related model files via mxlint.io.readYaml/listdir/isdir:

  • 002_0012 walks generalization chains across module domain models
  • 001_0013 enumerates every module's Security$ModuleSecurity
  • 001_0009 (existing) reads Security$ProjectSecurity

Because the test harness runs with the rule's own directory as the working directory, these tests rely on small fixture model files committed next to the rule (Inheritance/, ModuleA/, ModuleB/, Security$ProjectSecurity.yaml). The rule scanner only loads .rego/.js/.ts, so the fixtures are ignored as rules. Note: the release workflow only excludes *_test.*, so these fixture .yaml files would currently be included in the published ruleset zip — harmless (inert), but a maintainer may want to exclude them.

Fixes to pre-existing failures

  • 001_0007, 001_0008, 005_0005 shipped OPA-native _test.rego files, which mxlint test-rules never executes (it looks for _test.yaml). Converted to _test.yaml.
  • 001_0009 StrictModeWithReactClient: its test used an unsupported files: key and an input shape that did not match the rule, so it always failed. Rewritten against the real ProjectSettings shape with a Security$ProjectSecurity.yaml fixture.
  • scripts/run-policy-tests.sh: newer mxlint-cli releases removed the --rules flag, so test-rules --rules ./rules errored out — and because the error text isn't FAIL, the script reported success without running any tests. Now points test-rules at a generated config so the suite actually runs.

Verification

All 176 test cases pass (exit 0, no failures) under both a build from mxlint-cli HEAD and the released v3.16.0 binary, run exactly as CI does via the updated run-policy-tests.sh.

Scope note

Rules were selected where the intent maps cleanly to model structures confirmable from real Mendix exports. ACR rules requiring expression/dataflow analysis (unused variables, empty-object dereference, identical branches, xpath analysis) are not implemented, as they don't fit mxlint's per-file rego/JS model. Threshold-based rules use clearly-named constants at the top of each rule for easy tuning.

🤖 Generated with Claude Code

https://claude.ai/code/session_018DNq3pmSsKsrV77qQ6tUcW


Generated by Claude Code

claude added 9 commits July 11, 2026 10:07
Implement six new rules derived from the CLEVR ACR (Application
Consistency Rules) catalog, mapped onto the Mendix model export
formats that mxlint already parses. Each rule ships with a passing
_test.yaml covering compliant and non-compliant cases.

Microflows (005):
- 005_0006 MicroflowCallsSelf: detect direct recursion (self-call),
  including calls nested inside loops.
- 005_0007 LoopInLoop: detect loops nested inside another loop.
- 005_0008 CommitWithoutEvents: detect commit/change actions that
  commit without events.
- 005_0009 MicroflowDocumentation: require documentation on microflows
  above a minimum number of actions.

Domain model (002):
- 002_0010 AssociationOwnerBoth: discourage associations owned by Both.
- 002_0011 EntityDocumentation: require documentation on entities above
  a minimum number of attributes.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018DNq3pmSsKsrV77qQ6tUcW
Add two more ACR-derived rules, including the first cross-file rule that
reads related model files via mxlint.io.readYaml.

- 002_0012 MultiLevelInheritance (JavaScript, cross-file): walks the
  generalization chain across module domain models and flags entities
  that inherit through more than two levels. Ships with a fixture domain
  model (Inheritance/) that the test resolves via mxlint.io.readYaml.
- 005_0010 CreateSystemEntity: flags microflows that instantiate System
  entities (e.g. System.User, System.Session) directly instead of using
  Administration.Account.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018DNq3pmSsKsrV77qQ6tUcW
Add six more ACR-derived rules across project settings, domain model,
pages and microflows.

Project settings (001):
- 001_0010 SecurityLevelCheckEverything: require SecurityLevel = CheckEverything.
- 001_0011 StrictPageUrlCheck: require strict page URL check enabled.
- 001_0012 AllowOneSessionPerUser: discourage multiple sessions per user.

Domain model (002):
- 002_0013 AttributeNaming: attributes should be PascalCase.

Pages (004):
- 004_0005 PageDocumentation: pages should have documentation.

Microflows (005):
- 005_0011 TodoAnnotations: flag leftover TODO/FIXME annotations.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018DNq3pmSsKsrV77qQ6tUcW
001_0013 UnassignedModuleRole (JavaScript, cross-file): lists every
module's Security$ModuleSecurity via mxlint.io.listdir/isdir/readYaml and
flags module roles that are not assigned to any user role in the project
security. Ships with ModuleA/ModuleB fixtures for the test.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018DNq3pmSsKsrV77qQ6tUcW
- 002_0014 AmountAccessRules: limit access rules per entity.
- 005_0012 ParameterAmount: limit microflow parameters.
- 006_0002 ModuleRolesAmount: limit module roles per module.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018DNq3pmSsKsrV77qQ6tUcW
- 006_0003 MicroflowEntityAccess: exposed microflows performing database
  operations should apply entity access.
- 005_0013 SplitCaption: exclusive/inclusive splits should have a caption.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018DNq3pmSsKsrV77qQ6tUcW
- 007_0001 EnumerationPrefix (new enumerations category): enumerations
  should start with a recognizable prefix (ENU).
- 003_0002 MinimumMendixVersion: flag unsupported Mendix major versions
  read from Metadata ProductVersion.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018DNq3pmSsKsrV77qQ6tUcW
- 005_0014 NoContinueErrorHandling: flag activities that continue on
  error without handling it.
- 005_0015 LoopHeavyAction: flag heavy actions (retrieve, microflow call,
  web service, mapping, etc.) inside a loop.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018DNq3pmSsKsrV77qQ6tUcW
The three rego rules 001_0007, 001_0008 and 005_0005 shipped OPA-native
_test.rego files, which `mxlint test-rules` does not execute (it looks for
_test.yaml). Convert them to _test.yaml so they are actually exercised.

The 001_0009 strict-mode JavaScript rule reads Security$ProjectSecurity
via mxlint.io; its test used an unsupported `files:` key and the wrong
input shape, so it never passed. Rewrite the test to use the real
ProjectSettings shape plus a Security$ProjectSecurity.yaml fixture.

Also fix scripts/run-policy-tests.sh: newer mxlint-cli releases removed
the `--rules` flag, so `test-rules --rules ./rules` errored out and the
script reported success without running any tests. Point test-rules at a
generated config instead so the suite actually runs (176 test cases pass).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018DNq3pmSsKsrV77qQ6tUcW
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