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
-
-
-
-