Skip to content

Commit f45876f

Browse files
authored
PR to fix #824 and reference to #823 (#828)
* Updated DefaultEncoder.getCanonicalizedURI(URI) javadoc to indicate that the method takes into consideration canonicalization of mixed/multi encoded URLs as specified in ESAPI.props 'allowMixed' and 'allowMultiple' accordingly. * Per issue #824. Updated DefaultEncoder.getCanonicalizedURI(URI) javadoc to indicate that the method takes into consideration canonicalization of mixed/multi encoded URLs as specified in ESAPI.props 'allowMixed' and 'allowMultiple' accordingly. * Fixed #824 by nesting the original canonicalize call into the else block of the check to see whether or not we were dealing with a query segment.
1 parent 7a9ec00 commit f45876f

File tree

4 files changed

+122
-20
lines changed

4 files changed

+122
-20
lines changed

src/main/java/org/owasp/esapi/codecs/LegacyHTMLEntityCodec.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/**
2-
* OWASP Enterprise Security API (ESAPI)
2+
* OWASP Enterprise Security API (ESAPI)
33
*
44
* This file is part of the Open Web Application Security Project (OWASP)
55
* Enterprise Security API (ESAPI) project. For details, please see

src/main/java/org/owasp/esapi/reference/DefaultEncoder.java

+16-9
Original file line numberDiff line numberDiff line change
@@ -520,6 +520,9 @@ public byte[] decodeFromBase64(String input) throws IOException {
520520
* This will extract each piece of a URI according to parse zone as specified in <a href="https://www.ietf.org/rfc/rfc3986.txt">RFC-3986</a> section 3,
521521
* and it will construct a canonicalized String representing a version of the URI that is safe to
522522
* run regex against.
523+
*
524+
* NOTE: This method will obey the ESAPI.properties configurations for allowing
525+
* Mixed and Multiple Encoding URLs.
523526
*
524527
* @param dirtyUri
525528
* @return Canonicalized URI string.
@@ -548,7 +551,6 @@ public String getCanonicalizedURI(URI dirtyUri) throws IntrusionException{
548551
parseMap.put(UriSegment.SCHEME, dirtyUri.getScheme());
549552
//authority = [ userinfo "@" ] host [ ":" port ]
550553
parseMap.put(UriSegment.AUTHORITY, dirtyUri.getRawAuthority());
551-
parseMap.put(UriSegment.SCHEMSPECIFICPART, dirtyUri.getRawSchemeSpecificPart());
552554
parseMap.put(UriSegment.HOST, dirtyUri.getHost());
553555
//if port is undefined, it will return -1
554556
Integer port = new Integer(dirtyUri.getPort());
@@ -557,9 +559,6 @@ public String getCanonicalizedURI(URI dirtyUri) throws IntrusionException{
557559
parseMap.put(UriSegment.QUERY, dirtyUri.getRawQuery());
558560
parseMap.put(UriSegment.FRAGMENT, dirtyUri.getRawFragment());
559561

560-
//Now we canonicalize each part and build our string.
561-
StringBuilder sb = new StringBuilder();
562-
563562
//Replace all the items in the map with canonicalized versions.
564563

565564
Set<UriSegment> set = parseMap.keySet();
@@ -568,8 +567,7 @@ public String getCanonicalizedURI(URI dirtyUri) throws IntrusionException{
568567
boolean allowMixed = sg.getBooleanProp("Encoder.AllowMixedEncoding");
569568
boolean allowMultiple = sg.getBooleanProp("Encoder.AllowMultipleEncoding");
570569
for(UriSegment seg: set){
571-
String value = canonicalize(parseMap.get(seg), allowMultiple, allowMixed);
572-
value = value == null ? "" : value;
570+
String value = "";
573571
//In the case of a uri query, we need to break up and canonicalize the internal parts of the query.
574572
if(seg == UriSegment.QUERY && null != parseMap.get(seg)){
575573
StringBuilder qBuilder = new StringBuilder();
@@ -597,6 +595,10 @@ public String getCanonicalizedURI(URI dirtyUri) throws IntrusionException{
597595
} catch (UnsupportedEncodingException e) {
598596
logger.debug(Logger.EVENT_FAILURE, "decoding error when parsing [" + dirtyUri.toString() + "]");
599597
}
598+
} else {
599+
String extractedInput = parseMap.get(seg);
600+
value = canonicalize(extractedInput, allowMultiple, allowMixed);
601+
value = value == null ? "" : value;
600602
}
601603
//Check if the port is -1, if it is, omit it from the output.
602604
if(seg == UriSegment.PORT){
@@ -618,11 +620,16 @@ public String getCanonicalizedURI(URI dirtyUri) throws IntrusionException{
618620
*/
619621
protected String buildUrl(Map<UriSegment, String> parseMap){
620622
StringBuilder sb = new StringBuilder();
621-
sb.append(parseMap.get(UriSegment.SCHEME))
622-
.append("://")
623+
boolean schemePresent = parseMap.get(UriSegment.SCHEME).equals("") ? false : true;
624+
625+
if(schemePresent) {
626+
sb.append(parseMap.get(UriSegment.SCHEME))
627+
.append("://");
628+
}
629+
623630
//can't use SCHEMESPECIFICPART for this, because we need to canonicalize all the parts of the query.
624631
//USERINFO is also deprecated. So we technically have more than we need.
625-
.append(parseMap.get(UriSegment.AUTHORITY) == null || parseMap.get(UriSegment.AUTHORITY).equals("") ? "" : parseMap.get(UriSegment.AUTHORITY))
632+
sb.append(parseMap.get(UriSegment.AUTHORITY) == null || parseMap.get(UriSegment.AUTHORITY).equals("") ? "" : parseMap.get(UriSegment.AUTHORITY))
626633
.append(parseMap.get(UriSegment.PATH) == null || parseMap.get(UriSegment.PATH).equals("") ? "" : parseMap.get(UriSegment.PATH))
627634
.append(parseMap.get(UriSegment.QUERY) == null || parseMap.get(UriSegment.QUERY).equals("")
628635
? "" : "?" + parseMap.get(UriSegment.QUERY))

src/test/java/org/owasp/esapi/codecs/HTMLEntityCodecTest.java

+20
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import static org.junit.Assert.assertEquals;
44

5+
import org.junit.Ignore;
56
import org.junit.Test;
67

78
public class HTMLEntityCodecTest {
@@ -48,4 +49,23 @@ public void testMixedBmpAndNonBmp(){
4849
String input = bmp + nonBMP;
4950
assertEquals(expected, codec.encode(new char[0], input));
5051
}
52+
53+
@Test
54+
/**
55+
* TODO: The following methods are unit tests I'm checking in for an issue to be worked and fixed.
56+
*/
57+
@Ignore("Pre check-in for issue #827")
58+
public void testIssue827() {
59+
String input = "/webapp/ux/home?d=1705914006565&status=login&ticket=1705914090394_HzJpTROVfhW-JhRW0OqDbHu7tWXXlgrKSUmOzIMsZNCcUIiYGMXX_Q%3D%3D&newsess=false&roleid=DP010101/0007&origin=ourprogram";
60+
String expected = input;
61+
assertEquals(expected, codec.decode(input));
62+
}
63+
64+
@Test
65+
@Ignore("Pre check-in for issue #827")
66+
public void testIssue827OnlyOR() {
67+
String input = "&origin=ourprogram";
68+
String expected = input;
69+
assertEquals(expected, codec.decode(input));
70+
}
5171
}

src/test/java/org/owasp/esapi/reference/EncoderTest.java

+85-10
Original file line numberDiff line numberDiff line change
@@ -15,25 +15,21 @@
1515
*/
1616
package org.owasp.esapi.reference;
1717

18-
import static org.junit.Assert.assertEquals;
1918
import static org.junit.Assert.assertNotEquals;
2019

2120
import java.io.IOException;
2221
import java.io.UnsupportedEncodingException;
2322
import java.net.URI;
24-
import java.util.List;
2523
import java.util.ArrayList;
2624
import java.util.Arrays;
27-
import java.util.HashMap;
28-
import java.util.Map;
29-
import java.util.Map.Entry;
30-
import java.util.regex.Matcher;
31-
import java.util.regex.Pattern;
25+
import java.util.List;
3226

27+
import org.junit.Ignore;
3328
import org.owasp.esapi.ESAPI;
3429
import org.owasp.esapi.Encoder;
3530
import org.owasp.esapi.EncoderConstants;
36-
import org.owasp.esapi.codecs.CSSCodec;
31+
import org.owasp.esapi.SecurityConfiguration;
32+
import org.owasp.esapi.SecurityConfigurationWrapper;
3733
import org.owasp.esapi.codecs.Codec;
3834
import org.owasp.esapi.codecs.HTMLEntityCodec;
3935
import org.owasp.esapi.codecs.MySQLCodec;
@@ -45,8 +41,7 @@
4541
import org.owasp.esapi.errors.EncodingException;
4642
import org.owasp.esapi.errors.IntrusionException;
4743
import org.owasp.esapi.Randomizer;
48-
import org.owasp.esapi.SecurityConfiguration;
49-
import org.owasp.esapi.SecurityConfigurationWrapper;
44+
5045

5146
import junit.framework.Test;
5247
import junit.framework.TestCase;
@@ -747,6 +742,7 @@ public void testDecodeFromURL() throws Exception {
747742
fail();
748743
}
749744
try {
745+
//FIXME: Rewrite this to use expected Exceptions.
750746
instance.decodeFromURL( "%3xridiculous" );
751747
fail();
752748
} catch( Exception e ) {
@@ -985,6 +981,50 @@ public void testGetCanonicalizedUri() throws Exception {
985981
assertEquals(expectedUri, e.getCanonicalizedURI(uri));
986982

987983
}
984+
985+
public void testGetCanonicalizedUriWithAnHTMLEntityCollision() throws Exception {
986+
System.out.println("GetCanonicalizedUriWithAnHTMLEntityCollision");
987+
Encoder e = ESAPI.encoder();
988+
989+
String expectedUri = "http://[email protected]/path_to/resource?foo=bar&para1=test";
990+
//Please note that section 3.2.1 of RFC-3986 explicitly states not to encode
991+
//password information as in http://palpatine:[email protected], and this will
992+
//not appear in the userinfo field.
993+
String input = "http://[email protected]/path_to/resource?foo=bar&para1=test";
994+
URI uri = new URI(input);
995+
System.out.println(uri.toString());
996+
assertEquals(expectedUri, e.getCanonicalizedURI(uri));
997+
998+
}
999+
1000+
@org.junit.Ignore("Pre-check in unit test for issue #826")
1001+
public void Issue826GetCanonicalizedUriWithMultipleEncoding() throws Exception {
1002+
System.out.println("GetCanonicalizedUriWithAnHTMLEntityCollision");
1003+
Encoder e = ESAPI.encoder();
1004+
String expectedUri = "http://[email protected]/path_to/resource?foo=bar&para1=&amp;amp;amp;test";
1005+
//Please note that section 3.2.1 of RFC-3986 explicitly states not to encode
1006+
//password information as in http://palpatine:[email protected], and this will
1007+
//not appear in the userinfo field.
1008+
String input = "http://[email protected]/path_to/resource?foo=bar&para1=&amp;amp;amp;test";
1009+
URI uri = new URI(input);
1010+
System.out.println(uri.toString());
1011+
assertEquals(expectedUri, e.getCanonicalizedURI(uri));
1012+
1013+
}
1014+
public void testGetCanonicalizedUriWithMultQueryParams() throws Exception {
1015+
System.out.println("getCanonicalizedUri");
1016+
Encoder e = ESAPI.encoder();
1017+
1018+
String expectedUri = "http://palpatine@foo bar.com/path_to/resource?foo=bar&bar=foo#frag";
1019+
//Please note that section 3.2.1 of RFC-3986 explicitly states not to encode
1020+
//password information as in http://palpatine:[email protected], and this will
1021+
//not appear in the userinfo field.
1022+
String input = "http://palpatine@foo%20bar.com/path_to/resource?foo=bar&bar=foo#frag";
1023+
URI uri = new URI(input);
1024+
System.out.println(uri.toString());
1025+
assertEquals(expectedUri, e.getCanonicalizedURI(uri));
1026+
1027+
}
9881028

9891029
public void testGetCanonicalizedUriPiazza() throws Exception {
9901030
System.out.println("getCanonicalizedUriPiazza");
@@ -1000,6 +1040,41 @@ public void testGetCanonicalizedUriPiazza() throws Exception {
10001040
assertEquals(expectedUri, e.getCanonicalizedURI(uri));
10011041

10021042
}
1043+
1044+
public void testIssue824() throws Exception {
1045+
System.out.println("getCanonicalizedUriPiazza");
1046+
Encoder e = ESAPI.encoder();
1047+
1048+
String expectedUri = "/webapp/ux/home?d=1705914006565&status=login&ticket=1705914090394_HzJpTROVfhW-JhRW0OqDbHu7tWXXlgrKSUmOzIMsZNCcUIiYGMXX_Q==&newsess=false&roleid=DP010101/0007&origin=ourprogram";
1049+
//Please note that section 3.2.1 of RFC-3986 explicitly states not to encode
1050+
//password information as in http://palpatine:[email protected], and this will
1051+
//not appear in the userinfo field.
1052+
String input = "/webapp/ux/home?d=1705914006565&status=login&ticket=1705914090394_HzJpTROVfhW-JhRW0OqDbHu7tWXXlgrKSUmOzIMsZNCcUIiYGMXX_Q%3D%3D&newsess=false&roleid=DP010101/0007&origin=ourprogram";
1053+
URI uri = new URI(input);
1054+
System.out.println(uri.toString());
1055+
assertEquals(expectedUri, e.getCanonicalizedURI(uri));
1056+
1057+
}
1058+
1059+
@org.junit.Ignore("Pre-check in unit test for issue #826")
1060+
public void Issue826GetCanonicalizedDoubleAmpersand() throws Exception {
1061+
System.out.println("getCanonicalizedDoubleAmpersand");
1062+
Encoder e = ESAPI.encoder();
1063+
String expectedUri = "http://127.0.0.1:3000/campaigns?goal=all&section=active&sort-by=-id&status=Draft%2C&html=&amp;contentLaunched";
1064+
//http://127.0.0.1:3000/campaigns?goal=all&section=active&sort-by=-id&status=Draft,&html=null&=null&amp;contentLaunched=null
1065+
/*
1066+
* In this case, the URI class should break up the HTML entity in the query so
1067+
*/
1068+
String input = "http://127.0.0.1:3000/campaigns?goal=all&section=active&sort-by=-id&status=Draft%2C&html=&&amp;contentLaunched";
1069+
URI uri = new URI(input);
1070+
System.out.println(uri.toString());
1071+
try {
1072+
assertEquals(expectedUri, e.getCanonicalizedURI(uri));
1073+
fail();
1074+
} catch (Exception ex) {
1075+
//Expected
1076+
}
1077+
}
10031078

10041079
public void testGetCanonicalizedUriWithMailto() throws Exception {
10051080
System.out.println("getCanonicalizedUriWithMailto");

0 commit comments

Comments
 (0)