7.0 is bundled with the new v3 ext (announcement coming, I have a long list ATM) which switches to the lightweight OWSAP encoder.
The encoding output with v3 is more nuanced than the older version, which encoded all characters
https://luceeserver.atlassian.net/browse/LDEV-6160
It’s smaller and way less complicated that the ESAPI Library we have been using, which still requires libs with CVEs and is a bit of a clusterfuck and has caused endless stability problems and tickets
https://luceeserver.atlassian.net/browse/LDEV-5960
WORKAROUND you can simply downgrade to the last v2 version
ESAPI vs OWASP Encoder: encodeForHTMLAttribute Comparison
Background
Lucee 7’s ESAPI extension (v3.0.0.11-BETA) switched from the old ESAPI library to OWASP Encoder on January 7, 2026 (commit b28c5c8). This changes the behaviour of encodeForHTMLAttribute() and related functions.
Summary
| Library | Approach | Example: "hello world" |
|---|---|---|
| ESAPI (Lucee 6) | Encodes almost everything | hello world |
| OWASP Encoder (Lucee 7) | Encodes only dangerous chars | hello world |
Detailed Character Comparison
| Input | ESAPI (Lucee 6) | OWASP (Lucee 7) | Different? |
|---|---|---|---|
hello world |
hello world |
hello world |
Yes |
hello\tworld |
hello	world |
hello\tworld |
Yes |
hello\nworld |
hello
world |
hello\nworld |
Yes |
a=b |
a=b |
a=b |
Yes |
a:b |
a:b |
a:b |
Yes |
a/b |
a/b |
a/b |
Yes |
a'b |
a'b |
a'b |
Yes (format) |
a"b |
a"b |
a"b |
Yes (format) |
a<b |
a<b |
a<b |
Yes (format) |
a>b |
a>b |
a>b |
Yes |
a&b |
a&b |
a&b |
Yes (format) |
a,b |
a,b |
a,b |
No |
a.b |
a.b |
a.b |
No |
a-b |
a-b |
a-b |
No |
a_b |
a_b |
a_b |
No |
a!b |
a!b |
a!b |
Yes |
a@b |
a@b |
a@b |
Yes |
a#b |
a#b |
a#b |
Yes |
a$b |
a$b |
a$b |
Yes |
a%b |
a%b |
a%b |
Yes |
a^b |
a^b |
a^b |
Yes |
a*b |
a*b |
a*b |
Yes |
a(b |
a(b |
a(b |
Yes |
a)b |
a)b |
a)b |
Yes |
a+b |
a+b |
a+b |
Yes |
a;b |
a;b |
a;b |
Yes |
a`b |
a`b |
a`b |
Yes |
a~b |
a~b |
a~b |
Yes |
a[b |
a[b |
a[b |
Yes |
a]b |
a]b |
a]b |
Yes |
a{b |
a{b |
a{b |
Yes |
a}b |
a}b |
a}b |
Yes |
a|b |
a|b |
a|b |
Yes |
a\b |
a\b |
a\b |
Yes |
Immune Characters (Not Encoded)
| Library | Immune Characters |
|---|---|
| ESAPI | Alphanumeric + , . - _ |
| OWASP | Everything except & < ' " |
Entity Format Differences
Even for characters both libraries encode, the format differs:
| Character | ESAPI | OWASP |
|---|---|---|
& |
& (hex) |
& (named) |
< |
< (hex) |
< (named) |
' |
' (hex) |
' (decimal) |
" |
" (hex) |
" (decimal) |
Security Analysis
Both approaches are equally secure for quoted HTML attributes. The difference is philosophical:
- ESAPI: “Encode everything that isn’t explicitly safe” (paranoid/allowlist)
- OWASP: “Encode only what’s actually dangerous” (minimal/blocklist)
In a properly quoted HTML attribute like value="..." or value='...':
- Spaces, tabs, newlines are safe
- Equals signs, colons, slashes are safe
- Most punctuation is safe
- Only
&,<, and the quote character used need encoding
Why This Matters
- Test failures: Unit tests comparing exact encoded output will fail
- String length: OWASP output is shorter (more efficient)
- Readability: OWASP output is more human-readable
- Functionally equivalent: Both decode to the same result in browsers
Recommendation
Update tests to either:
- Compare decoded values rather than encoded strings
- Accept both encoding formats as valid
- Use pattern matching that allows for different entity formats
Source Code References
- ESAPI immune chars:
DefaultEncoder.javaline 97:IMMUNE_HTMLATTR = {',', '.', '-', '_'} - OWASP attribute encoding:
XMLEncoder.javaMode.ATTRIBUTE encodes only"&<\'\"" - Lucee extension switch:
extension-esapicommitb28c5c8(Jan 7, 2026)