decodeFromURL fails with various keywords

Hi,
I just discovered a weierd behavior with the decodeFromURL function:
When encoding a URL and then decoding it again sometimes decodeFromURL fails with a org.owasp.esapi.errors.IntrusionException: Input validation failure
Error Message:
Multiple (2x) and mixed encoding (2x) detected in <string>

Log Entry
"ERROR","FILES","03/28/2023","10:19:43","coldbox.system.Bootstrap",
"Error: Input validation failure | ExtraInfo:{
    "Extended_Info":"",
    "Message":"Input validation failure",
    "UserMessage":"Input validation failure",
    "Detail":"",
    "additional":{},
    "TagContext":<TagContext>,
    "ErrorCode":"0",
    "LogMessage":"Multiple (2x) and mixed encoding (2x) detected in <string>",
    "type":"org.owasp.esapi.errors.IntrusionException",
    "StackTrace":"lucee.runtime.exp.NativeException: Input validation failure
                    at org.owasp.esapi.reference.DefaultEncoder.canonicalize(DefaultEncoder.java:182)
                    at org.owasp.esapi.reference.DefaultEncoder.canonicalize(DefaultEncoder.java:130)
                    at org.owasp.esapi.reference.DefaultEncoder.decodeFromURL(DefaultEncoder.java:437)
                    at org.lucee.extension.esapi.functions.ESAPIDecode.decode(ESAPIDecode.java:41)
                    at org.lucee.extension.esapi.functions.DecodeFromURL.call(DecodeFromURL.java:30)
                    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
                    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
                    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
                    at java.base/java.lang.reflect.Method.invoke(Method.java:566)
                    at lucee.runtime.reflection.pairs.MethodInstance.invoke(MethodInstance.java:56)
                    at lucee.runtime.reflection.Reflector.callStaticMethod(Reflector.java:951)
                    at lucee.runtime.functions.BIFProxy.invoke(BIFProxy.java:42)
                    at lucee.runtime.functions.FunctionHandlerPool.invoke(FunctionHandlerPool.java:40)
                    ... 2 more\n
                    at lucee.runtime.type.UDFImpl.implementation(UDFImpl.java:112)
                    at lucee.runtime.type.UDFImpl._call(UDFImpl.java:350)
                    at lucee.runtime.type.UDFImpl.call(UDFImpl.java:223)
                    at lucee.runtime.ComponentImpl._call(ComponentImpl.java:696)
                    at lucee.runtime.ComponentImpl._call(ComponentImpl.java:584)
                    at lucee.runtime.ComponentImpl.call(ComponentImpl.java:1931)
                    at lucee.runtime.util.VariableUtilImpl.callFunctionWithoutNamedValues(VariableUtilImpl.java:787)
                    at lucee.runtime.PageContextImpl.getFunction(PageContextImpl.java:1775)
                    ... 2 more\n
                    at lucee.runtime.type.UDFImpl.implementation(UDFImpl.java:112)
                    at lucee.runtime.type.UDFImpl._call(UDFImpl.java:350)
                    at lucee.runtime.type.UDFImpl.call(UDFImpl.java:223)
                    at lucee.runtime.type.scope.UndefinedImpl.call(UndefinedImpl.java:786)
                    at lucee.runtime.util.VariableUtilImpl.callFunctionWithoutNamedValues(VariableUtilImpl.java:787)
                    at lucee.runtime.PageContextImpl.getFunction(PageContextImpl.java:1775)
                    ... 2 more\n
                    at lucee.runtime.type.UDFImpl.implementation(UDFImpl.java:112)
                    at lucee.runtime.type.UDFImpl._call(UDFImpl.java:350)
                    at lucee.runtime.type.UDFImpl.callWithNamedValues(UDFImpl.java:213)
                    at lucee.runtime.ComponentImpl._call(ComponentImpl.java:697)
                    at lucee.runtime.ComponentImpl._call(ComponentImpl.java:584)
                    at lucee.runtime.ComponentImpl.callWithNamedValues(ComponentImpl.java:1950)
                    at lucee.runtime.util.VariableUtilImpl.callFunctionWithNamedValues(VariableUtilImpl.java:866)
                    at lucee.runtime.PageContextImpl.getFunctionWithNamedValues(PageContextImpl.java:1794)
                    at system.web.controller_cfc$cf.udfCall3(/coldbox/system/web/Controller.cfc:877)
                    at system.web.controller_cfc$cf.udfCall(/coldbox/system/web/Controller.cfc)
                    at lucee.runtime.type.UDFImpl.implementation(UDFImpl.java:112)
                    at lucee.runtime.type.UDFImpl._call(UDFImpl.java:350)
                    at lucee.runtime.type.UDFImpl.callWithNamedValues(UDFImpl.java:213)
                    at lucee.runtime.type.scope.UndefinedImpl.callWithNamedValues(UndefinedImpl.java:804)
                    at lucee.runtime.util.VariableUtilImpl.callFunctionWithNamedValues(VariableUtilImpl.java:866)
                    at lucee.runtime.PageContextImpl.getFunctionWithNamedValues(PageContextImpl.java:1794)
                    at system.web.controller_cfc$cf.udfCall3(/coldbox/system/web/Controller.cfc:658)
                    at system.web.controller_cfc$cf.udfCall(/coldbox/system/web/Controller.cfc)
                    at lucee.runtime.type.UDFImpl.implementation(UDFImpl.java:112)
                    at lucee.runtime.type.UDFImpl._call(UDFImpl.java:350)
                    at lucee.runtime.type.UDFImpl.callWithNamedValues(UDFImpl.java:213)
                    at lucee.runtime.ComponentImpl._call(ComponentImpl.java:697)
                    at lucee.runtime.ComponentImpl._call(ComponentImpl.java:584)
                    at lucee.runtime.ComponentImpl.callWithNamedValues(ComponentImpl.java:1950)
                    at lucee.runtime.util.VariableUtilImpl.callFunctionWithNamedValues(VariableUtilImpl.java:866)
                    at lucee.runtime.PageContextImpl.getFunctionWithNamedValues(PageContextImpl.java:1794)
                    at coldbox.system.bootstrap_cfc$cf.udfCall1(/coldbox/system/Bootstrap.cfc:290)
                    at coldbox.system.bootstrap_cfc$cf.udfCall(/coldbox/system/Bootstrap.cfc)
                    at lucee.runtime.type.UDFImpl.implementation(UDFImpl.java:112)
                    at lucee.runtime.type.UDFImpl._call(UDFImpl.java:350)
                    at lucee.runtime.type.UDFImpl.call(UDFImpl.java:223)
                    at lucee.runtime.type.scope.UndefinedImpl.call(UndefinedImpl.java:786)
                    at lucee.runtime.util.VariableUtilImpl.callFunctionWithoutNamedValues(VariableUtilImpl.java:787)
                    at lucee.runtime.PageContextImpl.getFunction(PageContextImpl.java:1775)
                    at coldbox.system.bootstrap_cfc$cf.udfCall1(/coldbox/system/Bootstrap.cfc:507)
                    at coldbox.system.bootstrap_cfc$cf.udfCall(/coldbox/system/Bootstrap.cfc)
                    at lucee.runtime.type.UDFImpl.implementation(UDFImpl.java:112)
                    at lucee.runtime.type.UDFImpl._call(UDFImpl.java:350)
                    at lucee.runtime.type.UDFImpl.call(UDFImpl.java:223)
                    at lucee.runtime.ComponentImpl._call(ComponentImpl.java:696)
                    at lucee.runtime.ComponentImpl._call(ComponentImpl.java:584)
                    at lucee.runtime.ComponentImpl.call(ComponentImpl.java:1931)
                    at lucee.runtime.util.VariableUtilImpl.callFunctionWithoutNamedValues(VariableUtilImpl.java:787)
                    at lucee.runtime.PageContextImpl.getFunction(PageContextImpl.java:1775)
                    at application_cfc$cf.udfCall(/Application.cfc:176)
                    at lucee.runtime.type.UDFImpl.implementation(UDFImpl.java:112)
                    at lucee.runtime.type.UDFImpl._call(UDFImpl.java:350)
                    at lucee.runtime.type.UDFImpl.call(UDFImpl.java:223)
                    at lucee.runtime.ComponentImpl._call(ComponentImpl.java:696)
                    at lucee.runtime.ComponentImpl._call(ComponentImpl.java:584)
                    at lucee.runtime.ComponentImpl.call(ComponentImpl.java:1931)
                    at lucee.runtime.listener.ModernAppListener.call(ModernAppListener.java:444)
                    at lucee.runtime.listener.ModernAppListener._onRequest(ModernAppListener.java:135)
                    at lucee.runtime.listener.MixedAppListener.onRequest(MixedAppListener.java:44)
                    at lucee.runtime.PageContextImpl.execute(PageContextImpl.java:2493)
                    at lucee.runtime.PageContextImpl._execute(PageContextImpl.java:2478)
                    at lucee.runtime.PageContextImpl.executeCFML(PageContextImpl.java:2449)
                    at lucee.runtime.engine.Request.exe(Request.java:45)
                    at lucee.runtime.engine.CFMLEngineImpl._service(CFMLEngineImpl.java:1216)
                    at lucee.runtime.engine.CFMLEngineImpl.serviceCFML(CFMLEngineImpl.java:1162)
                    at lucee.loader.engine.CFMLEngineWrapper.serviceCFML(CFMLEngineWrapper.java:97)
                    at lucee.loader.servlet.CFMLServlet.service(CFMLServlet.java:51)
                    at javax.servlet.http.HttpServlet.service(HttpServlet.java:741)
                    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231)
                    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
                    at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53)
                    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
                    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
                    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:202)
                    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96)
                    at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:541)
                    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:139)
                    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92)
                    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74)
                    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343)
                    at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:373)
                    at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65)
                    at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:868)
                    at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1590)
                    at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
                    at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
                    at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
                    at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
                    at java.base/java.lang.Thread.run(Thread.java:834)
                  Caused by: org.owasp.esapi.errors.IntrusionException: Input validation failure
                    ... 117 more\n","ExtendedInfo":""}"

After some testing I discoverd that this happens everytime when using a query param like “LANG” or “PARAM” in my query String.

I prepared a small template to reproduce my issues:
TryCF:TryCF.com sample

Test script
<cfscript>
posTestURL = "https://test.example/api/v1/index.html?uname=tester&lanu=en&prm123=test";
negTestURL = "https://test.example/api/v1/index.html?uname=tester&lang=en&param1=test";
try{
    enc = encodeForURL( posTestURL );
    writeDump( var = enc, label = "Encoded posTestURL" );
    dec = decodeFromURL( enc );
    writeDump( var = dec, label = "Decoded posTestURL" );
    enc = encodeForURL( negTestURL );
    writeDump( var = enc, label = "Encoded negTestURL" );
    dec = decodeFromURL( enc );
    writeDump( var = dec, label = "Decoded negTestURL" );
} catch ( any error ) {
    writeDump( var = error, label = "Exception" );
}
</cfscript>

I have to wonder if this is a bug in the function o if this is expected: As I am calling the function decodeFromURL I would expect that the only encoding expected is URL encoding. If i would have called the decode function this error would make more sense to me as the encoding has to be identified first.

Sidenote: The code sample above fails in ACF10 & ACF11 but works fine from ACF2016 on.

OS: Debian 5.10.162-1 (2023-01-21) x86_64
Java Version: 11.0.7 (AdoptOpenJDK) 64bit
Tomcat Version: Apache Tomcat/9.0.35
Lucee Version: Lucee 5.3.10.120

@christian_h created a ticket for this issue in JIRA: [LDEV-4437] - Lucee

1 Like