From 7ac8ffa545cb025a540069ce481064164009b1d9 Mon Sep 17 00:00:00 2001 From: Gian Tschingt <336450015+GianTschingt@users.noreply.github.com> Date: Fri, 23 Oct 2026 08:59:41 +0200 Subject: [PATCH 1/2] LDEV-6699 decodeFromURL(): keep a % that is not a valid escape instead of throwing decodeURL used Canonicalize.decode(), whose reflective call into Lucee's URLDecoder always failed and fell back to java.net.URLDecoder, which throws on a bare %. It now uses the lenient Canonicalize.decodePercent() from LDEV-4743 after turning + into a space (form decoding as before; %2B still decodes to +). The now unused Canonicalize.decode() is removed. --- source/java/.flattened-pom.xml | 67 +++++++++++++++++++ .../guard/functions/GuardDecode.java | 9 ++- .../extension/guard/util/Canonicalize.java | 21 ------ tests/LDEV6699.cfc | 35 ++++++++++ 4 files changed, 109 insertions(+), 23 deletions(-) create mode 100644 source/java/.flattened-pom.xml create mode 100644 tests/LDEV6699.cfc diff --git a/source/java/.flattened-pom.xml b/source/java/.flattened-pom.xml new file mode 100644 index 0000000..b6997e2 --- /dev/null +++ b/source/java/.flattened-pom.xml @@ -0,0 +1,67 @@ + + + 4.0.0 + org.lucee + esapi + 3.1.0.5-SNAPSHOT + Guard Functions + Guard security functions implementation for Lucee + http://maven.lucee.org/esapi + + + Apache License, Version 2.0 + http://www.apache.org/licenses/LICENSE-2.0.txt + + + + + micstriit + Michael Offner + michael@lucee.org + Lucee Association Switzerland + http://lucee.org + + Project-Administrator + Developer + + +1 + + + + scm:git:git://github.com/lucee/extension-esapi.git + scm:git:git@github.com:lucee/extension-esapi.git + 3.1.0.5-SNAPSHOT + https://github.com/lucee/extension-esapi + + + + org.owasp.encoder + encoder + 1.4.0 + compile + + + org.jsoup + jsoup + 1.22.1 + compile + + + org.lucee + lucee + 7.0.0.395 + provided + + + + + + org.sonatype.central + central-publishing-maven-plugin + 0.8.0 + true + + + + diff --git a/source/java/src/org/lucee/extension/guard/functions/GuardDecode.java b/source/java/src/org/lucee/extension/guard/functions/GuardDecode.java index df9e816..5bb6f3a 100644 --- a/source/java/src/org/lucee/extension/guard/functions/GuardDecode.java +++ b/source/java/src/org/lucee/extension/guard/functions/GuardDecode.java @@ -52,8 +52,13 @@ public static String decode(String item, short decFrom) throws PageException { } - private static String decodeURL(String input) throws PageException { - return org.lucee.extension.guard.util.Canonicalize.decode(input); + /** + * URL (form) decoding: "+" becomes a space, valid %XX escapes are decoded and a "%" that does not start a + * valid escape is kept as is instead of throwing (LDEV-6699). "+" is replaced first, so an encoded plus + * (%2B) still decodes to "+". + */ + private static String decodeURL(String input) { + return org.lucee.extension.guard.util.Canonicalize.decodePercent(input.replace('+', ' ')); } /** diff --git a/source/java/src/org/lucee/extension/guard/util/Canonicalize.java b/source/java/src/org/lucee/extension/guard/util/Canonicalize.java index 5b54fa9..f4eb1b7 100644 --- a/source/java/src/org/lucee/extension/guard/util/Canonicalize.java +++ b/source/java/src/org/lucee/extension/guard/util/Canonicalize.java @@ -2,14 +2,12 @@ import java.io.ByteArrayOutputStream; import java.io.UnsupportedEncodingException; -import java.net.URLDecoder; import java.nio.ByteBuffer; import java.nio.charset.CharacterCodingException; import java.nio.charset.CharsetDecoder; import java.nio.charset.CodingErrorAction; import java.nio.charset.StandardCharsets; -import lucee.loader.engine.CFMLEngine; import lucee.loader.engine.CFMLEngineFactory; import lucee.runtime.exp.PageException; import lucee.runtime.util.Strings; @@ -203,25 +201,6 @@ private static int hex(char c) { return -1; } - public static String decode(String str) throws PageException { - CFMLEngine eng = CFMLEngineFactory.getInstance(); - try { - Class clazz = eng.getClassUtil().loadClass("lucee.commons.net.URLDecoder"); - - // decode(String str, boolean force) - return eng.getCastUtil().toString(eng.getClassUtil().callMethod(clazz, eng.getCreationUtil().createKey("decode"), new Object[] { str, false })); - } - catch (Exception e) { - try { - return URLDecoder.decode(str, "UTF-8"); - } - catch (UnsupportedEncodingException e1) { - throw eng.getCastUtil().toPageException(e); - } - } - - } - /** * Example usage * diff --git a/tests/LDEV6699.cfc b/tests/LDEV6699.cfc new file mode 100644 index 0000000..e17e189 --- /dev/null +++ b/tests/LDEV6699.cfc @@ -0,0 +1,35 @@ +component extends="org.lucee.cfml.test.LuceeTestCase" labels="guard" { + function run( testResults , testBox ) { + describe( title='Testcase for LDEV-6699: decodeFromURL() with a % that is not a valid escape' , body=function() { + + it( title='a trailing % is kept as is', body=function() { + expect( decodeFromURL( "100%" ) ).toBe( "100%" ); + expect( guardDecode( "url", "100%" ) ).toBe( "100%" ); + }); + + it( title='a % followed by non hex characters is kept as is', body=function() { + expect( decodeFromURL( "50% off" ) ).toBe( "50% off" ); + expect( decodeFromURL( "(80 - 100%).pdf" ) ).toBe( "(80 - 100%).pdf" ); + expect( decodeFromURL( "a%zzb" ) ).toBe( "a%zzb" ); + expect( decodeFromURL( "a%4" ) ).toBe( "a%4" ); + }); + + it( title='valid escapes next to a bare % are still decoded', body=function() { + expect( decodeFromURL( "%3Cb%3E 100%" ) ).toBe( " 100%" ); + expect( decodeFromURL( "%E2%82%AC 5%" ) ).toBe( "#chr( 8364 )# 5%" ); + expect( decodeFromURL( "100%25" ) ).toBe( "100%" ); + }); + + it( title='+ is decoded to a space, an encoded + stays a +', body=function() { + expect( decodeFromURL( "a+b" ) ).toBe( "a b" ); + expect( decodeFromURL( "1%2B1" ) ).toBe( "1+1" ); + expect( decodeFromURL( "50%+off" ) ).toBe( "50% off" ); + }); + + it( title='decodeFromURL() is the inverse of encodeForURL()', body=function() { + var str = "a b+c&d=e/100%"; + expect( decodeFromURL( encodeForURL( str ) ) ).toBe( str ); + }); + }); + } +} From 0155ef6e1ce01496d13a63ff5544cacde09532f0 Mon Sep 17 00:00:00 2001 From: Gian Tschingt <336450015+GianTschingt@users.noreply.github.com> Date: Fri, 23 Oct 2026 09:00:03 +0200 Subject: [PATCH 2/2] LDEV-6699 remove generated .flattened-pom.xml added by mistake --- source/java/.flattened-pom.xml | 67 ---------------------------------- 1 file changed, 67 deletions(-) delete mode 100644 source/java/.flattened-pom.xml diff --git a/source/java/.flattened-pom.xml b/source/java/.flattened-pom.xml deleted file mode 100644 index b6997e2..0000000 --- a/source/java/.flattened-pom.xml +++ /dev/null @@ -1,67 +0,0 @@ - - - 4.0.0 - org.lucee - esapi - 3.1.0.5-SNAPSHOT - Guard Functions - Guard security functions implementation for Lucee - http://maven.lucee.org/esapi - - - Apache License, Version 2.0 - http://www.apache.org/licenses/LICENSE-2.0.txt - - - - - micstriit - Michael Offner - michael@lucee.org - Lucee Association Switzerland - http://lucee.org - - Project-Administrator - Developer - - +1 - - - - scm:git:git://github.com/lucee/extension-esapi.git - scm:git:git@github.com:lucee/extension-esapi.git - 3.1.0.5-SNAPSHOT - https://github.com/lucee/extension-esapi - - - - org.owasp.encoder - encoder - 1.4.0 - compile - - - org.jsoup - jsoup - 1.22.1 - compile - - - org.lucee - lucee - 7.0.0.395 - provided - - - - - - org.sonatype.central - central-publishing-maven-plugin - 0.8.0 - true - - - -