Malformed Expression Delimiters
ID |
html.jsf_expr_syntactically_valid |
Severity |
high |
Remediation Complexity |
trivial |
Remediation Risk |
low |
Remediation Effort |
low |
Resource |
Reliability |
Language |
Html |
Tags |
best-practice, reliability |
Description
Reports an expression whose delimiters do not balance, in an attribute value or in page text. Four cases are reported:
| Case | Example |
|---|---|
Never closed |
|
Nothing to evaluate |
|
A closer with no expression open |
|
An expression opened inside an expression |
|
Both #{…} and ${…} are read. The check is not specific to one view technology even though
the syntax came from Jakarta Faces: a template engine that writes its message and variable
expressions the same way breaks on an unterminated one in exactly the same manner.
What this rule does NOT check
Delimiter structure only. The expression language itself is not parsed. This rule says nothing about:
-
whether
#{userBean.nmae}names a property that exists; -
whether the operators, literals and function calls between the braces are legal expression language;
-
whether a method expression has the right arity or signature;
-
whether the value is of a type the attribute accepts.
Each of those needs a real expression-language parser bound to the application’s bean model, which no static reading of the markup has. What is reported is the part that is decidable from the text alone.
The scan is conservative for the same reason. A value is examined only once it contains an { or
${ opener at all, so a JavaScript event handler, a {{…}} interpolation and a JSON data
attribute are never read. Within a value, braces are counted rather than matched pairwise, so a
collection literal nested in an expression and a JavaScript block sitting beside one both balance;
quoting is honoured, so a brace inside a string literal is text; and a backslash escapes the
character after it, so \{…} is the literal it is meant to be.
Text inside a <script>, a <pre>, a <code> or a <textarea> is not read at all. What is
written there is code in another language printed verbatim, and a JavaScript template literal spells
its placeholder ${…} exactly as an expression does — a nested one inside a code sample would
otherwise be reported as an expression inside an expression.
A Thymeleaf preprocessing substitution — #{order.error.msg.${code}} — is not a nested
expression either. The fragment between the two __ marks is evaluated first and its result spliced
into the expression around it, so the two never evaluate at once.
Rationale
<h:outputText value="#{userBean.name" />
<p>Total: #{cart.total</p>
<h:commandButton value="Save" action="#{orderBean.save" />
What makes a malformed expression worth a rule is how it fails. It is not an error the container
reports — it is text the container prints. The page renders with #{userBean.name written across it
where the user’s name should be, and the only thing that catches it is somebody looking at that
page in a browser, in that state, and recognising it as wrong rather than as placeholder copy.
The failure survives every check that would normally catch a typo. It compiles, because there is nothing to compile. It passes a schema validation of the markup, because the value is a well-formed attribute value. It passes a unit test of the backing bean, because the bean is never called. And it is invisible in a diff review, because a missing closing brace at the end of a long attribute value looks like every other long attribute value.
The third line above is the one that costs most. An unterminated expression in an action
attribute is not a rendering blemish: the button loses its behaviour entirely, because the
container has no method expression to invoke and the attribute is a literal string instead. The page
looks correct and the button does nothing.
An empty #{} and a surplus closing brace fail the same silent way — a stray } is simply printed
after the value it follows.
Remediation
Balance the delimiters.
<h:outputText value="#{userBean.name}" />
<p>Total: #{cart.total}</p>
<h:commandButton value="Save" action="#{orderBean.save}" />
A closing brace that belongs to the text rather than to an expression needs no expression syntax
around it, and a literal { is written \{:
<h:outputText value="Braces { but no expression }" />
<h:outputText value="\#{notAnExpression}" />
Because these findings render rather than fail, they are worth fixing in bulk once found: a page that has one unterminated expression usually has it in the block that was last edited, and its neighbours are worth reading at the same time.