Give cfproperty and cfparam their real attribute sets - #62
Merged
Conversation
CFPropertyStatement's validAttributes held cfparam's six -- name, type, default, max, min, pattern. cfproperty accepts 65. Every ORM and bean property in real code carries getter, setter, persistent, fieldtype or ormtype, and all of them counted as invalid. getValidAttributes() is public static, so consumers asking cfparser what a property may carry were handed cfparam's answer. CFParamStatement was missing MAXLENGTH, so param name="x" maxlength="5" counted as invalid too. Both sets now come from the shipped dictionaries, which agree on all three tags across cf11 and lucee5, so a hardcoded set can be right for both dialects. CFLockStatement is the one place validateAttributes is actually called, and its five match the dictionary exactly -- the mechanism was never the problem, the data was. ## Still not wired up #39 left open whether to call validateAttributes for param and property. The answer is no, and the sets above are why it can now be argued rather than guessed. validateAttributes throws and parseScript does not catch, so one unrecognised attribute aborts the whole file rather than reporting an error on the statement. These sets are pinned to dictionaries that ship with the parser and will fall behind whatever the next CF release adds -- so that abort would eventually land on valid code, in a parser whose job is to survive unfamiliar input. The wrong sets made this a much bigger hazard than it looked: enabling property validation would have rejected almost every persistent CFC ever written. cfadmin is left alone. Its set carries RETURNVARIABLE, which the lucee4.5 dictionary omits but Lucee does accept, so it is a permissive superset rather than a bug. 322 tests, ./gradlew build, CFLint's 675 against a clean build. Three of the four new tests fail with cfml.parsing/src/main stashed; the fourth pins that unknown attributes are still rejected, so it passes either way by design. Refs #39, #43
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Resolves the question #43 left open, by answering it with evidence rather than judgement. Refs #39.
The blocker was never the wiring
#43 deferred "should
validateAttributesbe wired up for param and property?" on the grounds that it throws. Going to answer that, I checked whether the allowed sets were even correct. They are not.CFLockStatementCFParamStatementMAXLENGTHCFPropertyStatementCFPropertyStatement.validAttributeswasNAME,TYPE,DEFAULT,MAX,MIN,PATTERN— a copy of cfparam's list. Only the first three are cfproperty attributes at all. Sogetter,setter,persistent,fieldtype,ormtype,required,hint— the attributes on essentially every accessor-generating or persistent CFC — all counted as invalid.That reframes the original question entirely. Wiring property validation up would not have been a debatable trade; it would have rejected almost every ORM entity ever written.
getValidAttributes()ispublic staticon both classes, so this was reachable from outside regardless of wiring. That makes it a live bug, not dormant code.Both sets now come from the dictionaries
The shipped dictionaries agree on all three tags across
cf11andlucee5— no dialect divergence — so a hardcoded set can be correct for both:CFLockStatementmatching the dictionary exactly is the useful control here: the validation mechanism was never broken, only the data it was given.Still not wired up, and now for a better reason
validateAttributesthrows,parseScriptdoes not catch, so one unrecognised attribute aborts the whole file instead of reporting an error on the statement. There is no error-reporting path throughCFScriptStatementVisitorto change that without real plumbing.The decisive argument is staleness: these sets are pinned to dictionaries that ship with the parser, and will fall behind whatever the next CF release adds. A hardcoded allowlist that throws is a time bomb aimed at valid code, in a parser whose job is to survive unfamiliar input. Correct data makes that failure rarer, not impossible.
So the sets are now right for anyone who asks — including CFLint, which has the dictionary and a proper diagnostic channel — and the parser still does not abort files over an attribute it has not heard of.
cfadminis deliberately untouched: its set carriesRETURNVARIABLE, which thelucee4.5dictionary omits but Lucee accepts. A permissive superset, not a bug.Tests
Four added. Three fail with
cfml.parsing/src/mainstashed:The fourth,
testPropertyStillRejectsUnknownAttributes, passes either way by design — it guards the rejection path so a future widening of the set cannot quietly turn validation into a no-op.Verification
./gradlew buildGenerated by Claude Code