CVE-2021-28170 Fix expression delimiter escaping (#160)

Co-authored-by: rmartinc rmartinc@redhat.com
diff --git a/impl/src/main/java/com/sun/el/parser/ELParser.jjt b/impl/src/main/java/com/sun/el/parser/ELParser.jjt
index 9411460..2757ebd 100644
--- a/impl/src/main/java/com/sun/el/parser/ELParser.jjt
+++ b/impl/src/main/java/com/sun/el/parser/ELParser.jjt
@@ -453,8 +453,8 @@
   < LITERAL_EXPRESSION:
     ((~["\\", "$", "#"])
       | ("\\" ("\\" | "$" | "#"))
-      | ("$" ~["{", "$", "#"])
-      | ("#" ~["{", "$", "#"])
+      | ("$" ~["{", "$", "#", "\\"])
+      | ("#" ~["{", "$", "#", "\\"])
     )+
     | "$"
     | "#"
diff --git a/impl/src/main/java/com/sun/el/parser/ELParserTokenManager.java b/impl/src/main/java/com/sun/el/parser/ELParserTokenManager.java
index 7cafd4e..077425e 100644
--- a/impl/src/main/java/com/sun/el/parser/ELParserTokenManager.java
+++ b/impl/src/main/java/com/sun/el/parser/ELParserTokenManager.java
@@ -16,8 +16,6 @@
  
 /* Generated By:JJTree&JavaCC: Do not edit this line. ELParserTokenManager.java */
 package com.sun.el.parser;
-import java.io.StringReader;
-import jakarta.el.ELException;
 
 /** Token Manager. */
 public class ELParserTokenManager implements ELParserConstants
@@ -201,7 +199,7 @@
                   jjCheckNAddStates(0, 3);
                   break;
                case 4:
-                  if ((0xf7ffffffffffffffL & l) == 0L)
+                  if ((0xf7ffffffefffffffL & l) == 0L)
                      break;
                   if (kind > 1)
                      kind = 1;
diff --git a/src/test/java/org/glassfish/el/test/EscapingTest.java b/src/test/java/org/glassfish/el/test/EscapingTest.java
new file mode 100644
index 0000000..b21e72b
--- /dev/null
+++ b/src/test/java/org/glassfish/el/test/EscapingTest.java
@@ -0,0 +1,55 @@
+package org.glassfish.el.test;
+
+import jakarta.el.ELManager;
+import jakarta.el.ELProcessor;
+import jakarta.el.ValueExpression;
+import org.junit.Assert;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+
+public class EscapingTest {
+
+    static ELProcessor elp;
+    static ELManager elm;
+
+    public EscapingTest() {
+    }
+
+    @BeforeClass
+    public static void setUpClass() throws Exception {
+        elp = new ELProcessor();
+        elm = elp.getELManager();
+    }
+
+    @Test
+    public void testEscape01() {
+        assertEquals("$2", evaluateExpression("$${1+1}"));
+        assertEquals("$${1+1}", evaluateExpression("$\\${1+1}"));
+    }
+
+    @Test
+    public void testEscape02() {
+        assertEquals("$2", evaluateExpression("$#{1+1}"));
+        assertEquals("$#{1+1}", evaluateExpression("$\\#{1+1}"));
+    }
+
+    @Test
+    public void testEscape03() {
+        assertEquals("#2", evaluateExpression("##{1+1}"));
+        assertEquals("##{1+1}", evaluateExpression("#\\#{1+1}"));
+    }
+
+    @Test
+    public void testEscape04() {
+        assertEquals("#2", evaluateExpression("#${1+1}"));
+        assertEquals("#${1+1}", evaluateExpression("#\\${1+1}"));
+    }
+
+    private String evaluateExpression(String expr) {
+        ValueExpression v = ELManager.getExpressionFactory().createValueExpression(
+                elm.getELContext(), expr, String.class);
+        return (String) v.getValue(elm.getELContext());
+    }
+}