Skip to content

fix(events): detect RustCFML under reportAsLucee impersonation (v0.507+)#3327

Open
bpamiri wants to merge 1 commit into
developfrom
peter/rustcfml-reportaslucee-detection
Open

fix(events): detect RustCFML under reportAsLucee impersonation (v0.507+)#3327
bpamiri wants to merge 1 commit into
developfrom
peter/rustcfml-reportaslucee-detection

Conversation

@bpamiri

@bpamiri bpamiri commented Jul 23, 2026

Copy link
Copy Markdown
Collaborator

Summary

RustCFML v0.507.0 turned on reportAsLucee by default: server.coldfusion.productName now reports "Lucee" (with Lucee's classic productVersion), and the real engine version moves to server.lucee.version behind a Lucee-major prefix (7.0.519.0 = RustCFML 0.519.0). The upstream commit explicitly names Wheels' engine gate as the motivation and documents server.lucee.versionName == "RustCFML" as the stable identity marker (their own isRustCFML() BIF keys on it).

$detectEngine() identified RustCFML solely by productName == "RustCFML", so on v0.507+ it fell through to the Lucee branch and selected LuceeAdapter — making RustCFMLAdapter dead code. Concrete misroutes observed:

  • isRustCFML() → false: EngineCapabilitiesSpec asserted JVM class-loading on a JVM-less engine (the ladder failure that surfaced this)
  • supportsImageInfo() → true (Base default): $imageTag() proceeds into cfimage action="info" paths that error on RustCFML
  • databaseAdapters/Base.cfc serverName == "Lucee" branch → the Lucee-only psq=false cfquery attribute applied to every query
  • $checkMinimumVersion satisfied by the impersonated version string instead of the accept-any-version RustCFML branch

Fix

Add a detection branch matching server.lucee.versionName == "RustCFML" ordered before the Lucee branch, deriving serverVersion by dropping the impersonated major segment (ListRest, so a future 8.-prefix keeps working). Pre-v0.507 scopes still hit the original productName branch first; real Lucee carries a different versionName (e.g. Gelert) and falls through unchanged.

Tests

  • New detection-ladder spec with the exact v0.519.0 impersonating scope shape; existing Lucee fixture extended with a realistic versionName to pin the negative path.
  • Full core suite green locally on Lucee 7 + SQLite (4694 passed).
  • Live-verified against the actual RustCFML v0.519.0 binary: detection selects RustCFML, the previously-failing EngineCapabilitiesSpec guard fires and passes, and a bad-spec set diff vs the unfixed run shows zero collateral regressions. (It also exposed that migrationSpec's multi-column-index "fix" on the ladder was an artifact of the psq=false misroute — it reverts to its known-failing baseline state with correct detection.)

Upstream context: RustCFML/RustCFML#283, RustCFML/RustCFML#284, RustCFML/RustCFML#285 filed today for the three genuine v0.482–v0.511 engine regressions found in the same investigation.

🤖 Generated with Claude Code

RustCFML v0.507.0 defaults reportAsLucee to true: server.coldfusion.productName
now reports "Lucee" (with Lucee's productVersion) and the real engine version
moves to server.lucee.version behind a Lucee-major prefix ("7.0.519.0" means
RustCFML 0.519.0). $detectEngine keyed solely on productName == "RustCFML", so
v0.507+ fell through to the Lucee branch, instantiated LuceeAdapter, and every
RustCFMLAdapter override went dead (isRustCFML() false, supportsImageInfo()
true, the Lucee-only psq=false cfquery attribute applied, version gate
satisfied by the impersonated string).

Match the marker upstream documents as stable identity — their own
isRustCFML() BIF keys on it — server.lucee.versionName == "RustCFML", ordered
before the Lucee branch, deriving the version by dropping the impersonated
major segment. Pre-v0.507 scopes still hit the productName branch first; real
Lucee carries a different versionName and falls through unchanged.

Verified live against the v0.519.0 binary: detection selects RustCFML again
and the EngineCapabilitiesSpec guard fires (spec passes) with zero collateral
spec regressions.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: Peter Amiri <petera@pai.com>

@wheels-bot wheels-bot Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wheels Bot — Reviewer

TL;DR — This PR fixes engine detection so RustCFML v0.507+ running in its default reportAsLucee mode is no longer misclassified as Lucee (which had turned RustCFMLAdapter into dead code). It adds a server.lucee.versionName == "RustCFML" detection branch ordered before the Lucee branch, plus a positive spec for the impersonation scope and a negative-path pin on the real-Lucee fixture. The change is small, correct, cross-engine safe, and well-tested. Verdict: approve.

Correctness

The new branch at vendor/wheels/events/onapplicationstart.cfc:562-569 is ordered correctly — after the productName == "RustCFML" branch (pre-v0.507 path) and before the generic Lucee branch, so both RustCFML variants resolve before real Lucee can claim the scope.

Version derivation is right: ListRest("7.0.519.0", ".") yields "0.519.0", dropping only the impersonated Lucee major. This produces the same 0.MINOR.PATCH shape the pre-v0.507 productName branch produces (the existing spec at line 202-203 uses "0.417.0"), and RustCFMLAdapter(serverVersion) consumes that format unchanged. Using ListRest (drop-first) rather than a hardcoded 7. strip also keeps a future 8.-prefix working.

Guards are safe: the StructKeyExists checks on lucee and versionName mean a Lucee scope lacking versionName cleanly falls through to the plain Lucee branch (no null deref).

Cross-engine

No concerns. ListRest, StructKeyExists, and string == are portable across Lucee 5/6/7, Adobe 2018-2025, and BoxLang. No closures, no struct member-function calls, no reserved-scope shadowing, no application-scope function members — none of the cross-engine invariants in CLAUDE.md are touched.

Tests

Good coverage. The new spec (engineAdapterSpec.cfc:206) exercises the exact v0.519.0 impersonating scope shape and asserts both serverName and the stripped serverVersion. The negative path is pinned by extending the existing Lucee fixture with a realistic versionName ("Gelert", line ~225), proving real Lucee still falls through. Uses BDD wheels.WheelsTest correctly.

Docs

  • Changelog fragment present and correctly named: changelog.d/rustcfml-reportaslucee-detection.fixed.md (.fixed type, no direct CHANGELOG.md edit).
  • The $detectEngine() docblock is updated to explain the v0.507+ reportAsLucee behavior.
  • Nit (non-blocking): the PR carries docs + enhancement labels, but this is a fix. Worth a maintainer relabel for triage accuracy — not a code issue.

Commits

fix(events): detect RustCFML under reportAsLucee impersonation (v0.507+) — valid conventional-commit type/scope, header well under 100 chars, sign-off present (Signed-off-by: Peter Amiri) matching the author. The body explains the why (which markers are stable, why ordering matters), not just the what.

@alexskinner

Copy link
Copy Markdown

server.lucee.versionname tells you its RustCFML

The purpose of the change was to make sure the various ways that frameworks check for Lucee could find it in order to go down the lucee compatibility path but if they looks at the actual lucee versionname they could see it was RustCFML

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants