From 02c95cd460af3254dc870a66f20dc82ad7064e21 Mon Sep 17 00:00:00 2001 From: karansanwal Date: Tue, 21 Oct 2014 17:14:53 +0530 Subject: [PATCH 0001/1069] Fix Issue # 195 : BaseValidationRule.assertValid(String context, String input) causes NPE if input is not valid. Made changes as required, also getvalid(string,string) should be called instead. Could not test the changes. Let me know if the error persists. Contact : karansanwal@gmail.com --- .../esapi/reference/validation/BaseValidationRule.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/owasp/esapi/reference/validation/BaseValidationRule.java b/src/main/java/org/owasp/esapi/reference/validation/BaseValidationRule.java index 1929ff02a..538c658c4 100644 --- a/src/main/java/org/owasp/esapi/reference/validation/BaseValidationRule.java +++ b/src/main/java/org/owasp/esapi/reference/validation/BaseValidationRule.java @@ -89,7 +89,7 @@ public final void setEncoder( Encoder encoder ) { * {@inheritDoc} */ public void assertValid( String context, String input ) throws ValidationException { - getValid( context, input, null ); + getValid( context, input ); } /** @@ -100,7 +100,11 @@ public Object getValid( String context, String input, ValidationErrorList errorL try { valid = getValid( context, input ); } catch (ValidationException e) { - errorList.addError(context, e); + if( errorList == null { + throw e; + } else { + errorList.addError(context, e); + } } return valid; } From 82715bf4e806fd8d5e7775c60f44cfa3e303ec69 Mon Sep 17 00:00:00 2001 From: karansanwal Date: Wed, 22 Oct 2014 19:07:29 +0530 Subject: [PATCH 0002/1069] Fix to Google Issue 212 to know if an uncaught exception is a security exception or usual runtime exception. --- src/main/java/org/owasp/esapi/errors/IntrusionException.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/owasp/esapi/errors/IntrusionException.java b/src/main/java/org/owasp/esapi/errors/IntrusionException.java index 4af7f4122..6332f297b 100644 --- a/src/main/java/org/owasp/esapi/errors/IntrusionException.java +++ b/src/main/java/org/owasp/esapi/errors/IntrusionException.java @@ -28,7 +28,7 @@ * * @author Jeff Williams (jeff.williams@aspectsecurity.com) */ -public class IntrusionException extends RuntimeException { +public class IntrusionException extends EnterpriseSecurityException { /** The Constant serialVersionUID. */ private static final long serialVersionUID = 1L; From 7ac1c84151564c33706796a090084cb86db9b2ac Mon Sep 17 00:00:00 2001 From: karansanwal Date: Tue, 28 Oct 2014 00:43:49 +0530 Subject: [PATCH 0003/1069] Fix to Issue # 190 --- .../java/org/owasp/esapi/reference/DefaultHTTPUtilities.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/owasp/esapi/reference/DefaultHTTPUtilities.java b/src/main/java/org/owasp/esapi/reference/DefaultHTTPUtilities.java index dc99d76b3..767a49438 100644 --- a/src/main/java/org/owasp/esapi/reference/DefaultHTTPUtilities.java +++ b/src/main/java/org/owasp/esapi/reference/DefaultHTTPUtilities.java @@ -811,7 +811,7 @@ public void sendForward( String location ) throws AccessControlException,Servle public void sendRedirect(HttpServletResponse response, String location) throws AccessControlException, IOException { if (!ESAPI.validator().isValidRedirectLocation("Redirect", location, false)) { logger.fatal(Logger.SECURITY_FAILURE, "Bad redirect location: " + location); - throw new IOException("Redirect failed"); + throw new AccessControlException("Redirect failed"); } response.sendRedirect(location); } From 764dad9c96b2bc765a3cd7f3ca62706622724ae2 Mon Sep 17 00:00:00 2001 From: Constantino Cronemberger Date: Mon, 8 Dec 2014 14:13:34 -0200 Subject: [PATCH 0004/1069] fix issue 268: https://code.google.com/p/owasp-esapi-java/issues/detail?id=268 Log4JLogger.java doesn't output correct file & line number because FQCN isn't forwarded to Log4J --- src/main/java/org/owasp/esapi/reference/Log4JLogger.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/owasp/esapi/reference/Log4JLogger.java b/src/main/java/org/owasp/esapi/reference/Log4JLogger.java index 7782f89c7..af7045663 100644 --- a/src/main/java/org/owasp/esapi/reference/Log4JLogger.java +++ b/src/main/java/org/owasp/esapi/reference/Log4JLogger.java @@ -447,7 +447,9 @@ private void log(Level level, EventType type, String message, Throwable throwabl } // log the message - log(level, "[" + typeInfo + getUserInfo() + " -> " + appInfo + "] " + clean, throwable); + // Fix for https://code.google.com/p/owasp-esapi-java/issues/detail?id=268 + // need to pass callerFQCN so the log is not generated as if it were always generated from this wrapper class + log(Log4JLogger.class.getName(), level, "[" + typeInfo + getUserInfo() + " -> " + appInfo + "] " + clean, throwable); } /** From a543edb00bf22adc1ee08097389b0b9fbe4cedf4 Mon Sep 17 00:00:00 2001 From: Constantino Cronemberger Date: Mon, 8 Dec 2014 15:36:09 -0200 Subject: [PATCH 0005/1069] fix issue 268: test for this issue --- .../esapi/reference/Log4JLoggerTest.java | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/src/test/java/org/owasp/esapi/reference/Log4JLoggerTest.java b/src/test/java/org/owasp/esapi/reference/Log4JLoggerTest.java index cddf71e20..55734a9c8 100644 --- a/src/test/java/org/owasp/esapi/reference/Log4JLoggerTest.java +++ b/src/test/java/org/owasp/esapi/reference/Log4JLoggerTest.java @@ -16,12 +16,15 @@ package org.owasp.esapi.reference; import java.io.IOException; +import java.io.StringWriter; import java.util.Arrays; import junit.framework.Test; import junit.framework.TestCase; import junit.framework.TestSuite; +import org.apache.log4j.*; +import org.apache.log4j.xml.XMLLayout; import org.owasp.esapi.ESAPI; import org.owasp.esapi.Logger; import org.owasp.esapi.errors.AuthenticationException; @@ -460,4 +463,24 @@ public void testAlways() { } } + /** + * Validation for issue: https://code.google.com/p/owasp-esapi-java/issues/detail?id=268 + * Line number must be the line of the caller and not of the wrapper. + */ + public void testLine() { + StringWriter sw = new StringWriter(); + Layout layout = new PatternLayout("%d{ISO8601}%5p [%t] %C:%L - %m%n"); + Appender appender = new WriterAppender(layout, sw); + log4JLogger.addAppender(appender); + try { + log4JLogger.fatal("testLine"); + String generatedLine = sw.toString(); + System.out.println("-> " + generatedLine + " <-"); + assertTrue("generated line should not have the name of the wrapper class", + // need to add the ":" sufix otherwise the test would also fail with this test class name + !generatedLine.contains(Log4JLogger.class.getName()+":")); + } finally { + log4JLogger.removeAppender(appender); + } + } } From bc3b476ade2ccfb8b95ad1cb6b69614f9d1ccb7e Mon Sep 17 00:00:00 2001 From: Constantino Cronemberger Date: Mon, 8 Dec 2014 16:31:40 -0200 Subject: [PATCH 0006/1069] using a special Layout instance to make sure this test is more robust --- .../esapi/reference/Log4JLoggerTest.java | 40 +++++++++++++------ 1 file changed, 27 insertions(+), 13 deletions(-) diff --git a/src/test/java/org/owasp/esapi/reference/Log4JLoggerTest.java b/src/test/java/org/owasp/esapi/reference/Log4JLoggerTest.java index 55734a9c8..99c5faaf0 100644 --- a/src/test/java/org/owasp/esapi/reference/Log4JLoggerTest.java +++ b/src/test/java/org/owasp/esapi/reference/Log4JLoggerTest.java @@ -15,16 +15,13 @@ */ package org.owasp.esapi.reference; -import java.io.IOException; -import java.io.StringWriter; -import java.util.Arrays; - import junit.framework.Test; import junit.framework.TestCase; import junit.framework.TestSuite; - -import org.apache.log4j.*; -import org.apache.log4j.xml.XMLLayout; +import org.apache.log4j.Appender; +import org.apache.log4j.Layout; +import org.apache.log4j.WriterAppender; +import org.apache.log4j.spi.LoggingEvent; import org.owasp.esapi.ESAPI; import org.owasp.esapi.Logger; import org.owasp.esapi.errors.AuthenticationException; @@ -32,6 +29,10 @@ import org.owasp.esapi.http.MockHttpServletRequest; import org.owasp.esapi.http.MockHttpServletResponse; +import java.io.IOException; +import java.io.StringWriter; +import java.util.Arrays; + /** * The Class LoggerTest. * @@ -468,17 +469,30 @@ public void testAlways() { * Line number must be the line of the caller and not of the wrapper. */ public void testLine() { + final String message = "testing only"; StringWriter sw = new StringWriter(); - Layout layout = new PatternLayout("%d{ISO8601}%5p [%t] %C:%L - %m%n"); + Layout layout = new Layout() { + @Override + public String format(LoggingEvent event) { + assertEquals("the calling class is this test class", Log4JLoggerTest.class.getName(),event.getLocationInformation().getClassName()); + return message; + } + + @Override + public boolean ignoresThrowable() { + return false; + } + + @Override + public void activateOptions() { + + } + }; Appender appender = new WriterAppender(layout, sw); log4JLogger.addAppender(appender); try { log4JLogger.fatal("testLine"); - String generatedLine = sw.toString(); - System.out.println("-> " + generatedLine + " <-"); - assertTrue("generated line should not have the name of the wrapper class", - // need to add the ":" sufix otherwise the test would also fail with this test class name - !generatedLine.contains(Log4JLogger.class.getName()+":")); + assertEquals("message not generated as expected", message, sw.toString()); } finally { log4JLogger.removeAppender(appender); } From a0cf3e11d45b9abf64a93173ed24c00701e5e4c7 Mon Sep 17 00:00:00 2001 From: karansanwal Date: Tue, 16 Dec 2014 12:50:32 +0530 Subject: [PATCH 0007/1069] Update BaseValidationRule.java A minor mistake in previous commit --- .../owasp/esapi/reference/validation/BaseValidationRule.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/owasp/esapi/reference/validation/BaseValidationRule.java b/src/main/java/org/owasp/esapi/reference/validation/BaseValidationRule.java index 538c658c4..8201aafd7 100644 --- a/src/main/java/org/owasp/esapi/reference/validation/BaseValidationRule.java +++ b/src/main/java/org/owasp/esapi/reference/validation/BaseValidationRule.java @@ -100,7 +100,7 @@ public Object getValid( String context, String input, ValidationErrorList errorL try { valid = getValid( context, input ); } catch (ValidationException e) { - if( errorList == null { + if( errorList == null) { throw e; } else { errorList.addError(context, e); From 2dbb419112227e12b315c51ca21df9b61a808e0e Mon Sep 17 00:00:00 2001 From: Arpit Gupta Date: Thu, 1 Jan 2015 17:31:33 +0530 Subject: [PATCH 0008/1069] Fix for issue #289 https://github.com/ESAPI/esapi-java-legacy/issues/289 When using ClickJackFilter, In some cases response header is not set. http://stackoverflow.com/questions/11371755/clickjacking-filter-to-add-x -frame-options-in-response --- src/main/java/org/owasp/esapi/filters/ClickjackFilter.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/owasp/esapi/filters/ClickjackFilter.java b/src/main/java/org/owasp/esapi/filters/ClickjackFilter.java index 735d21ec0..86025b1dd 100644 --- a/src/main/java/org/owasp/esapi/filters/ClickjackFilter.java +++ b/src/main/java/org/owasp/esapi/filters/ClickjackFilter.java @@ -95,8 +95,8 @@ public void init(FilterConfig filterConfig) { public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { HttpServletResponse res = (HttpServletResponse)response; - chain.doFilter(request, response); res.addHeader("X-FRAME-OPTIONS", mode ); + chain.doFilter(request, response); } /** From 92c2e839f6fbab80318d29840e3eb69000f2b3eb Mon Sep 17 00:00:00 2001 From: Ben Sleek Date: Fri, 10 Apr 2015 15:13:13 -0400 Subject: [PATCH 0009/1069] Added test for Issue #195 --- .../validation/BaseValidationRuleTest.java | 101 ++++++++++++++++++ 1 file changed, 101 insertions(+) create mode 100644 src/test/java/org/owasp/esapi/reference/validation/BaseValidationRuleTest.java diff --git a/src/test/java/org/owasp/esapi/reference/validation/BaseValidationRuleTest.java b/src/test/java/org/owasp/esapi/reference/validation/BaseValidationRuleTest.java new file mode 100644 index 000000000..f7adfccdb --- /dev/null +++ b/src/test/java/org/owasp/esapi/reference/validation/BaseValidationRuleTest.java @@ -0,0 +1,101 @@ +/** + * OWASP Enterprise Security API (ESAPI) + * + * This file is part of the Open Web Application Security Project (OWASP) + * Enterprise Security API (ESAPI) project. For details, please see + * http://www.owasp.org/index.php/ESAPI. + * + * Copyright (c) 2007 - The OWASP Foundation + * + * The ESAPI is published by OWASP under the BSD license. You should read and accept the + * LICENSE before you use, modify, and/or redistribute this software. + * + * @author Ben Sleek Sparta Systems + * @created 2015 + */ +package org.owasp.esapi.reference.validation; + +import junit.framework.Test; +import junit.framework.TestCase; +import junit.framework.TestSuite; + +import org.owasp.esapi.Encoder; +import org.owasp.esapi.errors.ValidationException; + +public class BaseValidationRuleTest extends TestCase { + + /** + * Instantiates a new base validation rule test. + * + * @param testName + * the test name + */ + public BaseValidationRuleTest(String testName) { + super(testName); + } + + /** + * {@inheritDoc} + * + * @throws Exception + */ + protected void setUp() throws Exception { + // none + } + + /** + * {@inheritDoc} + * + * @throws Exception + */ + protected void tearDown() throws Exception { + // none + } + + /** + * Suite. + * + * @return the test + */ + public static Test suite() { + TestSuite suite = new TestSuite(BaseValidationRuleTest.class); + return suite; + } + + /** + * Verifies assertValid throws ValidationException on invalid input + * Validates fix for Google issue #195 + * + * @throws ValidationException + */ + public void testAssertValid() throws ValidationException { + SampleValidationRule rule = new SampleValidationRule("UnitTest"); + try { + rule.assertValid("testcontext", "badinput"); + fail(); + } catch (ValidationException e) { + // success + } + } + + public class SampleValidationRule extends BaseValidationRule { + + public SampleValidationRule(String typeName, Encoder encoder) { + super(typeName, encoder); + } + + public SampleValidationRule(String typeName) { + super(typeName); + } + + @Override + protected Object sanitize(String context, String input) { + return null; + } + + public Object getValid(String context, String input) throws ValidationException { + throw new ValidationException("Demonstration Exception", "Demonstration Exception"); + } + + } +} From 2e08dac45f166de8d3b4f86148897b1b6986e216 Mon Sep 17 00:00:00 2001 From: taringamberini Date: Sat, 25 Apr 2015 20:02:35 +0200 Subject: [PATCH 0010/1069] Set the locale suitable for US dates PROBLEM The org.owasp.esapi.errors.ValidationException is thrown with a logMessage: Invalid date: context=datetest1, format=java.text.SimpleDateFormat@2aca24a9, input=September 11, 2001 and a detailMessage: datetest1: Invalid date must follow the java.text.DecimalFormat@674dc format CAUSE The SimpleDateFormat.getDateInstance() is default locale dependent, so the date September 11, 2001 can't be correctly formatted on my Java Virtual Machine Locale which is it_IT. SOLUTION Use the US locale. Fixes ESAPI/esapi-java-legacy#345 --- src/test/java/org/owasp/esapi/reference/ValidatorTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/org/owasp/esapi/reference/ValidatorTest.java b/src/test/java/org/owasp/esapi/reference/ValidatorTest.java index 9402630bb..f1bf4b914 100644 --- a/src/test/java/org/owasp/esapi/reference/ValidatorTest.java +++ b/src/test/java/org/owasp/esapi/reference/ValidatorTest.java @@ -323,7 +323,7 @@ public void testIsInvalidFilename() { public void testIsValidDate() { System.out.println("isValidDate"); Validator instance = ESAPI.validator(); - DateFormat format = SimpleDateFormat.getDateInstance(); + DateFormat format = SimpleDateFormat.getDateInstance(SimpleDateFormat.MEDIUM, Locale.US); assertTrue(instance.isValidDate("datetest1", "September 11, 2001", format, true)); assertFalse(instance.isValidDate("datetest2", null, format, false)); assertFalse(instance.isValidDate("datetest3", "", format, false)); From 948592a763bece1434bc99c0ed2007cb27106a4e Mon Sep 17 00:00:00 2001 From: Kad DEMBELE Date: Mon, 27 Apr 2015 21:48:53 +0200 Subject: [PATCH 0011/1069] Fix for issue #344 --- src/test/java/org/owasp/esapi/reference/ValidatorTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/org/owasp/esapi/reference/ValidatorTest.java b/src/test/java/org/owasp/esapi/reference/ValidatorTest.java index 9402630bb..946962d6f 100644 --- a/src/test/java/org/owasp/esapi/reference/ValidatorTest.java +++ b/src/test/java/org/owasp/esapi/reference/ValidatorTest.java @@ -297,7 +297,7 @@ public void testGetValidSafeHTML() throws Exception { assertEquals("Test. <
load=alert()
", rule.getSafe("test", "Test. <
load=alert()")); assertEquals("Test.
b
", rule.getSafe("test", "Test.
b
")); - assertEquals("Test.", rule.getSafe("test", "Test. alert(document.cookie)")); + assertEquals("Test.", rule.getSafe("test", "Test. ")); assertEquals("Test. alert(document.cookie)", rule.getSafe("test", "Test. alert(document.cookie)")); assertEquals("Test. alert(document.cookie)", rule.getSafe("test", "Test. alert(document.cookie)")); // TODO: ENHANCE waiting for a way to validate text headed for an attribute for scripts From db106031cf3babe086dfb0b5e7d971a2d10fc242 Mon Sep 17 00:00:00 2001 From: Kad DEMBELE Date: Mon, 27 Apr 2015 23:14:02 +0200 Subject: [PATCH 0012/1069] Using the right AccessControlException class to throw Exception --- .../esapi/reference/DefaultHTTPUtilities.java | 48 +++++++++++++------ 1 file changed, 34 insertions(+), 14 deletions(-) diff --git a/src/main/java/org/owasp/esapi/reference/DefaultHTTPUtilities.java b/src/main/java/org/owasp/esapi/reference/DefaultHTTPUtilities.java index 767a49438..6c56ce2e9 100644 --- a/src/main/java/org/owasp/esapi/reference/DefaultHTTPUtilities.java +++ b/src/main/java/org/owasp/esapi/reference/DefaultHTTPUtilities.java @@ -15,15 +15,17 @@ */ package org.owasp.esapi.reference; -import org.apache.commons.fileupload.FileItem; -import org.apache.commons.fileupload.ProgressListener; -import org.apache.commons.fileupload.disk.DiskFileItemFactory; -import org.apache.commons.fileupload.servlet.ServletFileUpload; -import org.owasp.esapi.*; -import org.owasp.esapi.codecs.Hex; -import org.owasp.esapi.crypto.CipherText; -import org.owasp.esapi.crypto.PlainText; -import org.owasp.esapi.errors.*; +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.TreeMap; +import java.util.concurrent.ConcurrentHashMap; import javax.servlet.RequestDispatcher; import javax.servlet.ServletException; @@ -31,10 +33,28 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; -import java.io.File; -import java.io.IOException; -import java.util.*; -import java.util.concurrent.ConcurrentHashMap; + +import org.apache.commons.fileupload.FileItem; +import org.apache.commons.fileupload.ProgressListener; +import org.apache.commons.fileupload.disk.DiskFileItemFactory; +import org.apache.commons.fileupload.servlet.ServletFileUpload; +import org.owasp.esapi.ESAPI; +import org.owasp.esapi.HTTPUtilities; +import org.owasp.esapi.Logger; +import org.owasp.esapi.StringUtilities; +import org.owasp.esapi.User; +import org.owasp.esapi.ValidationErrorList; +import org.owasp.esapi.codecs.Hex; +import org.owasp.esapi.crypto.CipherText; +import org.owasp.esapi.crypto.PlainText; +import org.owasp.esapi.errors.AccessControlException; +import org.owasp.esapi.errors.AuthenticationException; +import org.owasp.esapi.errors.EncodingException; +import org.owasp.esapi.errors.EncryptionException; +import org.owasp.esapi.errors.IntegrityException; +import org.owasp.esapi.errors.IntrusionException; +import org.owasp.esapi.errors.ValidationException; +import org.owasp.esapi.errors.ValidationUploadException; /** * Reference implementation of the HTTPUtilities interface. This implementation @@ -811,7 +831,7 @@ public void sendForward( String location ) throws AccessControlException,Servle public void sendRedirect(HttpServletResponse response, String location) throws AccessControlException, IOException { if (!ESAPI.validator().isValidRedirectLocation("Redirect", location, false)) { logger.fatal(Logger.SECURITY_FAILURE, "Bad redirect location: " + location); - throw new AccessControlException("Redirect failed"); + throw new AccessControlException("Redirect failed", "Bad Redirect location: " + location); } response.sendRedirect(location); } From fc101e4ca88b47fe0a8ab16a505bcf3ab4ccc4be Mon Sep 17 00:00:00 2001 From: Kad DEMBELE Date: Tue, 28 Apr 2015 00:30:32 +0200 Subject: [PATCH 0013/1069] Update ValidatorTest.java --- src/test/java/org/owasp/esapi/reference/ValidatorTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/org/owasp/esapi/reference/ValidatorTest.java b/src/test/java/org/owasp/esapi/reference/ValidatorTest.java index dbb754221..e272289f4 100644 --- a/src/test/java/org/owasp/esapi/reference/ValidatorTest.java +++ b/src/test/java/org/owasp/esapi/reference/ValidatorTest.java @@ -297,7 +297,7 @@ public void testGetValidSafeHTML() throws Exception { assertEquals("Test. <
load=alert()
", rule.getSafe("test", "Test. <
load=alert()")); assertEquals("Test.
b
", rule.getSafe("test", "Test.
b
")); - assertEquals("Test.", rule.getSafe("test", "Test. ")); + assertEquals("Test.alert(document.cookie)", rule.getSafe("test", "Test. alert(document.cookie)")); assertEquals("Test. alert(document.cookie)", rule.getSafe("test", "Test. alert(document.cookie)")); assertEquals("Test. alert(document.cookie)", rule.getSafe("test", "Test. alert(document.cookie)")); // TODO: ENHANCE waiting for a way to validate text headed for an attribute for scripts From 2bca895904eb6b2fd8a06c6d813110d331cfcdd2 Mon Sep 17 00:00:00 2001 From: Kad DEMBELE Date: Tue, 28 Apr 2015 00:34:21 +0200 Subject: [PATCH 0014/1069] Update ValidatorTest.java --- src/test/java/org/owasp/esapi/reference/ValidatorTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/org/owasp/esapi/reference/ValidatorTest.java b/src/test/java/org/owasp/esapi/reference/ValidatorTest.java index e272289f4..8a5359211 100644 --- a/src/test/java/org/owasp/esapi/reference/ValidatorTest.java +++ b/src/test/java/org/owasp/esapi/reference/ValidatorTest.java @@ -297,7 +297,7 @@ public void testGetValidSafeHTML() throws Exception { assertEquals("Test. <
load=alert()
", rule.getSafe("test", "Test. <
load=alert()")); assertEquals("Test.
b
", rule.getSafe("test", "Test.
b
")); - assertEquals("Test.alert(document.cookie)", rule.getSafe("test", "Test. alert(document.cookie)")); + assertEquals("Test. alert(document.cookie)", rule.getSafe("test", "Test. alert(document.cookie)")); assertEquals("Test. alert(document.cookie)", rule.getSafe("test", "Test. alert(document.cookie)")); assertEquals("Test. alert(document.cookie)", rule.getSafe("test", "Test. alert(document.cookie)")); // TODO: ENHANCE waiting for a way to validate text headed for an attribute for scripts From 20438dbae5ecf8a51f454f38d1e0525fe4b61eb6 Mon Sep 17 00:00:00 2001 From: Chris Schmidt Date: Sat, 30 May 2015 16:25:20 +0000 Subject: [PATCH 0015/1069] Fixed unit tests and DefaultHttpUtilities --- .codenvy/project.json | 1 + .../esapi/errors/AccessControlException.java | 114 +- .../esapi/reference/DefaultHTTPUtilities.java | 2044 +++++++-------- .../esapi/reference/HTTPUtilitiesTest.java | 1015 ++++---- .../owasp/esapi/reference/ValidatorTest.java | 2300 ++++++++--------- 5 files changed, 2738 insertions(+), 2736 deletions(-) create mode 100644 .codenvy/project.json diff --git a/.codenvy/project.json b/.codenvy/project.json new file mode 100644 index 000000000..df32873a0 --- /dev/null +++ b/.codenvy/project.json @@ -0,0 +1 @@ +{"builders":{"configs":{},"default":"maven"},"mixinTypes":["contribution"],"runners":{"configs":{"system:/java/codenvy-cli":{"ram":1000,"variables":{},"options":{}}},"default":"system:/java/codenvy-cli"},"type":"maven","attributes":{"languageVersion":["1.6"],"language":["java"],"contribute_branch":["master"],"contribute_mode":["contribute"]}} \ No newline at end of file diff --git a/src/main/java/org/owasp/esapi/errors/AccessControlException.java b/src/main/java/org/owasp/esapi/errors/AccessControlException.java index 2c54417ab..96eaf80c5 100644 --- a/src/main/java/org/owasp/esapi/errors/AccessControlException.java +++ b/src/main/java/org/owasp/esapi/errors/AccessControlException.java @@ -1,57 +1,57 @@ -/** - * OWASP Enterprise Security API (ESAPI) - * - * This file is part of the Open Web Application Security Project (OWASP) - * Enterprise Security API (ESAPI) project. For details, please see - * http://www.owasp.org/index.php/ESAPI. - * - * Copyright (c) 2007 - The OWASP Foundation - * - * The ESAPI is published by OWASP under the BSD license. You should read and accept the - * LICENSE before you use, modify, and/or redistribute this software. - * - * @author Jeff Williams Aspect Security - * @created 2007 - */ -package org.owasp.esapi.errors; - -/** - * An AccessControlException should be thrown when a user attempts to access a - * resource that they are not authorized for. - * - * @author Jeff Williams (jeff.williams@aspectsecurity.com) - */ -public class AccessControlException extends EnterpriseSecurityException { - - /** The Constant serialVersionUID. */ - private static final long serialVersionUID = 1L; - - /** - * Instantiates a new access control exception. - */ - protected AccessControlException() { - // hidden - } - - /** - * Creates a new instance of {@code AccessControlException}. - * - * @param userMessage - * @param logMessage - */ - public AccessControlException(String userMessage, String logMessage) { - super(userMessage, logMessage); - } - - /** - * Instantiates a new access control exception. - * - * @param userMessage the message displayed to the user - * @param logMessage the message logged - * @param cause the root cause - */ - public AccessControlException(String userMessage, String logMessage, Throwable cause) { - super(userMessage, logMessage, cause); - } - -} +/** + * OWASP Enterprise Security API (ESAPI) + * + * This file is part of the Open Web Application Security Project (OWASP) + * Enterprise Security API (ESAPI) project. For details, please see + * http://www.owasp.org/index.php/ESAPI. + * + * Copyright (c) 2007 - The OWASP Foundation + * + * The ESAPI is published by OWASP under the BSD license. You should read and accept the + * LICENSE before you use, modify, and/or redistribute this software. + * + * @author Jeff Williams Aspect Security + * @created 2007 + */ +package org.owasp.esapi.errors; + +/** + * An AccessControlException should be thrown when a user attempts to access a + * resource that they are not authorized for. + * + * @author Jeff Williams (jeff.williams@aspectsecurity.com) + */ +public class AccessControlException extends EnterpriseSecurityException { + + /** The Constant serialVersionUID. */ + private static final long serialVersionUID = 1L; + + /** + * Instantiates a new access control exception. + */ + protected AccessControlException() { + // hidden + } + + /** + * Creates a new instance of {@code AccessControlException}. + * + * @param userMessage + * @param logMessage + */ + public AccessControlException(String userMessage, String logMessage) { + super(userMessage, logMessage); + } + + /** + * Instantiates a new access control exception. + * + * @param userMessage the message displayed to the user + * @param logMessage the message logged + * @param cause the root cause + */ + public AccessControlException(String userMessage, String logMessage, Throwable cause) { + super(userMessage, logMessage, cause); + } + +} diff --git a/src/main/java/org/owasp/esapi/reference/DefaultHTTPUtilities.java b/src/main/java/org/owasp/esapi/reference/DefaultHTTPUtilities.java index 767a49438..654a7216a 100644 --- a/src/main/java/org/owasp/esapi/reference/DefaultHTTPUtilities.java +++ b/src/main/java/org/owasp/esapi/reference/DefaultHTTPUtilities.java @@ -1,1022 +1,1022 @@ -/** - * OWASP Enterprise Security API (ESAPI) - * - * This file is part of the Open Web Application Security Project (OWASP) - * Enterprise Security API (ESAPI) project. For details, please see - * http://www.owasp.org/index.php/ESAPI. - * - * Copyright (c) 2007 - The OWASP Foundation - * - * The ESAPI is published by OWASP under the BSD license. You should read and accept the - * LICENSE before you use, modify, and/or redistribute this software. - * - * @author Jeff Williams Aspect Security - * @created 2007 - */ -package org.owasp.esapi.reference; - -import org.apache.commons.fileupload.FileItem; -import org.apache.commons.fileupload.ProgressListener; -import org.apache.commons.fileupload.disk.DiskFileItemFactory; -import org.apache.commons.fileupload.servlet.ServletFileUpload; -import org.owasp.esapi.*; -import org.owasp.esapi.codecs.Hex; -import org.owasp.esapi.crypto.CipherText; -import org.owasp.esapi.crypto.PlainText; -import org.owasp.esapi.errors.*; - -import javax.servlet.RequestDispatcher; -import javax.servlet.ServletException; -import javax.servlet.http.Cookie; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import javax.servlet.http.HttpSession; -import java.io.File; -import java.io.IOException; -import java.util.*; -import java.util.concurrent.ConcurrentHashMap; - -/** - * Reference implementation of the HTTPUtilities interface. This implementation - * uses the Apache Commons FileUploader library, which in turn uses the Apache - * Commons IO library. - *

- * To simplify the interface, some methods use the current request and response that - * are tracked by ThreadLocal variables in the Authenticator. This means that you - * must have called ESAPI.authenticator().setCurrentHTTP(request, response) before - * calling these methods. - *

- * Typically, this is done by calling the Authenticator.login() method, which - * calls setCurrentHTTP() automatically. However if you want to use these methods - * in another application, you should explicitly call setCurrentHTTP() in your - * own code. In either case, you *must* call ESAPI.clearCurrent() to clear threadlocal - * variables before the thread is reused. The advantages of having identity everywhere - * outweigh the disadvantages of this approach. - * - * @author Jeff Williams (jeff.williams .at. aspectsecurity.com) Aspect Security - * @since June 1, 2007 - * @see org.owasp.esapi.HTTPUtilities - */ -public class DefaultHTTPUtilities implements org.owasp.esapi.HTTPUtilities { - private static volatile HTTPUtilities instance = null; - - public static HTTPUtilities getInstance() { - if ( instance == null ) { - synchronized ( DefaultHTTPUtilities.class ) { - if ( instance == null ) { - instance = new DefaultHTTPUtilities(); - } - } - } - return instance; - } - - /** - * Defines the ThreadLocalRequest to store the current request for this thread. - */ - private class ThreadLocalRequest extends InheritableThreadLocal { - - public HttpServletRequest getRequest() { - return super.get(); - } - - public HttpServletRequest initialValue() { - return null; - } - - public void setRequest(HttpServletRequest newRequest) { - super.set(newRequest); - } - } - - /** - * Defines the ThreadLocalResponse to store the current response for this thread. - */ - private class ThreadLocalResponse extends InheritableThreadLocal { - - public HttpServletResponse getResponse() { - return super.get(); - } - - public HttpServletResponse initialValue() { - return null; - } - - public void setResponse(HttpServletResponse newResponse) { - super.set(newResponse); - } - } - - /** The logger. */ - private final Logger logger = ESAPI.getLogger("HTTPUtilities"); - - /** The max bytes. */ - static final int maxBytes = ESAPI.securityConfiguration().getAllowedFileUploadSize(); - - - /* - * The currentRequest ThreadLocal variable is used to make the currentRequest available to any call in any part of an - * application. This enables API's for actions that require the request to be much simpler. For example, the logout() - * method in the Authenticator class requires the currentRequest to get the session in order to invalidate it. - */ - private ThreadLocalRequest currentRequest = new ThreadLocalRequest(); - - /* - * The currentResponse ThreadLocal variable is used to make the currentResponse available to any call in any part of an - * application. This enables API's for actions that require the response to be much simpler. For example, the logout() - * method in the Authenticator class requires the currentResponse to kill the Session ID cookie. - */ - private ThreadLocalResponse currentResponse = new ThreadLocalResponse(); - - - - /** - * No arg constructor. - */ - public DefaultHTTPUtilities() { - } - - - /** - * {@inheritDoc} - * This implementation uses a custom "set-cookie" header rather than Java's - * cookie interface which doesn't allow the use of HttpOnly. Configure the - * HttpOnly and Secure settings in ESAPI.properties. - */ - public void addCookie( Cookie cookie ) { - addCookie( getCurrentResponse(), cookie ); - } - - /** - * {@inheritDoc} - * This implementation uses a custom "set-cookie" header rather than Java's - * cookie interface which doesn't allow the use of HttpOnly. Configure the - * HttpOnly and Secure settings in ESAPI.properties. - */ - public void addCookie(HttpServletResponse response, Cookie cookie) { - String name = cookie.getName(); - String value = cookie.getValue(); - int maxAge = cookie.getMaxAge(); - String domain = cookie.getDomain(); - String path = cookie.getPath(); - boolean secure = cookie.getSecure(); - - // validate the name and value - ValidationErrorList errors = new ValidationErrorList(); - String cookieName = ESAPI.validator().getValidInput("cookie name", name, "HTTPCookieName", 50, false, errors); - String cookieValue = ESAPI.validator().getValidInput("cookie value", value, "HTTPCookieValue", 5000, false, errors); - - // if there are no errors, then set the cookie either with a header or normally - if (errors.size() == 0) { - if ( ESAPI.securityConfiguration().getForceHttpOnlyCookies() ) { - String header = createCookieHeader(cookieName, cookieValue, maxAge, domain, path, secure); - addHeader(response, "Set-Cookie", header); - } else { - // Issue 23 - If the ESAPI Configuration is set to force secure cookies, force the secure flag on the cookie before setting it - cookie.setSecure( secure || ESAPI.securityConfiguration().getForceSecureCookies() ); - response.addCookie(cookie); - } - return; - } - logger.warning(Logger.SECURITY_FAILURE, "Attempt to add unsafe data to cookie (skip mode). Skipping cookie and continuing."); - } - - - - /** - * {@inheritDoc} - */ - public String addCSRFToken(String href) { - User user = ESAPI.authenticator().getCurrentUser(); - if (user.isAnonymous()) { - return href; - } - - // if there are already parameters append with &, otherwise append with ? - String token = CSRF_TOKEN_NAME + "=" + user.getCSRFToken(); - return href.indexOf( '?') != -1 ? href + "&" + token : href + "?" + token; - } - - /** - * {@inheritDoc} - */ - public void addHeader(String name, String value) { - addHeader( getCurrentResponse(), name, value ); - } - - /** - * {@inheritDoc} - */ - public void addHeader(HttpServletResponse response, String name, String value) { - try { - String strippedName = StringUtilities.replaceLinearWhiteSpace(name); - String strippedValue = StringUtilities.replaceLinearWhiteSpace(value); - String safeName = ESAPI.validator().getValidInput("addHeader", strippedName, "HTTPHeaderName", 20, false); - String safeValue = ESAPI.validator().getValidInput("addHeader", strippedValue, "HTTPHeaderValue", 500, false); - response.addHeader(safeName, safeValue); - } catch (ValidationException e) { - logger.warning(Logger.SECURITY_FAILURE, "Attempt to add invalid header denied", e); - } - } - - /** - * {@inheritDoc} - */ - public void assertSecureChannel() throws AccessControlException { - assertSecureChannel( getCurrentRequest() ); - } - - /** - * {@inheritDoc} - * - * This implementation ignores the built-in isSecure() method - * and uses the URL to determine if the request was transmitted over SSL. - * This is because SSL may have been terminated somewhere outside the - * container. - */ - public void assertSecureChannel(HttpServletRequest request) throws AccessControlException { - if ( request == null ) { - throw new AccessControlException( "Insecure request received", "HTTP request was null" ); - } - StringBuffer sb = request.getRequestURL(); - if ( sb == null ) { - throw new AccessControlException( "Insecure request received", "HTTP request URL was null" ); - } - String url = sb.toString(); - if ( !url.startsWith( "https" ) ) { - throw new AccessControlException( "Insecure request received", "HTTP request did not use SSL" ); - } - } - - /** - * {@inheritDoc} - */ - public void assertSecureRequest() throws AccessControlException { - assertSecureRequest( getCurrentRequest() ); - } - - /** - * {@inheritDoc} - */ - public void assertSecureRequest(HttpServletRequest request) throws AccessControlException { - assertSecureChannel( request ); - String receivedMethod = request.getMethod(); - String requiredMethod = "POST"; - if ( !receivedMethod.equals( requiredMethod ) ) { - throw new AccessControlException( "Insecure request received", "Received request using " + receivedMethod + " when only " + requiredMethod + " is allowed" ); - } - } - - /** - * {@inheritDoc} - */ - public HttpSession changeSessionIdentifier() throws AuthenticationException { - return changeSessionIdentifier( getCurrentRequest() ); - } - - /** - * {@inheritDoc} - */ - public HttpSession changeSessionIdentifier(HttpServletRequest request) throws AuthenticationException { - - // get the current session - HttpSession oldSession = request.getSession(); - - // make a copy of the session content - Map temp = new ConcurrentHashMap(); - Enumeration e = oldSession.getAttributeNames(); - while (e != null && e.hasMoreElements()) { - String name = (String) e.nextElement(); - Object value = oldSession.getAttribute(name); - temp.put(name, value); - } - - // kill the old session and create a new one - oldSession.invalidate(); - HttpSession newSession = request.getSession(); - User user = ESAPI.authenticator().getCurrentUser(); - user.addSession( newSession ); - user.removeSession( oldSession ); - - // copy back the session content - for (Map.Entry stringObjectEntry : temp.entrySet()) - { - newSession.setAttribute(stringObjectEntry.getKey(), stringObjectEntry.getValue()); - } - return newSession; - } - - /** - * {@inheritDoc} - */ - public void clearCurrent() { - currentRequest.set(null); - currentResponse.set(null); - } - - private String createCookieHeader(String name, String value, int maxAge, String domain, String path, boolean secure) { - // create the special cookie header instead of creating a Java cookie - // Set-Cookie:=[; =][; expires=][; - // domain=][; path=][; secure][;HttpOnly] - String header = name + "=" + value; - header += "; Max-Age=" + maxAge; - if (domain != null) { - header += "; Domain=" + domain; - } - if (path != null) { - header += "; Path=" + path; - } - if ( secure || ESAPI.securityConfiguration().getForceSecureCookies() ) { - header += "; Secure"; - } - if ( ESAPI.securityConfiguration().getForceHttpOnlyCookies() ) { - header += "; HttpOnly"; - } - return header; - } - - /** - * {@inheritDoc} - */ - public String decryptHiddenField(String encrypted) { - try { - return decryptString(encrypted); - } catch( EncryptionException e ) { - throw new IntrusionException("Invalid request","Tampering detected. Hidden field data did not decrypt properly.", e); - } - } - - /** - * {@inheritDoc} - */ - public Map decryptQueryString(String encrypted) throws EncryptionException { - String plaintext = decryptString(encrypted); - return queryToMap(plaintext); - } - - /** - * {@inheritDoc} - */ - public Map decryptStateFromCookie() throws EncryptionException { - return decryptStateFromCookie( getCurrentRequest() ); - } - - /** - * {@inheritDoc} - * - * @param request - */ - public Map decryptStateFromCookie(HttpServletRequest request) throws EncryptionException { - try { - String encrypted = getCookie( request, ESAPI_STATE ); - if ( encrypted == null ) return new HashMap(); - String plaintext = decryptString(encrypted); - return queryToMap( plaintext ); - } catch( ValidationException e ) { - return null; - } - } - - /** - * {@inheritDoc} - */ - public String encryptHiddenField(String value) throws EncryptionException { - return encryptString(value); - } - - /** - * {@inheritDoc} - */ - public String encryptQueryString(String query) throws EncryptionException { - return encryptString(query); - } - - /** - * {@inheritDoc} - */ - public void encryptStateInCookie(HttpServletResponse response, Map cleartext) throws EncryptionException { - StringBuilder sb = new StringBuilder(); - Iterator i = cleartext.entrySet().iterator(); - while ( i.hasNext() ) { - try { - Map.Entry entry = (Map.Entry)i.next(); - - // What do these need to be URL encoded? They are encrypted! - String name = ESAPI.encoder().encodeForURL( entry.getKey().toString() ); - String value = ESAPI.encoder().encodeForURL( entry.getValue().toString() ); - sb.append(name).append("=").append(value); - if ( i.hasNext() ) sb.append( "&" ); - } catch( EncodingException e ) { - logger.error(Logger.SECURITY_FAILURE, "Problem encrypting state in cookie - skipping entry", e ); - } - } - - String encrypted = encryptString(sb.toString()); - - if ( encrypted.length() > (MAX_COOKIE_LEN ) ) { - logger.error(Logger.SECURITY_FAILURE, "Problem encrypting state in cookie - skipping entry"); - throw new EncryptionException("Encryption failure", "Encrypted cookie state of " + encrypted.length() + " longer than allowed " + MAX_COOKIE_LEN ); - } - - Cookie cookie = new Cookie( ESAPI_STATE, encrypted ); - addCookie( response, cookie ); - } - - /** - * {@inheritDoc} - */ - public void encryptStateInCookie( Map cleartext ) throws EncryptionException { - encryptStateInCookie( getCurrentResponse(), cleartext ); - } - - - /** - * {@inheritDoc} - */ - public String getCookie( HttpServletRequest request, String name ) throws ValidationException { - Cookie c = getFirstCookie( request, name ); - if ( c == null ) return null; - String value = c.getValue(); - return ESAPI.validator().getValidInput("HTTP cookie value: " + value, value, "HTTPCookieValue", 1000, false); - } - - /** - * {@inheritDoc} - */ - public String getCookie( String name ) throws ValidationException { - return getCookie( getCurrentRequest(), name ); - } - - /** - * {@inheritDoc} - */ - public String getCSRFToken() { - User user = ESAPI.authenticator().getCurrentUser(); - if (user == null) return null; - return user.getCSRFToken(); - } - - - /** - * {@inheritDoc} - */ - public HttpServletRequest getCurrentRequest() { - return currentRequest.getRequest(); - } - - - /** - * {@inheritDoc} - */ - public HttpServletResponse getCurrentResponse() { - return currentResponse.getResponse(); - } - - /** - * {@inheritDoc} - */ - public List getFileUploads() throws ValidationException { - return getFileUploads( getCurrentRequest(), ESAPI.securityConfiguration().getUploadDirectory(), ESAPI.securityConfiguration().getAllowedFileExtensions() ); - } - - /** - * {@inheritDoc} - */ - public List getFileUploads(HttpServletRequest request) throws ValidationException { - return getFileUploads(request, ESAPI.securityConfiguration().getUploadDirectory(), ESAPI.securityConfiguration().getAllowedFileExtensions()); - } - - /** - * {@inheritDoc} - */ - public List getFileUploads(HttpServletRequest request, File finalDir ) throws ValidationException { - return getFileUploads(request, finalDir, ESAPI.securityConfiguration().getAllowedFileExtensions()); - } - - /** - * {@inheritDoc} - */ - public List getFileUploads(HttpServletRequest request, File finalDir, List allowedExtensions) throws ValidationException { - File tempDir = ESAPI.securityConfiguration().getUploadTempDirectory(); - if ( !tempDir.exists() ) { - if ( !tempDir.mkdirs() ) throw new ValidationUploadException( "Upload failed", "Could not create temp directory: " + tempDir.getAbsolutePath() ); - } - - if( finalDir != null){ - if ( !finalDir.exists() ) { - if ( !finalDir.mkdirs() ) throw new ValidationUploadException( "Upload failed", "Could not create final upload directory: " + finalDir.getAbsolutePath() ); - } - } - else { - if ( !ESAPI.securityConfiguration().getUploadDirectory().exists()) { - if ( !ESAPI.securityConfiguration().getUploadDirectory().mkdirs() ) throw new ValidationUploadException( "Upload failed", "Could not create final upload directory: " + ESAPI.securityConfiguration().getUploadDirectory().getAbsolutePath() ); - } - finalDir = ESAPI.securityConfiguration().getUploadDirectory(); - } - - List newFiles = new ArrayList(); - try { - final HttpSession session = request.getSession(false); - if (!ServletFileUpload.isMultipartContent(request)) { - throw new ValidationUploadException("Upload failed", "Not a multipart request"); - } - - // this factory will store ALL files in the temp directory, - // regardless of size - DiskFileItemFactory factory = new DiskFileItemFactory(0, tempDir); - ServletFileUpload upload = new ServletFileUpload(factory); - upload.setSizeMax(maxBytes); - - // Create a progress listener - ProgressListener progressListener = new ProgressListener() { - private long megaBytes = -1; - private long progress = 0; - - public void update(long pBytesRead, long pContentLength, int pItems) { - if (pItems == 0) - return; - long mBytes = pBytesRead / 1000000; - if (megaBytes == mBytes) - return; - megaBytes = mBytes; - progress = (long) (((double) pBytesRead / (double) pContentLength) * 100); - if ( session != null ) { - session.setAttribute("progress", Long.toString(progress)); - } - // logger.logSuccess(Logger.SECURITY, " Item " + pItems + " (" + progress + "% of " + pContentLength + " bytes]"); - } - }; - upload.setProgressListener(progressListener); - - List items = upload.parseRequest(request); - for (FileItem item : items) - { - if (!item.isFormField() && item.getName() != null && !(item.getName().equals(""))) - { - String[] fparts = item.getName().split("[\\/\\\\]"); - String filename = fparts[fparts.length - 1]; - - if (!ESAPI.validator().isValidFileName("upload", filename, allowedExtensions, false)) - { - throw new ValidationUploadException("Upload only simple filenames with the following extensions " + allowedExtensions, "Upload failed isValidFileName check"); - } - - logger.info(Logger.SECURITY_SUCCESS, "File upload requested: " + filename); - File f = new File(finalDir, filename); - if (f.exists()) - { - String[] parts = filename.split("\\/."); - String extension = ""; - if (parts.length > 1) - { - extension = parts[parts.length - 1]; - } - String filenm = filename.substring(0, filename.length() - extension.length()); - f = File.createTempFile(filenm, "." + extension, finalDir); - } - item.write(f); - newFiles.add(f); - // delete temporary file - item.delete(); - logger.fatal(Logger.SECURITY_SUCCESS, "File successfully uploaded: " + f); - if (session != null) - { - session.setAttribute("progress", Long.toString(0)); - } - } - } - } catch (Exception e) { - if (e instanceof ValidationUploadException) { - throw (ValidationException)e; - } - throw new ValidationUploadException("Upload failure", "Problem during upload:" + e.getMessage(), e); - } - return Collections.synchronizedList(newFiles); - } - - - - /** - * Utility to return the first cookie matching the provided name. - * @param request - * @param name - */ - private Cookie getFirstCookie(HttpServletRequest request, String name) { - Cookie[] cookies = request.getCookies(); - if (cookies != null) { - for (Cookie cookie : cookies) - { - if (cookie.getName().equals(name)) - { - return cookie; - } - } - } - return null; - } - - /** - * {@inheritDoc} - */ - public String getHeader( HttpServletRequest request, String name ) throws ValidationException { - String value = request.getHeader(name); - return ESAPI.validator().getValidInput("HTTP header value: " + value, value, "HTTPHeaderValue", 150, false); - } - - - /** - * {@inheritDoc} - */ - public String getHeader( String name ) throws ValidationException { - return getHeader( getCurrentRequest(), name ); - } - - - /** - * {@inheritDoc} - */ - public String getParameter( HttpServletRequest request, String name ) throws ValidationException { - String value = request.getParameter(name); - return ESAPI.validator().getValidInput("HTTP parameter value: " + value, value, "HTTPParameterValue", 2000, true); - } - - /** - * {@inheritDoc} - */ - public String getParameter( String name ) throws ValidationException { - return getParameter( getCurrentRequest(), name ); - } - - /** - * {@inheritDoc} - */ - public void killAllCookies() { - killAllCookies( getCurrentRequest(), getCurrentResponse() ); - } - - /** - * {@inheritDoc} - * - * @param request - * @param response - */ - public void killAllCookies(HttpServletRequest request, HttpServletResponse response) { - Cookie[] cookies = request.getCookies(); - if (cookies != null) { - for (Cookie cookie : cookies) - { - killCookie(request, response, cookie.getName()); - } - } - } - - - /** - * {@inheritDoc} - * - * @param request - * @param response - * @param name - */ - public void killCookie(HttpServletRequest request, HttpServletResponse response, String name) { - String path = "//"; - String domain=""; - Cookie cookie = getFirstCookie(request, name); - if ( cookie != null ) { - path = cookie.getPath(); - domain = cookie.getDomain(); - } - Cookie deleter = new Cookie( name, "deleted" ); - deleter.setMaxAge( 0 ); - if ( domain != null ) deleter.setDomain( domain ); - if ( path != null ) deleter.setPath( path ); - response.addCookie( deleter ); - } - - - /** - * {@inheritDoc} - */ - public void killCookie( String name ) { - killCookie( getCurrentRequest(), getCurrentResponse(), name ); - } - - - /** - * {@inheritDoc} - */ - public void logHTTPRequest() { - logHTTPRequest( getCurrentRequest(), logger, null ); - } - - /** - * {@inheritDoc} - */ - public void logHTTPRequest(HttpServletRequest request, Logger logger) { - logHTTPRequest( request, logger, null ); - } - - /** - * Formats an HTTP request into a log suitable string. This implementation logs the remote host IP address (or - * hostname if available), the request method (GET/POST), the URL, and all the querystring and form parameters. All - * the parameters are presented as though they were in the URL even if they were in a form. Any parameters that - * match items in the parameterNamesToObfuscate are shown as eight asterisks. - * - * - * @param request - */ - public void logHTTPRequest(HttpServletRequest request, Logger logger, List parameterNamesToObfuscate) { - StringBuilder params = new StringBuilder(); - Iterator i = request.getParameterMap().keySet().iterator(); - while (i.hasNext()) { - String key = (String) i.next(); - String[] value = (String[]) request.getParameterMap().get(key); - for (int j = 0; j < value.length; j++) { - params.append(key).append("="); - if (parameterNamesToObfuscate != null && parameterNamesToObfuscate.contains(key)) { - params.append("********"); - } else { - params.append(value[j]); - } - if (j < value.length - 1) { - params.append("&"); - } - } - if (i.hasNext()) - params.append("&"); - } - Cookie[] cookies = request.getCookies(); - if ( cookies != null ) { - for (Cookie cooky : cookies) - { - if (!cooky.getName().equals(ESAPI.securityConfiguration().getHttpSessionIdName())) - { - params.append("+").append(cooky.getName()).append("=").append(cooky.getValue()); - } - } - } - String msg = request.getMethod() + " " + request.getRequestURL() + (params.length() > 0 ? "?" + params : ""); - logger.info(Logger.SECURITY_SUCCESS, msg); - } - - private Map queryToMap(String query) { - TreeMap map = new TreeMap(); - String[] parts = query.split("&"); - for (String part : parts) - { - try - { - String[] nvpair = part.split("="); - String name = ESAPI.encoder().decodeFromURL(nvpair[0]); - String value = ESAPI.encoder().decodeFromURL(nvpair[1]); - map.put(name, value); - } - catch (EncodingException e) - { - // skip the nvpair with the encoding problem - note this is already logged. - } - } - return map; - } - - /** - * {@inheritDoc} - * - * This implementation simply checks to make sure that the forward location starts with "WEB-INF" and - * is intended for use in frameworks that forward to JSP files inside the WEB-INF folder. - */ - public void sendForward(HttpServletRequest request, HttpServletResponse response, String location) throws AccessControlException,ServletException,IOException { - if (!location.startsWith("WEB-INF")) { - throw new AccessControlException("Forward failed", "Bad forward location: " + location); - } - RequestDispatcher dispatcher = request.getRequestDispatcher(location); - dispatcher.forward( request, response ); - } - - /** - * {@inheritDoc} - */ - public void sendForward( String location ) throws AccessControlException,ServletException,IOException { - sendForward( getCurrentRequest(), getCurrentResponse(), location); - } - - /** - * {@inheritDoc} - * - * This implementation checks against the list of safe redirect locations defined in ESAPI.properties. - * - * @param response - */ - public void sendRedirect(HttpServletResponse response, String location) throws AccessControlException, IOException { - if (!ESAPI.validator().isValidRedirectLocation("Redirect", location, false)) { - logger.fatal(Logger.SECURITY_FAILURE, "Bad redirect location: " + location); - throw new AccessControlException("Redirect failed"); - } - response.sendRedirect(location); - } - - /** - * {@inheritDoc} - */ - public void sendRedirect( String location ) throws AccessControlException,IOException { - sendRedirect( getCurrentResponse(), location); - } - - /** - * {@inheritDoc} - */ - public void setContentType() { - setContentType( getCurrentResponse() ); - } - - - /** - * {@inheritDoc} - */ - public void setContentType(HttpServletResponse response) { - response.setContentType((ESAPI.securityConfiguration()).getResponseContentType()); - } - - /** - * {@inheritDoc} - */ - public void setCurrentHTTP(HttpServletRequest request, HttpServletResponse response) { - currentRequest.setRequest(request); - currentResponse.setResponse(response); - } - - /** - * {@inheritDoc} - */ - public void setHeader(HttpServletResponse response, String name, String value) { - try { - String strippedName = StringUtilities.replaceLinearWhiteSpace(name); - String strippedValue = StringUtilities.replaceLinearWhiteSpace(value); - String safeName = ESAPI.validator().getValidInput("setHeader", strippedName, "HTTPHeaderName", 20, false); - String safeValue = ESAPI.validator().getValidInput("setHeader", strippedValue, "HTTPHeaderValue", 500, false); - response.setHeader(safeName, safeValue); - } catch (ValidationException e) { - logger.warning(Logger.SECURITY_FAILURE, "Attempt to set invalid header denied", e); - } - } - - - /** - * {@inheritDoc} - */ - public void setHeader( String name, String value ) { - setHeader( getCurrentResponse(), name, value ); - } - - - /** - * {@inheritDoc} - */ - public void setNoCacheHeaders() { - setNoCacheHeaders( getCurrentResponse() ); - } - - /** - * {@inheritDoc} - * - * @param response - */ - public void setNoCacheHeaders(HttpServletResponse response) { - // HTTP 1.1 - response.setHeader("Cache-Control", "no-store, no-cache, must-revalidate"); - - // HTTP 1.0 - response.setHeader("Pragma","no-cache"); - response.setDateHeader("Expires", -1); - } - - /** - * {@inheritDoc} - * - * Save the user's remember me data in an encrypted cookie and send it to the user. - * Any old remember me cookie is destroyed first. Setting this cookie will keep the user - * logged in until the maxAge passes, the password is changed, or the cookie is deleted. - * If the cookie exists for the current user, it will automatically be used by ESAPI to - * log the user in, if the data is valid and not expired. - * - * @param request - * @param response - */ - public String setRememberToken( HttpServletRequest request, HttpServletResponse response, String password, int maxAge, String domain, String path ) { - User user = ESAPI.authenticator().getCurrentUser(); - try { - killCookie(request, response, REMEMBER_TOKEN_COOKIE_NAME ); - // seal already contains random data - String clearToken = user.getAccountName() + "|" + password; - long expiry = ESAPI.encryptor().getRelativeTimeStamp(maxAge * 1000); - String cryptToken = ESAPI.encryptor().seal(clearToken, expiry); - - // Do NOT URLEncode cryptToken before creating cookie. See Google Issue # 144, - // which was marked as "WontFix". - - Cookie cookie = new Cookie( REMEMBER_TOKEN_COOKIE_NAME, cryptToken ); - cookie.setMaxAge( maxAge ); - cookie.setDomain( domain ); - cookie.setPath( path ); - response.addCookie( cookie ); - logger.info(Logger.SECURITY_SUCCESS, "Enabled remember me token for " + user.getAccountName() ); - return cryptToken; - } catch( IntegrityException e ) { - logger.warning(Logger.SECURITY_FAILURE, "Attempt to set remember me token failed for " + user.getAccountName(), e ); - return null; - } - } - - /** - * {@inheritDoc} - */ - public String setRememberToken( String password, int maxAge, String domain, String path ) { - return setRememberToken( getCurrentRequest(), getCurrentResponse(), password, maxAge, domain, path ); - } - - - /** - * {@inheritDoc} - */ - public void verifyCSRFToken() throws IntrusionException { - verifyCSRFToken( getCurrentRequest() ); - } - - /** - * {@inheritDoc} - * - * This implementation uses the CSRF_TOKEN_NAME parameter for the token. - * - * @param request - */ - public void verifyCSRFToken(HttpServletRequest request) throws IntrusionException { - User user = ESAPI.authenticator().getCurrentUser(); - - // check if user authenticated with this request - no CSRF protection required - if( request.getAttribute(user.getCSRFToken()) != null ) { - return; - } - String token = request.getParameter(CSRF_TOKEN_NAME); - if ( !user.getCSRFToken().equals( token ) ) { - throw new IntrusionException("Authentication failed", "Possibly forged HTTP request without proper CSRF token detected"); - } - } - - /** - * {@inheritDoc} - */ - public T getSessionAttribute( String key ) { - final HttpSession session = ESAPI.currentRequest().getSession(false); - if ( session != null ) - return (T) session.getAttribute(key); - return null; - } - - /** - * {@inheritDoc} - */ - public T getSessionAttribute(HttpSession session, String key) - { - return (T) session.getAttribute(key); - } - - /** - * {@inheritDoc} - */ - public T getRequestAttribute(String key) - { - return (T) ESAPI.currentRequest().getAttribute(key); - } - - /** - * {@inheritDoc} - */ - public T getRequestAttribute(HttpServletRequest request, String key) - { - return (T) request.getAttribute( key ); - } - - ///////////////////// - - /* Helper method to encrypt using new Encryptor encryption methods and - * return the serialized ciphertext as a hex-encoded string. - */ - private String encryptString(String plaintext) throws EncryptionException { - PlainText pt = new PlainText(plaintext); - CipherText ct = ESAPI.encryptor().encrypt(pt); - byte[] serializedCiphertext = ct.asPortableSerializedByteArray(); - return Hex.encode(serializedCiphertext, false); - } - - /* Helper method to decrypt a hex-encode serialized ciphertext string and - * to decrypt it using the new Encryptor decryption methods. - */ - private String decryptString(String ciphertext) throws EncryptionException { - byte[] serializedCiphertext = Hex.decode(ciphertext); - CipherText restoredCipherText = - CipherText.fromPortableSerializedBytes(serializedCiphertext); - PlainText plaintext = ESAPI.encryptor().decrypt(restoredCipherText); - return plaintext.toString(); - } -} +/** + * OWASP Enterprise Security API (ESAPI) + * + * This file is part of the Open Web Application Security Project (OWASP) + * Enterprise Security API (ESAPI) project. For details, please see + * http://www.owasp.org/index.php/ESAPI. + * + * Copyright (c) 2007 - The OWASP Foundation + * + * The ESAPI is published by OWASP under the BSD license. You should read and accept the + * LICENSE before you use, modify, and/or redistribute this software. + * + * @author Jeff Williams Aspect Security + * @created 2007 + */ +package org.owasp.esapi.reference; + +import org.apache.commons.fileupload.FileItem; +import org.apache.commons.fileupload.ProgressListener; +import org.apache.commons.fileupload.disk.DiskFileItemFactory; +import org.apache.commons.fileupload.servlet.ServletFileUpload; +import org.owasp.esapi.*; +import org.owasp.esapi.codecs.Hex; +import org.owasp.esapi.crypto.CipherText; +import org.owasp.esapi.crypto.PlainText; +import org.owasp.esapi.errors.*; + +import javax.servlet.RequestDispatcher; +import javax.servlet.ServletException; +import javax.servlet.http.Cookie; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpSession; +import java.io.File; +import java.io.IOException; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; + +/** + * Reference implementation of the HTTPUtilities interface. This implementation + * uses the Apache Commons FileUploader library, which in turn uses the Apache + * Commons IO library. + *

+ * To simplify the interface, some methods use the current request and response that + * are tracked by ThreadLocal variables in the Authenticator. This means that you + * must have called ESAPI.authenticator().setCurrentHTTP(request, response) before + * calling these methods. + *

+ * Typically, this is done by calling the Authenticator.login() method, which + * calls setCurrentHTTP() automatically. However if you want to use these methods + * in another application, you should explicitly call setCurrentHTTP() in your + * own code. In either case, you *must* call ESAPI.clearCurrent() to clear threadlocal + * variables before the thread is reused. The advantages of having identity everywhere + * outweigh the disadvantages of this approach. + * + * @author Jeff Williams (jeff.williams .at. aspectsecurity.com) Aspect Security + * @since June 1, 2007 + * @see org.owasp.esapi.HTTPUtilities + */ +public class DefaultHTTPUtilities implements org.owasp.esapi.HTTPUtilities { + private static volatile HTTPUtilities instance = null; + + public static HTTPUtilities getInstance() { + if ( instance == null ) { + synchronized ( DefaultHTTPUtilities.class ) { + if ( instance == null ) { + instance = new DefaultHTTPUtilities(); + } + } + } + return instance; + } + + /** + * Defines the ThreadLocalRequest to store the current request for this thread. + */ + private class ThreadLocalRequest extends InheritableThreadLocal { + + public HttpServletRequest getRequest() { + return super.get(); + } + + public HttpServletRequest initialValue() { + return null; + } + + public void setRequest(HttpServletRequest newRequest) { + super.set(newRequest); + } + } + + /** + * Defines the ThreadLocalResponse to store the current response for this thread. + */ + private class ThreadLocalResponse extends InheritableThreadLocal { + + public HttpServletResponse getResponse() { + return super.get(); + } + + public HttpServletResponse initialValue() { + return null; + } + + public void setResponse(HttpServletResponse newResponse) { + super.set(newResponse); + } + } + + /** The logger. */ + private final Logger logger = ESAPI.getLogger("HTTPUtilities"); + + /** The max bytes. */ + static final int maxBytes = ESAPI.securityConfiguration().getAllowedFileUploadSize(); + + + /* + * The currentRequest ThreadLocal variable is used to make the currentRequest available to any call in any part of an + * application. This enables API's for actions that require the request to be much simpler. For example, the logout() + * method in the Authenticator class requires the currentRequest to get the session in order to invalidate it. + */ + private ThreadLocalRequest currentRequest = new ThreadLocalRequest(); + + /* + * The currentResponse ThreadLocal variable is used to make the currentResponse available to any call in any part of an + * application. This enables API's for actions that require the response to be much simpler. For example, the logout() + * method in the Authenticator class requires the currentResponse to kill the Session ID cookie. + */ + private ThreadLocalResponse currentResponse = new ThreadLocalResponse(); + + + + /** + * No arg constructor. + */ + public DefaultHTTPUtilities() { + } + + + /** + * {@inheritDoc} + * This implementation uses a custom "set-cookie" header rather than Java's + * cookie interface which doesn't allow the use of HttpOnly. Configure the + * HttpOnly and Secure settings in ESAPI.properties. + */ + public void addCookie( Cookie cookie ) { + addCookie( getCurrentResponse(), cookie ); + } + + /** + * {@inheritDoc} + * This implementation uses a custom "set-cookie" header rather than Java's + * cookie interface which doesn't allow the use of HttpOnly. Configure the + * HttpOnly and Secure settings in ESAPI.properties. + */ + public void addCookie(HttpServletResponse response, Cookie cookie) { + String name = cookie.getName(); + String value = cookie.getValue(); + int maxAge = cookie.getMaxAge(); + String domain = cookie.getDomain(); + String path = cookie.getPath(); + boolean secure = cookie.getSecure(); + + // validate the name and value + ValidationErrorList errors = new ValidationErrorList(); + String cookieName = ESAPI.validator().getValidInput("cookie name", name, "HTTPCookieName", 50, false, errors); + String cookieValue = ESAPI.validator().getValidInput("cookie value", value, "HTTPCookieValue", 5000, false, errors); + + // if there are no errors, then set the cookie either with a header or normally + if (errors.size() == 0) { + if ( ESAPI.securityConfiguration().getForceHttpOnlyCookies() ) { + String header = createCookieHeader(cookieName, cookieValue, maxAge, domain, path, secure); + addHeader(response, "Set-Cookie", header); + } else { + // Issue 23 - If the ESAPI Configuration is set to force secure cookies, force the secure flag on the cookie before setting it + cookie.setSecure( secure || ESAPI.securityConfiguration().getForceSecureCookies() ); + response.addCookie(cookie); + } + return; + } + logger.warning(Logger.SECURITY_FAILURE, "Attempt to add unsafe data to cookie (skip mode). Skipping cookie and continuing."); + } + + + + /** + * {@inheritDoc} + */ + public String addCSRFToken(String href) { + User user = ESAPI.authenticator().getCurrentUser(); + if (user.isAnonymous()) { + return href; + } + + // if there are already parameters append with &, otherwise append with ? + String token = CSRF_TOKEN_NAME + "=" + user.getCSRFToken(); + return href.indexOf( '?') != -1 ? href + "&" + token : href + "?" + token; + } + + /** + * {@inheritDoc} + */ + public void addHeader(String name, String value) { + addHeader( getCurrentResponse(), name, value ); + } + + /** + * {@inheritDoc} + */ + public void addHeader(HttpServletResponse response, String name, String value) { + try { + String strippedName = StringUtilities.replaceLinearWhiteSpace(name); + String strippedValue = StringUtilities.replaceLinearWhiteSpace(value); + String safeName = ESAPI.validator().getValidInput("addHeader", strippedName, "HTTPHeaderName", 20, false); + String safeValue = ESAPI.validator().getValidInput("addHeader", strippedValue, "HTTPHeaderValue", 500, false); + response.addHeader(safeName, safeValue); + } catch (ValidationException e) { + logger.warning(Logger.SECURITY_FAILURE, "Attempt to add invalid header denied", e); + } + } + + /** + * {@inheritDoc} + */ + public void assertSecureChannel() throws AccessControlException { + assertSecureChannel( getCurrentRequest() ); + } + + /** + * {@inheritDoc} + * + * This implementation ignores the built-in isSecure() method + * and uses the URL to determine if the request was transmitted over SSL. + * This is because SSL may have been terminated somewhere outside the + * container. + */ + public void assertSecureChannel(HttpServletRequest request) throws AccessControlException { + if ( request == null ) { + throw new AccessControlException( "Insecure request received", "HTTP request was null" ); + } + StringBuffer sb = request.getRequestURL(); + if ( sb == null ) { + throw new AccessControlException( "Insecure request received", "HTTP request URL was null" ); + } + String url = sb.toString(); + if ( !url.startsWith( "https" ) ) { + throw new AccessControlException( "Insecure request received", "HTTP request did not use SSL" ); + } + } + + /** + * {@inheritDoc} + */ + public void assertSecureRequest() throws AccessControlException { + assertSecureRequest( getCurrentRequest() ); + } + + /** + * {@inheritDoc} + */ + public void assertSecureRequest(HttpServletRequest request) throws AccessControlException { + assertSecureChannel( request ); + String receivedMethod = request.getMethod(); + String requiredMethod = "POST"; + if ( !receivedMethod.equals( requiredMethod ) ) { + throw new AccessControlException( "Insecure request received", "Received request using " + receivedMethod + " when only " + requiredMethod + " is allowed" ); + } + } + + /** + * {@inheritDoc} + */ + public HttpSession changeSessionIdentifier() throws AuthenticationException { + return changeSessionIdentifier( getCurrentRequest() ); + } + + /** + * {@inheritDoc} + */ + public HttpSession changeSessionIdentifier(HttpServletRequest request) throws AuthenticationException { + + // get the current session + HttpSession oldSession = request.getSession(); + + // make a copy of the session content + Map temp = new ConcurrentHashMap(); + Enumeration e = oldSession.getAttributeNames(); + while (e != null && e.hasMoreElements()) { + String name = (String) e.nextElement(); + Object value = oldSession.getAttribute(name); + temp.put(name, value); + } + + // kill the old session and create a new one + oldSession.invalidate(); + HttpSession newSession = request.getSession(); + User user = ESAPI.authenticator().getCurrentUser(); + user.addSession( newSession ); + user.removeSession( oldSession ); + + // copy back the session content + for (Map.Entry stringObjectEntry : temp.entrySet()) + { + newSession.setAttribute(stringObjectEntry.getKey(), stringObjectEntry.getValue()); + } + return newSession; + } + + /** + * {@inheritDoc} + */ + public void clearCurrent() { + currentRequest.set(null); + currentResponse.set(null); + } + + private String createCookieHeader(String name, String value, int maxAge, String domain, String path, boolean secure) { + // create the special cookie header instead of creating a Java cookie + // Set-Cookie:=[; =][; expires=][; + // domain=][; path=][; secure][;HttpOnly] + String header = name + "=" + value; + header += "; Max-Age=" + maxAge; + if (domain != null) { + header += "; Domain=" + domain; + } + if (path != null) { + header += "; Path=" + path; + } + if ( secure || ESAPI.securityConfiguration().getForceSecureCookies() ) { + header += "; Secure"; + } + if ( ESAPI.securityConfiguration().getForceHttpOnlyCookies() ) { + header += "; HttpOnly"; + } + return header; + } + + /** + * {@inheritDoc} + */ + public String decryptHiddenField(String encrypted) { + try { + return decryptString(encrypted); + } catch( EncryptionException e ) { + throw new IntrusionException("Invalid request","Tampering detected. Hidden field data did not decrypt properly.", e); + } + } + + /** + * {@inheritDoc} + */ + public Map decryptQueryString(String encrypted) throws EncryptionException { + String plaintext = decryptString(encrypted); + return queryToMap(plaintext); + } + + /** + * {@inheritDoc} + */ + public Map decryptStateFromCookie() throws EncryptionException { + return decryptStateFromCookie( getCurrentRequest() ); + } + + /** + * {@inheritDoc} + * + * @param request + */ + public Map decryptStateFromCookie(HttpServletRequest request) throws EncryptionException { + try { + String encrypted = getCookie( request, ESAPI_STATE ); + if ( encrypted == null ) return new HashMap(); + String plaintext = decryptString(encrypted); + return queryToMap( plaintext ); + } catch( ValidationException e ) { + return null; + } + } + + /** + * {@inheritDoc} + */ + public String encryptHiddenField(String value) throws EncryptionException { + return encryptString(value); + } + + /** + * {@inheritDoc} + */ + public String encryptQueryString(String query) throws EncryptionException { + return encryptString(query); + } + + /** + * {@inheritDoc} + */ + public void encryptStateInCookie(HttpServletResponse response, Map cleartext) throws EncryptionException { + StringBuilder sb = new StringBuilder(); + Iterator i = cleartext.entrySet().iterator(); + while ( i.hasNext() ) { + try { + Map.Entry entry = (Map.Entry)i.next(); + + // What do these need to be URL encoded? They are encrypted! + String name = ESAPI.encoder().encodeForURL( entry.getKey().toString() ); + String value = ESAPI.encoder().encodeForURL( entry.getValue().toString() ); + sb.append(name).append("=").append(value); + if ( i.hasNext() ) sb.append( "&" ); + } catch( EncodingException e ) { + logger.error(Logger.SECURITY_FAILURE, "Problem encrypting state in cookie - skipping entry", e ); + } + } + + String encrypted = encryptString(sb.toString()); + + if ( encrypted.length() > (MAX_COOKIE_LEN ) ) { + logger.error(Logger.SECURITY_FAILURE, "Problem encrypting state in cookie - skipping entry"); + throw new EncryptionException("Encryption failure", "Encrypted cookie state of " + encrypted.length() + " longer than allowed " + MAX_COOKIE_LEN ); + } + + Cookie cookie = new Cookie( ESAPI_STATE, encrypted ); + addCookie( response, cookie ); + } + + /** + * {@inheritDoc} + */ + public void encryptStateInCookie( Map cleartext ) throws EncryptionException { + encryptStateInCookie( getCurrentResponse(), cleartext ); + } + + + /** + * {@inheritDoc} + */ + public String getCookie( HttpServletRequest request, String name ) throws ValidationException { + Cookie c = getFirstCookie( request, name ); + if ( c == null ) return null; + String value = c.getValue(); + return ESAPI.validator().getValidInput("HTTP cookie value: " + value, value, "HTTPCookieValue", 1000, false); + } + + /** + * {@inheritDoc} + */ + public String getCookie( String name ) throws ValidationException { + return getCookie( getCurrentRequest(), name ); + } + + /** + * {@inheritDoc} + */ + public String getCSRFToken() { + User user = ESAPI.authenticator().getCurrentUser(); + if (user == null) return null; + return user.getCSRFToken(); + } + + + /** + * {@inheritDoc} + */ + public HttpServletRequest getCurrentRequest() { + return currentRequest.getRequest(); + } + + + /** + * {@inheritDoc} + */ + public HttpServletResponse getCurrentResponse() { + return currentResponse.getResponse(); + } + + /** + * {@inheritDoc} + */ + public List getFileUploads() throws ValidationException { + return getFileUploads( getCurrentRequest(), ESAPI.securityConfiguration().getUploadDirectory(), ESAPI.securityConfiguration().getAllowedFileExtensions() ); + } + + /** + * {@inheritDoc} + */ + public List getFileUploads(HttpServletRequest request) throws ValidationException { + return getFileUploads(request, ESAPI.securityConfiguration().getUploadDirectory(), ESAPI.securityConfiguration().getAllowedFileExtensions()); + } + + /** + * {@inheritDoc} + */ + public List getFileUploads(HttpServletRequest request, File finalDir ) throws ValidationException { + return getFileUploads(request, finalDir, ESAPI.securityConfiguration().getAllowedFileExtensions()); + } + + /** + * {@inheritDoc} + */ + public List getFileUploads(HttpServletRequest request, File finalDir, List allowedExtensions) throws ValidationException { + File tempDir = ESAPI.securityConfiguration().getUploadTempDirectory(); + if ( !tempDir.exists() ) { + if ( !tempDir.mkdirs() ) throw new ValidationUploadException( "Upload failed", "Could not create temp directory: " + tempDir.getAbsolutePath() ); + } + + if( finalDir != null){ + if ( !finalDir.exists() ) { + if ( !finalDir.mkdirs() ) throw new ValidationUploadException( "Upload failed", "Could not create final upload directory: " + finalDir.getAbsolutePath() ); + } + } + else { + if ( !ESAPI.securityConfiguration().getUploadDirectory().exists()) { + if ( !ESAPI.securityConfiguration().getUploadDirectory().mkdirs() ) throw new ValidationUploadException( "Upload failed", "Could not create final upload directory: " + ESAPI.securityConfiguration().getUploadDirectory().getAbsolutePath() ); + } + finalDir = ESAPI.securityConfiguration().getUploadDirectory(); + } + + List newFiles = new ArrayList(); + try { + final HttpSession session = request.getSession(false); + if (!ServletFileUpload.isMultipartContent(request)) { + throw new ValidationUploadException("Upload failed", "Not a multipart request"); + } + + // this factory will store ALL files in the temp directory, + // regardless of size + DiskFileItemFactory factory = new DiskFileItemFactory(0, tempDir); + ServletFileUpload upload = new ServletFileUpload(factory); + upload.setSizeMax(maxBytes); + + // Create a progress listener + ProgressListener progressListener = new ProgressListener() { + private long megaBytes = -1; + private long progress = 0; + + public void update(long pBytesRead, long pContentLength, int pItems) { + if (pItems == 0) + return; + long mBytes = pBytesRead / 1000000; + if (megaBytes == mBytes) + return; + megaBytes = mBytes; + progress = (long) (((double) pBytesRead / (double) pContentLength) * 100); + if ( session != null ) { + session.setAttribute("progress", Long.toString(progress)); + } + // logger.logSuccess(Logger.SECURITY, " Item " + pItems + " (" + progress + "% of " + pContentLength + " bytes]"); + } + }; + upload.setProgressListener(progressListener); + + List items = upload.parseRequest(request); + for (FileItem item : items) + { + if (!item.isFormField() && item.getName() != null && !(item.getName().equals(""))) + { + String[] fparts = item.getName().split("[\\/\\\\]"); + String filename = fparts[fparts.length - 1]; + + if (!ESAPI.validator().isValidFileName("upload", filename, allowedExtensions, false)) + { + throw new ValidationUploadException("Upload only simple filenames with the following extensions " + allowedExtensions, "Upload failed isValidFileName check"); + } + + logger.info(Logger.SECURITY_SUCCESS, "File upload requested: " + filename); + File f = new File(finalDir, filename); + if (f.exists()) + { + String[] parts = filename.split("\\/."); + String extension = ""; + if (parts.length > 1) + { + extension = parts[parts.length - 1]; + } + String filenm = filename.substring(0, filename.length() - extension.length()); + f = File.createTempFile(filenm, "." + extension, finalDir); + } + item.write(f); + newFiles.add(f); + // delete temporary file + item.delete(); + logger.fatal(Logger.SECURITY_SUCCESS, "File successfully uploaded: " + f); + if (session != null) + { + session.setAttribute("progress", Long.toString(0)); + } + } + } + } catch (Exception e) { + if (e instanceof ValidationUploadException) { + throw (ValidationException)e; + } + throw new ValidationUploadException("Upload failure", "Problem during upload:" + e.getMessage(), e); + } + return Collections.synchronizedList(newFiles); + } + + + + /** + * Utility to return the first cookie matching the provided name. + * @param request + * @param name + */ + private Cookie getFirstCookie(HttpServletRequest request, String name) { + Cookie[] cookies = request.getCookies(); + if (cookies != null) { + for (Cookie cookie : cookies) + { + if (cookie.getName().equals(name)) + { + return cookie; + } + } + } + return null; + } + + /** + * {@inheritDoc} + */ + public String getHeader( HttpServletRequest request, String name ) throws ValidationException { + String value = request.getHeader(name); + return ESAPI.validator().getValidInput("HTTP header value: " + value, value, "HTTPHeaderValue", 150, false); + } + + + /** + * {@inheritDoc} + */ + public String getHeader( String name ) throws ValidationException { + return getHeader( getCurrentRequest(), name ); + } + + + /** + * {@inheritDoc} + */ + public String getParameter( HttpServletRequest request, String name ) throws ValidationException { + String value = request.getParameter(name); + return ESAPI.validator().getValidInput("HTTP parameter value: " + value, value, "HTTPParameterValue", 2000, true); + } + + /** + * {@inheritDoc} + */ + public String getParameter( String name ) throws ValidationException { + return getParameter( getCurrentRequest(), name ); + } + + /** + * {@inheritDoc} + */ + public void killAllCookies() { + killAllCookies( getCurrentRequest(), getCurrentResponse() ); + } + + /** + * {@inheritDoc} + * + * @param request + * @param response + */ + public void killAllCookies(HttpServletRequest request, HttpServletResponse response) { + Cookie[] cookies = request.getCookies(); + if (cookies != null) { + for (Cookie cookie : cookies) + { + killCookie(request, response, cookie.getName()); + } + } + } + + + /** + * {@inheritDoc} + * + * @param request + * @param response + * @param name + */ + public void killCookie(HttpServletRequest request, HttpServletResponse response, String name) { + String path = "//"; + String domain=""; + Cookie cookie = getFirstCookie(request, name); + if ( cookie != null ) { + path = cookie.getPath(); + domain = cookie.getDomain(); + } + Cookie deleter = new Cookie( name, "deleted" ); + deleter.setMaxAge( 0 ); + if ( domain != null ) deleter.setDomain( domain ); + if ( path != null ) deleter.setPath( path ); + response.addCookie( deleter ); + } + + + /** + * {@inheritDoc} + */ + public void killCookie( String name ) { + killCookie( getCurrentRequest(), getCurrentResponse(), name ); + } + + + /** + * {@inheritDoc} + */ + public void logHTTPRequest() { + logHTTPRequest( getCurrentRequest(), logger, null ); + } + + /** + * {@inheritDoc} + */ + public void logHTTPRequest(HttpServletRequest request, Logger logger) { + logHTTPRequest( request, logger, null ); + } + + /** + * Formats an HTTP request into a log suitable string. This implementation logs the remote host IP address (or + * hostname if available), the request method (GET/POST), the URL, and all the querystring and form parameters. All + * the parameters are presented as though they were in the URL even if they were in a form. Any parameters that + * match items in the parameterNamesToObfuscate are shown as eight asterisks. + * + * + * @param request + */ + public void logHTTPRequest(HttpServletRequest request, Logger logger, List parameterNamesToObfuscate) { + StringBuilder params = new StringBuilder(); + Iterator i = request.getParameterMap().keySet().iterator(); + while (i.hasNext()) { + String key = (String) i.next(); + String[] value = (String[]) request.getParameterMap().get(key); + for (int j = 0; j < value.length; j++) { + params.append(key).append("="); + if (parameterNamesToObfuscate != null && parameterNamesToObfuscate.contains(key)) { + params.append("********"); + } else { + params.append(value[j]); + } + if (j < value.length - 1) { + params.append("&"); + } + } + if (i.hasNext()) + params.append("&"); + } + Cookie[] cookies = request.getCookies(); + if ( cookies != null ) { + for (Cookie cooky : cookies) + { + if (!cooky.getName().equals(ESAPI.securityConfiguration().getHttpSessionIdName())) + { + params.append("+").append(cooky.getName()).append("=").append(cooky.getValue()); + } + } + } + String msg = request.getMethod() + " " + request.getRequestURL() + (params.length() > 0 ? "?" + params : ""); + logger.info(Logger.SECURITY_SUCCESS, msg); + } + + private Map queryToMap(String query) { + TreeMap map = new TreeMap(); + String[] parts = query.split("&"); + for (String part : parts) + { + try + { + String[] nvpair = part.split("="); + String name = ESAPI.encoder().decodeFromURL(nvpair[0]); + String value = ESAPI.encoder().decodeFromURL(nvpair[1]); + map.put(name, value); + } + catch (EncodingException e) + { + // skip the nvpair with the encoding problem - note this is already logged. + } + } + return map; + } + + /** + * {@inheritDoc} + * + * This implementation simply checks to make sure that the forward location starts with "WEB-INF" and + * is intended for use in frameworks that forward to JSP files inside the WEB-INF folder. + */ + public void sendForward(HttpServletRequest request, HttpServletResponse response, String location) throws AccessControlException,ServletException,IOException { + if (!location.startsWith("WEB-INF")) { + throw new AccessControlException("Forward failed", "Bad forward location: " + location); + } + RequestDispatcher dispatcher = request.getRequestDispatcher(location); + dispatcher.forward( request, response ); + } + + /** + * {@inheritDoc} + */ + public void sendForward( String location ) throws AccessControlException,ServletException,IOException { + sendForward( getCurrentRequest(), getCurrentResponse(), location); + } + + /** + * {@inheritDoc} + * + * This implementation checks against the list of safe redirect locations defined in ESAPI.properties. + * + * @param response + */ + public void sendRedirect(HttpServletResponse response, String location) throws AccessControlException, IOException { + if (!ESAPI.validator().isValidRedirectLocation("Redirect", location, false)) { + logger.fatal(Logger.SECURITY_FAILURE, "Bad redirect location: " + location); + throw new AccessControlException("Redirect failed", "Bad redirect location: " + location); + } + response.sendRedirect(location); + } + + /** + * {@inheritDoc} + */ + public void sendRedirect( String location ) throws AccessControlException,IOException { + sendRedirect( getCurrentResponse(), location); + } + + /** + * {@inheritDoc} + */ + public void setContentType() { + setContentType( getCurrentResponse() ); + } + + + /** + * {@inheritDoc} + */ + public void setContentType(HttpServletResponse response) { + response.setContentType((ESAPI.securityConfiguration()).getResponseContentType()); + } + + /** + * {@inheritDoc} + */ + public void setCurrentHTTP(HttpServletRequest request, HttpServletResponse response) { + currentRequest.setRequest(request); + currentResponse.setResponse(response); + } + + /** + * {@inheritDoc} + */ + public void setHeader(HttpServletResponse response, String name, String value) { + try { + String strippedName = StringUtilities.replaceLinearWhiteSpace(name); + String strippedValue = StringUtilities.replaceLinearWhiteSpace(value); + String safeName = ESAPI.validator().getValidInput("setHeader", strippedName, "HTTPHeaderName", 20, false); + String safeValue = ESAPI.validator().getValidInput("setHeader", strippedValue, "HTTPHeaderValue", 500, false); + response.setHeader(safeName, safeValue); + } catch (ValidationException e) { + logger.warning(Logger.SECURITY_FAILURE, "Attempt to set invalid header denied", e); + } + } + + + /** + * {@inheritDoc} + */ + public void setHeader( String name, String value ) { + setHeader( getCurrentResponse(), name, value ); + } + + + /** + * {@inheritDoc} + */ + public void setNoCacheHeaders() { + setNoCacheHeaders( getCurrentResponse() ); + } + + /** + * {@inheritDoc} + * + * @param response + */ + public void setNoCacheHeaders(HttpServletResponse response) { + // HTTP 1.1 + response.setHeader("Cache-Control", "no-store, no-cache, must-revalidate"); + + // HTTP 1.0 + response.setHeader("Pragma","no-cache"); + response.setDateHeader("Expires", -1); + } + + /** + * {@inheritDoc} + * + * Save the user's remember me data in an encrypted cookie and send it to the user. + * Any old remember me cookie is destroyed first. Setting this cookie will keep the user + * logged in until the maxAge passes, the password is changed, or the cookie is deleted. + * If the cookie exists for the current user, it will automatically be used by ESAPI to + * log the user in, if the data is valid and not expired. + * + * @param request + * @param response + */ + public String setRememberToken( HttpServletRequest request, HttpServletResponse response, String password, int maxAge, String domain, String path ) { + User user = ESAPI.authenticator().getCurrentUser(); + try { + killCookie(request, response, REMEMBER_TOKEN_COOKIE_NAME ); + // seal already contains random data + String clearToken = user.getAccountName() + "|" + password; + long expiry = ESAPI.encryptor().getRelativeTimeStamp(maxAge * 1000); + String cryptToken = ESAPI.encryptor().seal(clearToken, expiry); + + // Do NOT URLEncode cryptToken before creating cookie. See Google Issue # 144, + // which was marked as "WontFix". + + Cookie cookie = new Cookie( REMEMBER_TOKEN_COOKIE_NAME, cryptToken ); + cookie.setMaxAge( maxAge ); + cookie.setDomain( domain ); + cookie.setPath( path ); + response.addCookie( cookie ); + logger.info(Logger.SECURITY_SUCCESS, "Enabled remember me token for " + user.getAccountName() ); + return cryptToken; + } catch( IntegrityException e ) { + logger.warning(Logger.SECURITY_FAILURE, "Attempt to set remember me token failed for " + user.getAccountName(), e ); + return null; + } + } + + /** + * {@inheritDoc} + */ + public String setRememberToken( String password, int maxAge, String domain, String path ) { + return setRememberToken( getCurrentRequest(), getCurrentResponse(), password, maxAge, domain, path ); + } + + + /** + * {@inheritDoc} + */ + public void verifyCSRFToken() throws IntrusionException { + verifyCSRFToken( getCurrentRequest() ); + } + + /** + * {@inheritDoc} + * + * This implementation uses the CSRF_TOKEN_NAME parameter for the token. + * + * @param request + */ + public void verifyCSRFToken(HttpServletRequest request) throws IntrusionException { + User user = ESAPI.authenticator().getCurrentUser(); + + // check if user authenticated with this request - no CSRF protection required + if( request.getAttribute(user.getCSRFToken()) != null ) { + return; + } + String token = request.getParameter(CSRF_TOKEN_NAME); + if ( !user.getCSRFToken().equals( token ) ) { + throw new IntrusionException("Authentication failed", "Possibly forged HTTP request without proper CSRF token detected"); + } + } + + /** + * {@inheritDoc} + */ + public T getSessionAttribute( String key ) { + final HttpSession session = ESAPI.currentRequest().getSession(false); + if ( session != null ) + return (T) session.getAttribute(key); + return null; + } + + /** + * {@inheritDoc} + */ + public T getSessionAttribute(HttpSession session, String key) + { + return (T) session.getAttribute(key); + } + + /** + * {@inheritDoc} + */ + public T getRequestAttribute(String key) + { + return (T) ESAPI.currentRequest().getAttribute(key); + } + + /** + * {@inheritDoc} + */ + public T getRequestAttribute(HttpServletRequest request, String key) + { + return (T) request.getAttribute( key ); + } + + ///////////////////// + + /* Helper method to encrypt using new Encryptor encryption methods and + * return the serialized ciphertext as a hex-encoded string. + */ + private String encryptString(String plaintext) throws EncryptionException { + PlainText pt = new PlainText(plaintext); + CipherText ct = ESAPI.encryptor().encrypt(pt); + byte[] serializedCiphertext = ct.asPortableSerializedByteArray(); + return Hex.encode(serializedCiphertext, false); + } + + /* Helper method to decrypt a hex-encode serialized ciphertext string and + * to decrypt it using the new Encryptor decryption methods. + */ + private String decryptString(String ciphertext) throws EncryptionException { + byte[] serializedCiphertext = Hex.decode(ciphertext); + CipherText restoredCipherText = + CipherText.fromPortableSerializedBytes(serializedCiphertext); + PlainText plaintext = ESAPI.encryptor().decrypt(restoredCipherText); + return plaintext.toString(); + } +} diff --git a/src/test/java/org/owasp/esapi/reference/HTTPUtilitiesTest.java b/src/test/java/org/owasp/esapi/reference/HTTPUtilitiesTest.java index 40f9e219e..2c89a8e10 100644 --- a/src/test/java/org/owasp/esapi/reference/HTTPUtilitiesTest.java +++ b/src/test/java/org/owasp/esapi/reference/HTTPUtilitiesTest.java @@ -1,507 +1,508 @@ -/** - * OWASP Enterprise Security API (ESAPI) - * - * This file is part of the Open Web Application Security Project (OWASP) - * Enterprise Security API (ESAPI) project. For details, please see - * http://www.owasp.org/index.php/ESAPI. - * - * Copyright (c) 2007 - The OWASP Foundation - * - * The ESAPI is published by OWASP under the BSD license. You should read and accept the - * LICENSE before you use, modify, and/or redistribute this software. - * - * @author Jeff Williams Aspect Security - * @created 2007 - */ -package org.owasp.esapi.reference; - -import junit.framework.Test; -import junit.framework.TestCase; -import junit.framework.TestSuite; -import org.owasp.esapi.Authenticator; -import org.owasp.esapi.ESAPI; -import org.owasp.esapi.EncoderConstants; -import org.owasp.esapi.HTTPUtilities; -import org.owasp.esapi.User; -import org.owasp.esapi.codecs.Hex; -import org.owasp.esapi.crypto.CipherText; -import org.owasp.esapi.crypto.PlainText; -import org.owasp.esapi.errors.AuthenticationException; -import org.owasp.esapi.errors.EncryptionException; -import org.owasp.esapi.errors.EnterpriseSecurityException; -import org.owasp.esapi.errors.ValidationException; -import org.owasp.esapi.http.MockHttpServletRequest; -import org.owasp.esapi.http.MockHttpServletResponse; -import org.owasp.esapi.http.MockHttpSession; -import org.owasp.esapi.util.FileTestUtils; - -import javax.servlet.http.Cookie; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpSession; -import java.io.File; -import java.io.IOException; -import java.util.*; - -/** - * The Class HTTPUtilitiesTest. - * - * @author Jeff Williams (jeff.williams@aspectsecurity.com) - */ -public class HTTPUtilitiesTest extends TestCase -{ - private static final Class CLASS = HTTPUtilitiesTest.class; - private static final String CLASS_NAME = CLASS.getName(); - - /** - * Suite. - * - * @return the test - */ - public static Test suite() { - return new TestSuite(HTTPUtilitiesTest.class); - } - - /** - * Instantiates a new HTTP utilities test. - * - * @param testName the test name - */ - public HTTPUtilitiesTest(String testName) { - super(testName); - } - - /** - * {@inheritDoc} - * @throws Exception - */ - protected void setUp() throws Exception { - // none - } - - /** - * {@inheritDoc} - * @throws Exception - */ - protected void tearDown() throws Exception { - // none - } - - public void testCSRFToken() throws Exception { - System.out.println( "CSRFToken"); - String username = ESAPI.randomizer().getRandomString(8, EncoderConstants.CHAR_ALPHANUMERICS); - User user = ESAPI.authenticator().createUser(username, "addCSRFToken", "addCSRFToken"); - ESAPI.authenticator().setCurrentUser( user ); - String token = ESAPI.httpUtilities().getCSRFToken(); - assertEquals( 8, token.length() ); - MockHttpServletRequest request = new MockHttpServletRequest(); - try { - ESAPI.httpUtilities().verifyCSRFToken(request); - fail(); - } catch( Exception e ) { - // expected - } - request.addParameter( DefaultHTTPUtilities.CSRF_TOKEN_NAME, token ); - ESAPI.httpUtilities().verifyCSRFToken(request); - } - - /** - * Test of addCSRFToken method, of class org.owasp.esapi.HTTPUtilities. - * @throws AuthenticationException - */ - public void testAddCSRFToken() throws AuthenticationException { - Authenticator instance = ESAPI.authenticator(); - String username = ESAPI.randomizer().getRandomString(8, EncoderConstants.CHAR_ALPHANUMERICS); - User user = instance.createUser(username, "addCSRFToken", "addCSRFToken"); - instance.setCurrentUser( user ); - - System.out.println("addCSRFToken"); - String csrf1=ESAPI.httpUtilities().addCSRFToken("/test1"); - System.out.println( "CSRF1:" + csrf1); - assertTrue(csrf1.indexOf("?") > -1); - - String csrf2=ESAPI.httpUtilities().addCSRFToken("/test1?one=two"); - System.out.println( "CSRF1:" + csrf1); - assertTrue(csrf2.indexOf("&") > -1); - } - - - /** - * Test of assertSecureRequest method, of class org.owasp.esapi.HTTPUtilities. - */ - public void testAssertSecureRequest() { - System.out.println("assertSecureRequest"); - MockHttpServletRequest request = new MockHttpServletRequest(); - try { - request.setRequestURL( "http://example.com"); - ESAPI.httpUtilities().assertSecureRequest( request ); - fail(); - } catch( Exception e ) { - // pass - } - try { - request.setRequestURL( "ftp://example.com"); - ESAPI.httpUtilities().assertSecureRequest( request ); - fail(); - } catch( Exception e ) { - // pass - } - try { - request.setRequestURL( ""); - ESAPI.httpUtilities().assertSecureRequest( request ); - fail(); - } catch( Exception e ) { - // pass - } - try { - request.setRequestURL( null ); - ESAPI.httpUtilities().assertSecureRequest( request ); - fail(); - } catch( Exception e ) { - // pass - } - try { - request.setRequestURL( "https://example.com"); - ESAPI.httpUtilities().assertSecureRequest( request ); - // pass - } catch( Exception e ) { - fail(); - } - } - - - /** - * Test of sendRedirect method, of class org.owasp.esapi.HTTPUtilities. - * - * @throws EnterpriseSecurityException - */ - public void testChangeSessionIdentifier() throws EnterpriseSecurityException { - System.out.println("changeSessionIdentifier"); - MockHttpServletRequest request = new MockHttpServletRequest(); - MockHttpServletResponse response = new MockHttpServletResponse(); - MockHttpSession session = (MockHttpSession) request.getSession(); - ESAPI.httpUtilities().setCurrentHTTP(request, response); - session.setAttribute("one", "one"); - session.setAttribute("two", "two"); - session.setAttribute("three", "three"); - String id1 = session.getId(); - session = (MockHttpSession) ESAPI.httpUtilities().changeSessionIdentifier( request ); - String id2 = session.getId(); - assertTrue(!id1.equals(id2)); - assertEquals("one", (String) session.getAttribute("one")); - } - - /** - * Test of formatHttpRequestForLog method, of class org.owasp.esapi.HTTPUtilities. - * @throws IOException - */ - public void testGetFileUploads() throws IOException { - File home = null; - - try - { - home = FileTestUtils.createTmpDirectory(CLASS_NAME); - String content = "--ridiculous\r\nContent-Disposition: form-data; name=\"upload\"; filename=\"testupload.txt\"\r\nContent-Type: application/octet-stream\r\n\r\nThis is a test of the multipart broadcast system.\r\nThis is only a test.\r\nStop.\r\n\r\n--ridiculous\r\nContent-Disposition: form-data; name=\"submit\"\r\n\r\nSubmit Query\r\n--ridiculous--\r\nEpilogue"; - - MockHttpServletResponse response = new MockHttpServletResponse(); - MockHttpServletRequest request1 = new MockHttpServletRequest("/test", content.getBytes(response.getCharacterEncoding())); - ESAPI.httpUtilities().setCurrentHTTP(request1, response); - try { - ESAPI.httpUtilities().getFileUploads(request1, home); - fail(); - } catch( ValidationException e ) { - // expected - } - - MockHttpServletRequest request2 = new MockHttpServletRequest("/test", content.getBytes(response.getCharacterEncoding())); - request2.setContentType( "multipart/form-data; boundary=ridiculous"); - ESAPI.httpUtilities().setCurrentHTTP(request2, response); - try { - List list = ESAPI.httpUtilities().getFileUploads(request2, home); - Iterator i = list.iterator(); - while ( i.hasNext() ) { - File f = (File)i.next(); - System.out.println( " " + f.getAbsolutePath() ); - } - assertTrue( list.size() > 0 ); - } catch (ValidationException e) { - fail(); - } - - MockHttpServletRequest request4 = new MockHttpServletRequest("/test", content.getBytes(response.getCharacterEncoding())); - request4.setContentType( "multipart/form-data; boundary=ridiculous"); - ESAPI.httpUtilities().setCurrentHTTP(request4, response); - System.err.println("UPLOAD DIRECTORY: " + ESAPI.securityConfiguration().getUploadDirectory()); - try { - List list = ESAPI.httpUtilities().getFileUploads(request4, home); - Iterator i = list.iterator(); - while ( i.hasNext() ) { - File f = (File)i.next(); - System.out.println( " " + f.getAbsolutePath() ); - } - assertTrue( list.size() > 0 ); - } catch (ValidationException e) { - System.err.println("ERROR: " + e.toString()); - fail(); - } - - MockHttpServletRequest request3 = new MockHttpServletRequest("/test", content.replaceAll("txt", "ridiculous").getBytes(response.getCharacterEncoding())); - request3.setContentType( "multipart/form-data; boundary=ridiculous"); - ESAPI.httpUtilities().setCurrentHTTP(request3, response); - try { - ESAPI.httpUtilities().getFileUploads(request3, home); - fail(); - } catch (ValidationException e) { - // expected - } - } - finally - { - FileTestUtils.deleteRecursively(home); - } - - } - - - - /** - * Test of killAllCookies method, of class org.owasp.esapi.HTTPUtilities. - */ - public void testKillAllCookies() { - System.out.println("killAllCookies"); - MockHttpServletRequest request = new MockHttpServletRequest(); - MockHttpServletResponse response = new MockHttpServletResponse(); - assertTrue(response.getCookies().isEmpty()); - ArrayList list = new ArrayList(); - list.add(new Cookie("test1", "1")); - list.add(new Cookie("test2", "2")); - list.add(new Cookie("test3", "3")); - request.setCookies(list); - ESAPI.httpUtilities().killAllCookies(request, response); - assertTrue(response.getCookies().size() == 3); - } - - /** - * Test of killCookie method, of class org.owasp.esapi.HTTPUtilities. - */ - public void testKillCookie() { - System.out.println("killCookie"); - MockHttpServletRequest request = new MockHttpServletRequest(); - MockHttpServletResponse response = new MockHttpServletResponse(); - ESAPI.httpUtilities().setCurrentHTTP(request, response); - assertTrue(response.getCookies().isEmpty()); - ArrayList list = new ArrayList(); - list.add(new Cookie("test1", "1")); - list.add(new Cookie("test2", "2")); - list.add(new Cookie("test3", "3")); - request.setCookies(list); - ESAPI.httpUtilities().killCookie( request, response, "test1" ); - assertTrue(response.getCookies().size() == 1); - } - - /** - * Test of sendRedirect method, of class org.owasp.esapi.HTTPUtilities. - * - * @throws ValidationException the validation exception - * @throws IOException Signals that an I/O exception has occurred. - */ - public void testSendSafeRedirect() throws Exception { - System.out.println("sendSafeRedirect"); - MockHttpServletResponse response = new MockHttpServletResponse(); - try { - ESAPI.httpUtilities().sendRedirect(response, "/test1/abcdefg"); - ESAPI.httpUtilities().sendRedirect(response,"/test2/1234567"); - } catch (IOException e) { - fail(); - } - try { - ESAPI.httpUtilities().sendRedirect(response,"http://www.aspectsecurity.com"); - fail(); - } catch (IOException e) { - // expected - } - try { - ESAPI.httpUtilities().sendRedirect(response,"/ridiculous"); - fail(); - } catch (IOException e) { - // expected - } - } - - /** - * Test of setCookie method, of class org.owasp.esapi.HTTPUtilities. - */ - public void testSetCookie() { - System.out.println("setCookie"); - HTTPUtilities instance = ESAPI.httpUtilities(); - MockHttpServletResponse response = new MockHttpServletResponse(); - assertTrue(response.getHeaderNames().isEmpty()); - - instance.addCookie( response, new Cookie( "test1", "test1" ) ); - assertTrue(response.getHeaderNames().size() == 1); - - instance.addCookie( response, new Cookie( "test2", "test2" ) ); - assertTrue(response.getHeaderNames().size() == 2); - - // test illegal name - instance.addCookie( response, new Cookie( "teshttp://www.owasp.org/index.php/ESAPI. + * + * Copyright (c) 2007 - The OWASP Foundation + * + * The ESAPI is published by OWASP under the BSD license. You should read and accept the + * LICENSE before you use, modify, and/or redistribute this software. + * + * @author Jeff Williams Aspect Security + * @created 2007 + */ +package org.owasp.esapi.reference; + +import junit.framework.Test; +import junit.framework.TestCase; +import junit.framework.TestSuite; +import org.owasp.esapi.Authenticator; +import org.owasp.esapi.ESAPI; +import org.owasp.esapi.EncoderConstants; +import org.owasp.esapi.HTTPUtilities; +import org.owasp.esapi.User; +import org.owasp.esapi.codecs.Hex; +import org.owasp.esapi.crypto.CipherText; +import org.owasp.esapi.crypto.PlainText; +import org.owasp.esapi.errors.AccessControlException; +import org.owasp.esapi.errors.AuthenticationException; +import org.owasp.esapi.errors.EncryptionException; +import org.owasp.esapi.errors.EnterpriseSecurityException; +import org.owasp.esapi.errors.ValidationException; +import org.owasp.esapi.http.MockHttpServletRequest; +import org.owasp.esapi.http.MockHttpServletResponse; +import org.owasp.esapi.http.MockHttpSession; +import org.owasp.esapi.util.FileTestUtils; + +import javax.servlet.http.Cookie; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpSession; +import java.io.File; +import java.io.IOException; +import java.util.*; + +/** + * The Class HTTPUtilitiesTest. + * + * @author Jeff Williams (jeff.williams@aspectsecurity.com) + */ +public class HTTPUtilitiesTest extends TestCase +{ + private static final Class CLASS = HTTPUtilitiesTest.class; + private static final String CLASS_NAME = CLASS.getName(); + + /** + * Suite. + * + * @return the test + */ + public static Test suite() { + return new TestSuite(HTTPUtilitiesTest.class); + } + + /** + * Instantiates a new HTTP utilities test. + * + * @param testName the test name + */ + public HTTPUtilitiesTest(String testName) { + super(testName); + } + + /** + * {@inheritDoc} + * @throws Exception + */ + protected void setUp() throws Exception { + // none + } + + /** + * {@inheritDoc} + * @throws Exception + */ + protected void tearDown() throws Exception { + // none + } + + public void testCSRFToken() throws Exception { + System.out.println( "CSRFToken"); + String username = ESAPI.randomizer().getRandomString(8, EncoderConstants.CHAR_ALPHANUMERICS); + User user = ESAPI.authenticator().createUser(username, "addCSRFToken", "addCSRFToken"); + ESAPI.authenticator().setCurrentUser( user ); + String token = ESAPI.httpUtilities().getCSRFToken(); + assertEquals( 8, token.length() ); + MockHttpServletRequest request = new MockHttpServletRequest(); + try { + ESAPI.httpUtilities().verifyCSRFToken(request); + fail(); + } catch( Exception e ) { + // expected + } + request.addParameter( DefaultHTTPUtilities.CSRF_TOKEN_NAME, token ); + ESAPI.httpUtilities().verifyCSRFToken(request); + } + + /** + * Test of addCSRFToken method, of class org.owasp.esapi.HTTPUtilities. + * @throws AuthenticationException + */ + public void testAddCSRFToken() throws AuthenticationException { + Authenticator instance = ESAPI.authenticator(); + String username = ESAPI.randomizer().getRandomString(8, EncoderConstants.CHAR_ALPHANUMERICS); + User user = instance.createUser(username, "addCSRFToken", "addCSRFToken"); + instance.setCurrentUser( user ); + + System.out.println("addCSRFToken"); + String csrf1=ESAPI.httpUtilities().addCSRFToken("/test1"); + System.out.println( "CSRF1:" + csrf1); + assertTrue(csrf1.indexOf("?") > -1); + + String csrf2=ESAPI.httpUtilities().addCSRFToken("/test1?one=two"); + System.out.println( "CSRF1:" + csrf1); + assertTrue(csrf2.indexOf("&") > -1); + } + + + /** + * Test of assertSecureRequest method, of class org.owasp.esapi.HTTPUtilities. + */ + public void testAssertSecureRequest() { + System.out.println("assertSecureRequest"); + MockHttpServletRequest request = new MockHttpServletRequest(); + try { + request.setRequestURL( "http://example.com"); + ESAPI.httpUtilities().assertSecureRequest( request ); + fail(); + } catch( Exception e ) { + // pass + } + try { + request.setRequestURL( "ftp://example.com"); + ESAPI.httpUtilities().assertSecureRequest( request ); + fail(); + } catch( Exception e ) { + // pass + } + try { + request.setRequestURL( ""); + ESAPI.httpUtilities().assertSecureRequest( request ); + fail(); + } catch( Exception e ) { + // pass + } + try { + request.setRequestURL( null ); + ESAPI.httpUtilities().assertSecureRequest( request ); + fail(); + } catch( Exception e ) { + // pass + } + try { + request.setRequestURL( "https://example.com"); + ESAPI.httpUtilities().assertSecureRequest( request ); + // pass + } catch( Exception e ) { + fail(); + } + } + + + /** + * Test of sendRedirect method, of class org.owasp.esapi.HTTPUtilities. + * + * @throws EnterpriseSecurityException + */ + public void testChangeSessionIdentifier() throws EnterpriseSecurityException { + System.out.println("changeSessionIdentifier"); + MockHttpServletRequest request = new MockHttpServletRequest(); + MockHttpServletResponse response = new MockHttpServletResponse(); + MockHttpSession session = (MockHttpSession) request.getSession(); + ESAPI.httpUtilities().setCurrentHTTP(request, response); + session.setAttribute("one", "one"); + session.setAttribute("two", "two"); + session.setAttribute("three", "three"); + String id1 = session.getId(); + session = (MockHttpSession) ESAPI.httpUtilities().changeSessionIdentifier( request ); + String id2 = session.getId(); + assertTrue(!id1.equals(id2)); + assertEquals("one", (String) session.getAttribute("one")); + } + + /** + * Test of formatHttpRequestForLog method, of class org.owasp.esapi.HTTPUtilities. + * @throws IOException + */ + public void testGetFileUploads() throws IOException { + File home = null; + + try + { + home = FileTestUtils.createTmpDirectory(CLASS_NAME); + String content = "--ridiculous\r\nContent-Disposition: form-data; name=\"upload\"; filename=\"testupload.txt\"\r\nContent-Type: application/octet-stream\r\n\r\nThis is a test of the multipart broadcast system.\r\nThis is only a test.\r\nStop.\r\n\r\n--ridiculous\r\nContent-Disposition: form-data; name=\"submit\"\r\n\r\nSubmit Query\r\n--ridiculous--\r\nEpilogue"; + + MockHttpServletResponse response = new MockHttpServletResponse(); + MockHttpServletRequest request1 = new MockHttpServletRequest("/test", content.getBytes(response.getCharacterEncoding())); + ESAPI.httpUtilities().setCurrentHTTP(request1, response); + try { + ESAPI.httpUtilities().getFileUploads(request1, home); + fail(); + } catch( ValidationException e ) { + // expected + } + + MockHttpServletRequest request2 = new MockHttpServletRequest("/test", content.getBytes(response.getCharacterEncoding())); + request2.setContentType( "multipart/form-data; boundary=ridiculous"); + ESAPI.httpUtilities().setCurrentHTTP(request2, response); + try { + List list = ESAPI.httpUtilities().getFileUploads(request2, home); + Iterator i = list.iterator(); + while ( i.hasNext() ) { + File f = (File)i.next(); + System.out.println( " " + f.getAbsolutePath() ); + } + assertTrue( list.size() > 0 ); + } catch (ValidationException e) { + fail(); + } + + MockHttpServletRequest request4 = new MockHttpServletRequest("/test", content.getBytes(response.getCharacterEncoding())); + request4.setContentType( "multipart/form-data; boundary=ridiculous"); + ESAPI.httpUtilities().setCurrentHTTP(request4, response); + System.err.println("UPLOAD DIRECTORY: " + ESAPI.securityConfiguration().getUploadDirectory()); + try { + List list = ESAPI.httpUtilities().getFileUploads(request4, home); + Iterator i = list.iterator(); + while ( i.hasNext() ) { + File f = (File)i.next(); + System.out.println( " " + f.getAbsolutePath() ); + } + assertTrue( list.size() > 0 ); + } catch (ValidationException e) { + System.err.println("ERROR: " + e.toString()); + fail(); + } + + MockHttpServletRequest request3 = new MockHttpServletRequest("/test", content.replaceAll("txt", "ridiculous").getBytes(response.getCharacterEncoding())); + request3.setContentType( "multipart/form-data; boundary=ridiculous"); + ESAPI.httpUtilities().setCurrentHTTP(request3, response); + try { + ESAPI.httpUtilities().getFileUploads(request3, home); + fail(); + } catch (ValidationException e) { + // expected + } + } + finally + { + FileTestUtils.deleteRecursively(home); + } + + } + + + + /** + * Test of killAllCookies method, of class org.owasp.esapi.HTTPUtilities. + */ + public void testKillAllCookies() { + System.out.println("killAllCookies"); + MockHttpServletRequest request = new MockHttpServletRequest(); + MockHttpServletResponse response = new MockHttpServletResponse(); + assertTrue(response.getCookies().isEmpty()); + ArrayList list = new ArrayList(); + list.add(new Cookie("test1", "1")); + list.add(new Cookie("test2", "2")); + list.add(new Cookie("test3", "3")); + request.setCookies(list); + ESAPI.httpUtilities().killAllCookies(request, response); + assertTrue(response.getCookies().size() == 3); + } + + /** + * Test of killCookie method, of class org.owasp.esapi.HTTPUtilities. + */ + public void testKillCookie() { + System.out.println("killCookie"); + MockHttpServletRequest request = new MockHttpServletRequest(); + MockHttpServletResponse response = new MockHttpServletResponse(); + ESAPI.httpUtilities().setCurrentHTTP(request, response); + assertTrue(response.getCookies().isEmpty()); + ArrayList list = new ArrayList(); + list.add(new Cookie("test1", "1")); + list.add(new Cookie("test2", "2")); + list.add(new Cookie("test3", "3")); + request.setCookies(list); + ESAPI.httpUtilities().killCookie( request, response, "test1" ); + assertTrue(response.getCookies().size() == 1); + } + + /** + * Test of sendRedirect method, of class org.owasp.esapi.HTTPUtilities. + * + * @throws ValidationException the validation exception + * @throws IOException Signals that an I/O exception has occurred. + */ + public void testSendSafeRedirect() throws Exception { + System.out.println("sendSafeRedirect"); + MockHttpServletResponse response = new MockHttpServletResponse(); + try { + ESAPI.httpUtilities().sendRedirect(response, "/test1/abcdefg"); + ESAPI.httpUtilities().sendRedirect(response,"/test2/1234567"); + } catch (AccessControlException e) { + fail(); + } + try { + ESAPI.httpUtilities().sendRedirect(response,"http://www.aspectsecurity.com"); + fail(); + } catch (AccessControlException e) { + // expected + } + try { + ESAPI.httpUtilities().sendRedirect(response,"/ridiculous"); + fail(); + } catch (AccessControlException e) { + // expected + } + } + + /** + * Test of setCookie method, of class org.owasp.esapi.HTTPUtilities. + */ + public void testSetCookie() { + System.out.println("setCookie"); + HTTPUtilities instance = ESAPI.httpUtilities(); + MockHttpServletResponse response = new MockHttpServletResponse(); + assertTrue(response.getHeaderNames().isEmpty()); + + instance.addCookie( response, new Cookie( "test1", "test1" ) ); + assertTrue(response.getHeaderNames().size() == 1); + + instance.addCookie( response, new Cookie( "test2", "test2" ) ); + assertTrue(response.getHeaderNames().size() == 2); + + // test illegal name + instance.addCookie( response, new Cookie( "teshttp://www.owasp.org/index.php/ESAPI. - * - * Copyright (c) 2007 - The OWASP Foundation - * - * The ESAPI is published by OWASP under the BSD license. You should read and accept the - * LICENSE before you use, modify, and/or redistribute this software. - * - * @author Jeff Williams Aspect Security - * @created 2007 - */ -package org.owasp.esapi.reference; - -import java.io.BufferedReader; -import java.io.ByteArrayInputStream; -import java.io.File; -import java.io.IOException; -import java.io.InputStreamReader; -import java.io.UnsupportedEncodingException; -import java.text.DateFormat; -import java.text.SimpleDateFormat; -import java.util.*; - -import junit.framework.Test; -import junit.framework.TestCase; -import junit.framework.TestSuite; - -import org.owasp.esapi.*; -import org.owasp.esapi.errors.ValidationException; -import org.owasp.esapi.filters.SecurityWrapperRequest; -import org.owasp.esapi.http.MockHttpServletRequest; -import org.owasp.esapi.http.MockHttpServletResponse; -import org.owasp.esapi.reference.validation.HTMLValidationRule; -import org.owasp.esapi.reference.validation.StringValidationRule; - -import javax.servlet.http.Cookie; -import javax.servlet.http.HttpServletRequest; - -/** - * The Class ValidatorTest. - * - * @author Mike Fauzy (mike.fauzy@aspectsecurity.com) - * @author Jeff Williams (jeff.williams@aspectsecurity.com) - */ -public class ValidatorTest extends TestCase { - - private static final String PREFERRED_ENCODING = "UTF-8"; - - public static Test suite() { - return new TestSuite(ValidatorTest.class); - } - - /** - * Instantiates a new HTTP utilities test. - * - * @param testName the test name - */ - public ValidatorTest(String testName) { - super(testName); - } - - /** - * {@inheritDoc} - * - * @throws Exception - */ - protected void setUp() throws Exception { - // none - } - - /** - * {@inheritDoc} - * - * @throws Exception - */ - protected void tearDown() throws Exception { - // none - } - - public void testAddRule() { - Validator validator = ESAPI.validator(); - ValidationRule rule = new StringValidationRule("ridiculous"); - validator.addRule(rule); - assertEquals(rule, validator.getRule("ridiculous")); - } - - public void testAssertValidFileUpload() { - // assertValidFileUpload(String, String, String, byte[], int, boolean, ValidationErrorList) - } - - public void testGetPrintable1() { - // getValidPrintable(String, char[], int, boolean, ValidationErrorList) - } - - public void testGetPrintable2() { - // getValidPrintable(String, String, int, boolean, ValidationErrorList) - } - - public void testGetRule() { - Validator validator = ESAPI.validator(); - ValidationRule rule = new StringValidationRule("rule"); - validator.addRule(rule); - assertEquals(rule, validator.getRule("rule")); - assertFalse(rule == validator.getRule("ridiculous")); - } - - public void testGetValidCreditCard() { - System.out.println("getValidCreditCard"); - Validator instance = ESAPI.validator(); - ValidationErrorList errors = new ValidationErrorList(); - - assertTrue(instance.isValidCreditCard("cctest1", "1234 9876 0000 0008", false)); - assertTrue(instance.isValidCreditCard("cctest2", "1234987600000008", false)); - assertFalse(instance.isValidCreditCard("cctest3", "12349876000000081", false)); - assertFalse(instance.isValidCreditCard("cctest4", "4417 1234 5678 9112", false)); - - instance.getValidCreditCard("cctest5", "1234 9876 0000 0008", false, errors); - assertEquals(0, errors.size()); - instance.getValidCreditCard("cctest6", "1234987600000008", false, errors); - assertEquals(0, errors.size()); - instance.getValidCreditCard("cctest7", "12349876000000081", false, errors); - assertEquals(1, errors.size()); - instance.getValidCreditCard("cctest8", "4417 1234 5678 9112", false, errors); - assertEquals(2, errors.size()); - - assertTrue(instance.isValidCreditCard("cctest1", "1234 9876 0000 0008", false, errors)); - assertTrue(errors.size()==2); - assertTrue(instance.isValidCreditCard("cctest2", "1234987600000008", false, errors)); - assertTrue(errors.size()==2); - assertFalse(instance.isValidCreditCard("cctest3", "12349876000000081", false, errors)); - assertTrue(errors.size()==3); - assertFalse(instance.isValidCreditCard("cctest4", "4417 1234 5678 9112", false, errors)); - assertTrue(errors.size()==4); - } - - public void testGetValidDate() throws Exception { - System.out.println("getValidDate"); - Validator instance = ESAPI.validator(); - ValidationErrorList errors = new ValidationErrorList(); - assertTrue(instance.getValidDate("datetest1", "June 23, 1967", DateFormat.getDateInstance(DateFormat.MEDIUM, Locale.US), false) != null); - instance.getValidDate("datetest2", "freakshow", DateFormat.getDateInstance(), false, errors); - assertEquals(1, errors.size()); - - // TODO: This test case fails due to an apparent bug in SimpleDateFormat - // Note: This seems to be fixed in JDK 6. Will leave it commented out since - // we only require JDK 5. -kww - instance.getValidDate("test", "June 32, 2008", DateFormat.getDateInstance(), false, errors); - // assertEquals( 2, errors.size() ); - } - - // FIXME: Should probably use SecurityConfigurationWrapper and force - // Validator.AcceptLenientDates to be false. - public void testLenientDate() { - System.out.println("testLenientDate"); - boolean acceptLenientDates = ESAPI.securityConfiguration().getLenientDatesAccepted(); - if ( acceptLenientDates ) { - assertTrue("Lenient date test skipped because Validator.AcceptLenientDates set to true", true); - return; - } - - Date lenientDateTest = null; - try { - // lenientDateTest will be null when Validator.AcceptLenientDates - // is set to false (the default). - Validator instance = ESAPI.validator(); - lenientDateTest = instance.getValidDate("datatest3-lenient", "15/2/2009 11:83:00", - DateFormat.getDateInstance(DateFormat.SHORT, Locale.US), - false); - fail("Failed to throw expected ValidationException when Validator.AcceptLenientDates set to false."); - } catch (ValidationException ve) { - assertNull( lenientDateTest ); - Throwable cause = ve.getCause(); - assertTrue( cause.getClass().getName().equals("java.text.ParseException") ); - } catch (Exception e) { - fail("Caught unexpected exception: " + e.getClass().getName() + "; msg: " + e); - } - } - - public void testGetValidDirectoryPath() throws Exception { - System.out.println("getValidDirectoryPath"); - Validator instance = ESAPI.validator(); - ValidationErrorList errors = new ValidationErrorList(); - // find a directory that exists - File parent = new File("/"); - String path = ESAPI.securityConfiguration().getResourceFile("ESAPI.properties").getParentFile().getCanonicalPath(); - instance.getValidDirectoryPath("dirtest1", path, parent, true, errors); - assertEquals(0, errors.size()); - instance.getValidDirectoryPath("dirtest2", null, parent, false, errors); - assertEquals(1, errors.size()); - instance.getValidDirectoryPath("dirtest3", "ridicul%00ous", parent, false, errors); - assertEquals(2, errors.size()); - } - - public void testGetValidDouble() { - System.out.println("getValidDouble"); - Validator instance = ESAPI.validator(); - ValidationErrorList errors = new ValidationErrorList(); - instance.getValidDouble("dtest1", "1.0", 0, 20, true, errors); - assertEquals(0, errors.size()); - instance.getValidDouble("dtest2", null, 0, 20, true, errors); - assertEquals(0, errors.size()); - instance.getValidDouble("dtest3", null, 0, 20, false, errors); - assertEquals(1, errors.size()); - instance.getValidDouble("dtest4", "ridiculous", 0, 20, true, errors); - assertEquals(2, errors.size()); - instance.getValidDouble("dtest5", "" + (Double.MAX_VALUE), 0, 20, true, errors); - assertEquals(3, errors.size()); - instance.getValidDouble("dtest6", "" + (Double.MAX_VALUE + .00001), 0, 20, true, errors); - assertEquals(4, errors.size()); - } - - public void testGetValidFileContent() { - System.out.println("getValidFileContent"); - Validator instance = ESAPI.validator(); - ValidationErrorList errors = new ValidationErrorList(); - byte[] bytes = null; - try { - bytes = "12345".getBytes(PREFERRED_ENCODING); - } - catch (UnsupportedEncodingException e) { - fail(PREFERRED_ENCODING + " not a supported encoding?!?!!"); - } - instance.getValidFileContent("test", bytes, 5, true, errors); - assertEquals(0, errors.size()); - instance.getValidFileContent("test", bytes, 4, true, errors); - assertEquals(1, errors.size()); - } - - public void testGetValidFileName() throws Exception { - System.out.println("getValidFileName"); - Validator instance = ESAPI.validator(); - ValidationErrorList errors = new ValidationErrorList(); - String testName = "aspe%20ct.jar"; - assertEquals("Percent encoding is not changed", testName, instance.getValidFileName("test", testName, ESAPI.securityConfiguration().getAllowedFileExtensions(), false, errors)); - } - - public void testGetValidInput() { - System.out.println("getValidInput"); - Validator instance = ESAPI.validator(); - ValidationErrorList errors = new ValidationErrorList(); - // instance.getValidInput(String, String, String, int, boolean, ValidationErrorList) - } - - public void testGetValidInteger() { - System.out.println("getValidInteger"); - Validator instance = ESAPI.validator(); - ValidationErrorList errors = new ValidationErrorList(); - // instance.getValidInteger(String, String, int, int, boolean, ValidationErrorList) - } - - public void testGetValidListItem() { - System.out.println("getValidListItem"); - Validator instance = ESAPI.validator(); - ValidationErrorList errors = new ValidationErrorList(); - // instance.getValidListItem(String, String, List, ValidationErrorList) - } - - public void testGetValidNumber() { - System.out.println("getValidNumber"); - Validator instance = ESAPI.validator(); - ValidationErrorList errors = new ValidationErrorList(); - // instance.getValidNumber(String, String, long, long, boolean, ValidationErrorList) - } - - public void testGetValidRedirectLocation() { - System.out.println("getValidRedirectLocation"); - Validator instance = ESAPI.validator(); - ValidationErrorList errors = new ValidationErrorList(); - // instance.getValidRedirectLocation(String, String, boolean, ValidationErrorList) - } - - public void testGetValidSafeHTML() throws Exception { - System.out.println("getValidSafeHTML"); - Validator instance = ESAPI.validator(); - ValidationErrorList errors = new ValidationErrorList(); - - // new school test case setup - HTMLValidationRule rule = new HTMLValidationRule("test"); - ESAPI.validator().addRule(rule); - - assertEquals("Test.", ESAPI.validator().getRule("test").getValid("test", "Test. ")); - - String test1 = "Jeff"; - String result1 = instance.getValidSafeHTML("test", test1, 100, false, errors); - assertEquals(test1, result1); - - String test2 = "Aspect Security"; - String result2 = instance.getValidSafeHTML("test", test2, 100, false, errors); - assertEquals(test2, result2); - - String test3 = "Test. "; - assertEquals("Test.", rule.getSafe("test", test3)); - - assertEquals("Test. <

load=alert()
", rule.getSafe("test", "Test. <
load=alert()")); - assertEquals("Test.
b
", rule.getSafe("test", "Test.
b
")); - assertEquals("Test.", rule.getSafe("test", "Test. alert(document.cookie)")); - assertEquals("Test. alert(document.cookie)", rule.getSafe("test", "Test. alert(document.cookie)")); - assertEquals("Test. alert(document.cookie)", rule.getSafe("test", "Test. alert(document.cookie)")); - // TODO: ENHANCE waiting for a way to validate text headed for an attribute for scripts - // This would be nice to catch, but just looks like text to AntiSamy - // assertFalse(instance.isValidSafeHTML("test", "\" onload=\"alert(document.cookie)\" ")); - // String result4 = instance.getValidSafeHTML("test", test4); - // assertEquals("", result4); - } - - public void testIsInvalidFilename() { - System.out.println("testIsInvalidFilename"); - Validator instance = ESAPI.validator(); - char invalidChars[] = "/\\:*?\"<>|".toCharArray(); - for (int i = 0; i < invalidChars.length; i++) { - assertFalse(invalidChars[i] + " is an invalid character for a filename", - instance.isValidFileName("test", "as" + invalidChars[i] + "pect.jar", false)); - } - assertFalse("Files must have an extension", instance.isValidFileName("test", "", false)); - assertFalse("Files must have a valid extension", instance.isValidFileName("test.invalidExtension", "", false)); - assertFalse("Filennames cannot be the empty string", instance.isValidFileName("test", "", false)); - } - - public void testIsValidDate() { - System.out.println("isValidDate"); - Validator instance = ESAPI.validator(); - DateFormat format = SimpleDateFormat.getDateInstance(SimpleDateFormat.MEDIUM, Locale.US); - assertTrue(instance.isValidDate("datetest1", "September 11, 2001", format, true)); - assertFalse(instance.isValidDate("datetest2", null, format, false)); - assertFalse(instance.isValidDate("datetest3", "", format, false)); - - ValidationErrorList errors = new ValidationErrorList(); - assertTrue(instance.isValidDate("datetest1", "September 11, 2001", format, true, errors)); - assertTrue(errors.size()==0); - assertFalse(instance.isValidDate("datetest2", null, format, false, errors)); - assertTrue(errors.size()==1); - assertFalse(instance.isValidDate("datetest3", "", format, false, errors)); - assertTrue(errors.size()==2); - - } - - public void testIsValidDirectoryPath() throws IOException { - System.out.println("isValidDirectoryPath"); - - // get an encoder with a special list of codecs and make a validator out of it - List list = new ArrayList(); - list.add("HTMLEntityCodec"); - Encoder encoder = new DefaultEncoder(list); - Validator instance = new DefaultValidator(encoder); - - boolean isWindows = (System.getProperty("os.name").indexOf("Windows") != -1) ? true : false; - File parent = new File("/"); - - ValidationErrorList errors = new ValidationErrorList(); - - if (isWindows) { - String sysRoot = new File(System.getenv("SystemRoot")).getCanonicalPath(); - // Windows paths that don't exist and thus should fail - assertFalse(instance.isValidDirectoryPath("test", "c:\\ridiculous", parent, false)); - assertFalse(instance.isValidDirectoryPath("test", "c:\\jeff", parent, false)); - assertFalse(instance.isValidDirectoryPath("test", "c:\\temp\\..\\etc", parent, false)); - - // Windows paths - assertTrue(instance.isValidDirectoryPath("test", "C:\\", parent, false)); // Windows root directory - assertTrue(instance.isValidDirectoryPath("test", sysRoot, parent, false)); // Windows always exist directory - assertFalse(instance.isValidDirectoryPath("test", sysRoot + "\\System32\\cmd.exe", parent, false)); // Windows command shell - - // Unix specific paths should not pass - assertFalse(instance.isValidDirectoryPath("test", "/tmp", parent, false)); // Unix Temporary directory - assertFalse(instance.isValidDirectoryPath("test", "/bin/sh", parent, false)); // Unix Standard shell - assertFalse(instance.isValidDirectoryPath("test", "/etc/config", parent, false)); - - // Unix specific paths that should not exist or work - assertFalse(instance.isValidDirectoryPath("test", "/etc/ridiculous", parent, false)); - assertFalse(instance.isValidDirectoryPath("test", "/tmp/../etc", parent, false)); - - assertFalse(instance.isValidDirectoryPath("test1", "c:\\ridiculous", parent, false, errors)); - assertTrue(errors.size()==1); - assertFalse(instance.isValidDirectoryPath("test2", "c:\\jeff", parent, false, errors)); - assertTrue(errors.size()==2); - assertFalse(instance.isValidDirectoryPath("test3", "c:\\temp\\..\\etc", parent, false, errors)); - assertTrue(errors.size()==3); - - // Windows paths - assertTrue(instance.isValidDirectoryPath("test4", "C:\\", parent, false, errors)); // Windows root directory - assertTrue(errors.size()==3); - assertTrue(instance.isValidDirectoryPath("test5", sysRoot, parent, false, errors)); // Windows always exist directory - assertTrue(errors.size()==3); - assertFalse(instance.isValidDirectoryPath("test6", sysRoot + "\\System32\\cmd.exe", parent, false, errors)); // Windows command shell - assertTrue(errors.size()==4); - - // Unix specific paths should not pass - assertFalse(instance.isValidDirectoryPath("test7", "/tmp", parent, false, errors)); // Unix Temporary directory - assertTrue(errors.size()==5); - assertFalse(instance.isValidDirectoryPath("test8", "/bin/sh", parent, false, errors)); // Unix Standard shell - assertTrue(errors.size()==6); - assertFalse(instance.isValidDirectoryPath("test9", "/etc/config", parent, false, errors)); - assertTrue(errors.size()==7); - - // Unix specific paths that should not exist or work - assertFalse(instance.isValidDirectoryPath("test10", "/etc/ridiculous", parent, false, errors)); - assertTrue(errors.size()==8); - assertFalse(instance.isValidDirectoryPath("test11", "/tmp/../etc", parent, false, errors)); - assertTrue(errors.size()==9); - - } else { - // Windows paths should fail - assertFalse(instance.isValidDirectoryPath("test", "c:\\ridiculous", parent, false)); - assertFalse(instance.isValidDirectoryPath("test", "c:\\temp\\..\\etc", parent, false)); - - // Standard Windows locations should fail - assertFalse(instance.isValidDirectoryPath("test", "c:\\", parent, false)); // Windows root directory - assertFalse(instance.isValidDirectoryPath("test", "c:\\Windows\\temp", parent, false)); // Windows temporary directory - assertFalse(instance.isValidDirectoryPath("test", "c:\\Windows\\System32\\cmd.exe", parent, false)); // Windows command shell - - // Unix specific paths should pass - assertTrue(instance.isValidDirectoryPath("test", "/", parent, false)); // Root directory - assertTrue(instance.isValidDirectoryPath("test", "/bin", parent, false)); // Always exist directory - - // Unix specific paths that should not exist or work - assertFalse(instance.isValidDirectoryPath("test", "/bin/sh", parent, false)); // Standard shell, not dir - assertFalse(instance.isValidDirectoryPath("test", "/etc/ridiculous", parent, false)); - assertFalse(instance.isValidDirectoryPath("test", "/tmp/../etc", parent, false)); - - // Windows paths should fail - assertFalse(instance.isValidDirectoryPath("test1", "c:\\ridiculous", parent, false, errors)); - assertTrue(errors.size()==1); - assertFalse(instance.isValidDirectoryPath("test2", "c:\\temp\\..\\etc", parent, false, errors)); - assertTrue(errors.size()==2); - - // Standard Windows locations should fail - assertFalse(instance.isValidDirectoryPath("test3", "c:\\", parent, false, errors)); // Windows root directory - assertTrue(errors.size()==3); - assertFalse(instance.isValidDirectoryPath("test4", "c:\\Windows\\temp", parent, false, errors)); // Windows temporary directory - assertTrue(errors.size()==4); - assertFalse(instance.isValidDirectoryPath("test5", "c:\\Windows\\System32\\cmd.exe", parent, false, errors)); // Windows command shell - assertTrue(errors.size()==5); - - // Unix specific paths should pass - assertTrue(instance.isValidDirectoryPath("test6", "/", parent, false, errors)); // Root directory - assertTrue(errors.size()==5); - assertTrue(instance.isValidDirectoryPath("test7", "/bin", parent, false, errors)); // Always exist directory - assertTrue(errors.size()==5); - - // Unix specific paths that should not exist or work - assertFalse(instance.isValidDirectoryPath("test8", "/bin/sh", parent, false, errors)); // Standard shell, not dir - assertTrue(errors.size()==6); - assertFalse(instance.isValidDirectoryPath("test9", "/etc/ridiculous", parent, false, errors)); - assertTrue(errors.size()==7); - assertFalse(instance.isValidDirectoryPath("test10", "/tmp/../etc", parent, false, errors)); - assertTrue(errors.size()==8); - } - } - - public void TestIsValidDirectoryPath() { - // isValidDirectoryPath(String, String, boolean) - } - - public void testIsValidDouble() { - // isValidDouble(String, String, double, double, boolean) - Validator instance = ESAPI.validator(); - ValidationErrorList errors = new ValidationErrorList(); - //testing negative range - assertFalse(instance.isValidDouble("test1", "-4", 1, 10, false, errors)); - assertTrue(errors.size() == 1); - assertTrue(instance.isValidDouble("test2", "-4", -10, 10, false, errors)); - assertTrue(errors.size() == 1); - //testing null value - assertTrue(instance.isValidDouble("test3", null, -10, 10, true, errors)); - assertTrue(errors.size() == 1); - assertFalse(instance.isValidDouble("test4", null, -10, 10, false, errors)); - assertTrue(errors.size() == 2); - //testing empty string - assertTrue(instance.isValidDouble("test5", "", -10, 10, true, errors)); - assertTrue(errors.size() == 2); - assertFalse(instance.isValidDouble("test6", "", -10, 10, false, errors)); - assertTrue(errors.size() == 3); - //testing improper range - assertFalse(instance.isValidDouble("test7", "50.0", 10, -10, false, errors)); - assertTrue(errors.size() == 4); - //testing non-integers - assertTrue(instance.isValidDouble("test8", "4.3214", -10, 10, true, errors)); - assertTrue(errors.size() == 4); - assertTrue(instance.isValidDouble("test9", "-1.65", -10, 10, true, errors)); - assertTrue(errors.size() == 4); - //other testing - assertTrue(instance.isValidDouble("test10", "4", 1, 10, false, errors)); - assertTrue(errors.size() == 4); - assertTrue(instance.isValidDouble("test11", "400", 1, 10000, false, errors)); - assertTrue(errors.size() == 4); - assertTrue(instance.isValidDouble("test12", "400000000", 1, 400000000, false, errors)); - assertTrue(errors.size() == 4); - assertFalse(instance.isValidDouble("test13", "4000000000000", 1, 10000, false, errors)); - assertTrue(errors.size() == 5); - assertFalse(instance.isValidDouble("test14", "alsdkf", 10, 10000, false, errors)); - assertTrue(errors.size() == 6); - assertFalse(instance.isValidDouble("test15", "--10", 10, 10000, false, errors)); - assertTrue(errors.size() == 7); - assertFalse(instance.isValidDouble("test16", "14.1414234x", 10, 10000, false, errors)); - assertTrue(errors.size() == 8); - assertFalse(instance.isValidDouble("test17", "Infinity", 10, 10000, false, errors)); - assertTrue(errors.size() == 9); - assertFalse(instance.isValidDouble("test18", "-Infinity", 10, 10000, false, errors)); - assertTrue(errors.size() == 10); - assertFalse(instance.isValidDouble("test19", "NaN", 10, 10000, false, errors)); - assertTrue(errors.size() == 11); - assertFalse(instance.isValidDouble("test20", "-NaN", 10, 10000, false, errors)); - assertTrue(errors.size() == 12); - assertFalse(instance.isValidDouble("test21", "+NaN", 10, 10000, false, errors)); - assertTrue(errors.size() == 13); - assertTrue(instance.isValidDouble("test22", "1e-6", -999999999, 999999999, false, errors)); - assertTrue(errors.size() == 13); - assertTrue(instance.isValidDouble("test23", "-1e-6", -999999999, 999999999, false, errors)); - assertTrue(errors.size() == 13); - } - - public void testIsValidFileContent() { - System.out.println("isValidFileContent"); - byte[] content = null; - try { - content = "This is some file content".getBytes(PREFERRED_ENCODING); - } - catch (UnsupportedEncodingException e) { - fail(PREFERRED_ENCODING + " not a supported encoding?!?!!!"); - } - Validator instance = ESAPI.validator(); - assertTrue(instance.isValidFileContent("test", content, 100, false)); - } - - public void testIsValidFileName() { - System.out.println("isValidFileName"); - Validator instance = ESAPI.validator(); - assertTrue("Simple valid filename with a valid extension", instance.isValidFileName("test", "aspect.jar", false)); - assertTrue("All valid filename characters are accepted", instance.isValidFileName("test", "!@#$%^&{}[]()_+-=,.~'` abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890.jar", false)); - assertTrue("Legal filenames that decode to legal filenames are accepted", instance.isValidFileName("test", "aspe%20ct.jar", false)); - - ValidationErrorList errors = new ValidationErrorList(); - assertTrue("Simple valid filename with a valid extension", instance.isValidFileName("test", "aspect.jar", false, errors)); - assertTrue("All valid filename characters are accepted", instance.isValidFileName("test", "!@#$%^&{}[]()_+-=,.~'` abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890.jar", false, errors)); - assertTrue("Legal filenames that decode to legal filenames are accepted", instance.isValidFileName("test", "aspe%20ct.jar", false, errors)); - assertTrue(errors.size() == 0); - } - - public void testIsValidFileUpload() throws IOException { - System.out.println("isValidFileUpload"); - String filepath = new File(System.getProperty("user.dir")).getCanonicalPath(); - String filename = "aspect.jar"; - File parent = new File("/").getCanonicalFile(); - ValidationErrorList errors = new ValidationErrorList(); - byte[] content = null; - try { - content = "This is some file content".getBytes(PREFERRED_ENCODING); - } - catch (UnsupportedEncodingException e) { - fail(PREFERRED_ENCODING + " not a supported encoding?!?!!!"); - } - Validator instance = ESAPI.validator(); - assertTrue(instance.isValidFileUpload("test", filepath, filename, parent, content, 100, false)); - assertTrue(instance.isValidFileUpload("test", filepath, filename, parent, content, 100, false, errors)); - assertTrue(errors.size() == 0); - - filepath = "/ridiculous"; - filename = "aspect.jar"; - try { - content = "This is some file content".getBytes(PREFERRED_ENCODING); - } - catch (UnsupportedEncodingException e) { - fail(PREFERRED_ENCODING + " not a supported encoding?!?!!!"); - } - assertFalse(instance.isValidFileUpload("test", filepath, filename, parent, content, 100, false)); - assertFalse(instance.isValidFileUpload("test", filepath, filename, parent, content, 100, false, errors)); - assertTrue(errors.size() == 1); - } - - public void testIsValidHTTPRequestParameterSet() { - // isValidHTTPRequestParameterSet(String, Set, Set) - } - - public void testisValidInput() { - System.out.println("isValidInput"); - Validator instance = ESAPI.validator(); - assertTrue(instance.isValidInput("test", "jeff.williams@aspectsecurity.com", "Email", 100, false)); - assertFalse(instance.isValidInput("test", "jeff.williams@@aspectsecurity.com", "Email", 100, false)); - assertFalse(instance.isValidInput("test", "jeff.williams@aspectsecurity", "Email", 100, false)); - assertTrue(instance.isValidInput("test", "jeff.wil'liams@aspectsecurity.com", "Email", 100, false)); - assertTrue(instance.isValidInput("test", "jeff.wil''liams@aspectsecurity.com", "Email", 100, false)); - assertTrue(instance.isValidInput("test", "123.168.100.234", "IPAddress", 100, false)); - assertTrue(instance.isValidInput("test", "192.168.1.234", "IPAddress", 100, false)); - assertFalse(instance.isValidInput("test", "..168.1.234", "IPAddress", 100, false)); - assertFalse(instance.isValidInput("test", "10.x.1.234", "IPAddress", 100, false)); - assertTrue(instance.isValidInput("test", "http://www.aspectsecurity.com", "URL", 100, false)); - assertFalse(instance.isValidInput("test", "http:///www.aspectsecurity.com", "URL", 100, false)); - assertFalse(instance.isValidInput("test", "http://www.aspect security.com", "URL", 100, false)); - assertTrue(instance.isValidInput("test", "078-05-1120", "SSN", 100, false)); - assertTrue(instance.isValidInput("test", "078 05 1120", "SSN", 100, false)); - assertTrue(instance.isValidInput("test", "078051120", "SSN", 100, false)); - assertFalse(instance.isValidInput("test", "987-65-4320", "SSN", 100, false)); - assertFalse(instance.isValidInput("test", "000-00-0000", "SSN", 100, false)); - assertFalse(instance.isValidInput("test", "(555) 555-5555", "SSN", 100, false)); - assertFalse(instance.isValidInput("test", "test", "SSN", 100, false)); - assertTrue(instance.isValidInput("test", "jeffWILLIAMS123", "HTTPParameterValue", 100, false)); - assertTrue(instance.isValidInput("test", "jeff .-/+=@_ WILLIAMS", "HTTPParameterValue", 100, false)); - // Removed per Issue 116 - The '*' character is valid as a parameter character -// assertFalse(instance.isValidInput("test", "jeff*WILLIAMS", "HTTPParameterValue", 100, false)); - assertFalse(instance.isValidInput("test", "jeff^WILLIAMS", "HTTPParameterValue", 100, false)); - assertFalse(instance.isValidInput("test", "jeff\\WILLIAMS", "HTTPParameterValue", 100, false)); - - assertTrue(instance.isValidInput("test", null, "Email", 100, true)); - assertFalse(instance.isValidInput("test", null, "Email", 100, false)); - - ValidationErrorList errors = new ValidationErrorList(); - - assertTrue(instance.isValidInput("test1", "jeff.williams@aspectsecurity.com", "Email", 100, false, errors)); - assertTrue(errors.size()==0); - assertFalse(instance.isValidInput("test2", "jeff.williams@@aspectsecurity.com", "Email", 100, false, errors)); - assertTrue(errors.size()==1); - assertFalse(instance.isValidInput("test3", "jeff.williams@aspectsecurity", "Email", 100, false, errors)); - assertTrue(errors.size()==2); - assertTrue(instance.isValidInput("test4", "jeff.wil'liams@aspectsecurity.com", "Email", 100, false, errors)); - assertTrue(errors.size()==2); - assertTrue(instance.isValidInput("test5", "jeff.wil''liams@aspectsecurity.com", "Email", 100, false, errors)); - assertTrue(errors.size()==2); - assertTrue(instance.isValidInput("test6", "123.168.100.234", "IPAddress", 100, false, errors)); - assertTrue(errors.size()==2); - assertTrue(instance.isValidInput("test7", "192.168.1.234", "IPAddress", 100, false, errors)); - assertTrue(errors.size()==2); - assertFalse(instance.isValidInput("test8", "..168.1.234", "IPAddress", 100, false, errors)); - assertTrue(errors.size()==3); - assertFalse(instance.isValidInput("test9", "10.x.1.234", "IPAddress", 100, false, errors)); - assertTrue(errors.size()==4); - assertTrue(instance.isValidInput("test10", "http://www.aspectsecurity.com", "URL", 100, false, errors)); - assertTrue(errors.size()==4); - assertFalse(instance.isValidInput("test11", "http:///www.aspectsecurity.com", "URL", 100, false, errors)); - assertTrue(errors.size()==5); - assertFalse(instance.isValidInput("test12", "http://www.aspect security.com", "URL", 100, false, errors)); - assertTrue(errors.size()==6); - assertTrue(instance.isValidInput("test13", "078-05-1120", "SSN", 100, false, errors)); - assertTrue(errors.size()==6); - assertTrue(instance.isValidInput("test14", "078 05 1120", "SSN", 100, false, errors)); - assertTrue(errors.size()==6); - assertTrue(instance.isValidInput("test15", "078051120", "SSN", 100, false, errors)); - assertTrue(errors.size()==6); - assertFalse(instance.isValidInput("test16", "987-65-4320", "SSN", 100, false, errors)); - assertTrue(errors.size()==7); - assertFalse(instance.isValidInput("test17", "000-00-0000", "SSN", 100, false, errors)); - assertTrue(errors.size()==8); - assertFalse(instance.isValidInput("test18", "(555) 555-5555", "SSN", 100, false, errors)); - assertTrue(errors.size()==9); - assertFalse(instance.isValidInput("test19", "test", "SSN", 100, false, errors)); - assertTrue(errors.size()==10); - assertTrue(instance.isValidInput("test20", "jeffWILLIAMS123", "HTTPParameterValue", 100, false, errors)); - assertTrue(errors.size()==10); - assertTrue(instance.isValidInput("test21", "jeff .-/+=@_ WILLIAMS", "HTTPParameterValue", 100, false, errors)); - assertTrue(errors.size()==10); - // Removed per Issue 116 - The '*' character is valid as a parameter character -// assertFalse(instance.isValidInput("test", "jeff*WILLIAMS", "HTTPParameterValue", 100, false)); - assertFalse(instance.isValidInput("test22", "jeff^WILLIAMS", "HTTPParameterValue", 100, false, errors)); - assertTrue(errors.size()==11); - assertFalse(instance.isValidInput("test23", "jeff\\WILLIAMS", "HTTPParameterValue", 100, false, errors)); - assertTrue(errors.size()==12); - - assertTrue(instance.isValidInput("test", null, "Email", 100, true, errors)); - assertFalse(instance.isValidInput("test", null, "Email", 100, false, errors)); - } - - public void testIsValidInteger() { - System.out.println("isValidInteger"); - Validator instance = ESAPI.validator(); - //testing negative range - assertFalse(instance.isValidInteger("test", "-4", 1, 10, false)); - assertTrue(instance.isValidInteger("test", "-4", -10, 10, false)); - //testing null value - assertTrue(instance.isValidInteger("test", null, -10, 10, true)); - assertFalse(instance.isValidInteger("test", null, -10, 10, false)); - //testing empty string - assertTrue(instance.isValidInteger("test", "", -10, 10, true)); - assertFalse(instance.isValidInteger("test", "", -10, 10, false)); - //testing improper range - assertFalse(instance.isValidInteger("test", "50", 10, -10, false)); - //testing non-integers - assertFalse(instance.isValidInteger("test", "4.3214", -10, 10, true)); - assertFalse(instance.isValidInteger("test", "-1.65", -10, 10, true)); - //other testing - assertTrue(instance.isValidInteger("test", "4", 1, 10, false)); - assertTrue(instance.isValidInteger("test", "400", 1, 10000, false)); - assertTrue(instance.isValidInteger("test", "400000000", 1, 400000000, false)); - assertFalse(instance.isValidInteger("test", "4000000000000", 1, 10000, false)); - assertFalse(instance.isValidInteger("test", "alsdkf", 10, 10000, false)); - assertFalse(instance.isValidInteger("test", "--10", 10, 10000, false)); - assertFalse(instance.isValidInteger("test", "14.1414234x", 10, 10000, false)); - assertFalse(instance.isValidInteger("test", "Infinity", 10, 10000, false)); - assertFalse(instance.isValidInteger("test", "-Infinity", 10, 10000, false)); - assertFalse(instance.isValidInteger("test", "NaN", 10, 10000, false)); - assertFalse(instance.isValidInteger("test", "-NaN", 10, 10000, false)); - assertFalse(instance.isValidInteger("test", "+NaN", 10, 10000, false)); - assertFalse(instance.isValidInteger("test", "1e-6", -999999999, 999999999, false)); - assertFalse(instance.isValidInteger("test", "-1e-6", -999999999, 999999999, false)); - - ValidationErrorList errors = new ValidationErrorList(); - //testing negative range - assertFalse(instance.isValidInteger("test1", "-4", 1, 10, false, errors)); - assertTrue(errors.size() == 1); - assertTrue(instance.isValidInteger("test2", "-4", -10, 10, false, errors)); - assertTrue(errors.size() == 1); - //testing null value - assertTrue(instance.isValidInteger("test3", null, -10, 10, true, errors)); - assertTrue(errors.size() == 1); - assertFalse(instance.isValidInteger("test4", null, -10, 10, false, errors)); - assertTrue(errors.size() == 2); - //testing empty string - assertTrue(instance.isValidInteger("test5", "", -10, 10, true, errors)); - assertTrue(errors.size() == 2); - assertFalse(instance.isValidInteger("test6", "", -10, 10, false, errors)); - assertTrue(errors.size() == 3); - //testing improper range - assertFalse(instance.isValidInteger("test7", "50", 10, -10, false, errors)); - assertTrue(errors.size() == 4); - //testing non-integers - assertFalse(instance.isValidInteger("test8", "4.3214", -10, 10, true, errors)); - assertTrue(errors.size() == 5); - assertFalse(instance.isValidInteger("test9", "-1.65", -10, 10, true, errors)); - assertTrue(errors.size() == 6); - //other testing - assertTrue(instance.isValidInteger("test10", "4", 1, 10, false, errors)); - assertTrue(errors.size() == 6); - assertTrue(instance.isValidInteger("test11", "400", 1, 10000, false, errors)); - assertTrue(errors.size() == 6); - assertTrue(instance.isValidInteger("test12", "400000000", 1, 400000000, false, errors)); - assertTrue(errors.size() == 6); - assertFalse(instance.isValidInteger("test13", "4000000000000", 1, 10000, false, errors)); - assertTrue(errors.size() == 7); - assertFalse(instance.isValidInteger("test14", "alsdkf", 10, 10000, false, errors)); - assertTrue(errors.size() == 8); - assertFalse(instance.isValidInteger("test15", "--10", 10, 10000, false, errors)); - assertTrue(errors.size() == 9); - assertFalse(instance.isValidInteger("test16", "14.1414234x", 10, 10000, false, errors)); - assertTrue(errors.size() == 10); - assertFalse(instance.isValidInteger("test17", "Infinity", 10, 10000, false, errors)); - assertTrue(errors.size() == 11); - assertFalse(instance.isValidInteger("test18", "-Infinity", 10, 10000, false, errors)); - assertTrue(errors.size() == 12); - assertFalse(instance.isValidInteger("test19", "NaN", 10, 10000, false, errors)); - assertTrue(errors.size() == 13); - assertFalse(instance.isValidInteger("test20", "-NaN", 10, 10000, false, errors)); - assertTrue(errors.size() == 14); - assertFalse(instance.isValidInteger("test21", "+NaN", 10, 10000, false, errors)); - assertTrue(errors.size() == 15); - assertFalse(instance.isValidInteger("test22", "1e-6", -999999999, 999999999, false, errors)); - assertTrue(errors.size() == 16); - assertFalse(instance.isValidInteger("test23", "-1e-6", -999999999, 999999999, false, errors)); - assertTrue(errors.size() == 17); - - } - - public void testIsValidListItem() { - System.out.println("isValidListItem"); - Validator instance = ESAPI.validator(); - List list = new ArrayList(); - list.add("one"); - list.add("two"); - assertTrue(instance.isValidListItem("test", "one", list)); - assertFalse(instance.isValidListItem("test", "three", list)); - - ValidationErrorList errors = new ValidationErrorList(); - assertTrue(instance.isValidListItem("test1", "one", list, errors)); - assertTrue(errors.size()==0); - assertFalse(instance.isValidListItem("test2", "three", list, errors)); - assertTrue(errors.size()==1); - } - - public void testIsValidNumber() { - System.out.println("isValidNumber"); - Validator instance = ESAPI.validator(); - //testing negative range - assertFalse(instance.isValidNumber("test", "-4", 1, 10, false)); - assertTrue(instance.isValidNumber("test", "-4", -10, 10, false)); - //testing null value - assertTrue(instance.isValidNumber("test", null, -10, 10, true)); - assertFalse(instance.isValidNumber("test", null, -10, 10, false)); - //testing empty string - assertTrue(instance.isValidNumber("test", "", -10, 10, true)); - assertFalse(instance.isValidNumber("test", "", -10, 10, false)); - //testing improper range - assertFalse(instance.isValidNumber("test", "5", 10, -10, false)); - //testing non-integers - assertTrue(instance.isValidNumber("test", "4.3214", -10, 10, true)); - assertTrue(instance.isValidNumber("test", "-1.65", -10, 10, true)); - //other testing - assertTrue(instance.isValidNumber("test", "4", 1, 10, false)); - assertTrue(instance.isValidNumber("test", "400", 1, 10000, false)); - assertTrue(instance.isValidNumber("test", "400000000", 1, 400000000, false)); - assertFalse(instance.isValidNumber("test", "4000000000000", 1, 10000, false)); - assertFalse(instance.isValidNumber("test", "alsdkf", 10, 10000, false)); - assertFalse(instance.isValidNumber("test", "--10", 10, 10000, false)); - assertFalse(instance.isValidNumber("test", "14.1414234x", 10, 10000, false)); - assertFalse(instance.isValidNumber("test", "Infinity", 10, 10000, false)); - assertFalse(instance.isValidNumber("test", "-Infinity", 10, 10000, false)); - assertFalse(instance.isValidNumber("test", "NaN", 10, 10000, false)); - assertFalse(instance.isValidNumber("test", "-NaN", 10, 10000, false)); - assertFalse(instance.isValidNumber("test", "+NaN", 10, 10000, false)); - assertTrue(instance.isValidNumber("test", "1e-6", -999999999, 999999999, false)); - assertTrue(instance.isValidNumber("test", "-1e-6", -999999999, 999999999, false)); - - ValidationErrorList errors = new ValidationErrorList(); - //testing negative range - assertFalse(instance.isValidNumber("test1", "-4", 1, 10, false, errors)); - assertTrue(errors.size()==1); - assertTrue(instance.isValidNumber("test2", "-4", -10, 10, false, errors)); - assertTrue(errors.size()==1); - //testing null value - assertTrue(instance.isValidNumber("test3", null, -10, 10, true, errors)); - assertTrue(errors.size()==1); - assertFalse(instance.isValidNumber("test4", null, -10, 10, false, errors)); - assertTrue(errors.size()==2); - //testing empty string - assertTrue(instance.isValidNumber("test5", "", -10, 10, true, errors)); - assertTrue(errors.size()==2); - assertFalse(instance.isValidNumber("test6", "", -10, 10, false, errors)); - assertTrue(errors.size()==3); - //testing improper range - assertFalse(instance.isValidNumber("test7", "5", 10, -10, false, errors)); - assertTrue(errors.size()==4); - //testing non-integers - assertTrue(instance.isValidNumber("test8", "4.3214", -10, 10, true, errors)); - assertTrue(errors.size()==4); - assertTrue(instance.isValidNumber("test9", "-1.65", -10, 10, true, errors)); - assertTrue(errors.size()==4); - //other testing - assertTrue(instance.isValidNumber("test10", "4", 1, 10, false, errors)); - assertTrue(errors.size()==4); - assertTrue(instance.isValidNumber("test11", "400", 1, 10000, false, errors)); - assertTrue(errors.size()==4); - assertTrue(instance.isValidNumber("test12", "400000000", 1, 400000000, false, errors)); - assertTrue(errors.size()==4); - assertFalse(instance.isValidNumber("test13", "4000000000000", 1, 10000, false, errors)); - assertTrue(errors.size()==5); - assertFalse(instance.isValidNumber("test14", "alsdkf", 10, 10000, false, errors)); - assertTrue(errors.size()==6); - assertFalse(instance.isValidNumber("test15", "--10", 10, 10000, false, errors)); - assertTrue(errors.size()==7); - assertFalse(instance.isValidNumber("test16", "14.1414234x", 10, 10000, false, errors)); - assertTrue(errors.size()==8); - assertFalse(instance.isValidNumber("test17", "Infinity", 10, 10000, false, errors)); - assertTrue(errors.size()==9); - assertFalse(instance.isValidNumber("test18", "-Infinity", 10, 10000, false, errors)); - assertTrue(errors.size()==10); - assertFalse(instance.isValidNumber("test19", "NaN", 10, 10000, false, errors)); - assertTrue(errors.size()==11); - assertFalse(instance.isValidNumber("test20", "-NaN", 10, 10000, false, errors)); - assertTrue(errors.size()==12); - assertFalse(instance.isValidNumber("test21", "+NaN", 10, 10000, false, errors)); - assertTrue(errors.size()==13); - assertTrue(instance.isValidNumber("test22", "1e-6", -999999999, 999999999, false, errors)); - assertTrue(errors.size()==13); - assertTrue(instance.isValidNumber("test23", "-1e-6", -999999999, 999999999, false, errors)); - assertTrue(errors.size()==13); - } - - public void testIsValidParameterSet() { - System.out.println("isValidParameterSet"); - Set requiredNames = new HashSet(); - requiredNames.add("p1"); - requiredNames.add("p2"); - requiredNames.add("p3"); - Set optionalNames = new HashSet(); - optionalNames.add("p4"); - optionalNames.add("p5"); - optionalNames.add("p6"); - MockHttpServletRequest request = new MockHttpServletRequest(); - MockHttpServletResponse response = new MockHttpServletResponse(); - request.addParameter("p1", "value"); - request.addParameter("p2", "value"); - request.addParameter("p3", "value"); - ESAPI.httpUtilities().setCurrentHTTP(request, response); - Validator instance = ESAPI.validator(); - ValidationErrorList errors = new ValidationErrorList(); - assertTrue(instance.isValidHTTPRequestParameterSet("HTTPParameters", request, requiredNames, optionalNames)); - assertTrue(instance.isValidHTTPRequestParameterSet("HTTPParameters", request, requiredNames, optionalNames,errors)); - assertTrue(errors.size()==0); - request.addParameter("p4", "value"); - request.addParameter("p5", "value"); - request.addParameter("p6", "value"); - assertTrue(instance.isValidHTTPRequestParameterSet("HTTPParameters", request, requiredNames, optionalNames)); - assertTrue(instance.isValidHTTPRequestParameterSet("HTTPParameters", request, requiredNames, optionalNames, errors)); - assertTrue(errors.size()==0); - request.removeParameter("p1"); - assertFalse(instance.isValidHTTPRequestParameterSet("HTTPParameters", request, requiredNames, optionalNames)); - assertFalse(instance.isValidHTTPRequestParameterSet("HTTPParameters", request, requiredNames, optionalNames, errors)); - assertTrue(errors.size() ==1); - } - - public void testIsValidPrintable() { - System.out.println("isValidPrintable"); - Validator instance = ESAPI.validator(); - assertTrue(instance.isValidPrintable("name", "abcDEF", 100, false)); - assertTrue(instance.isValidPrintable("name", "!@#R()*$;><()", 100, false)); - char[] chars = {0x60, (char) 0xFF, 0x10, 0x25}; - assertFalse(instance.isValidPrintable("name", chars, 100, false)); - assertFalse(instance.isValidPrintable("name", "%08", 100, false)); - - ValidationErrorList errors = new ValidationErrorList(); - assertTrue(instance.isValidPrintable("name1", "abcDEF", 100, false, errors)); - assertTrue(errors.size()==0); - assertTrue(instance.isValidPrintable("name2", "!@#R()*$;><()", 100, false, errors)); - assertTrue(errors.size()==0); - assertFalse(instance.isValidPrintable("name3", chars, 100, false, errors)); - assertTrue(errors.size()==1); - assertFalse(instance.isValidPrintable("name4", "%08", 100, false, errors)); - assertTrue(errors.size()==2); - - } - - public void testIsValidRedirectLocation() { - // isValidRedirectLocation(String, String, boolean) - } - - public void testIsValidSafeHTML() { - System.out.println("isValidSafeHTML"); - Validator instance = ESAPI.validator(); - - assertTrue(instance.isValidSafeHTML("test", "Jeff", 100, false)); - assertTrue(instance.isValidSafeHTML("test", "Aspect Security", 100, false)); - assertTrue(instance.isValidSafeHTML("test", "Test. ", 100, false)); - assertTrue(instance.isValidSafeHTML("test", "Test.
", 100, false)); - assertTrue(instance.isValidSafeHTML("test", "Test. alert(document.cookie)", 100, false)); - assertTrue(instance.isValidSafeHTML("test", "Test. alert(document.cookie)", 100, false)); - assertTrue(instance.isValidSafeHTML("test", "Test. alert(document.cookie)", 100, false)); - - // TODO: waiting for a way to validate text headed for an attribute for scripts - // This would be nice to catch, but just looks like text to AntiSamy - // assertFalse(instance.isValidSafeHTML("test", "\" onload=\"alert(document.cookie)\" ")); - ValidationErrorList errors = new ValidationErrorList(); - assertTrue(instance.isValidSafeHTML("test1", "Jeff", 100, false, errors)); - assertTrue(instance.isValidSafeHTML("test2", "Aspect Security", 100, false, errors)); - assertTrue(instance.isValidSafeHTML("test3", "Test. ", 100, false, errors)); - assertTrue(instance.isValidSafeHTML("test4", "Test.
", 100, false, errors)); - assertTrue(instance.isValidSafeHTML("test5", "Test. alert(document.cookie)", 100, false, errors)); - assertTrue(instance.isValidSafeHTML("test6", "Test. alert(document.cookie)", 100, false, errors)); - assertTrue(instance.isValidSafeHTML("test7", "Test. alert(document.cookie)", 100, false, errors)); - assertTrue(errors.size() == 0); - - } - - public void testSafeReadLine() { - System.out.println("safeReadLine"); - - byte[] bytes = null; - try { - bytes = "testString".getBytes(PREFERRED_ENCODING); - } - catch (UnsupportedEncodingException e1) { - fail(PREFERRED_ENCODING + " not a supported encoding?!?!!!"); - } - ByteArrayInputStream s = new ByteArrayInputStream(bytes); - Validator instance = ESAPI.validator(); - try { - instance.safeReadLine(s, -1); - fail(); - } - catch (ValidationException e) { - // Expected - } - s.reset(); - try { - instance.safeReadLine(s, 4); - fail(); - } - catch (ValidationException e) { - // Expected - } - s.reset(); - try { - String u = instance.safeReadLine(s, 20); - assertEquals("testString", u); - } - catch (ValidationException e) { - fail(); - } - - // This sub-test attempts to validate that BufferedReader.readLine() and safeReadLine() are similar in operation - // for the nominal case - try { - s.reset(); - InputStreamReader isr = new InputStreamReader(s); - BufferedReader br = new BufferedReader(isr); - String u = br.readLine(); - s.reset(); - String v = instance.safeReadLine(s, 20); - assertEquals(u, v); - } - catch (IOException e) { - fail(); - } - catch (ValidationException e) { - fail(); - } - } - - public void testIssue82_SafeString_Bad_Regex() { - Validator instance = ESAPI.validator(); - try { - instance.getValidInput("address", "55 main st. pasadena ak", "SafeString", 512, false); - } - catch (ValidationException e) { - fail(e.getLogMessage()); - } - } - - public void testGetParameterMap() { -//testing Validator.HTTPParameterName and Validator.HTTPParameterValue - MockHttpServletRequest request = new MockHttpServletRequest(); - SecurityWrapperRequest safeRequest = new SecurityWrapperRequest(request); -//an example of a parameter from displaytag, should pass - request.addParameter("d-49653-p", "pass"); - request.addParameter(""); - request.addParameter("f2", ""); - request.addParameter("f3", ""); - for (int i = 1; i <= 4; i++) { - assertTrue(safeRequest.getParameter("p" + i).equals(request.getParameter("p" + i))); - } - for (int i = 1; i <= 2; i++) { - boolean testResult = false; - try { - testResult = safeRequest.getParameter("f" + i).equals(request.getParameter("f" + i)); - } catch (NullPointerException npe) { - //the test is this block SHOULD fail. a NPE is an acceptable failure state - testResult = false; //redundant, just being descriptive here - } - assertFalse(testResult); - } - assertNull(safeRequest.getParameter("e1")); - - //This is revealing problems with Jeff's original SafeRequest - //mishandling of the AllowNull parameter. I'm adding a new Google code - //bug to track this. - // - //assertNotNull(safeRequest.getParameter("e1", false)); - } - - public void testGetCookies() { -//testing Validator.HTTPCookieName and Validator.HTTPCookieValue - MockHttpServletRequest request = new MockHttpServletRequest(); - SecurityWrapperRequest safeRequest = new SecurityWrapperRequest(request); -//should support a base64-encode value - request.setCookie("p1", "34=VJhjv7jiDu7tsdLrQQ2KcUwpfWUM2_mBae6UA8ttk4wBHdxxQ-1IBxyCOn3LWE08SDhpnBcJ7N5Vze48F2t8a1R_hXt7PX1BvgTM0pn-T4JkqGTm_tlmV4RmU3GT-dgn"); - request.setCookie("f1", "XSS"); - request.setCookie("load-balancing", "pass"); - request.setCookie("'bypass", "fail"); - Cookie[] cookies = safeRequest.getCookies(); - assertEquals(cookies[0].getValue(), request.getCookies()[0].getValue()); - assertEquals(cookies[1].getName(), request.getCookies()[2].getName()); - assertTrue(cookies.length == 2); - } - - public void testGetHeader() { -//testing Validator.HTTPHeaderValue - MockHttpServletRequest request = new MockHttpServletRequest(); - SecurityWrapperRequest safeRequest = new SecurityWrapperRequest(request); - request.addHeader("p1", "login"); - request.addHeader("f1", "XSS"); - request.addHeader("p2", generateStringOfLength(150)); - request.addHeader("f2", generateStringOfLength(151)); - assertEquals(safeRequest.getHeader("p1"), request.getHeader("p1")); - assertEquals(safeRequest.getHeader("p2"), request.getHeader("p2")); - assertFalse(safeRequest.getHeader("f1").equals(request.getHeader("f1"))); - assertFalse(safeRequest.getHeader("f2").equals(request.getHeader("f2"))); - assertNull(safeRequest.getHeader("p3")); - } - - public void testGetHeaderNames() { -//testing Validator.HTTPHeaderName - MockHttpServletRequest request = new MockHttpServletRequest(); - SecurityWrapperRequest safeRequest = new SecurityWrapperRequest(request); - request.addHeader("d-49653-p", "pass"); - request.addHeader(""); - assertFalse(safeRequest.getQueryString().equals(request.getQueryString())); - request.setQueryString("mail=bob@alice.com-passwd=johny"); - assertTrue(safeRequest.getQueryString().equals(request.getQueryString())); - request.setQueryString("mail=bob@alice.com-passwd=johny&special"); //= is missing! - assertFalse(safeRequest.getQueryString().equals(request.getQueryString())); - } - - public void testGetRequestURI() { -//testing Validator.HTTPURI - MockHttpServletRequest request = new MockHttpServletRequest(); - SecurityWrapperRequest safeRequest = new SecurityWrapperRequest(request); - try { - request.setRequestURI("/app/page.jsp"); - } catch (UnsupportedEncodingException ignored) { - } - assertEquals(safeRequest.getRequestURI(), request.getRequestURI()); - } - - private String generateStringOfLength(int length) { - StringBuilder longString = new StringBuilder(); - for (int i = 0; i < length; i++) { - longString.append("a"); - } - return longString.toString(); - } - - public void testGetContextPath() { - // Root Context Path ("") - assertTrue(ESAPI.validator().isValidInput("HTTPContextPath", "", "HTTPContextPath", 512, true)); - // Deployed Context Path ("/context") - assertTrue(ESAPI.validator().isValidInput("HTTPContextPath", "/context", "HTTPContextPath", 512, true)); - // Fail-case - URL Splitting - assertFalse(ESAPI.validator().isValidInput("HTTPContextPath", "/\\nGET http://evil.com", "HTTPContextPath", 512, true)); - } -} +/** + * OWASP Enterprise Security API (ESAPI) + * + * This file is part of the Open Web Application Security Project (OWASP) + * Enterprise Security API (ESAPI) project. For details, please see + * http://www.owasp.org/index.php/ESAPI. + * + * Copyright (c) 2007 - The OWASP Foundation + * + * The ESAPI is published by OWASP under the BSD license. You should read and accept the + * LICENSE before you use, modify, and/or redistribute this software. + * + * @author Jeff Williams Aspect Security + * @created 2007 + */ +package org.owasp.esapi.reference; + +import java.io.BufferedReader; +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.UnsupportedEncodingException; +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.*; + +import junit.framework.Test; +import junit.framework.TestCase; +import junit.framework.TestSuite; + +import org.owasp.esapi.*; +import org.owasp.esapi.errors.ValidationException; +import org.owasp.esapi.filters.SecurityWrapperRequest; +import org.owasp.esapi.http.MockHttpServletRequest; +import org.owasp.esapi.http.MockHttpServletResponse; +import org.owasp.esapi.reference.validation.HTMLValidationRule; +import org.owasp.esapi.reference.validation.StringValidationRule; + +import javax.servlet.http.Cookie; +import javax.servlet.http.HttpServletRequest; + +/** + * The Class ValidatorTest. + * + * @author Mike Fauzy (mike.fauzy@aspectsecurity.com) + * @author Jeff Williams (jeff.williams@aspectsecurity.com) + */ +public class ValidatorTest extends TestCase { + + private static final String PREFERRED_ENCODING = "UTF-8"; + + public static Test suite() { + return new TestSuite(ValidatorTest.class); + } + + /** + * Instantiates a new HTTP utilities test. + * + * @param testName the test name + */ + public ValidatorTest(String testName) { + super(testName); + } + + /** + * {@inheritDoc} + * + * @throws Exception + */ + protected void setUp() throws Exception { + // none + } + + /** + * {@inheritDoc} + * + * @throws Exception + */ + protected void tearDown() throws Exception { + // none + } + + public void testAddRule() { + Validator validator = ESAPI.validator(); + ValidationRule rule = new StringValidationRule("ridiculous"); + validator.addRule(rule); + assertEquals(rule, validator.getRule("ridiculous")); + } + + public void testAssertValidFileUpload() { + // assertValidFileUpload(String, String, String, byte[], int, boolean, ValidationErrorList) + } + + public void testGetPrintable1() { + // getValidPrintable(String, char[], int, boolean, ValidationErrorList) + } + + public void testGetPrintable2() { + // getValidPrintable(String, String, int, boolean, ValidationErrorList) + } + + public void testGetRule() { + Validator validator = ESAPI.validator(); + ValidationRule rule = new StringValidationRule("rule"); + validator.addRule(rule); + assertEquals(rule, validator.getRule("rule")); + assertFalse(rule == validator.getRule("ridiculous")); + } + + public void testGetValidCreditCard() { + System.out.println("getValidCreditCard"); + Validator instance = ESAPI.validator(); + ValidationErrorList errors = new ValidationErrorList(); + + assertTrue(instance.isValidCreditCard("cctest1", "1234 9876 0000 0008", false)); + assertTrue(instance.isValidCreditCard("cctest2", "1234987600000008", false)); + assertFalse(instance.isValidCreditCard("cctest3", "12349876000000081", false)); + assertFalse(instance.isValidCreditCard("cctest4", "4417 1234 5678 9112", false)); + + instance.getValidCreditCard("cctest5", "1234 9876 0000 0008", false, errors); + assertEquals(0, errors.size()); + instance.getValidCreditCard("cctest6", "1234987600000008", false, errors); + assertEquals(0, errors.size()); + instance.getValidCreditCard("cctest7", "12349876000000081", false, errors); + assertEquals(1, errors.size()); + instance.getValidCreditCard("cctest8", "4417 1234 5678 9112", false, errors); + assertEquals(2, errors.size()); + + assertTrue(instance.isValidCreditCard("cctest1", "1234 9876 0000 0008", false, errors)); + assertTrue(errors.size()==2); + assertTrue(instance.isValidCreditCard("cctest2", "1234987600000008", false, errors)); + assertTrue(errors.size()==2); + assertFalse(instance.isValidCreditCard("cctest3", "12349876000000081", false, errors)); + assertTrue(errors.size()==3); + assertFalse(instance.isValidCreditCard("cctest4", "4417 1234 5678 9112", false, errors)); + assertTrue(errors.size()==4); + } + + public void testGetValidDate() throws Exception { + System.out.println("getValidDate"); + Validator instance = ESAPI.validator(); + ValidationErrorList errors = new ValidationErrorList(); + assertTrue(instance.getValidDate("datetest1", "June 23, 1967", DateFormat.getDateInstance(DateFormat.MEDIUM, Locale.US), false) != null); + instance.getValidDate("datetest2", "freakshow", DateFormat.getDateInstance(), false, errors); + assertEquals(1, errors.size()); + + // TODO: This test case fails due to an apparent bug in SimpleDateFormat + // Note: This seems to be fixed in JDK 6. Will leave it commented out since + // we only require JDK 5. -kww + instance.getValidDate("test", "June 32, 2008", DateFormat.getDateInstance(), false, errors); + // assertEquals( 2, errors.size() ); + } + + // FIXME: Should probably use SecurityConfigurationWrapper and force + // Validator.AcceptLenientDates to be false. + public void testLenientDate() { + System.out.println("testLenientDate"); + boolean acceptLenientDates = ESAPI.securityConfiguration().getLenientDatesAccepted(); + if ( acceptLenientDates ) { + assertTrue("Lenient date test skipped because Validator.AcceptLenientDates set to true", true); + return; + } + + Date lenientDateTest = null; + try { + // lenientDateTest will be null when Validator.AcceptLenientDates + // is set to false (the default). + Validator instance = ESAPI.validator(); + lenientDateTest = instance.getValidDate("datatest3-lenient", "15/2/2009 11:83:00", + DateFormat.getDateInstance(DateFormat.SHORT, Locale.US), + false); + fail("Failed to throw expected ValidationException when Validator.AcceptLenientDates set to false."); + } catch (ValidationException ve) { + assertNull( lenientDateTest ); + Throwable cause = ve.getCause(); + assertTrue( cause.getClass().getName().equals("java.text.ParseException") ); + } catch (Exception e) { + fail("Caught unexpected exception: " + e.getClass().getName() + "; msg: " + e); + } + } + + public void testGetValidDirectoryPath() throws Exception { + System.out.println("getValidDirectoryPath"); + Validator instance = ESAPI.validator(); + ValidationErrorList errors = new ValidationErrorList(); + // find a directory that exists + File parent = new File("/"); + String path = ESAPI.securityConfiguration().getResourceFile("ESAPI.properties").getParentFile().getCanonicalPath(); + instance.getValidDirectoryPath("dirtest1", path, parent, true, errors); + assertEquals(0, errors.size()); + instance.getValidDirectoryPath("dirtest2", null, parent, false, errors); + assertEquals(1, errors.size()); + instance.getValidDirectoryPath("dirtest3", "ridicul%00ous", parent, false, errors); + assertEquals(2, errors.size()); + } + + public void testGetValidDouble() { + System.out.println("getValidDouble"); + Validator instance = ESAPI.validator(); + ValidationErrorList errors = new ValidationErrorList(); + instance.getValidDouble("dtest1", "1.0", 0, 20, true, errors); + assertEquals(0, errors.size()); + instance.getValidDouble("dtest2", null, 0, 20, true, errors); + assertEquals(0, errors.size()); + instance.getValidDouble("dtest3", null, 0, 20, false, errors); + assertEquals(1, errors.size()); + instance.getValidDouble("dtest4", "ridiculous", 0, 20, true, errors); + assertEquals(2, errors.size()); + instance.getValidDouble("dtest5", "" + (Double.MAX_VALUE), 0, 20, true, errors); + assertEquals(3, errors.size()); + instance.getValidDouble("dtest6", "" + (Double.MAX_VALUE + .00001), 0, 20, true, errors); + assertEquals(4, errors.size()); + } + + public void testGetValidFileContent() { + System.out.println("getValidFileContent"); + Validator instance = ESAPI.validator(); + ValidationErrorList errors = new ValidationErrorList(); + byte[] bytes = null; + try { + bytes = "12345".getBytes(PREFERRED_ENCODING); + } + catch (UnsupportedEncodingException e) { + fail(PREFERRED_ENCODING + " not a supported encoding?!?!!"); + } + instance.getValidFileContent("test", bytes, 5, true, errors); + assertEquals(0, errors.size()); + instance.getValidFileContent("test", bytes, 4, true, errors); + assertEquals(1, errors.size()); + } + + public void testGetValidFileName() throws Exception { + System.out.println("getValidFileName"); + Validator instance = ESAPI.validator(); + ValidationErrorList errors = new ValidationErrorList(); + String testName = "aspe%20ct.jar"; + assertEquals("Percent encoding is not changed", testName, instance.getValidFileName("test", testName, ESAPI.securityConfiguration().getAllowedFileExtensions(), false, errors)); + } + + public void testGetValidInput() { + System.out.println("getValidInput"); + Validator instance = ESAPI.validator(); + ValidationErrorList errors = new ValidationErrorList(); + // instance.getValidInput(String, String, String, int, boolean, ValidationErrorList) + } + + public void testGetValidInteger() { + System.out.println("getValidInteger"); + Validator instance = ESAPI.validator(); + ValidationErrorList errors = new ValidationErrorList(); + // instance.getValidInteger(String, String, int, int, boolean, ValidationErrorList) + } + + public void testGetValidListItem() { + System.out.println("getValidListItem"); + Validator instance = ESAPI.validator(); + ValidationErrorList errors = new ValidationErrorList(); + // instance.getValidListItem(String, String, List, ValidationErrorList) + } + + public void testGetValidNumber() { + System.out.println("getValidNumber"); + Validator instance = ESAPI.validator(); + ValidationErrorList errors = new ValidationErrorList(); + // instance.getValidNumber(String, String, long, long, boolean, ValidationErrorList) + } + + public void testGetValidRedirectLocation() { + System.out.println("getValidRedirectLocation"); + Validator instance = ESAPI.validator(); + ValidationErrorList errors = new ValidationErrorList(); + // instance.getValidRedirectLocation(String, String, boolean, ValidationErrorList) + } + + public void testGetValidSafeHTML() throws Exception { + System.out.println("getValidSafeHTML"); + Validator instance = ESAPI.validator(); + ValidationErrorList errors = new ValidationErrorList(); + + // new school test case setup + HTMLValidationRule rule = new HTMLValidationRule("test"); + ESAPI.validator().addRule(rule); + + assertEquals("Test.", ESAPI.validator().getRule("test").getValid("test", "Test. ")); + + String test1 = "Jeff"; + String result1 = instance.getValidSafeHTML("test", test1, 100, false, errors); + assertEquals(test1, result1); + + String test2 = "Aspect Security"; + String result2 = instance.getValidSafeHTML("test", test2, 100, false, errors); + assertEquals(test2, result2); + + String test3 = "Test. "; + assertEquals("Test.", rule.getSafe("test", test3)); + + assertEquals("Test. <
load=alert()
", rule.getSafe("test", "Test. <
load=alert()")); + assertEquals("Test.
b
", rule.getSafe("test", "Test.
b
")); + assertEquals("Test. alert(document.cookie)", rule.getSafe("test", "Test. alert(document.cookie)")); + assertEquals("Test. alert(document.cookie)", rule.getSafe("test", "Test. alert(document.cookie)")); + assertEquals("Test. alert(document.cookie)", rule.getSafe("test", "Test. alert(document.cookie)")); + // TODO: ENHANCE waiting for a way to validate text headed for an attribute for scripts + // This would be nice to catch, but just looks like text to AntiSamy + // assertFalse(instance.isValidSafeHTML("test", "\" onload=\"alert(document.cookie)\" ")); + // String result4 = instance.getValidSafeHTML("test", test4); + // assertEquals("", result4); + } + + public void testIsInvalidFilename() { + System.out.println("testIsInvalidFilename"); + Validator instance = ESAPI.validator(); + char invalidChars[] = "/\\:*?\"<>|".toCharArray(); + for (int i = 0; i < invalidChars.length; i++) { + assertFalse(invalidChars[i] + " is an invalid character for a filename", + instance.isValidFileName("test", "as" + invalidChars[i] + "pect.jar", false)); + } + assertFalse("Files must have an extension", instance.isValidFileName("test", "", false)); + assertFalse("Files must have a valid extension", instance.isValidFileName("test.invalidExtension", "", false)); + assertFalse("Filennames cannot be the empty string", instance.isValidFileName("test", "", false)); + } + + public void testIsValidDate() { + System.out.println("isValidDate"); + Validator instance = ESAPI.validator(); + DateFormat format = SimpleDateFormat.getDateInstance(SimpleDateFormat.MEDIUM, Locale.US); + assertTrue(instance.isValidDate("datetest1", "September 11, 2001", format, true)); + assertFalse(instance.isValidDate("datetest2", null, format, false)); + assertFalse(instance.isValidDate("datetest3", "", format, false)); + + ValidationErrorList errors = new ValidationErrorList(); + assertTrue(instance.isValidDate("datetest1", "September 11, 2001", format, true, errors)); + assertTrue(errors.size()==0); + assertFalse(instance.isValidDate("datetest2", null, format, false, errors)); + assertTrue(errors.size()==1); + assertFalse(instance.isValidDate("datetest3", "", format, false, errors)); + assertTrue(errors.size()==2); + + } + + public void testIsValidDirectoryPath() throws IOException { + System.out.println("isValidDirectoryPath"); + + // get an encoder with a special list of codecs and make a validator out of it + List list = new ArrayList(); + list.add("HTMLEntityCodec"); + Encoder encoder = new DefaultEncoder(list); + Validator instance = new DefaultValidator(encoder); + + boolean isWindows = (System.getProperty("os.name").indexOf("Windows") != -1) ? true : false; + File parent = new File("/"); + + ValidationErrorList errors = new ValidationErrorList(); + + if (isWindows) { + String sysRoot = new File(System.getenv("SystemRoot")).getCanonicalPath(); + // Windows paths that don't exist and thus should fail + assertFalse(instance.isValidDirectoryPath("test", "c:\\ridiculous", parent, false)); + assertFalse(instance.isValidDirectoryPath("test", "c:\\jeff", parent, false)); + assertFalse(instance.isValidDirectoryPath("test", "c:\\temp\\..\\etc", parent, false)); + + // Windows paths + assertTrue(instance.isValidDirectoryPath("test", "C:\\", parent, false)); // Windows root directory + assertTrue(instance.isValidDirectoryPath("test", sysRoot, parent, false)); // Windows always exist directory + assertFalse(instance.isValidDirectoryPath("test", sysRoot + "\\System32\\cmd.exe", parent, false)); // Windows command shell + + // Unix specific paths should not pass + assertFalse(instance.isValidDirectoryPath("test", "/tmp", parent, false)); // Unix Temporary directory + assertFalse(instance.isValidDirectoryPath("test", "/bin/sh", parent, false)); // Unix Standard shell + assertFalse(instance.isValidDirectoryPath("test", "/etc/config", parent, false)); + + // Unix specific paths that should not exist or work + assertFalse(instance.isValidDirectoryPath("test", "/etc/ridiculous", parent, false)); + assertFalse(instance.isValidDirectoryPath("test", "/tmp/../etc", parent, false)); + + assertFalse(instance.isValidDirectoryPath("test1", "c:\\ridiculous", parent, false, errors)); + assertTrue(errors.size()==1); + assertFalse(instance.isValidDirectoryPath("test2", "c:\\jeff", parent, false, errors)); + assertTrue(errors.size()==2); + assertFalse(instance.isValidDirectoryPath("test3", "c:\\temp\\..\\etc", parent, false, errors)); + assertTrue(errors.size()==3); + + // Windows paths + assertTrue(instance.isValidDirectoryPath("test4", "C:\\", parent, false, errors)); // Windows root directory + assertTrue(errors.size()==3); + assertTrue(instance.isValidDirectoryPath("test5", sysRoot, parent, false, errors)); // Windows always exist directory + assertTrue(errors.size()==3); + assertFalse(instance.isValidDirectoryPath("test6", sysRoot + "\\System32\\cmd.exe", parent, false, errors)); // Windows command shell + assertTrue(errors.size()==4); + + // Unix specific paths should not pass + assertFalse(instance.isValidDirectoryPath("test7", "/tmp", parent, false, errors)); // Unix Temporary directory + assertTrue(errors.size()==5); + assertFalse(instance.isValidDirectoryPath("test8", "/bin/sh", parent, false, errors)); // Unix Standard shell + assertTrue(errors.size()==6); + assertFalse(instance.isValidDirectoryPath("test9", "/etc/config", parent, false, errors)); + assertTrue(errors.size()==7); + + // Unix specific paths that should not exist or work + assertFalse(instance.isValidDirectoryPath("test10", "/etc/ridiculous", parent, false, errors)); + assertTrue(errors.size()==8); + assertFalse(instance.isValidDirectoryPath("test11", "/tmp/../etc", parent, false, errors)); + assertTrue(errors.size()==9); + + } else { + // Windows paths should fail + assertFalse(instance.isValidDirectoryPath("test", "c:\\ridiculous", parent, false)); + assertFalse(instance.isValidDirectoryPath("test", "c:\\temp\\..\\etc", parent, false)); + + // Standard Windows locations should fail + assertFalse(instance.isValidDirectoryPath("test", "c:\\", parent, false)); // Windows root directory + assertFalse(instance.isValidDirectoryPath("test", "c:\\Windows\\temp", parent, false)); // Windows temporary directory + assertFalse(instance.isValidDirectoryPath("test", "c:\\Windows\\System32\\cmd.exe", parent, false)); // Windows command shell + + // Unix specific paths should pass + assertTrue(instance.isValidDirectoryPath("test", "/", parent, false)); // Root directory + assertTrue(instance.isValidDirectoryPath("test", "/bin", parent, false)); // Always exist directory + + // Unix specific paths that should not exist or work + assertFalse(instance.isValidDirectoryPath("test", "/bin/sh", parent, false)); // Standard shell, not dir + assertFalse(instance.isValidDirectoryPath("test", "/etc/ridiculous", parent, false)); + assertFalse(instance.isValidDirectoryPath("test", "/tmp/../etc", parent, false)); + + // Windows paths should fail + assertFalse(instance.isValidDirectoryPath("test1", "c:\\ridiculous", parent, false, errors)); + assertTrue(errors.size()==1); + assertFalse(instance.isValidDirectoryPath("test2", "c:\\temp\\..\\etc", parent, false, errors)); + assertTrue(errors.size()==2); + + // Standard Windows locations should fail + assertFalse(instance.isValidDirectoryPath("test3", "c:\\", parent, false, errors)); // Windows root directory + assertTrue(errors.size()==3); + assertFalse(instance.isValidDirectoryPath("test4", "c:\\Windows\\temp", parent, false, errors)); // Windows temporary directory + assertTrue(errors.size()==4); + assertFalse(instance.isValidDirectoryPath("test5", "c:\\Windows\\System32\\cmd.exe", parent, false, errors)); // Windows command shell + assertTrue(errors.size()==5); + + // Unix specific paths should pass + assertTrue(instance.isValidDirectoryPath("test6", "/", parent, false, errors)); // Root directory + assertTrue(errors.size()==5); + assertTrue(instance.isValidDirectoryPath("test7", "/bin", parent, false, errors)); // Always exist directory + assertTrue(errors.size()==5); + + // Unix specific paths that should not exist or work + assertFalse(instance.isValidDirectoryPath("test8", "/bin/sh", parent, false, errors)); // Standard shell, not dir + assertTrue(errors.size()==6); + assertFalse(instance.isValidDirectoryPath("test9", "/etc/ridiculous", parent, false, errors)); + assertTrue(errors.size()==7); + assertFalse(instance.isValidDirectoryPath("test10", "/tmp/../etc", parent, false, errors)); + assertTrue(errors.size()==8); + } + } + + public void TestIsValidDirectoryPath() { + // isValidDirectoryPath(String, String, boolean) + } + + public void testIsValidDouble() { + // isValidDouble(String, String, double, double, boolean) + Validator instance = ESAPI.validator(); + ValidationErrorList errors = new ValidationErrorList(); + //testing negative range + assertFalse(instance.isValidDouble("test1", "-4", 1, 10, false, errors)); + assertTrue(errors.size() == 1); + assertTrue(instance.isValidDouble("test2", "-4", -10, 10, false, errors)); + assertTrue(errors.size() == 1); + //testing null value + assertTrue(instance.isValidDouble("test3", null, -10, 10, true, errors)); + assertTrue(errors.size() == 1); + assertFalse(instance.isValidDouble("test4", null, -10, 10, false, errors)); + assertTrue(errors.size() == 2); + //testing empty string + assertTrue(instance.isValidDouble("test5", "", -10, 10, true, errors)); + assertTrue(errors.size() == 2); + assertFalse(instance.isValidDouble("test6", "", -10, 10, false, errors)); + assertTrue(errors.size() == 3); + //testing improper range + assertFalse(instance.isValidDouble("test7", "50.0", 10, -10, false, errors)); + assertTrue(errors.size() == 4); + //testing non-integers + assertTrue(instance.isValidDouble("test8", "4.3214", -10, 10, true, errors)); + assertTrue(errors.size() == 4); + assertTrue(instance.isValidDouble("test9", "-1.65", -10, 10, true, errors)); + assertTrue(errors.size() == 4); + //other testing + assertTrue(instance.isValidDouble("test10", "4", 1, 10, false, errors)); + assertTrue(errors.size() == 4); + assertTrue(instance.isValidDouble("test11", "400", 1, 10000, false, errors)); + assertTrue(errors.size() == 4); + assertTrue(instance.isValidDouble("test12", "400000000", 1, 400000000, false, errors)); + assertTrue(errors.size() == 4); + assertFalse(instance.isValidDouble("test13", "4000000000000", 1, 10000, false, errors)); + assertTrue(errors.size() == 5); + assertFalse(instance.isValidDouble("test14", "alsdkf", 10, 10000, false, errors)); + assertTrue(errors.size() == 6); + assertFalse(instance.isValidDouble("test15", "--10", 10, 10000, false, errors)); + assertTrue(errors.size() == 7); + assertFalse(instance.isValidDouble("test16", "14.1414234x", 10, 10000, false, errors)); + assertTrue(errors.size() == 8); + assertFalse(instance.isValidDouble("test17", "Infinity", 10, 10000, false, errors)); + assertTrue(errors.size() == 9); + assertFalse(instance.isValidDouble("test18", "-Infinity", 10, 10000, false, errors)); + assertTrue(errors.size() == 10); + assertFalse(instance.isValidDouble("test19", "NaN", 10, 10000, false, errors)); + assertTrue(errors.size() == 11); + assertFalse(instance.isValidDouble("test20", "-NaN", 10, 10000, false, errors)); + assertTrue(errors.size() == 12); + assertFalse(instance.isValidDouble("test21", "+NaN", 10, 10000, false, errors)); + assertTrue(errors.size() == 13); + assertTrue(instance.isValidDouble("test22", "1e-6", -999999999, 999999999, false, errors)); + assertTrue(errors.size() == 13); + assertTrue(instance.isValidDouble("test23", "-1e-6", -999999999, 999999999, false, errors)); + assertTrue(errors.size() == 13); + } + + public void testIsValidFileContent() { + System.out.println("isValidFileContent"); + byte[] content = null; + try { + content = "This is some file content".getBytes(PREFERRED_ENCODING); + } + catch (UnsupportedEncodingException e) { + fail(PREFERRED_ENCODING + " not a supported encoding?!?!!!"); + } + Validator instance = ESAPI.validator(); + assertTrue(instance.isValidFileContent("test", content, 100, false)); + } + + public void testIsValidFileName() { + System.out.println("isValidFileName"); + Validator instance = ESAPI.validator(); + assertTrue("Simple valid filename with a valid extension", instance.isValidFileName("test", "aspect.jar", false)); + assertTrue("All valid filename characters are accepted", instance.isValidFileName("test", "!@#$%^&{}[]()_+-=,.~'` abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890.jar", false)); + assertTrue("Legal filenames that decode to legal filenames are accepted", instance.isValidFileName("test", "aspe%20ct.jar", false)); + + ValidationErrorList errors = new ValidationErrorList(); + assertTrue("Simple valid filename with a valid extension", instance.isValidFileName("test", "aspect.jar", false, errors)); + assertTrue("All valid filename characters are accepted", instance.isValidFileName("test", "!@#$%^&{}[]()_+-=,.~'` abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890.jar", false, errors)); + assertTrue("Legal filenames that decode to legal filenames are accepted", instance.isValidFileName("test", "aspe%20ct.jar", false, errors)); + assertTrue(errors.size() == 0); + } + + public void testIsValidFileUpload() throws IOException { + System.out.println("isValidFileUpload"); + String filepath = new File(System.getProperty("user.dir")).getCanonicalPath(); + String filename = "aspect.jar"; + File parent = new File("/").getCanonicalFile(); + ValidationErrorList errors = new ValidationErrorList(); + byte[] content = null; + try { + content = "This is some file content".getBytes(PREFERRED_ENCODING); + } + catch (UnsupportedEncodingException e) { + fail(PREFERRED_ENCODING + " not a supported encoding?!?!!!"); + } + Validator instance = ESAPI.validator(); + assertTrue(instance.isValidFileUpload("test", filepath, filename, parent, content, 100, false)); + assertTrue(instance.isValidFileUpload("test", filepath, filename, parent, content, 100, false, errors)); + assertTrue(errors.size() == 0); + + filepath = "/ridiculous"; + filename = "aspect.jar"; + try { + content = "This is some file content".getBytes(PREFERRED_ENCODING); + } + catch (UnsupportedEncodingException e) { + fail(PREFERRED_ENCODING + " not a supported encoding?!?!!!"); + } + assertFalse(instance.isValidFileUpload("test", filepath, filename, parent, content, 100, false)); + assertFalse(instance.isValidFileUpload("test", filepath, filename, parent, content, 100, false, errors)); + assertTrue(errors.size() == 1); + } + + public void testIsValidHTTPRequestParameterSet() { + // isValidHTTPRequestParameterSet(String, Set, Set) + } + + public void testisValidInput() { + System.out.println("isValidInput"); + Validator instance = ESAPI.validator(); + assertTrue(instance.isValidInput("test", "jeff.williams@aspectsecurity.com", "Email", 100, false)); + assertFalse(instance.isValidInput("test", "jeff.williams@@aspectsecurity.com", "Email", 100, false)); + assertFalse(instance.isValidInput("test", "jeff.williams@aspectsecurity", "Email", 100, false)); + assertTrue(instance.isValidInput("test", "jeff.wil'liams@aspectsecurity.com", "Email", 100, false)); + assertTrue(instance.isValidInput("test", "jeff.wil''liams@aspectsecurity.com", "Email", 100, false)); + assertTrue(instance.isValidInput("test", "123.168.100.234", "IPAddress", 100, false)); + assertTrue(instance.isValidInput("test", "192.168.1.234", "IPAddress", 100, false)); + assertFalse(instance.isValidInput("test", "..168.1.234", "IPAddress", 100, false)); + assertFalse(instance.isValidInput("test", "10.x.1.234", "IPAddress", 100, false)); + assertTrue(instance.isValidInput("test", "http://www.aspectsecurity.com", "URL", 100, false)); + assertFalse(instance.isValidInput("test", "http:///www.aspectsecurity.com", "URL", 100, false)); + assertFalse(instance.isValidInput("test", "http://www.aspect security.com", "URL", 100, false)); + assertTrue(instance.isValidInput("test", "078-05-1120", "SSN", 100, false)); + assertTrue(instance.isValidInput("test", "078 05 1120", "SSN", 100, false)); + assertTrue(instance.isValidInput("test", "078051120", "SSN", 100, false)); + assertFalse(instance.isValidInput("test", "987-65-4320", "SSN", 100, false)); + assertFalse(instance.isValidInput("test", "000-00-0000", "SSN", 100, false)); + assertFalse(instance.isValidInput("test", "(555) 555-5555", "SSN", 100, false)); + assertFalse(instance.isValidInput("test", "test", "SSN", 100, false)); + assertTrue(instance.isValidInput("test", "jeffWILLIAMS123", "HTTPParameterValue", 100, false)); + assertTrue(instance.isValidInput("test", "jeff .-/+=@_ WILLIAMS", "HTTPParameterValue", 100, false)); + // Removed per Issue 116 - The '*' character is valid as a parameter character +// assertFalse(instance.isValidInput("test", "jeff*WILLIAMS", "HTTPParameterValue", 100, false)); + assertFalse(instance.isValidInput("test", "jeff^WILLIAMS", "HTTPParameterValue", 100, false)); + assertFalse(instance.isValidInput("test", "jeff\\WILLIAMS", "HTTPParameterValue", 100, false)); + + assertTrue(instance.isValidInput("test", null, "Email", 100, true)); + assertFalse(instance.isValidInput("test", null, "Email", 100, false)); + + ValidationErrorList errors = new ValidationErrorList(); + + assertTrue(instance.isValidInput("test1", "jeff.williams@aspectsecurity.com", "Email", 100, false, errors)); + assertTrue(errors.size()==0); + assertFalse(instance.isValidInput("test2", "jeff.williams@@aspectsecurity.com", "Email", 100, false, errors)); + assertTrue(errors.size()==1); + assertFalse(instance.isValidInput("test3", "jeff.williams@aspectsecurity", "Email", 100, false, errors)); + assertTrue(errors.size()==2); + assertTrue(instance.isValidInput("test4", "jeff.wil'liams@aspectsecurity.com", "Email", 100, false, errors)); + assertTrue(errors.size()==2); + assertTrue(instance.isValidInput("test5", "jeff.wil''liams@aspectsecurity.com", "Email", 100, false, errors)); + assertTrue(errors.size()==2); + assertTrue(instance.isValidInput("test6", "123.168.100.234", "IPAddress", 100, false, errors)); + assertTrue(errors.size()==2); + assertTrue(instance.isValidInput("test7", "192.168.1.234", "IPAddress", 100, false, errors)); + assertTrue(errors.size()==2); + assertFalse(instance.isValidInput("test8", "..168.1.234", "IPAddress", 100, false, errors)); + assertTrue(errors.size()==3); + assertFalse(instance.isValidInput("test9", "10.x.1.234", "IPAddress", 100, false, errors)); + assertTrue(errors.size()==4); + assertTrue(instance.isValidInput("test10", "http://www.aspectsecurity.com", "URL", 100, false, errors)); + assertTrue(errors.size()==4); + assertFalse(instance.isValidInput("test11", "http:///www.aspectsecurity.com", "URL", 100, false, errors)); + assertTrue(errors.size()==5); + assertFalse(instance.isValidInput("test12", "http://www.aspect security.com", "URL", 100, false, errors)); + assertTrue(errors.size()==6); + assertTrue(instance.isValidInput("test13", "078-05-1120", "SSN", 100, false, errors)); + assertTrue(errors.size()==6); + assertTrue(instance.isValidInput("test14", "078 05 1120", "SSN", 100, false, errors)); + assertTrue(errors.size()==6); + assertTrue(instance.isValidInput("test15", "078051120", "SSN", 100, false, errors)); + assertTrue(errors.size()==6); + assertFalse(instance.isValidInput("test16", "987-65-4320", "SSN", 100, false, errors)); + assertTrue(errors.size()==7); + assertFalse(instance.isValidInput("test17", "000-00-0000", "SSN", 100, false, errors)); + assertTrue(errors.size()==8); + assertFalse(instance.isValidInput("test18", "(555) 555-5555", "SSN", 100, false, errors)); + assertTrue(errors.size()==9); + assertFalse(instance.isValidInput("test19", "test", "SSN", 100, false, errors)); + assertTrue(errors.size()==10); + assertTrue(instance.isValidInput("test20", "jeffWILLIAMS123", "HTTPParameterValue", 100, false, errors)); + assertTrue(errors.size()==10); + assertTrue(instance.isValidInput("test21", "jeff .-/+=@_ WILLIAMS", "HTTPParameterValue", 100, false, errors)); + assertTrue(errors.size()==10); + // Removed per Issue 116 - The '*' character is valid as a parameter character +// assertFalse(instance.isValidInput("test", "jeff*WILLIAMS", "HTTPParameterValue", 100, false)); + assertFalse(instance.isValidInput("test22", "jeff^WILLIAMS", "HTTPParameterValue", 100, false, errors)); + assertTrue(errors.size()==11); + assertFalse(instance.isValidInput("test23", "jeff\\WILLIAMS", "HTTPParameterValue", 100, false, errors)); + assertTrue(errors.size()==12); + + assertTrue(instance.isValidInput("test", null, "Email", 100, true, errors)); + assertFalse(instance.isValidInput("test", null, "Email", 100, false, errors)); + } + + public void testIsValidInteger() { + System.out.println("isValidInteger"); + Validator instance = ESAPI.validator(); + //testing negative range + assertFalse(instance.isValidInteger("test", "-4", 1, 10, false)); + assertTrue(instance.isValidInteger("test", "-4", -10, 10, false)); + //testing null value + assertTrue(instance.isValidInteger("test", null, -10, 10, true)); + assertFalse(instance.isValidInteger("test", null, -10, 10, false)); + //testing empty string + assertTrue(instance.isValidInteger("test", "", -10, 10, true)); + assertFalse(instance.isValidInteger("test", "", -10, 10, false)); + //testing improper range + assertFalse(instance.isValidInteger("test", "50", 10, -10, false)); + //testing non-integers + assertFalse(instance.isValidInteger("test", "4.3214", -10, 10, true)); + assertFalse(instance.isValidInteger("test", "-1.65", -10, 10, true)); + //other testing + assertTrue(instance.isValidInteger("test", "4", 1, 10, false)); + assertTrue(instance.isValidInteger("test", "400", 1, 10000, false)); + assertTrue(instance.isValidInteger("test", "400000000", 1, 400000000, false)); + assertFalse(instance.isValidInteger("test", "4000000000000", 1, 10000, false)); + assertFalse(instance.isValidInteger("test", "alsdkf", 10, 10000, false)); + assertFalse(instance.isValidInteger("test", "--10", 10, 10000, false)); + assertFalse(instance.isValidInteger("test", "14.1414234x", 10, 10000, false)); + assertFalse(instance.isValidInteger("test", "Infinity", 10, 10000, false)); + assertFalse(instance.isValidInteger("test", "-Infinity", 10, 10000, false)); + assertFalse(instance.isValidInteger("test", "NaN", 10, 10000, false)); + assertFalse(instance.isValidInteger("test", "-NaN", 10, 10000, false)); + assertFalse(instance.isValidInteger("test", "+NaN", 10, 10000, false)); + assertFalse(instance.isValidInteger("test", "1e-6", -999999999, 999999999, false)); + assertFalse(instance.isValidInteger("test", "-1e-6", -999999999, 999999999, false)); + + ValidationErrorList errors = new ValidationErrorList(); + //testing negative range + assertFalse(instance.isValidInteger("test1", "-4", 1, 10, false, errors)); + assertTrue(errors.size() == 1); + assertTrue(instance.isValidInteger("test2", "-4", -10, 10, false, errors)); + assertTrue(errors.size() == 1); + //testing null value + assertTrue(instance.isValidInteger("test3", null, -10, 10, true, errors)); + assertTrue(errors.size() == 1); + assertFalse(instance.isValidInteger("test4", null, -10, 10, false, errors)); + assertTrue(errors.size() == 2); + //testing empty string + assertTrue(instance.isValidInteger("test5", "", -10, 10, true, errors)); + assertTrue(errors.size() == 2); + assertFalse(instance.isValidInteger("test6", "", -10, 10, false, errors)); + assertTrue(errors.size() == 3); + //testing improper range + assertFalse(instance.isValidInteger("test7", "50", 10, -10, false, errors)); + assertTrue(errors.size() == 4); + //testing non-integers + assertFalse(instance.isValidInteger("test8", "4.3214", -10, 10, true, errors)); + assertTrue(errors.size() == 5); + assertFalse(instance.isValidInteger("test9", "-1.65", -10, 10, true, errors)); + assertTrue(errors.size() == 6); + //other testing + assertTrue(instance.isValidInteger("test10", "4", 1, 10, false, errors)); + assertTrue(errors.size() == 6); + assertTrue(instance.isValidInteger("test11", "400", 1, 10000, false, errors)); + assertTrue(errors.size() == 6); + assertTrue(instance.isValidInteger("test12", "400000000", 1, 400000000, false, errors)); + assertTrue(errors.size() == 6); + assertFalse(instance.isValidInteger("test13", "4000000000000", 1, 10000, false, errors)); + assertTrue(errors.size() == 7); + assertFalse(instance.isValidInteger("test14", "alsdkf", 10, 10000, false, errors)); + assertTrue(errors.size() == 8); + assertFalse(instance.isValidInteger("test15", "--10", 10, 10000, false, errors)); + assertTrue(errors.size() == 9); + assertFalse(instance.isValidInteger("test16", "14.1414234x", 10, 10000, false, errors)); + assertTrue(errors.size() == 10); + assertFalse(instance.isValidInteger("test17", "Infinity", 10, 10000, false, errors)); + assertTrue(errors.size() == 11); + assertFalse(instance.isValidInteger("test18", "-Infinity", 10, 10000, false, errors)); + assertTrue(errors.size() == 12); + assertFalse(instance.isValidInteger("test19", "NaN", 10, 10000, false, errors)); + assertTrue(errors.size() == 13); + assertFalse(instance.isValidInteger("test20", "-NaN", 10, 10000, false, errors)); + assertTrue(errors.size() == 14); + assertFalse(instance.isValidInteger("test21", "+NaN", 10, 10000, false, errors)); + assertTrue(errors.size() == 15); + assertFalse(instance.isValidInteger("test22", "1e-6", -999999999, 999999999, false, errors)); + assertTrue(errors.size() == 16); + assertFalse(instance.isValidInteger("test23", "-1e-6", -999999999, 999999999, false, errors)); + assertTrue(errors.size() == 17); + + } + + public void testIsValidListItem() { + System.out.println("isValidListItem"); + Validator instance = ESAPI.validator(); + List list = new ArrayList(); + list.add("one"); + list.add("two"); + assertTrue(instance.isValidListItem("test", "one", list)); + assertFalse(instance.isValidListItem("test", "three", list)); + + ValidationErrorList errors = new ValidationErrorList(); + assertTrue(instance.isValidListItem("test1", "one", list, errors)); + assertTrue(errors.size()==0); + assertFalse(instance.isValidListItem("test2", "three", list, errors)); + assertTrue(errors.size()==1); + } + + public void testIsValidNumber() { + System.out.println("isValidNumber"); + Validator instance = ESAPI.validator(); + //testing negative range + assertFalse(instance.isValidNumber("test", "-4", 1, 10, false)); + assertTrue(instance.isValidNumber("test", "-4", -10, 10, false)); + //testing null value + assertTrue(instance.isValidNumber("test", null, -10, 10, true)); + assertFalse(instance.isValidNumber("test", null, -10, 10, false)); + //testing empty string + assertTrue(instance.isValidNumber("test", "", -10, 10, true)); + assertFalse(instance.isValidNumber("test", "", -10, 10, false)); + //testing improper range + assertFalse(instance.isValidNumber("test", "5", 10, -10, false)); + //testing non-integers + assertTrue(instance.isValidNumber("test", "4.3214", -10, 10, true)); + assertTrue(instance.isValidNumber("test", "-1.65", -10, 10, true)); + //other testing + assertTrue(instance.isValidNumber("test", "4", 1, 10, false)); + assertTrue(instance.isValidNumber("test", "400", 1, 10000, false)); + assertTrue(instance.isValidNumber("test", "400000000", 1, 400000000, false)); + assertFalse(instance.isValidNumber("test", "4000000000000", 1, 10000, false)); + assertFalse(instance.isValidNumber("test", "alsdkf", 10, 10000, false)); + assertFalse(instance.isValidNumber("test", "--10", 10, 10000, false)); + assertFalse(instance.isValidNumber("test", "14.1414234x", 10, 10000, false)); + assertFalse(instance.isValidNumber("test", "Infinity", 10, 10000, false)); + assertFalse(instance.isValidNumber("test", "-Infinity", 10, 10000, false)); + assertFalse(instance.isValidNumber("test", "NaN", 10, 10000, false)); + assertFalse(instance.isValidNumber("test", "-NaN", 10, 10000, false)); + assertFalse(instance.isValidNumber("test", "+NaN", 10, 10000, false)); + assertTrue(instance.isValidNumber("test", "1e-6", -999999999, 999999999, false)); + assertTrue(instance.isValidNumber("test", "-1e-6", -999999999, 999999999, false)); + + ValidationErrorList errors = new ValidationErrorList(); + //testing negative range + assertFalse(instance.isValidNumber("test1", "-4", 1, 10, false, errors)); + assertTrue(errors.size()==1); + assertTrue(instance.isValidNumber("test2", "-4", -10, 10, false, errors)); + assertTrue(errors.size()==1); + //testing null value + assertTrue(instance.isValidNumber("test3", null, -10, 10, true, errors)); + assertTrue(errors.size()==1); + assertFalse(instance.isValidNumber("test4", null, -10, 10, false, errors)); + assertTrue(errors.size()==2); + //testing empty string + assertTrue(instance.isValidNumber("test5", "", -10, 10, true, errors)); + assertTrue(errors.size()==2); + assertFalse(instance.isValidNumber("test6", "", -10, 10, false, errors)); + assertTrue(errors.size()==3); + //testing improper range + assertFalse(instance.isValidNumber("test7", "5", 10, -10, false, errors)); + assertTrue(errors.size()==4); + //testing non-integers + assertTrue(instance.isValidNumber("test8", "4.3214", -10, 10, true, errors)); + assertTrue(errors.size()==4); + assertTrue(instance.isValidNumber("test9", "-1.65", -10, 10, true, errors)); + assertTrue(errors.size()==4); + //other testing + assertTrue(instance.isValidNumber("test10", "4", 1, 10, false, errors)); + assertTrue(errors.size()==4); + assertTrue(instance.isValidNumber("test11", "400", 1, 10000, false, errors)); + assertTrue(errors.size()==4); + assertTrue(instance.isValidNumber("test12", "400000000", 1, 400000000, false, errors)); + assertTrue(errors.size()==4); + assertFalse(instance.isValidNumber("test13", "4000000000000", 1, 10000, false, errors)); + assertTrue(errors.size()==5); + assertFalse(instance.isValidNumber("test14", "alsdkf", 10, 10000, false, errors)); + assertTrue(errors.size()==6); + assertFalse(instance.isValidNumber("test15", "--10", 10, 10000, false, errors)); + assertTrue(errors.size()==7); + assertFalse(instance.isValidNumber("test16", "14.1414234x", 10, 10000, false, errors)); + assertTrue(errors.size()==8); + assertFalse(instance.isValidNumber("test17", "Infinity", 10, 10000, false, errors)); + assertTrue(errors.size()==9); + assertFalse(instance.isValidNumber("test18", "-Infinity", 10, 10000, false, errors)); + assertTrue(errors.size()==10); + assertFalse(instance.isValidNumber("test19", "NaN", 10, 10000, false, errors)); + assertTrue(errors.size()==11); + assertFalse(instance.isValidNumber("test20", "-NaN", 10, 10000, false, errors)); + assertTrue(errors.size()==12); + assertFalse(instance.isValidNumber("test21", "+NaN", 10, 10000, false, errors)); + assertTrue(errors.size()==13); + assertTrue(instance.isValidNumber("test22", "1e-6", -999999999, 999999999, false, errors)); + assertTrue(errors.size()==13); + assertTrue(instance.isValidNumber("test23", "-1e-6", -999999999, 999999999, false, errors)); + assertTrue(errors.size()==13); + } + + public void testIsValidParameterSet() { + System.out.println("isValidParameterSet"); + Set requiredNames = new HashSet(); + requiredNames.add("p1"); + requiredNames.add("p2"); + requiredNames.add("p3"); + Set optionalNames = new HashSet(); + optionalNames.add("p4"); + optionalNames.add("p5"); + optionalNames.add("p6"); + MockHttpServletRequest request = new MockHttpServletRequest(); + MockHttpServletResponse response = new MockHttpServletResponse(); + request.addParameter("p1", "value"); + request.addParameter("p2", "value"); + request.addParameter("p3", "value"); + ESAPI.httpUtilities().setCurrentHTTP(request, response); + Validator instance = ESAPI.validator(); + ValidationErrorList errors = new ValidationErrorList(); + assertTrue(instance.isValidHTTPRequestParameterSet("HTTPParameters", request, requiredNames, optionalNames)); + assertTrue(instance.isValidHTTPRequestParameterSet("HTTPParameters", request, requiredNames, optionalNames,errors)); + assertTrue(errors.size()==0); + request.addParameter("p4", "value"); + request.addParameter("p5", "value"); + request.addParameter("p6", "value"); + assertTrue(instance.isValidHTTPRequestParameterSet("HTTPParameters", request, requiredNames, optionalNames)); + assertTrue(instance.isValidHTTPRequestParameterSet("HTTPParameters", request, requiredNames, optionalNames, errors)); + assertTrue(errors.size()==0); + request.removeParameter("p1"); + assertFalse(instance.isValidHTTPRequestParameterSet("HTTPParameters", request, requiredNames, optionalNames)); + assertFalse(instance.isValidHTTPRequestParameterSet("HTTPParameters", request, requiredNames, optionalNames, errors)); + assertTrue(errors.size() ==1); + } + + public void testIsValidPrintable() { + System.out.println("isValidPrintable"); + Validator instance = ESAPI.validator(); + assertTrue(instance.isValidPrintable("name", "abcDEF", 100, false)); + assertTrue(instance.isValidPrintable("name", "!@#R()*$;><()", 100, false)); + char[] chars = {0x60, (char) 0xFF, 0x10, 0x25}; + assertFalse(instance.isValidPrintable("name", chars, 100, false)); + assertFalse(instance.isValidPrintable("name", "%08", 100, false)); + + ValidationErrorList errors = new ValidationErrorList(); + assertTrue(instance.isValidPrintable("name1", "abcDEF", 100, false, errors)); + assertTrue(errors.size()==0); + assertTrue(instance.isValidPrintable("name2", "!@#R()*$;><()", 100, false, errors)); + assertTrue(errors.size()==0); + assertFalse(instance.isValidPrintable("name3", chars, 100, false, errors)); + assertTrue(errors.size()==1); + assertFalse(instance.isValidPrintable("name4", "%08", 100, false, errors)); + assertTrue(errors.size()==2); + + } + + public void testIsValidRedirectLocation() { + // isValidRedirectLocation(String, String, boolean) + } + + public void testIsValidSafeHTML() { + System.out.println("isValidSafeHTML"); + Validator instance = ESAPI.validator(); + + assertTrue(instance.isValidSafeHTML("test", "Jeff", 100, false)); + assertTrue(instance.isValidSafeHTML("test", "Aspect Security", 100, false)); + assertTrue(instance.isValidSafeHTML("test", "Test. ", 100, false)); + assertTrue(instance.isValidSafeHTML("test", "Test.
", 100, false)); + assertTrue(instance.isValidSafeHTML("test", "Test. alert(document.cookie)", 100, false)); + assertTrue(instance.isValidSafeHTML("test", "Test. alert(document.cookie)", 100, false)); + assertTrue(instance.isValidSafeHTML("test", "Test. alert(document.cookie)", 100, false)); + + // TODO: waiting for a way to validate text headed for an attribute for scripts + // This would be nice to catch, but just looks like text to AntiSamy + // assertFalse(instance.isValidSafeHTML("test", "\" onload=\"alert(document.cookie)\" ")); + ValidationErrorList errors = new ValidationErrorList(); + assertTrue(instance.isValidSafeHTML("test1", "Jeff", 100, false, errors)); + assertTrue(instance.isValidSafeHTML("test2", "Aspect Security", 100, false, errors)); + assertTrue(instance.isValidSafeHTML("test3", "Test. ", 100, false, errors)); + assertTrue(instance.isValidSafeHTML("test4", "Test.
", 100, false, errors)); + assertTrue(instance.isValidSafeHTML("test5", "Test. alert(document.cookie)", 100, false, errors)); + assertTrue(instance.isValidSafeHTML("test6", "Test. alert(document.cookie)", 100, false, errors)); + assertTrue(instance.isValidSafeHTML("test7", "Test. alert(document.cookie)", 100, false, errors)); + assertTrue(errors.size() == 0); + + } + + public void testSafeReadLine() { + System.out.println("safeReadLine"); + + byte[] bytes = null; + try { + bytes = "testString".getBytes(PREFERRED_ENCODING); + } + catch (UnsupportedEncodingException e1) { + fail(PREFERRED_ENCODING + " not a supported encoding?!?!!!"); + } + ByteArrayInputStream s = new ByteArrayInputStream(bytes); + Validator instance = ESAPI.validator(); + try { + instance.safeReadLine(s, -1); + fail(); + } + catch (ValidationException e) { + // Expected + } + s.reset(); + try { + instance.safeReadLine(s, 4); + fail(); + } + catch (ValidationException e) { + // Expected + } + s.reset(); + try { + String u = instance.safeReadLine(s, 20); + assertEquals("testString", u); + } + catch (ValidationException e) { + fail(); + } + + // This sub-test attempts to validate that BufferedReader.readLine() and safeReadLine() are similar in operation + // for the nominal case + try { + s.reset(); + InputStreamReader isr = new InputStreamReader(s); + BufferedReader br = new BufferedReader(isr); + String u = br.readLine(); + s.reset(); + String v = instance.safeReadLine(s, 20); + assertEquals(u, v); + } + catch (IOException e) { + fail(); + } + catch (ValidationException e) { + fail(); + } + } + + public void testIssue82_SafeString_Bad_Regex() { + Validator instance = ESAPI.validator(); + try { + instance.getValidInput("address", "55 main st. pasadena ak", "SafeString", 512, false); + } + catch (ValidationException e) { + fail(e.getLogMessage()); + } + } + + public void testGetParameterMap() { +//testing Validator.HTTPParameterName and Validator.HTTPParameterValue + MockHttpServletRequest request = new MockHttpServletRequest(); + SecurityWrapperRequest safeRequest = new SecurityWrapperRequest(request); +//an example of a parameter from displaytag, should pass + request.addParameter("d-49653-p", "pass"); + request.addParameter(""); + request.addParameter("f2", ""); + request.addParameter("f3", ""); + for (int i = 1; i <= 4; i++) { + assertTrue(safeRequest.getParameter("p" + i).equals(request.getParameter("p" + i))); + } + for (int i = 1; i <= 2; i++) { + boolean testResult = false; + try { + testResult = safeRequest.getParameter("f" + i).equals(request.getParameter("f" + i)); + } catch (NullPointerException npe) { + //the test is this block SHOULD fail. a NPE is an acceptable failure state + testResult = false; //redundant, just being descriptive here + } + assertFalse(testResult); + } + assertNull(safeRequest.getParameter("e1")); + + //This is revealing problems with Jeff's original SafeRequest + //mishandling of the AllowNull parameter. I'm adding a new Google code + //bug to track this. + // + //assertNotNull(safeRequest.getParameter("e1", false)); + } + + public void testGetCookies() { +//testing Validator.HTTPCookieName and Validator.HTTPCookieValue + MockHttpServletRequest request = new MockHttpServletRequest(); + SecurityWrapperRequest safeRequest = new SecurityWrapperRequest(request); +//should support a base64-encode value + request.setCookie("p1", "34=VJhjv7jiDu7tsdLrQQ2KcUwpfWUM2_mBae6UA8ttk4wBHdxxQ-1IBxyCOn3LWE08SDhpnBcJ7N5Vze48F2t8a1R_hXt7PX1BvgTM0pn-T4JkqGTm_tlmV4RmU3GT-dgn"); + request.setCookie("f1", "XSS"); + request.setCookie("load-balancing", "pass"); + request.setCookie("'bypass", "fail"); + Cookie[] cookies = safeRequest.getCookies(); + assertEquals(cookies[0].getValue(), request.getCookies()[0].getValue()); + assertEquals(cookies[1].getName(), request.getCookies()[2].getName()); + assertTrue(cookies.length == 2); + } + + public void testGetHeader() { +//testing Validator.HTTPHeaderValue + MockHttpServletRequest request = new MockHttpServletRequest(); + SecurityWrapperRequest safeRequest = new SecurityWrapperRequest(request); + request.addHeader("p1", "login"); + request.addHeader("f1", "XSS"); + request.addHeader("p2", generateStringOfLength(150)); + request.addHeader("f2", generateStringOfLength(151)); + assertEquals(safeRequest.getHeader("p1"), request.getHeader("p1")); + assertEquals(safeRequest.getHeader("p2"), request.getHeader("p2")); + assertFalse(safeRequest.getHeader("f1").equals(request.getHeader("f1"))); + assertFalse(safeRequest.getHeader("f2").equals(request.getHeader("f2"))); + assertNull(safeRequest.getHeader("p3")); + } + + public void testGetHeaderNames() { +//testing Validator.HTTPHeaderName + MockHttpServletRequest request = new MockHttpServletRequest(); + SecurityWrapperRequest safeRequest = new SecurityWrapperRequest(request); + request.addHeader("d-49653-p", "pass"); + request.addHeader(""); + assertFalse(safeRequest.getQueryString().equals(request.getQueryString())); + request.setQueryString("mail=bob@alice.com-passwd=johny"); + assertTrue(safeRequest.getQueryString().equals(request.getQueryString())); + request.setQueryString("mail=bob@alice.com-passwd=johny&special"); //= is missing! + assertFalse(safeRequest.getQueryString().equals(request.getQueryString())); + } + + public void testGetRequestURI() { +//testing Validator.HTTPURI + MockHttpServletRequest request = new MockHttpServletRequest(); + SecurityWrapperRequest safeRequest = new SecurityWrapperRequest(request); + try { + request.setRequestURI("/app/page.jsp"); + } catch (UnsupportedEncodingException ignored) { + } + assertEquals(safeRequest.getRequestURI(), request.getRequestURI()); + } + + private String generateStringOfLength(int length) { + StringBuilder longString = new StringBuilder(); + for (int i = 0; i < length; i++) { + longString.append("a"); + } + return longString.toString(); + } + + public void testGetContextPath() { + // Root Context Path ("") + assertTrue(ESAPI.validator().isValidInput("HTTPContextPath", "", "HTTPContextPath", 512, true)); + // Deployed Context Path ("/context") + assertTrue(ESAPI.validator().isValidInput("HTTPContextPath", "/context", "HTTPContextPath", 512, true)); + // Fail-case - URL Splitting + assertFalse(ESAPI.validator().isValidInput("HTTPContextPath", "/\\nGET http://evil.com", "HTTPContextPath", 512, true)); + } +} From 7b50eb7794827e4d08b0df89cd6b3055a84a3d2c Mon Sep 17 00:00:00 2001 From: kwwall Date: Wed, 30 Dec 2015 00:42:39 -0500 Subject: [PATCH 0016/1069] Specified minimum version # of 3.0 for Maven based on this error message: [ERROR] Project does not define required minimum version of Maven. [ERROR] Update the pom.xml to contain [ERROR] [ERROR] 3.0 [ERROR] --- pom.xml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pom.xml b/pom.xml index 89c9b250d..8ef61a99f 100644 --- a/pom.xml +++ b/pom.xml @@ -6,6 +6,10 @@ 2.1.1-SNAPSHOT jar + + 3.0 + + org.sonatype.oss oss-parent From ffe93c79ed386f70d4c0c06acfac5c41e19e1bac Mon Sep 17 00:00:00 2001 From: kwwall Date: Wed, 30 Dec 2015 00:45:03 -0500 Subject: [PATCH 0017/1069] Added protected CTORs taking (String userMessage) and (String userMessage, Throwable cause) arguments so that classes (e.g., such as IntrusionException) could sub-class these classes without compilation errors. --- .../errors/EnterpriseSecurityException.java | 28 +++++++++++++++++- .../EnterpriseSecurityRuntimeException.java | 29 ++++++++++++++++++- 2 files changed, 55 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/owasp/esapi/errors/EnterpriseSecurityException.java b/src/main/java/org/owasp/esapi/errors/EnterpriseSecurityException.java index a09df2c41..8c6d46114 100644 --- a/src/main/java/org/owasp/esapi/errors/EnterpriseSecurityException.java +++ b/src/main/java/org/owasp/esapi/errors/EnterpriseSecurityException.java @@ -48,12 +48,38 @@ public class EnterpriseSecurityException extends Exception { protected String logMessage = null; /** - * Instantiates a new security exception. + * Instantiates a new enterprise security exception. */ protected EnterpriseSecurityException() { // hidden } + /** + * Instantiates a new enterprise security exception with a user message. + * (Needed by anything which subclasses this.) + * + * @param userMessage Message displayed to user. + */ + protected EnterpriseSecurityException(String userMessage) { + // hidden + super(userMessage); + } + + /** + * Instantiates a new enterprise security exception with a user message + * and cause. (Needed by anything which subclasses this.) + * + * @param userMessage Message displayed to user. + * @param cause The cause (which is saved for later retrieval by the + * getCause() method). (A null value is permitted, and indicates that the + * cause is nonexistent or unknown.) + */ + protected EnterpriseSecurityException(String userMessage, Throwable cause) + { + // hidden + super(userMessage, cause); + } + /** * Creates a new instance of EnterpriseSecurityException. This exception is automatically logged, so that simply by * using this API, applications will generate an extensive security log. In addition, this exception is diff --git a/src/main/java/org/owasp/esapi/errors/EnterpriseSecurityRuntimeException.java b/src/main/java/org/owasp/esapi/errors/EnterpriseSecurityRuntimeException.java index 5f746f4b6..6dab2a54c 100644 --- a/src/main/java/org/owasp/esapi/errors/EnterpriseSecurityRuntimeException.java +++ b/src/main/java/org/owasp/esapi/errors/EnterpriseSecurityRuntimeException.java @@ -50,12 +50,39 @@ public class EnterpriseSecurityRuntimeException extends java.lang.RuntimeExcepti protected String logMessage = null; /** - * Instantiates a new security exception. + * Instantiates a new enterprise security runtime exception. */ protected EnterpriseSecurityRuntimeException() { // hidden } + /** + * Instantiates a new enterprise security runtime exception with a user + * message. (Needed by anything which subclasses this.) + * + * @param userMessage Message displayed to user. + */ + protected EnterpriseSecurityRuntimeException(String userMessage) { + super(userMessage); + } + + /** + * Instantiates a new enterprise security runtime exception with a + * user message and cause. (Needed by anything which subclasses this.) + * + * @param userMessage Message displayed to user. + * @param cause The cause (which is saved for later retrieval by the + * getCause() method). (A null value is permitted, and indicates that the + * cause is nonexistent or unknown.) + */ + protected EnterpriseSecurityRuntimeException(String userMessage, + Throwable cause) + { + // hidden + super(userMessage, cause); + } + + /** * Creates a new instance of EnterpriseSecurityException. This exception is automatically logged, so that simply by * using this API, applications will generate an extensive security log. In addition, this exception is From cd9d412fe51bf0a2826fdbc0abfdce23faf2fffe Mon Sep 17 00:00:00 2001 From: kwwall Date: Wed, 30 Dec 2015 00:50:27 -0500 Subject: [PATCH 0018/1069] To properly fix Google Issue #212 (i.e., GitHub issue #229), IntrusionException needed to subclass EnterpriseSecurityRuntimeException, not EnterpriseSecurityException. --- src/main/java/org/owasp/esapi/errors/IntrusionException.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/owasp/esapi/errors/IntrusionException.java b/src/main/java/org/owasp/esapi/errors/IntrusionException.java index 6332f297b..718f8a7c3 100644 --- a/src/main/java/org/owasp/esapi/errors/IntrusionException.java +++ b/src/main/java/org/owasp/esapi/errors/IntrusionException.java @@ -28,7 +28,7 @@ * * @author Jeff Williams (jeff.williams@aspectsecurity.com) */ -public class IntrusionException extends EnterpriseSecurityException { +public class IntrusionException extends EnterpriseSecurityRuntimeException { /** The Constant serialVersionUID. */ private static final long serialVersionUID = 1L; From c644f320d1d0f2bad11340845bf55d7747025200 Mon Sep 17 00:00:00 2001 From: kwwall Date: Thu, 31 Dec 2015 01:00:27 -0500 Subject: [PATCH 0019/1069] Changes being committed: Modified files: pom.xml Updated to newer version of dependency-check and some 3rd party jars have been updated. src/main/java/org/owasp/esapi/HTTPUtilities.java Javadoc updated. src/main/java/org/owasp/esapi/SecurityConfiguration.java Changed to extend the new EsapiPropertyLoader class. Deprecated many methods in favor of the new API to that the interface is not so bloated. src/main/java/org/owasp/esapi/reference/DefaultHTTPUtilities.java Minor changes to import statements and sendRedirect(HttpServletResponse,String) method. src/test/java/org/owasp/esapi/SecurityConfigurationWrapper.java Implements the 4 new methods (getIntProp(), getByteArrayProp(), etc.) from EsapiPropertyLoader interface. src/test/java/org/owasp/esapi/reference/ValidatorTest.java Cleanup of import statements. src/test/java/org/owasp/esapi/reference/HTTPUtilitiesTest.java Cleanup of import statements. ======================== New files: documentation/ESAPI-configuration-user-guide.md User documentation for new configuration features. src/main/java/org/owasp/esapi/configuration/AbstractPrioritizedPropertyLoader.java src/main/java/org/owasp/esapi/configuration/EsapiPropertyLoader.java src/main/java/org/owasp/esapi/configuration/EsapiPropertyLoaderFactory.java src/main/java/org/owasp/esapi/configuration/EsapiPropertyManager.java src/main/java/org/owasp/esapi/configuration/StandardEsapiPropertyLoader.java src/main/java/org/owasp/esapi/configuration/XmlEsapiPropertyLoader.java src/main/java/org/owasp/esapi/configuration/consts/EsapiConfiguration.java src/main/java/org/owasp/esapi/configuration/consts/EsapiConfigurationType.java src/main/resources/ESAPI-properties.xsd src/test/java/org/owasp/esapi/configuration/EsapiPropertyManagerTest.java src/test/java/org/owasp/esapi/configuration/StandardEsapiPropertyLoaderTest.java src/test/java/org/owasp/esapi/configuration/XmlEsapiPropertyLoaderTest.java src/test/resources/ESAPI-properties.xsd src/test/resources/esapi/ESAPI-test-2.properties src/test/resources/esapi/ESAPI-test-2.xml src/test/resources/esapi/ESAPI-test-invalid-content.xml src/test/resources/esapi/ESAPI-test.properties src/test/resources/esapi/ESAPI-test.xml Files used in implementing or testing new configuration properties. --- .../ESAPI-configuration-user-guide.md | 138 ++++++++ pom.xml | 10 +- .../java/org/owasp/esapi/HTTPUtilities.java | 1 - .../owasp/esapi/SecurityConfiguration.java | 132 ++++++- .../AbstractPrioritizedPropertyLoader.java | 62 ++++ .../configuration/EsapiPropertyLoader.java | 43 +++ .../EsapiPropertyLoaderFactory.java | 36 ++ .../configuration/EsapiPropertyManager.java | 99 ++++++ .../StandardEsapiPropertyLoader.java | 105 ++++++ .../configuration/XmlEsapiPropertyLoader.java | 134 +++++++ .../consts/EsapiConfiguration.java | 33 ++ .../consts/EsapiConfigurationType.java | 19 + .../esapi/reference/DefaultHTTPUtilities.java | 5 +- .../DefaultSecurityConfiguration.java | 130 +++++-- src/main/resources/ESAPI-properties.xsd | 17 + .../esapi/SecurityConfigurationWrapper.java | 22 ++ .../EsapiPropertyManagerTest.java | 327 ++++++++++++++++++ .../StandardEsapiPropertyLoaderTest.java | 268 ++++++++++++++ .../XmlEsapiPropertyLoaderTest.java | 269 ++++++++++++++ .../esapi/reference/HTTPUtilitiesTest.java | 14 +- .../owasp/esapi/reference/ValidatorTest.java | 17 +- src/test/resources/ESAPI-properties.xsd | 17 + .../resources/esapi/ESAPI-test-2.properties | 5 + src/test/resources/esapi/ESAPI-test-2.xml | 6 + .../esapi/ESAPI-test-invalid-content.xml | 7 + .../resources/esapi/ESAPI-test.properties | 8 + src/test/resources/esapi/ESAPI-test.xml | 10 + 27 files changed, 1875 insertions(+), 59 deletions(-) create mode 100644 documentation/ESAPI-configuration-user-guide.md create mode 100644 src/main/java/org/owasp/esapi/configuration/AbstractPrioritizedPropertyLoader.java create mode 100644 src/main/java/org/owasp/esapi/configuration/EsapiPropertyLoader.java create mode 100644 src/main/java/org/owasp/esapi/configuration/EsapiPropertyLoaderFactory.java create mode 100644 src/main/java/org/owasp/esapi/configuration/EsapiPropertyManager.java create mode 100644 src/main/java/org/owasp/esapi/configuration/StandardEsapiPropertyLoader.java create mode 100644 src/main/java/org/owasp/esapi/configuration/XmlEsapiPropertyLoader.java create mode 100644 src/main/java/org/owasp/esapi/configuration/consts/EsapiConfiguration.java create mode 100644 src/main/java/org/owasp/esapi/configuration/consts/EsapiConfigurationType.java create mode 100644 src/main/resources/ESAPI-properties.xsd create mode 100644 src/test/java/org/owasp/esapi/configuration/EsapiPropertyManagerTest.java create mode 100644 src/test/java/org/owasp/esapi/configuration/StandardEsapiPropertyLoaderTest.java create mode 100644 src/test/java/org/owasp/esapi/configuration/XmlEsapiPropertyLoaderTest.java create mode 100644 src/test/resources/ESAPI-properties.xsd create mode 100644 src/test/resources/esapi/ESAPI-test-2.properties create mode 100644 src/test/resources/esapi/ESAPI-test-2.xml create mode 100644 src/test/resources/esapi/ESAPI-test-invalid-content.xml create mode 100644 src/test/resources/esapi/ESAPI-test.properties create mode 100644 src/test/resources/esapi/ESAPI-test.xml diff --git a/documentation/ESAPI-configuration-user-guide.md b/documentation/ESAPI-configuration-user-guide.md new file mode 100644 index 000000000..72694bb05 --- /dev/null +++ b/documentation/ESAPI-configuration-user-guide.md @@ -0,0 +1,138 @@ +# ESAPI security configuration API enhancements - user guide + +## Motivation +High number of open Google Issues against security configuration component +highlighted problem with ESAPI configuration. Moreover, the rules for how and +where the ESAPI.properties file is found are overly complicated making questions +about it one of the most frequently asked questions. + +The ESAPI interface for its configuration (SecurityConfiguration) is overly +complicated; it has a 'getter' method specific to almost every ESAPI +configuration property. This complication leads to a unduly intricate, +non-modular reference implementation (DefaultSecurityConfiguration) that makes +it difficult to extend in terms of new functionality; e.g., when desiring to +introduce a new ESAPI property name in ESAPI.properties. + +A new, simpler security configuration interface and implementation is needed. +Such an implementation would not only be useful for ESAPI 2.x, but could very +well be used to build the configurator needed by ESAPI 3. + +This document describes following changes to ESAPI security API: + +1. API simplification +2. XML configuration support. +3. Multiple configuration files support. + +## API simplification + +New interface is introduced: EsapiPropertyLoader, which contains four general +methods for extraction of configuration properties: + +``` +public int getIntProp(String propertyName) throws ConfigurationException; +public byte[] getByteArrayProp(String propertyName) throws ConfigurationException; +public Boolean getBooleanProp(String propertyName) throws ConfigurationException; +public String getStringProp(String propertyName) throws ConfigurationException; +``` + +SecurityConfiguration interface is extended with this new contract. Old methods +have been deprecated as a result, in favor of these new methods. (TBD how long +until these deprecated methods are removed, but it will be a minumum of 2 years +or 1 major release [e.g., 3.x], whichever comes first. Also, we may not +necessarily remove all of them at once, depending on community feedback.) + +DefaultSecurityConfiguration implements the new contract. New contract methods implementations work as described in +'Multiple configuration files support' paragraph. + +## Multiple configuration files support + +EsapiPropertyManager is the new implementation for getting properties, which uses prioritized property loaders (each one associated with a specific configuration file). This allows to have multiple configuration files existing with priority connected to each one. At this moment, there +are two configuration files possible to use, the path to them is set through following Java +system properties: + +* org.owasp.esapi.opsteam = (higher priority config) +* org.owasp.esapi.devteam = (lower priority config) + +The first is intended for deployment by an operations team responsible for +enforcing security for configuration management enterprise-wide. The intent here +is to allow this operations team to enforce global / company-wide policies such +as the minimum encryption key size or permitted cryptographic algorithms. + +The second is intended for deployment by development teams and is more likely to +be useful and be tailored for each individual project based on project needs. + +If an ESAPI property is set via the configuration file identified by +org.owasp.esapi.opsteam then that property takes precedence over any property +set by the configuration file identified by org.owasp.esapi.devteam system +property. (A warning message will be logged if a property defined in the higher +priority configuration file is also defined in the configuration file of lower +priority.) + +The DefaultSecurityConfiguration class now uses this mechanism through the new +API for retrieving properties. + +It is not mandatory to have both files configured or even any of them for +DefaultSecurityConfiguration to work property. It can still use the single +ESAPI.properties to search for a property. In case of any of the configurations +or both of the existing, ESAPI.properties has LOWEST priority, so it will be +searched as last. + +### Example properties extraction through DefaultSecurityConfiguration + +```java +ESAPI.securityConfiguration().getBooleanProp("propertyXXX"); +``` + +where "propertyXXX" is some property name relevant to ESAPI (and +in this case, one that would hold a boolean value). See ESAPI.properties +for a list of current property names known to ESAPI. + +In above example, following happens: + +1. org.owasp.esapi.opsteam configuration is used to get propertyXXX and return it as boolean. +2. If (1) fails to find property, org.owasp.esapi.devteam is used to get propertyXXX and return it as boolean. +3. If (2) fails to find property, ESAPI.properties is used to get propertyXXX and return it as boolean. +4. If (3) fails to find property, unchecked ConfigurationException will be thrown. + +A ConfigurationException will be also thrown if propertyXXX was found in one +of the configurations, but it is impossible to convert it to boolean value. + +## XML configuration support + +XML configuration storage is supported. Both org.owasp.esapi.opsteam and +org.owasp.esapi.devteam can be XML files, but they must comply to the +following XSD schema: + +```xml + + + + + + + + + + + + + + + + + +``` + +### XML configuration example: +``` + + + test_string_property + 5 + invalid int + true + yes + no + invalid boolean + +``` diff --git a/pom.xml b/pom.xml index 8ef61a99f..79f870d2f 100644 --- a/pom.xml +++ b/pom.xml @@ -187,6 +187,14 @@ antisamy 1.5.3 + + + org.apache.xmlgraphics + batik-css + 1.8 + + + @@ -329,7 +337,7 @@ org.owasp dependency-check-maven - 1.1.4 + 1.2.11 false diff --git a/src/main/java/org/owasp/esapi/HTTPUtilities.java b/src/main/java/org/owasp/esapi/HTTPUtilities.java index 77fcd8284..6402796c8 100644 --- a/src/main/java/org/owasp/esapi/HTTPUtilities.java +++ b/src/main/java/org/owasp/esapi/HTTPUtilities.java @@ -445,7 +445,6 @@ public interface HTTPUtilities * @param response * @param location the URL to forward to, including parameters * @throws AccessControlException - * @throws ServletException * @throws IOException */ void sendRedirect(HttpServletResponse response, String location) throws AccessControlException, IOException; diff --git a/src/main/java/org/owasp/esapi/SecurityConfiguration.java b/src/main/java/org/owasp/esapi/SecurityConfiguration.java index 9c54acb9c..9d37c2580 100644 --- a/src/main/java/org/owasp/esapi/SecurityConfiguration.java +++ b/src/main/java/org/owasp/esapi/SecurityConfiguration.java @@ -16,6 +16,8 @@ */ package org.owasp.esapi; +import org.owasp.esapi.configuration.EsapiPropertyLoader; + import java.io.File; import java.io.IOException; import java.io.InputStream; @@ -48,53 +50,71 @@ * href="http://www.aspectsecurity.com">Aspect Security * @since June 1, 2007 */ -public interface SecurityConfiguration { +public interface SecurityConfiguration extends EsapiPropertyLoader { /** * Gets the application name, used for logging * * @return the name of the current application + * @deprecated Use SecurityConfiguration.getStringProp("appropriate_esapi_prop_name") instead. */ + @Deprecated public String getApplicationName(); /** * Returns the fully qualified classname of the ESAPI Logging implementation. + * @deprecated Use SecurityConfiguration.getStringProp("appropriate_esapi_prop_name") instead. */ + @Deprecated public String getLogImplementation(); /** * Returns the fully qualified classname of the ESAPI Authentication implementation. + * @deprecated Use SecurityConfiguration.getStringProp("appropriate_esapi_prop_name") instead. */ + @Deprecated public String getAuthenticationImplementation(); /** * Returns the fully qualified classname of the ESAPI Encoder implementation. + * @deprecated Use SecurityConfiguration.getStringProp("appropriate_esapi_prop_name") instead. */ + @Deprecated public String getEncoderImplementation(); /** * Returns the fully qualified classname of the ESAPI Access Control implementation. + * @deprecated Use SecurityConfiguration.getStringProp("appropriate_esapi_prop_name") instead. */ + @Deprecated public String getAccessControlImplementation(); /** * Returns the fully qualified classname of the ESAPI Intrusion Detection implementation. + * @deprecated Use SecurityConfiguration.getStringProp("appropriate_esapi_prop_name") instead. */ + @Deprecated public String getIntrusionDetectionImplementation(); /** * Returns the fully qualified classname of the ESAPI Randomizer implementation. + * @deprecated Use SecurityConfiguration.getStringProp("appropriate_esapi_prop_name") instead. */ + @Deprecated public String getRandomizerImplementation(); /** * Returns the fully qualified classname of the ESAPI Encryption implementation. + * @deprecated Use SecurityConfiguration.getStringProp("appropriate_esapi_prop_name") instead. */ + @Deprecated public String getEncryptionImplementation(); /** * Returns the fully qualified classname of the ESAPI Validation implementation. + * @deprecated Use SecurityConfiguration.getStringProp("appropriate_esapi_prop_name") instead. */ + @Deprecated public String getValidationImplementation(); /** @@ -112,17 +132,23 @@ public interface SecurityConfiguration { * * @return True if lenient dates are accepted; false otherwise. * @see java.text.DateFormat#setLenient(boolean) + * @deprecated Use SecurityConfiguration.getBooleanProp("appropriate_esapi_prop_name") instead. */ + @Deprecated public boolean getLenientDatesAccepted(); /** * Returns the fully qualified classname of the ESAPI OS Execution implementation. + * @deprecated Use SecurityConfiguration.getStringProp("appropriate_esapi_prop_name") instead. */ + @Deprecated public String getExecutorImplementation(); /** * Returns the fully qualified classname of the ESAPI HTTPUtilities implementation. + * @deprecated Use SecurityConfiguration.getStringProp("appropriate_esapi_prop_name") instead. */ + @Deprecated public String getHTTPUtilitiesImplementation(); /** @@ -130,7 +156,9 @@ public interface SecurityConfiguration { * of data that need to be protected by your application. * * @return the current master key + * @deprecated Use SecurityConfiguration.getByteArrayProp("appropriate_esapi_prop_name") instead. */ + @Deprecated public byte[] getMasterKey(); /** @@ -149,7 +177,9 @@ public interface SecurityConfiguration { * Gets the key length to use in cryptographic operations declared in the ESAPI properties file. * * @return the key length. + * @deprecated Use SecurityConfiguration.getIntProp("appropriate_esapi_prop_name") instead. */ + @Deprecated public int getEncryptionKeyLength(); /** @@ -157,7 +187,9 @@ public interface SecurityConfiguration { * where a salt is needed. * * @return the current master salt + * @deprecated Use SecurityConfiguration.getByteArrayProp("appropriate_esapi_prop_name") instead. */ + @Deprecated public byte[] getMasterSalt(); /** @@ -178,21 +210,27 @@ public interface SecurityConfiguration { * Gets the maximum allowed file upload size. * * @return the current allowed file upload size + * @deprecated Use SecurityConfiguration.getIntProp("appropriate_esapi_prop_name") instead. */ + @Deprecated public int getAllowedFileUploadSize(); /** * Gets the name of the password parameter used during user authentication. * * @return the name of the password parameter + * @deprecated Use SecurityConfiguration.getStringProp("appropriate_esapi_prop_name") instead. */ + @Deprecated public String getPasswordParameterName(); /** * Gets the name of the username parameter used during user authentication. * * @return the name of the username parameter + * @deprecated Use SecurityConfiguration.getStringProp("appropriate_esapi_prop_name") instead. */ + @Deprecated public String getUsernameParameterName(); /** @@ -202,7 +240,9 @@ public interface SecurityConfiguration { * and padding schemes. * * @return the current encryption algorithm + * @deprecated Use SecurityConfiguration.getStringProp("appropriate_esapi_prop_name") instead. */ + @Deprecated public String getEncryptionAlgorithm(); /** @@ -244,7 +284,9 @@ public interface SecurityConfiguration { * not the reference JCE implementation of "SunJCE"), and therefore it * should be avoided. * @return The cipher transformation. + * @deprecated Use SecurityConfiguration.getStringProp("appropriate_esapi_prop_name") instead. */ + @Deprecated public String getCipherTransformation(); /** @@ -269,7 +311,9 @@ public interface SecurityConfiguration { * completed the encryption / decryption with the new cipher * transformation. * @deprecated To be replaced by new class in ESAPI 2.1, but here if you need it - * until then. Details of replacement forthcoming to ESAPI-Dev list. + * until then. Details of replacement forthcoming to ESAPI-Dev + * list. Most likely to be replaced by a new public CTOR for + * JavaEncryptor that takes a list of properties to override. */ @Deprecated public String setCipherTransformation(String cipherXform); @@ -289,7 +333,9 @@ public interface SecurityConfiguration { * changed. * @return The property {@code Encryptor.PreferredJCEProvider} is returned. * @see org.owasp.esapi.crypto.SecurityProviderLoader + * @deprecated Use SecurityConfiguration.getStringProp("appropriate_esapi_prop_name") instead. */ + @Deprecated public String getPreferredJCEProvider(); // TODO - DISCUSS: Where should this web page (below) go? Maybe with the Javadoc? But where? @@ -306,7 +352,9 @@ public interface SecurityConfiguration { * "Why Is OWASP Changing ESAPI Encryption?". *

* @return {@code true} if a you want a MAC to be used, otherwise {@code false}. + * @deprecated Use SecurityConfiguration.getBooleanProp("appropriate_esapi_prop_name") instead. */ + @Deprecated public boolean useMACforCipherText(); /** @@ -322,7 +370,9 @@ public interface SecurityConfiguration { *

* @return True if it is OK to overwrite the {@code PlainText} objects * after encrypting, false otherwise. + * @deprecated Use SecurityConfiguration.getBooleanProp("appropriate_esapi_prop_name") instead. */ + @Deprecated public boolean overwritePlainText(); /** @@ -335,14 +385,23 @@ public interface SecurityConfiguration { * @return A string specifying the IV type. Should be "random" or "fixed". * * @see #getFixedIV() + * @deprecated Use SecurityConfiguration.getStringProp("appropriate_esapi_prop_name") instead. */ + @Deprecated public String getIVType(); /** * If a "fixed" (i.e., static) Initialization Vector (IV) is to be used, * this will return the IV value as a hex-encoded string. * @return The fixed IV as a hex-encoded string. + * @deprecated Short term: use SecurityConfiguration.getByteArrayProp("appropriate_esapi_prop_name") + * instead. Longer term: There will be a more general method in JavaEncryptor + * to explicitly set an IV. This whole concept of a single fixed IV has + * always been a kludge at best, as a concession to those who have used + * a single fixed IV in the past. It's time to put it to death + * as it was never intended for production in the first place. */ + @Deprecated public String getFixedIV(); /** @@ -366,7 +425,7 @@ public interface SecurityConfiguration { /** * Return {@code List} of strings of additional cipher modes that are * permitted (i.e., in addition to those returned by - * {@link #getPreferredCipherModes()}) to be used for encryption and + * {@link #getCombinedCipherModes()}) to be used for encryption and * decryption operations. *

* The list is taken from the comma-separated list of cipher modes specified @@ -377,7 +436,7 @@ public interface SecurityConfiguration { * was specified in {@code ESAPI.properties}; otherwise the empty list is * returned. * - * @see #getPreferredCipherModes() + * @see #getCombinedCipherModes() */ public List getAdditionalAllowedCipherModes(); @@ -385,14 +444,18 @@ public interface SecurityConfiguration { * Gets the hashing algorithm used by ESAPI to hash data. * * @return the current hashing algorithm + * @deprecated Use SecurityConfiguration.getStringProp("appropriate_esapi_prop_name") instead. */ + @Deprecated public String getHashAlgorithm(); /** * Gets the hash iterations used by ESAPI to hash data. * * @return the current hashing algorithm + * @deprecated Use SecurityConfiguration.getIntProp("appropriate_esapi_prop_name") instead. */ + @Deprecated public int getHashIterations(); /** @@ -400,7 +463,9 @@ public interface SecurityConfiguration { * Key Derivation Function (KDF). * * @return The KDF PRF algorithm name. + * @deprecated Use SecurityConfiguration.getStringProp("appropriate_esapi_prop_name") instead. */ + @Deprecated public String getKDFPseudoRandomFunction(); /** @@ -413,21 +478,27 @@ public interface SecurityConfiguration { * getResponseContentType(). * * @return the current character encoding scheme + * @deprecated Use SecurityConfiguration.getStringProp("appropriate_esapi_prop_name") instead. */ + @Deprecated public String getCharacterEncoding(); /** * Return true if multiple encoding is allowed * * @return whether multiple encoding is allowed when canonicalizing data + * @deprecated Use SecurityConfiguration.getBooleanProp("appropriate_esapi_prop_name") instead. */ + @Deprecated public boolean getAllowMultipleEncoding(); /** * Return true if mixed encoding is allowed * * @return whether mixed encoding is allowed when canonicalizing data + * @deprecated Use SecurityConfiguration.getBooleanProp("appropriate_esapi_prop_name") instead. */ + @Deprecated public boolean getAllowMixedEncoding(); /** @@ -441,21 +512,27 @@ public interface SecurityConfiguration { * Gets the digital signature algorithm used by ESAPI to generate and verify signatures. * * @return the current digital signature algorithm + * @deprecated Use SecurityConfiguration.getStringProp("appropriate_esapi_prop_name") instead. */ + @Deprecated public String getDigitalSignatureAlgorithm(); /** * Gets the digital signature key length used by ESAPI to generate and verify signatures. * * @return the current digital signature key length + * @deprecated Use SecurityConfiguration.getIntProp("appropriate_esapi_prop_name") instead. */ + @Deprecated public int getDigitalSignatureKeyLength(); /** * Gets the random number generation algorithm used to generate random numbers where needed. * * @return the current random number generation algorithm + * @deprecated Use SecurityConfiguration.getStringProp("appropriate_esapi_prop_name") instead. */ + @Deprecated public String getRandomAlgorithm(); /** @@ -463,7 +540,9 @@ public interface SecurityConfiguration { * many failures are detected within the alloted time period, the user's account will be locked. * * @return the number of failed login attempts that cause an account to be locked + * @deprecated Use SecurityConfiguration.getIntProp("appropriate_esapi_prop_name") instead. */ + @Deprecated public int getAllowedLoginAttempts(); /** @@ -472,14 +551,18 @@ public interface SecurityConfiguration { * when they change their password. * * @return the number of old hashed passwords to retain + * @deprecated Use SecurityConfiguration.getIntProp("appropriate_esapi_prop_name") instead. */ + @Deprecated public int getMaxOldPasswordHashes(); /** * Allows for complete disabling of all intrusion detection mechanisms * * @return true if intrusion detection should be disabled + * @deprecated Use SecurityConfiguration.getBooleanProp("appropriate_esapi_prop_name") instead. */ + @Deprecated public boolean getDisableIntrusionDetection(); /** @@ -500,28 +583,38 @@ public interface SecurityConfiguration { public File getResourceFile( String filename ); /** - * Forces new cookies to have HttpOnly flag set. + * Returns true if session cookies are required to have HttpOnly flag set. + * @deprecated Use SecurityConfiguration.getBooleanProp("appropriate_esapi_prop_name") instead. */ + @Deprecated public boolean getForceHttpOnlySession() ; /** - * Forces session cookies to have Secure flag set. + * Returns true if session cookies are required to have Secure flag set. + * @deprecated Use SecurityConfiguration.getBooleanProp("appropriate_esapi_prop_name") instead. */ + @Deprecated public boolean getForceSecureSession() ; /** - * Forces new cookies to have HttpOnly flag set. + * Returns true if new cookies are required to have HttpOnly flag set. + * @deprecated Use SecurityConfiguration.getBooleanProp("appropriate_esapi_prop_name") instead. */ + @Deprecated public boolean getForceHttpOnlyCookies() ; /** - * Forces new cookies to have Secure flag set. + * Returns true if new cookies are required to have Secure flag set. + * @deprecated Use SecurityConfiguration.getBooleanProp("appropriate_esapi_prop_name") instead. */ + @Deprecated public boolean getForceSecureCookies() ; /** * Returns the maximum allowable HTTP header size. + * @deprecated Use SecurityConfiguration.getIntProp("appropriate_esapi_prop_name") instead. */ + @Deprecated public int getMaxHttpHeaderSize() ; /** @@ -548,7 +641,9 @@ public interface SecurityConfiguration { * getCharacterEncoding(). * * @return The current content-type set for responses. + * @deprecated Use SecurityConfiguration.getStringProp("appropriate_esapi_prop_name") instead. */ + @Deprecated public String getResponseContentType(); /** @@ -556,14 +651,17 @@ public interface SecurityConfiguration { * likely "JSESSIONID" though this can be overridden. * * @return The name of the session identifier, like "JSESSIONID" + * @deprecated Use SecurityConfiguration.getStringProp("appropriate_esapi_prop_name") instead. */ + @Deprecated public String getHttpSessionIdName(); /** * Gets the length of the time to live window for remember me tokens (in milliseconds). * - * @return The time to live length for generated remember me tokens. + * @return The time to live length for generated "remember me" tokens. */ + // OPEN ISSUE: Can we replace w/ SecurityConfiguration.getIntProp("appropriate_esapi_prop_name") instead? public long getRememberTokenDuration(); @@ -573,7 +671,9 @@ public interface SecurityConfiguration { * function that enables a session to continue after reauthentication. * * @return The session idle timeout length. + * @deprecated Use SecurityConfiguration.getIntProp("appropriate_esapi_prop_name") instead. */ + @Deprecated public int getSessionIdleTimeoutLength(); /** @@ -582,7 +682,9 @@ public interface SecurityConfiguration { * provide a reauthenticate function that enables a session to continue after reauthentication. * * @return The session absolute timeout length. + * @deprecated Use SecurityConfiguration.getIntProp("appropriate_esapi_prop_name") instead. */ + @Deprecated public int getSessionAbsoluteTimeoutLength(); @@ -590,7 +692,9 @@ public interface SecurityConfiguration { * Returns whether HTML entity encoding should be applied to log entries. * * @return True if log entries are to be HTML Entity encoded. False otherwise. + * @deprecated Use SecurityConfiguration.getBooleanProp("appropriate_esapi_prop_name") instead. */ + @Deprecated public boolean getLogEncodingRequired(); /** @@ -598,7 +702,9 @@ public interface SecurityConfiguration { * single-server/single-app environments. * * @return True if ESAPI should log the application name, False otherwise + * @deprecated Use SecurityConfiguration.getBooleanProp("appropriate_esapi_prop_name") instead. */ + @Deprecated public boolean getLogApplicationName(); /** @@ -606,13 +712,17 @@ public interface SecurityConfiguration { * single-server environments. * * @return True if ESAPI should log the server IP and port, False otherwise + * @deprecated Use SecurityConfiguration.getBooleanProp("appropriate_esapi_prop_name") instead. */ + @Deprecated public boolean getLogServerIP(); /** * Returns the current log level. * @return An integer representing the current log level. + * @deprecated Use SecurityConfiguration.getIntProp("appropriate_esapi_prop_name") instead. */ + @Deprecated public int getLogLevel(); /** @@ -620,7 +730,9 @@ public interface SecurityConfiguration { * if it is not specified. * * @return the log file name defined in the properties file. + * @deprecated Use SecurityConfiguration.getStringProp("appropriate_esapi_prop_name") instead. */ + @Deprecated public String getLogFileName(); /** @@ -628,7 +740,9 @@ public interface SecurityConfiguration { * if it is not specified. Once the log hits this file size, it will roll over into a new log. * * @return the maximum size of a single log file (in bytes). + * @deprecated Use SecurityConfiguration.getIntProp("appropriate_esapi_prop_name") instead. */ + @Deprecated public int getMaxLogFileSize(); /** diff --git a/src/main/java/org/owasp/esapi/configuration/AbstractPrioritizedPropertyLoader.java b/src/main/java/org/owasp/esapi/configuration/AbstractPrioritizedPropertyLoader.java new file mode 100644 index 000000000..75e6497c7 --- /dev/null +++ b/src/main/java/org/owasp/esapi/configuration/AbstractPrioritizedPropertyLoader.java @@ -0,0 +1,62 @@ +package org.owasp.esapi.configuration; + + +import java.io.File; +import java.util.Properties; + +public abstract class AbstractPrioritizedPropertyLoader implements EsapiPropertyLoader, + Comparable { + + protected final String filename; + protected Properties properties; + + private final int priority; + + public AbstractPrioritizedPropertyLoader(String filename, int priority) { + this.priority = priority; + this.filename = filename; + initProperties(); + } + + /** + * Get priority of this property loader. If two and more loaders can return value for the same property key, + * the one with highest priority will be chosen. + * @return priority of this property loader + */ + public int priority() { + return priority; + } + + @Override + public int compareTo(AbstractPrioritizedPropertyLoader compared) { + if (this.priority > compared.priority()) { + return 1; + } else if (this.priority < compared.priority()) { + return -1; + } + return 0; + } + + public String name() { + return filename; + } + + /** + * Initializes properties object and fills it with data from configuration file. + */ + protected void initProperties() { + properties = new Properties(); + File file = new File(filename); + if (file.exists() && file.isFile()) { + loadPropertiesFromFile(file); + } else { + System.err.println("Configuration file " + filename + " does not exist"); + } + } + + /** + * Method that loads the data from configuration file to properties object. + * @param file + */ + protected abstract void loadPropertiesFromFile(File file); +} diff --git a/src/main/java/org/owasp/esapi/configuration/EsapiPropertyLoader.java b/src/main/java/org/owasp/esapi/configuration/EsapiPropertyLoader.java new file mode 100644 index 000000000..aafa89012 --- /dev/null +++ b/src/main/java/org/owasp/esapi/configuration/EsapiPropertyLoader.java @@ -0,0 +1,43 @@ +package org.owasp.esapi.configuration; + +import org.owasp.esapi.errors.ConfigurationException; + +/** + * Generic interface for loading security configuration properties. + */ +public interface EsapiPropertyLoader { + + /** + * Get any int type property from security configuration. + * + * @return property value. + * @throws ConfigurationException when property does not exist in configuration or has incorrect type. + */ + public int getIntProp(String propertyName) throws ConfigurationException; + + /** + * Get any byte array type property from security configuration. + * + * @return property value. + * @throws ConfigurationException when property does not exist in configuration or has incorrect type. + */ + public byte[] getByteArrayProp(String propertyName) throws ConfigurationException; + + /** + * Get any Boolean type property from security configuration. + * + * @return property value. + * @throws ConfigurationException when property does not exist in configuration or has incorrect type. + */ + public Boolean getBooleanProp(String propertyName) throws ConfigurationException; + + /** + * Get any property from security configuration. As every property can be returned as string, this method + * throws exception only when property does not exist. + * + * @return property value. + * @throws ConfigurationException when property does not exist in configuration. + */ + public String getStringProp(String propertyName) throws ConfigurationException; + +} diff --git a/src/main/java/org/owasp/esapi/configuration/EsapiPropertyLoaderFactory.java b/src/main/java/org/owasp/esapi/configuration/EsapiPropertyLoaderFactory.java new file mode 100644 index 000000000..19bc581bd --- /dev/null +++ b/src/main/java/org/owasp/esapi/configuration/EsapiPropertyLoaderFactory.java @@ -0,0 +1,36 @@ +package org.owasp.esapi.configuration; + +import org.owasp.esapi.configuration.consts.EsapiConfiguration; +import org.owasp.esapi.errors.ConfigurationException; + +import java.io.FileNotFoundException; + +import static org.owasp.esapi.configuration.consts.EsapiConfigurationType.PROPERTIES; +import static org.owasp.esapi.configuration.consts.EsapiConfigurationType.XML; + +/** + * Factory class that takes care of initialization of proper instance of EsapiPropertyLoader + * based on EsapiPropertiesStore + */ +public class EsapiPropertyLoaderFactory { + + public static AbstractPrioritizedPropertyLoader createPropertyLoader(EsapiConfiguration cfg) + throws ConfigurationException, FileNotFoundException { + String cfgPath = System.getProperty(cfg.getConfigName()); + if (cfgPath == null) { + throw new ConfigurationException("System property [" + cfg.getConfigName() + "] is not set"); + } + String fileExtension = cfgPath.substring(cfgPath.lastIndexOf('.') + 1); + + if (XML.getTypeName().equals(fileExtension)) { + return new XmlEsapiPropertyLoader(cfgPath, cfg.getPriority()); + } + if (PROPERTIES.getTypeName().equals(fileExtension)) { + return new StandardEsapiPropertyLoader(cfgPath, cfg.getPriority()); + } else { + throw new ConfigurationException("Configuration storage type [" + fileExtension + "] is not " + + "supported"); + } + } + +} diff --git a/src/main/java/org/owasp/esapi/configuration/EsapiPropertyManager.java b/src/main/java/org/owasp/esapi/configuration/EsapiPropertyManager.java new file mode 100644 index 000000000..d4020aeed --- /dev/null +++ b/src/main/java/org/owasp/esapi/configuration/EsapiPropertyManager.java @@ -0,0 +1,99 @@ +package org.owasp.esapi.configuration; + +import org.owasp.esapi.configuration.consts.EsapiConfiguration; +import org.owasp.esapi.errors.ConfigurationException; + +import java.util.TreeSet; + +import static org.owasp.esapi.configuration.EsapiPropertyLoaderFactory.createPropertyLoader; + +/** + * Manager used for loading security configuration properties. Does all the logic to obtain the correct property from + * correct source. Uses following system properties to find configuration files: + * - org.owasp.esapi.devteam - lower priority dev file path + * - org.owasp.esapi.opsteam - higher priority ops file path + */ +public class EsapiPropertyManager implements EsapiPropertyLoader { + + protected TreeSet loaders; + + public EsapiPropertyManager() { + initLoaders(); + } + + /** + * {@inheritDoc} + */ + @Override + public int getIntProp(String propertyName) throws ConfigurationException { + for (AbstractPrioritizedPropertyLoader loader : loaders) { + try { + return loader.getIntProp(propertyName); + } catch (ConfigurationException e) { + System.err.println("Property not found in " + loader.name()); + } + } + throw new ConfigurationException("Could not find property " + propertyName + " in configuration"); + } + + /** + * {@inheritDoc} + */ + @Override + public byte[] getByteArrayProp(String propertyName) throws ConfigurationException { + for (AbstractPrioritizedPropertyLoader loader : loaders) { + try { + return loader.getByteArrayProp(propertyName); + } catch (ConfigurationException e) { + System.err.println("Property not found in " + loader.name()); + } + } + throw new ConfigurationException("Could not find property " + propertyName + " in configuration"); + } + + /** + * {@inheritDoc} + */ + @Override + public Boolean getBooleanProp(String propertyName) throws ConfigurationException { + for (AbstractPrioritizedPropertyLoader loader : loaders) { + try { + return loader.getBooleanProp(propertyName); + } catch (ConfigurationException e) { + System.err.println("Property not found in " + loader.name()); + } + } + throw new ConfigurationException("Could not find property " + propertyName + " in configuration"); + } + + /** + * {@inheritDoc} + */ + @Override + public String getStringProp(String propertyName) throws ConfigurationException { + for (AbstractPrioritizedPropertyLoader loader : loaders) { + try { + return loader.getStringProp(propertyName); + } catch (ConfigurationException e) { + System.err.println("Property : " + propertyName + " not found in " + loader.name()); + } + } + throw new ConfigurationException("Could not find property " + propertyName + " in configuration"); + } + + private void initLoaders() { + loaders = new TreeSet(); + try { + loaders.add(createPropertyLoader(EsapiConfiguration.OPSTEAM_ESAPI_CFG)); + } catch (Exception e) { + System.err.println(e.getMessage()); + } + try { + loaders.add(createPropertyLoader(EsapiConfiguration.DEVTEAM_ESAPI_CFG)); + } catch (Exception e) { + System.err.println(e.getMessage()); + } + } + + +} diff --git a/src/main/java/org/owasp/esapi/configuration/StandardEsapiPropertyLoader.java b/src/main/java/org/owasp/esapi/configuration/StandardEsapiPropertyLoader.java new file mode 100644 index 000000000..6a896a2cd --- /dev/null +++ b/src/main/java/org/owasp/esapi/configuration/StandardEsapiPropertyLoader.java @@ -0,0 +1,105 @@ +package org.owasp.esapi.configuration; + +import org.owasp.esapi.ESAPI; +import org.owasp.esapi.errors.ConfigurationException; + +import java.io.*; + +/** + * Loader capable of loading single security configuration property from standard java properties configuration file. + */ +public class StandardEsapiPropertyLoader extends AbstractPrioritizedPropertyLoader { + + public StandardEsapiPropertyLoader(String filename, int priority) { + super(filename, priority); + } + + /** + * {@inheritDoc} + */ + @Override + public int getIntProp(String propertyName) throws ConfigurationException { + String property = properties.getProperty(propertyName); + if (property == null) { + throw new ConfigurationException("Property : " + propertyName + "not found in configuration"); + } + try { + return Integer.parseInt(property); + } catch (NumberFormatException e) { + throw new ConfigurationException("Incorrect type of : " + propertyName + ". Value " + property + + "cannot be converted to integer", e); + } + } + + /** + * {@inheritDoc} + */ + @Override + public byte[] getByteArrayProp(String propertyName) throws ConfigurationException { + String property = properties.getProperty(propertyName); + if (property == null) { + throw new ConfigurationException("Property : " + propertyName + "not found in default configuration"); + } + try { + return ESAPI.encoder().decodeFromBase64(property); + } catch (IOException e) { + throw new ConfigurationException("Incorrect type of : " + propertyName + ". Value " + property + + "cannot be converted to byte array", e); + } + } + + /** + * {@inheritDoc} + */ + @Override + public Boolean getBooleanProp(String propertyName) throws ConfigurationException { + String property = properties.getProperty(propertyName); + if (property == null) { + throw new ConfigurationException("Property : " + propertyName + "not found in default configuration"); + } + if (property.equalsIgnoreCase("true") || property.equalsIgnoreCase("yes")) { + return true; + } + if (property.equalsIgnoreCase("false") || property.equalsIgnoreCase("no")) { + return false; + } else { + throw new ConfigurationException("Incorrect type of : " + propertyName + ". Value " + property + + "cannot be converted to boolean"); + } + } + + /** + * {@inheritDoc} + */ + @Override + public String getStringProp(String propertyName) throws ConfigurationException { + String property = properties.getProperty(propertyName); + if (property == null) { + throw new ConfigurationException("Property : " + propertyName + "not found in default configuration"); + } + return property; + } + + /** + * Methods loads configuration from .properties file. + * @param file + */ + protected void loadPropertiesFromFile(File file) { + InputStream input = null; + try { + input = new FileInputStream(file); + properties.load(input); + } catch (IOException ex) { + System.err.println("Loading " + file.getName() + " via file I/O failed. Exception was: " + ex); + } finally { + if (input != null) { + try { + input.close(); + } catch (IOException e) { + System.err.println("Could not close stream"); + } + } + } + } + +} diff --git a/src/main/java/org/owasp/esapi/configuration/XmlEsapiPropertyLoader.java b/src/main/java/org/owasp/esapi/configuration/XmlEsapiPropertyLoader.java new file mode 100644 index 000000000..c48be38fc --- /dev/null +++ b/src/main/java/org/owasp/esapi/configuration/XmlEsapiPropertyLoader.java @@ -0,0 +1,134 @@ +package org.owasp.esapi.configuration; + +import org.owasp.esapi.ESAPI; +import org.owasp.esapi.errors.ConfigurationException; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + +import javax.xml.XMLConstants; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.transform.stream.StreamSource; +import javax.xml.validation.Schema; +import javax.xml.validation.SchemaFactory; +import javax.xml.validation.Validator; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; + +/** + * Loader capable of loading single security configuration property from xml configuration file. + */ +public class XmlEsapiPropertyLoader extends AbstractPrioritizedPropertyLoader { + + public XmlEsapiPropertyLoader(String filename, int priority) { + super(filename, priority); + } + + /** + * {@inheritDoc} + */ + @Override + public int getIntProp(String propertyName) throws ConfigurationException { + String property = properties.getProperty(propertyName); + if (property == null) { + throw new ConfigurationException("Property : " + propertyName + " not found in default configuration"); + } + try { + return Integer.parseInt(property); + } catch (NumberFormatException e) { + throw new ConfigurationException("Incorrect type of : " + propertyName + ". Value " + property + + "cannot be converted to integer", e); + } + } + + /** + * {@inheritDoc} + */ + @Override + public byte[] getByteArrayProp(String propertyName) throws ConfigurationException { + String property = properties.getProperty(propertyName); + if (property == null) { + throw new ConfigurationException("Property : " + propertyName + " not found in default configuration"); + } + try { + return ESAPI.encoder().decodeFromBase64(property); + } catch (IOException e) { + throw new ConfigurationException("Incorrect type of : " + propertyName + ". Value " + property + + "cannot be converted to byte array", e); + } + } + + /** + * {@inheritDoc} + */ + @Override + public Boolean getBooleanProp(String propertyName) throws ConfigurationException { + String property = properties.getProperty(propertyName); + if (property == null) { + throw new ConfigurationException("Property : " + propertyName + " not found in default configuration"); + } + if (property.equalsIgnoreCase("true") || property.equalsIgnoreCase("yes")) { + return true; + } + if (property.equalsIgnoreCase("false") || property.equalsIgnoreCase("no")) { + return false; + } else { + throw new ConfigurationException("Incorrect type of : " + propertyName + ". Value " + property + + "cannot be converted to boolean"); + } + } + + /** + * {@inheritDoc} + */ + @Override + public String getStringProp(String propertyName) throws ConfigurationException { + String property = properties.getProperty(propertyName); + if (property == null) { + throw new ConfigurationException("Property : " + propertyName + " not found in default configuration"); + } + return property; + } + + /** + * Methods loads configuration from .xml file. + * @param file + */ + protected void loadPropertiesFromFile(File file) throws ConfigurationException { + try { + validateAgainstXSD(new FileInputStream(file)); + + DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance(); + DocumentBuilder dBuilder = dbFactory.newDocumentBuilder(); + Document doc = dBuilder.parse(file); + doc.getDocumentElement().normalize(); + + NodeList nodeList = doc.getElementsByTagName("property"); + for (int i = 0; i < nodeList.getLength(); i++) { + Node node = nodeList.item(i); + if (node.getNodeType() == Node.ELEMENT_NODE) { + Element element = (Element) node; + String propertyKey = element.getAttribute("name"); + String propertyValue = element.getTextContent(); + properties.put(propertyKey, propertyValue); + } + } + } catch (Exception e) { + throw new ConfigurationException("Configuration file : " + filename + " has invalid schema." + e.getMessage(), e); + } + } + + private void validateAgainstXSD(InputStream xml) throws Exception { + InputStream xsd = getClass().getResourceAsStream("/ESAPI-properties.xsd"); + SchemaFactory factory = + SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI); + Schema schema = factory.newSchema(new StreamSource(xsd)); + Validator validator = schema.newValidator(); + validator.validate(new StreamSource(xml)); + } + +} diff --git a/src/main/java/org/owasp/esapi/configuration/consts/EsapiConfiguration.java b/src/main/java/org/owasp/esapi/configuration/consts/EsapiConfiguration.java new file mode 100644 index 000000000..025700e76 --- /dev/null +++ b/src/main/java/org/owasp/esapi/configuration/consts/EsapiConfiguration.java @@ -0,0 +1,33 @@ +package org.owasp.esapi.configuration.consts; + +/** + * Enum used for initialization of esapi configuration files. + */ +public enum EsapiConfiguration { + + OPSTEAM_ESAPI_CFG("org.owasp.esapi.opsteam", 1), + DEVTEAM_ESAPI_CFG("org.owasp.esapi.devteam", 2); + + /** + * Key of system property pointing to path esapi to configuration file. + */ + String configName; + + /** + * Priority of configuration (higher numer - higher priority). + */ + int priority; + + EsapiConfiguration(String configName, int priority) { + this.configName = configName; + this.priority = priority; + } + + public String getConfigName() { + return configName; + } + + public int getPriority() { + return priority; + } +} diff --git a/src/main/java/org/owasp/esapi/configuration/consts/EsapiConfigurationType.java b/src/main/java/org/owasp/esapi/configuration/consts/EsapiConfigurationType.java new file mode 100644 index 000000000..df3899c8a --- /dev/null +++ b/src/main/java/org/owasp/esapi/configuration/consts/EsapiConfigurationType.java @@ -0,0 +1,19 @@ +package org.owasp.esapi.configuration.consts; + + +/** + * Supported esapi configuration file types. + */ +public enum EsapiConfigurationType { + PROPERTIES("properties"), XML("xml"); + + String typeName; + + EsapiConfigurationType(String typeName) { + this.typeName = typeName; + } + + public String getTypeName() { + return typeName; + } +} diff --git a/src/main/java/org/owasp/esapi/reference/DefaultHTTPUtilities.java b/src/main/java/org/owasp/esapi/reference/DefaultHTTPUtilities.java index dddf41c95..b68c613bc 100644 --- a/src/main/java/org/owasp/esapi/reference/DefaultHTTPUtilities.java +++ b/src/main/java/org/owasp/esapi/reference/DefaultHTTPUtilities.java @@ -825,13 +825,11 @@ public void sendForward( String location ) throws AccessControlException,Servle * {@inheritDoc} * * This implementation checks against the list of safe redirect locations defined in ESAPI.properties. - * - * @param response */ public void sendRedirect(HttpServletResponse response, String location) throws AccessControlException, IOException { if (!ESAPI.validator().isValidRedirectLocation("Redirect", location, false)) { logger.fatal(Logger.SECURITY_FAILURE, "Bad redirect location: " + location); - throw new AccessControlException("Redirect failed", "Bad Redirect location: " + location); + throw new AccessControlException("Redirect failed", "Bad redirect location: " + location); } response.sendRedirect(location); } @@ -1040,3 +1038,4 @@ private String decryptString(String ciphertext) throws EncryptionException { return plaintext.toString(); } } + diff --git a/src/main/java/org/owasp/esapi/reference/DefaultSecurityConfiguration.java b/src/main/java/org/owasp/esapi/reference/DefaultSecurityConfiguration.java index fd95f04a6..21a5cb345 100644 --- a/src/main/java/org/owasp/esapi/reference/DefaultSecurityConfiguration.java +++ b/src/main/java/org/owasp/esapi/reference/DefaultSecurityConfiguration.java @@ -15,29 +15,19 @@ */ package org.owasp.esapi.reference; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.io.InputStream; -import java.net.URL; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.HashMap; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Properties; -import java.util.regex.Pattern; -import java.util.regex.PatternSyntaxException; - import org.apache.commons.lang.text.StrTokenizer; import org.owasp.esapi.ESAPI; import org.owasp.esapi.Logger; import org.owasp.esapi.SecurityConfiguration; +import org.owasp.esapi.configuration.EsapiPropertyManager; import org.owasp.esapi.errors.ConfigurationException; +import java.io.*; +import java.net.URL; +import java.util.*; +import java.util.regex.Pattern; +import java.util.regex.PatternSyntaxException; + /** * The reference {@code SecurityConfiguration} manages all the settings used by the ESAPI in a single place. In this reference * implementation, resources can be put in several locations, which are searched in the following order: @@ -219,6 +209,7 @@ public static SecurityConfiguration getInstance() { */ private String resourceDirectory = ".esapi"; // For backward compatibility (vs. "esapi") private final String resourceFile; + private EsapiPropertyManager esapiPropertyManager; // private static long lastModified = -1; @@ -229,6 +220,7 @@ public static SecurityConfiguration getInstance() { */ DefaultSecurityConfiguration(String resourceFile) { this.resourceFile = resourceFile; + this.esapiPropertyManager = new EsapiPropertyManager(); // load security configuration try { loadConfiguration(); @@ -661,8 +653,8 @@ private Properties loadConfigurationFromClasspath(String fileName) throws Illega // try resources folder if (in == null) { - currentClasspathSearchLocation = "resources/"; - in = currentLoader.getResourceAsStream("resources/" + fileName); + currentClasspathSearchLocation = "src/main/resources/"; + in = currentLoader.getResourceAsStream("src/main/resources/" + fileName); } // now load the properties @@ -998,8 +990,7 @@ public String getLogFileName() { public int getMaxLogFileSize() { return getESAPIProperty( MAX_LOG_FILE_SIZE, DEFAULT_MAX_LOG_FILE_SIZE ); } - - + /** * {@inheritDoc} */ @@ -1237,7 +1228,102 @@ protected List getESAPIProperty( String key, List def ) { return Arrays.asList( parts ); } - protected boolean shouldPrintProperties() { + /** + * {@inheritDoc} + * Looks for property in three configuration files in following order: + * 1.) In file defined as org.owasp.esapi.opsteam system property + * 2.) In file defined as org.owasp.esapi.devteam system property + * 3.) In ESAPI.properties* + */ + @Override + public int getIntProp(String propertyName) throws ConfigurationException { + try { + return esapiPropertyManager.getIntProp(propertyName); + } catch (ConfigurationException ex) { + String property = properties.getProperty(propertyName); + try { + return Integer.parseInt(property); + } catch (NumberFormatException e) { + throw new ConfigurationException( "SecurityConfiguration for " + propertyName + " has incorrect " + + "type"); + } + } + } + + /** + * {@inheritDoc} + * Looks for property in three configuration files in following order: + * 1.) In file defined as org.owasp.esapi.opsteam system property + * 2.) In file defined as org.owasp.esapi.devteam system property + * 3.) In ESAPI.properties + */ + @Override + public byte[] getByteArrayProp(String propertyName) throws ConfigurationException { + try { + return esapiPropertyManager.getByteArrayProp(propertyName); + } catch (ConfigurationException ex) { + String property = properties.getProperty(propertyName); + if ( property == null ) { + throw new ConfigurationException( "SecurityConfiguration for " + propertyName + " not found in ESAPI.properties"); + } + try { + return ESAPI.encoder().decodeFromBase64(property); + } catch( IOException e ) { + throw new ConfigurationException( "SecurityConfiguration for " + propertyName + " has incorrect " + + "type"); + } + } + } + + /** + * {@inheritDoc} + * Looks for property in three configuration files in following order: + * 1.) In file defined as org.owasp.esapi.opsteam system property + * 2.) In file defined as org.owasp.esapi.devteam system property + * 3.) In ESAPI.properties + */ + @Override + public Boolean getBooleanProp(String propertyName) throws ConfigurationException { + try { + return esapiPropertyManager.getBooleanProp(propertyName); + } catch (ConfigurationException ex) { + String property = properties.getProperty( propertyName ); + if ( property == null ) { + throw new ConfigurationException( "SecurityConfiguration for " + propertyName + " not found in ESAPI.properties"); + } + if ( property.equalsIgnoreCase("true") || property.equalsIgnoreCase("yes" ) ) { + return true; + } + if ( property.equalsIgnoreCase("false") || property.equalsIgnoreCase( "no" ) ) { + return false; + } + throw new ConfigurationException( "SecurityConfiguration for " + propertyName + " has incorrect " + + "type"); + } + } + + /** + * {@inheritDoc} + * Looks for property in three configuration files in following order: + * 1.) In file defined as org.owasp.esapi.opsteam system property + * 2.) In file defined as org.owasp.esapi.devteam system property + * 3.) In ESAPI.properties + */ + @Override + public String getStringProp(String propertyName) throws ConfigurationException { + try { + return esapiPropertyManager.getStringProp(propertyName); + } catch (ConfigurationException ex) { + String property = properties.getProperty( propertyName ); + if ( property == null ) { + throw new ConfigurationException( "SecurityConfiguration for " + propertyName + " not found in ESAPI.properties"); + } + return property; + } + } + + + protected boolean shouldPrintProperties() { return getESAPIProperty(PRINT_PROPERTIES_WHEN_LOADED, false); } diff --git a/src/main/resources/ESAPI-properties.xsd b/src/main/resources/ESAPI-properties.xsd new file mode 100644 index 000000000..523d3a124 --- /dev/null +++ b/src/main/resources/ESAPI-properties.xsd @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/java/org/owasp/esapi/SecurityConfigurationWrapper.java b/src/test/java/org/owasp/esapi/SecurityConfigurationWrapper.java index eb9c0c9f9..769c9eced 100644 --- a/src/test/java/org/owasp/esapi/SecurityConfigurationWrapper.java +++ b/src/test/java/org/owasp/esapi/SecurityConfigurationWrapper.java @@ -1,5 +1,7 @@ package org.owasp.esapi; +import org.owasp.esapi.errors.ConfigurationException; + import java.io.File; import java.io.IOException; import java.io.InputStream; @@ -576,6 +578,26 @@ public int getMaxLogFileSize() return wrapped.getMaxLogFileSize(); } + @Override + public int getIntProp(String propertyName) throws ConfigurationException { + return wrapped.getIntProp(propertyName); + } + + @Override + public byte[] getByteArrayProp(String propertyName) throws ConfigurationException { + return wrapped.getByteArrayProp(propertyName); + } + + @Override + public Boolean getBooleanProp(String propertyName) throws ConfigurationException { + return wrapped.getBooleanProp(propertyName); + } + + @Override + public String getStringProp(String propertyName) throws ConfigurationException { + return wrapped.getStringProp(propertyName); + } + /** * {@inheritDoc} */ diff --git a/src/test/java/org/owasp/esapi/configuration/EsapiPropertyManagerTest.java b/src/test/java/org/owasp/esapi/configuration/EsapiPropertyManagerTest.java new file mode 100644 index 000000000..09096a424 --- /dev/null +++ b/src/test/java/org/owasp/esapi/configuration/EsapiPropertyManagerTest.java @@ -0,0 +1,327 @@ +package org.owasp.esapi.configuration; + +import org.junit.Before; +import org.junit.Test; +import org.owasp.esapi.ESAPI; +import org.owasp.esapi.configuration.consts.EsapiConfiguration; +import org.owasp.esapi.errors.ConfigurationException; + +import java.io.File; +import java.io.IOException; + +import static junit.framework.Assert.*; + +public class EsapiPropertyManagerTest { + + private static String propFilename1; + private static String propFilename2; + private static String xmlFilename1; + private static String xmlFilename2; + + private EsapiPropertyManager testPropertyManager; + + @Before + public void init() { + System.setProperty(EsapiConfiguration.DEVTEAM_ESAPI_CFG.getConfigName(), ""); + System.setProperty(EsapiConfiguration.OPSTEAM_ESAPI_CFG.getConfigName(), ""); + propFilename1 = "src" + File.separator + "test" + File.separator + "resources" + File.separator + + "esapi" + File.separator + "ESAPI-test.properties"; + propFilename2 = "src" + File.separator + "test" + File.separator + "resources" + File.separator + + "esapi" + File.separator + "ESAPI-test-2.properties"; + xmlFilename1 = "src" + File.separator + "test" + File.separator + "resources" + File.separator + + "esapi" + File.separator + "ESAPI-test.xml"; + xmlFilename2 = "src" + File.separator + "test" + File.separator + "resources" + File.separator + + "esapi" + File.separator + "ESAPI-test-2.xml"; + } + + @Test + public void testPropertyManagerInitialized() { + // given + System.setProperty(EsapiConfiguration.DEVTEAM_ESAPI_CFG.getConfigName(), propFilename1); + System.setProperty(EsapiConfiguration.OPSTEAM_ESAPI_CFG.getConfigName(), propFilename2); + + // when + testPropertyManager = new EsapiPropertyManager(); + + // then + assertNotNull(testPropertyManager.loaders); + assertNotSame(0, testPropertyManager.loaders.size()); + } + + @Test + public void testStringPropFoundInLoader() { + // given + System.setProperty(EsapiConfiguration.DEVTEAM_ESAPI_CFG.getConfigName(), xmlFilename1); + String propertyKey = "string_property"; + String expectedPropertyValue = "test_string_property"; + + // when + testPropertyManager = new EsapiPropertyManager(); + String propertyValue = testPropertyManager.getStringProp(propertyKey); + + // then + assertEquals(expectedPropertyValue, propertyValue); + } + + @Test + public void testStringPropertyLoadedFromFileWithHigherPriority() { + // given + System.setProperty(EsapiConfiguration.DEVTEAM_ESAPI_CFG.getConfigName(), propFilename1); + System.setProperty(EsapiConfiguration.OPSTEAM_ESAPI_CFG.getConfigName(), xmlFilename2); + + String propertyKey = "string_property"; + String expectedValue = "test_string_property_2"; + + // when + testPropertyManager = new EsapiPropertyManager(); + String propertyValue = testPropertyManager.getStringProp(propertyKey); + + // then + assertEquals(expectedValue, propertyValue); + } + + @Test + public void testStringPropertyLoadedFromPropFileWithHigherPriority() { + // given + System.setProperty(EsapiConfiguration.DEVTEAM_ESAPI_CFG.getConfigName(), propFilename1); + System.setProperty(EsapiConfiguration.OPSTEAM_ESAPI_CFG.getConfigName(), propFilename2); + String propertyKey = "string_property"; + String expectedValue = "test_string_property_2"; + + // when + testPropertyManager = new EsapiPropertyManager(); + String propertyValue = testPropertyManager.getStringProp(propertyKey); + + // then + assertEquals(expectedValue, propertyValue); + } + + @Test + public void testStringPropertyLoadedFromXmlFileWithHigherPriority() { + // given + System.setProperty(EsapiConfiguration.DEVTEAM_ESAPI_CFG.getConfigName(), xmlFilename1); + System.setProperty(EsapiConfiguration.OPSTEAM_ESAPI_CFG.getConfigName(), xmlFilename2); + String propertyKey = "string_property"; + String expectedValue = "test_string_property_2"; + + // when + testPropertyManager = new EsapiPropertyManager(); + String propertyValue = testPropertyManager.getStringProp(propertyKey); + + // then + assertEquals(expectedValue, propertyValue); + } + + + @Test(expected = ConfigurationException.class) + public void testStringPropertyNotFoundByLoaderAndThrowException() { + // given + System.setProperty(EsapiConfiguration.DEVTEAM_ESAPI_CFG.getConfigName(), propFilename1); + String propertyKey = "non.existing.property"; + + // when + testPropertyManager = new EsapiPropertyManager(); + testPropertyManager.getStringProp(propertyKey); + + // then expect exception + } + + @Test + public void testIntPropFoundInLoader() { + // given + System.setProperty(EsapiConfiguration.DEVTEAM_ESAPI_CFG.getConfigName(), xmlFilename1); + String propertyKey = "int_property"; + int expectedPropertyValue = 5; + + // when + testPropertyManager = new EsapiPropertyManager(); + int propertyValue = testPropertyManager.getIntProp(propertyKey); + + // then + assertEquals(expectedPropertyValue, propertyValue); + } + + @Test + public void testIntPropertyLoadedFromFileWithHigherPriority() { + // given + System.setProperty(EsapiConfiguration.DEVTEAM_ESAPI_CFG.getConfigName(), propFilename1); + System.setProperty(EsapiConfiguration.OPSTEAM_ESAPI_CFG.getConfigName(), xmlFilename2); + String propertyKey = "int_property"; + int expectedValue = 52; + + // when + testPropertyManager = new EsapiPropertyManager(); + int propertyValue = testPropertyManager.getIntProp(propertyKey); + + // then + assertEquals(expectedValue, propertyValue); + } + + @Test + public void testIntPropertyLoadedFromPropFileWithHigherPriority() { + // given + System.setProperty(EsapiConfiguration.DEVTEAM_ESAPI_CFG.getConfigName(), propFilename1); + System.setProperty(EsapiConfiguration.OPSTEAM_ESAPI_CFG.getConfigName(), propFilename2); + String propertyKey = "int_property"; + int expectedValue = 52; // value from ESAPI-test-2.properties file + + // when + testPropertyManager = new EsapiPropertyManager(); + int propertyValue = testPropertyManager.getIntProp(propertyKey); + + // then + assertEquals(expectedValue, propertyValue); + } + + @Test + public void testIntPropertyLoadedFromXmlFileWithHigherPriority() { + // given + System.setProperty(EsapiConfiguration.DEVTEAM_ESAPI_CFG.getConfigName(), xmlFilename1); + System.setProperty(EsapiConfiguration.OPSTEAM_ESAPI_CFG.getConfigName(), xmlFilename2); + String propertyKey = "int_property"; + int expectedValue = 52; + + // when + testPropertyManager = new EsapiPropertyManager(); + int propertyValue = testPropertyManager.getIntProp(propertyKey); + + // then + assertEquals(expectedValue, propertyValue); + } + + + @Test(expected = ConfigurationException.class) + public void testIntPropertyNotFoundByLoaderAndThrowException() { + // given + String propertyKey = "non.existing.property"; + + // when + testPropertyManager = new EsapiPropertyManager(); + testPropertyManager.getIntProp(propertyKey); + + // then expect exception + } + + @Test + public void testBooleanPropFoundInLoader() { + // given + System.setProperty(EsapiConfiguration.DEVTEAM_ESAPI_CFG.getConfigName(), xmlFilename1); + String propertyKey = "boolean_property"; + boolean expectedPropertyValue = true; + + // when + testPropertyManager = new EsapiPropertyManager(); + boolean propertyValue = testPropertyManager.getBooleanProp(propertyKey); + + // then + assertEquals(expectedPropertyValue, propertyValue); + } + + @Test(expected = ConfigurationException.class) + public void testBooleanPropertyNotFoundByLoaderAndThrowException() { + // given + String propertyKey = "non.existing.property"; + + // when + testPropertyManager = new EsapiPropertyManager(); + testPropertyManager.getBooleanProp(propertyKey); + + // then expect exception + } + + @Test + public void testByteArrayPropFoundInLoader() { + // given + System.setProperty(EsapiConfiguration.DEVTEAM_ESAPI_CFG.getConfigName(), propFilename1); + String propertyKey = "string_property"; + byte[] expectedValue = new byte[0]; + try { + expectedValue = ESAPI.encoder().decodeFromBase64("test_string_property"); + } catch (IOException e) { + fail(e.getMessage()); + } + + // when + testPropertyManager = new EsapiPropertyManager(); + byte[] propertyValue = testPropertyManager.getByteArrayProp(propertyKey); + + // then + assertEquals(expectedValue, propertyValue); + } + + @Test + public void testByteArrayPropertyLoadedFromFileWithHigherPriority() { + // given + System.setProperty(EsapiConfiguration.DEVTEAM_ESAPI_CFG.getConfigName(), propFilename1); + System.setProperty(EsapiConfiguration.OPSTEAM_ESAPI_CFG.getConfigName(), xmlFilename2); + String propertyKey = "string_property"; + byte[] expectedValue = new byte[0]; + try { + expectedValue = ESAPI.encoder().decodeFromBase64("test_string_property_2"); + } catch (IOException e) { + fail(e.getMessage()); + } + + // when + testPropertyManager = new EsapiPropertyManager(); + byte[] propertyValue = testPropertyManager.getByteArrayProp(propertyKey); + + // then + assertEquals(expectedValue, propertyValue); + } + + @Test + public void testByteArrayPropertyLoadedFromPropFileWithHigherPriority() { + // given + System.setProperty(EsapiConfiguration.DEVTEAM_ESAPI_CFG.getConfigName(), propFilename1); + System.setProperty(EsapiConfiguration.OPSTEAM_ESAPI_CFG.getConfigName(), propFilename2); + String propertyKey = "string_property"; + byte[] expectedValue = new byte[0]; + try { + expectedValue = ESAPI.encoder().decodeFromBase64("test_string_property_2"); + } catch (IOException e) { + fail(e.getMessage()); + } + + // when + testPropertyManager = new EsapiPropertyManager(); + byte[] propertyValue = testPropertyManager.getByteArrayProp(propertyKey); + + // then + assertEquals(expectedValue, propertyValue); + } + + @Test + public void testByteArrayPropertyLoadedFromXmlFileWithHigherPriority() { + // given + System.setProperty(EsapiConfiguration.DEVTEAM_ESAPI_CFG.getConfigName(), xmlFilename1); + System.setProperty(EsapiConfiguration.OPSTEAM_ESAPI_CFG.getConfigName(), xmlFilename2); + String propertyKey = "string_property"; + byte[] expectedValue = new byte[0]; + try { + expectedValue = ESAPI.encoder().decodeFromBase64("test_string_property_2"); + } catch (IOException e) { + fail(e.getMessage()); + } + + // when + testPropertyManager = new EsapiPropertyManager(); + byte[] propertyValue = testPropertyManager.getByteArrayProp(propertyKey); + + // then + assertEquals(expectedValue, propertyValue); + } + + @Test(expected = ConfigurationException.class) + public void testByteArrayPropertyNotFoundByLoaderAndThrowException() { + // given + String propertyKey = "non.existing.property"; + + // when + testPropertyManager = new EsapiPropertyManager(); + testPropertyManager.getByteArrayProp(propertyKey); + + // then expect exception + } + +} diff --git a/src/test/java/org/owasp/esapi/configuration/StandardEsapiPropertyLoaderTest.java b/src/test/java/org/owasp/esapi/configuration/StandardEsapiPropertyLoaderTest.java new file mode 100644 index 000000000..cf0cffca9 --- /dev/null +++ b/src/test/java/org/owasp/esapi/configuration/StandardEsapiPropertyLoaderTest.java @@ -0,0 +1,268 @@ +package org.owasp.esapi.configuration; + + +import org.junit.Before; +import org.junit.Test; +import org.owasp.esapi.ESAPI; +import org.owasp.esapi.configuration.consts.EsapiConfiguration; +import org.owasp.esapi.errors.ConfigurationException; + +import java.io.File; +import java.io.IOException; + +import static junit.framework.Assert.*; + +public class StandardEsapiPropertyLoaderTest { + + private static String filename; + private static int priority; + + private StandardEsapiPropertyLoader testPropertyLoader; + + @Before + public void init() { + System.setProperty(EsapiConfiguration.DEVTEAM_ESAPI_CFG.getConfigName(), ""); + System.setProperty(EsapiConfiguration.OPSTEAM_ESAPI_CFG.getConfigName(), ""); + filename = "src" + File.separator + "test" + File.separator + "resources" + File.separator + + "esapi" + File.separator + "ESAPI-test.properties"; + priority = 1; + } + + @Test + public void testPropertiesLoaded() { + // when + testPropertyLoader = new StandardEsapiPropertyLoader(filename, priority); + + // then + assertFalse(testPropertyLoader.properties.isEmpty()); + } + + @Test + public void testPriority() { + // given + int expectedValue = 1; + + // when + testPropertyLoader = new StandardEsapiPropertyLoader(filename, priority); + int value = testPropertyLoader.priority(); + + // then + assertEquals(expectedValue, value); + } + + @Test + public void testLoadersAreEqual() { + // given + int expectedValue = 0; + + // when + testPropertyLoader = new StandardEsapiPropertyLoader(filename, priority); + StandardEsapiPropertyLoader otherPropertyLoader = new StandardEsapiPropertyLoader(filename, priority); + int value = testPropertyLoader.compareTo(otherPropertyLoader); + + // then + assertEquals(expectedValue, value); + } + + @Test + public void testCompareWithOtherLoaderWithHigherPriority() { + // given + int expectedValue = -1; + int higherPriority = 2; + + // when + testPropertyLoader = new StandardEsapiPropertyLoader(filename, priority); + StandardEsapiPropertyLoader otherPropertyLoader = new StandardEsapiPropertyLoader(filename, higherPriority); + int value = testPropertyLoader.compareTo(otherPropertyLoader); + + // then + assertEquals(expectedValue, value); + } + + @Test + public void testCompareWithOtherLoaderWithLowerPriority() { + // given + int expectedValue = 1; + int lowerPriority = 0; + + // when + testPropertyLoader = new StandardEsapiPropertyLoader(filename, priority); + StandardEsapiPropertyLoader otherPropertyLoader = new StandardEsapiPropertyLoader(filename, lowerPriority); + int value = testPropertyLoader.compareTo(otherPropertyLoader); + + // then + assertEquals(expectedValue, value); + } + + @Test + public void testGetIntProp() { + // given + String propertyKey = "int_property"; + + // when + testPropertyLoader = new StandardEsapiPropertyLoader(filename, priority); + int propertyValue = testPropertyLoader.getIntProp(propertyKey); + + // then + assertEquals(5, propertyValue); + } + + @Test(expected = ConfigurationException.class) + public void testIntPropertyNotFound() throws ConfigurationException { + // given + String propertyKey = "non-existing-key"; + + // when + testPropertyLoader = new StandardEsapiPropertyLoader(filename, priority); + testPropertyLoader.getIntProp(propertyKey); + + // then expect exception + } + + @Test(expected = ConfigurationException.class) + public void testIncorrectIntPropertyType() { + // given + String key = "invalid_int_property"; + + // when + testPropertyLoader = new StandardEsapiPropertyLoader(filename, priority); + testPropertyLoader.getIntProp(key); + + // then expect exception + } + + @Test + public void testGetStringProp() { + // given + String propertyKey = "string_property"; + String expectedValue = "test_string_property"; + + // when + testPropertyLoader = new StandardEsapiPropertyLoader(filename, priority); + String propertyValue = testPropertyLoader.getStringProp(propertyKey); + + // then + assertEquals(expectedValue, propertyValue); + } + + @Test(expected = ConfigurationException.class) + public void testStringPropertyNotFound() throws ConfigurationException { + // given + String propertyKey = "non-existing-key"; + + // when + testPropertyLoader = new StandardEsapiPropertyLoader(filename, priority); + testPropertyLoader.getStringProp(propertyKey); + + // then expect exception + } + + @Test + public void testGetBooleanProp() { + // given + String filename = "src" + File.separator + "test" + File.separator + "resources" + File.separator + + "esapi" + File.separator + "ESAPI-test.properties"; + int priority = 1; + String propertyKey = "boolean_property"; + boolean expectedValue = true; + + // when + testPropertyLoader = new StandardEsapiPropertyLoader(filename, priority); + boolean value = testPropertyLoader.getBooleanProp(propertyKey); + + // then + assertEquals(expectedValue, value); + } + + @Test + public void testGetBooleanYesProperty() { + // given + String key = "boolean_yes_property"; + boolean expectedValue = true; + + // when + testPropertyLoader = new StandardEsapiPropertyLoader(filename, priority); + boolean value = testPropertyLoader.getBooleanProp(key); + + // then + assertEquals(expectedValue, value); + } + + @Test + public void testGetBooleanNoProperty() { + // given + String key = "boolean_no_property"; + boolean expectedValue = false; + + // when + testPropertyLoader = new StandardEsapiPropertyLoader(filename, priority); + boolean value = testPropertyLoader.getBooleanProp(key); + + // then + assertEquals(expectedValue, value); + } + + @Test(expected = ConfigurationException.class) + public void testBooleanPropertyNotFound() throws ConfigurationException { + // given + String filename = "src" + File.separator + "test" + File.separator + "src/main/resources" + File.separator + + "esapi" + File.separator + "ESAPI-test.properties"; int priority = 1; + String propertyKey = "non-existing-key"; + + // when + testPropertyLoader = new StandardEsapiPropertyLoader(filename, priority); + testPropertyLoader.getBooleanProp(propertyKey); + + // then expect exception + } + + @Test(expected = ConfigurationException.class) + public void testIncorrectBooleanPropertyType() throws ConfigurationException { + // given + String key = "invalid_boolean_property"; + + // when + testPropertyLoader = new StandardEsapiPropertyLoader(filename, priority); + testPropertyLoader.getBooleanProp(key); + + // then expect exception + } + + @Test + public void testGetByteArrayProp() { + // given + String filename = "src" + File.separator + "test" + File.separator + "resources" + File.separator + + "esapi" + File.separator + "ESAPI-test.properties"; + int priority = 1; + String propertyKey = "string_property"; + + byte[] expectedValue = new byte[0]; + try { + expectedValue = ESAPI.encoder().decodeFromBase64("test_string_property"); + } catch (IOException e) { + fail(e.getMessage()); + } + + // when + testPropertyLoader = new StandardEsapiPropertyLoader(filename, priority); + byte[] value = testPropertyLoader.getByteArrayProp(propertyKey); + + // then + assertEquals(expectedValue, value); + } + + @Test(expected = ConfigurationException.class) + public void testByteArrayPropertyNotFound() throws ConfigurationException { + // given + String filename = "src" + File.separator + "test" + File.separator + "src/main/resources" + File.separator + + "esapi" + File.separator + "ESAPI-test.properties"; int priority = 1; + String propertyKey = "non-existing-key"; + + // when + testPropertyLoader = new StandardEsapiPropertyLoader(filename, priority); + testPropertyLoader.getByteArrayProp(propertyKey); + + // then expect exception + } + +} diff --git a/src/test/java/org/owasp/esapi/configuration/XmlEsapiPropertyLoaderTest.java b/src/test/java/org/owasp/esapi/configuration/XmlEsapiPropertyLoaderTest.java new file mode 100644 index 000000000..9a60f182b --- /dev/null +++ b/src/test/java/org/owasp/esapi/configuration/XmlEsapiPropertyLoaderTest.java @@ -0,0 +1,269 @@ +package org.owasp.esapi.configuration; + +import org.junit.Before; +import org.junit.Test; +import org.owasp.esapi.ESAPI; +import org.owasp.esapi.configuration.consts.EsapiConfiguration; +import org.owasp.esapi.errors.ConfigurationException; + +import java.io.File; +import java.io.IOException; + +import static junit.framework.Assert.*; + +public class XmlEsapiPropertyLoaderTest { + + private static String filename; + private static int priority; + + private XmlEsapiPropertyLoader testPropertyLoader; + + @Before + public void init() { + System.setProperty(EsapiConfiguration.DEVTEAM_ESAPI_CFG.getConfigName(), ""); + System.setProperty(EsapiConfiguration.OPSTEAM_ESAPI_CFG.getConfigName(), ""); + filename = "src" + File.separator + "test" + File.separator + "resources" + File.separator + + "esapi" + File.separator + "ESAPI-test.xml"; + priority = 1; + } + + @Test + public void testPropertiesLoaded() { + // when + testPropertyLoader = new XmlEsapiPropertyLoader(filename, priority); + + // then + assertFalse(testPropertyLoader.properties.isEmpty()); + } + + @Test(expected = ConfigurationException.class) + public void testInvalidPropertyFile() { + // given + String invalidFilename = "src" + File.separator + "test" + File.separator + "resources" + File.separator + + "esapi" + File.separator + "ESAPI-test-invalid-content.xml"; + + // when + testPropertyLoader = new XmlEsapiPropertyLoader(invalidFilename, priority); + + // then expect exception + } + + @Test + public void testPriority() { + // given + int expectedValue = 1; + + // when + testPropertyLoader = new XmlEsapiPropertyLoader(filename, priority); + int value = testPropertyLoader.priority(); + + // then + assertEquals(expectedValue, value); + } + + @Test + public void testLoadersAreEqual() { + // given + int expectedValue = 0; + + // when + testPropertyLoader = new XmlEsapiPropertyLoader(filename, priority); + StandardEsapiPropertyLoader otherPropertyLoader = new StandardEsapiPropertyLoader(filename, priority); + int value = testPropertyLoader.compareTo(otherPropertyLoader); + + // then + assertEquals(expectedValue, value); + } + + @Test + public void testCompareWithOtherLoaderWithHigherPriority() { + // given + int expectedValue = -1; + int higherPriority = 2; + + // when + testPropertyLoader = new XmlEsapiPropertyLoader(filename, priority); + StandardEsapiPropertyLoader otherPropertyLoader = new StandardEsapiPropertyLoader(filename, higherPriority); + int value = testPropertyLoader.compareTo(otherPropertyLoader); + + // then + assertEquals(expectedValue, value); + } + + @Test + public void testCompareWithOtherLoaderWithLowerPriority() { + // given + int expectedValue = 1; + int lowerPriority = 0; + + // when + testPropertyLoader = new XmlEsapiPropertyLoader(filename, priority); + StandardEsapiPropertyLoader otherPropertyLoader = new StandardEsapiPropertyLoader(filename, lowerPriority); + int value = testPropertyLoader.compareTo(otherPropertyLoader); + + // then + assertEquals(expectedValue, value); + } + + @Test + public void testGetIntProp() { + // given + String key = "int_property"; + int expectedValue = 5; + + // when + testPropertyLoader = new XmlEsapiPropertyLoader(filename, priority); + int value = testPropertyLoader.getIntProp(key); + + // then + assertEquals(expectedValue, value); + } + + @Test(expected = ConfigurationException.class) + public void testIntPropertyNotFound() throws ConfigurationException { + // given + String key = "non-existing-key"; + + // when + testPropertyLoader = new XmlEsapiPropertyLoader(filename, priority); + testPropertyLoader.getIntProp(key); + + // then expect exception + } + + @Test(expected = ConfigurationException.class) + public void testIncorrectIntPropertyType() { + // given + String key = "invalid_int_property"; + + // when + testPropertyLoader = new XmlEsapiPropertyLoader(filename, priority); + testPropertyLoader.getIntProp(key); + + // then expect exception + } + + @Test + public void testGetStringProp() { + // given + String key = "string_property"; + String expectedValue = "test_string_property"; + + // when + testPropertyLoader = new XmlEsapiPropertyLoader(filename, priority); + String value = testPropertyLoader.getStringProp(key); + + // then + assertEquals(expectedValue, value); + } + + @Test(expected = ConfigurationException.class) + public void testStringPropertyNotFound() throws ConfigurationException { + // given + String key = "non-existing-key"; + + // when + testPropertyLoader = new XmlEsapiPropertyLoader(filename, priority); + testPropertyLoader.getStringProp(key); + + // then expect exception + } + + @Test + public void testGetBooleanProp() { + // given + String key = "boolean_property"; + boolean expectedValue = true; + + // when + testPropertyLoader = new XmlEsapiPropertyLoader(filename, priority); + boolean value = testPropertyLoader.getBooleanProp(key); + + // then + assertEquals(expectedValue, value); + } + + @Test + public void testGetBooleanYesProperty() { + // given + String key = "boolean_yes_property"; + boolean expectedValue = true; + + // when + testPropertyLoader = new XmlEsapiPropertyLoader(filename, priority); + boolean value = testPropertyLoader.getBooleanProp(key); + + // then + assertEquals(expectedValue, value); + } + + @Test + public void testGetBooleanNoProperty() { + // given + String key = "boolean_no_property"; + boolean expectedValue = false; + + // when + testPropertyLoader = new XmlEsapiPropertyLoader(filename, priority); + boolean value = testPropertyLoader.getBooleanProp(key); + + // then + assertEquals(expectedValue, value); + } + + @Test(expected = ConfigurationException.class) + public void testBooleanPropertyNotFound() throws ConfigurationException { + // given + String key = "non-existing-key"; + + // when + testPropertyLoader = new XmlEsapiPropertyLoader(filename, priority); + testPropertyLoader.getBooleanProp(key); + + // then expect exception + } + + @Test(expected = ConfigurationException.class) + public void testIncorrectBooleanPropertyType() throws ConfigurationException { + // given + String key = "invalid_boolean_property"; + + // when + testPropertyLoader = new XmlEsapiPropertyLoader(filename, priority); + testPropertyLoader.getBooleanProp(key); + + // then expect exception + } + + @Test + public void testGetByteArrayProp() { + // given + String key = "string_property"; + byte[] expectedValue = new byte[0]; + try { + expectedValue = ESAPI.encoder().decodeFromBase64("test_string_property"); + } catch (IOException e) { + fail(e.getMessage()); + } + + // when + testPropertyLoader = new XmlEsapiPropertyLoader(filename, priority); + byte[] value = testPropertyLoader.getByteArrayProp(key); + + // then + assertEquals(expectedValue, value); + } + + @Test(expected = ConfigurationException.class) + public void testByteArrayPropertyNotFound() throws ConfigurationException { + // given + String key = "non-existing-key"; + + // when + testPropertyLoader = new XmlEsapiPropertyLoader(filename, priority); + testPropertyLoader.getByteArrayProp(key); + + // then expect exception + } + +} diff --git a/src/test/java/org/owasp/esapi/reference/HTTPUtilitiesTest.java b/src/test/java/org/owasp/esapi/reference/HTTPUtilitiesTest.java index 2c89a8e10..d86f98ba4 100644 --- a/src/test/java/org/owasp/esapi/reference/HTTPUtilitiesTest.java +++ b/src/test/java/org/owasp/esapi/reference/HTTPUtilitiesTest.java @@ -18,19 +18,10 @@ import junit.framework.Test; import junit.framework.TestCase; import junit.framework.TestSuite; -import org.owasp.esapi.Authenticator; -import org.owasp.esapi.ESAPI; -import org.owasp.esapi.EncoderConstants; -import org.owasp.esapi.HTTPUtilities; -import org.owasp.esapi.User; +import org.owasp.esapi.*; import org.owasp.esapi.codecs.Hex; import org.owasp.esapi.crypto.CipherText; -import org.owasp.esapi.crypto.PlainText; -import org.owasp.esapi.errors.AccessControlException; -import org.owasp.esapi.errors.AuthenticationException; -import org.owasp.esapi.errors.EncryptionException; -import org.owasp.esapi.errors.EnterpriseSecurityException; -import org.owasp.esapi.errors.ValidationException; +import org.owasp.esapi.errors.*; import org.owasp.esapi.http.MockHttpServletRequest; import org.owasp.esapi.http.MockHttpServletResponse; import org.owasp.esapi.http.MockHttpSession; @@ -506,3 +497,4 @@ public void testGetRequestAttribute() throws Exception { assertEquals( test2, 43f ); } } + diff --git a/src/test/java/org/owasp/esapi/reference/ValidatorTest.java b/src/test/java/org/owasp/esapi/reference/ValidatorTest.java index 8ac1e26b9..1c3916a41 100644 --- a/src/test/java/org/owasp/esapi/reference/ValidatorTest.java +++ b/src/test/java/org/owasp/esapi/reference/ValidatorTest.java @@ -15,20 +15,9 @@ */ package org.owasp.esapi.reference; -import java.io.BufferedReader; -import java.io.ByteArrayInputStream; -import java.io.File; -import java.io.IOException; -import java.io.InputStreamReader; -import java.io.UnsupportedEncodingException; -import java.text.DateFormat; -import java.text.SimpleDateFormat; -import java.util.*; - import junit.framework.Test; import junit.framework.TestCase; import junit.framework.TestSuite; - import org.owasp.esapi.*; import org.owasp.esapi.errors.ValidationException; import org.owasp.esapi.filters.SecurityWrapperRequest; @@ -38,7 +27,10 @@ import org.owasp.esapi.reference.validation.StringValidationRule; import javax.servlet.http.Cookie; -import javax.servlet.http.HttpServletRequest; +import java.io.*; +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.*; /** * The Class ValidatorTest. @@ -1148,3 +1140,4 @@ public void testGetContextPath() { assertFalse(ESAPI.validator().isValidInput("HTTPContextPath", "/\\nGET http://evil.com", "HTTPContextPath", 512, true)); } } + diff --git a/src/test/resources/ESAPI-properties.xsd b/src/test/resources/ESAPI-properties.xsd new file mode 100644 index 000000000..523d3a124 --- /dev/null +++ b/src/test/resources/ESAPI-properties.xsd @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/resources/esapi/ESAPI-test-2.properties b/src/test/resources/esapi/ESAPI-test-2.properties new file mode 100644 index 000000000..fb2e582b6 --- /dev/null +++ b/src/test/resources/esapi/ESAPI-test-2.properties @@ -0,0 +1,5 @@ +# Random properties for test purpose - all property keys could be found in ESAPI-test.properties but with +# different values. Used for test property files priorities. +string_property=test_string_property_2 +int_property=52 +boolean_property=false diff --git a/src/test/resources/esapi/ESAPI-test-2.xml b/src/test/resources/esapi/ESAPI-test-2.xml new file mode 100644 index 000000000..bec5597a4 --- /dev/null +++ b/src/test/resources/esapi/ESAPI-test-2.xml @@ -0,0 +1,6 @@ + + + test_string_property_2 + 52 + false + \ No newline at end of file diff --git a/src/test/resources/esapi/ESAPI-test-invalid-content.xml b/src/test/resources/esapi/ESAPI-test-invalid-content.xml new file mode 100644 index 000000000..d6dc1f74b --- /dev/null +++ b/src/test/resources/esapi/ESAPI-test-invalid-content.xml @@ -0,0 +1,7 @@ + + + test_string_property + 5 + true + invalid + \ No newline at end of file diff --git a/src/test/resources/esapi/ESAPI-test.properties b/src/test/resources/esapi/ESAPI-test.properties new file mode 100644 index 000000000..72dd9e50a --- /dev/null +++ b/src/test/resources/esapi/ESAPI-test.properties @@ -0,0 +1,8 @@ +# random properties for test purpose +string_property=test_string_property +int_property=5 +invalid_int_property=invalid int +boolean_property=true +boolean_yes_property=yes +boolean_no_property=no +invalid_boolean_property=invalid boolean \ No newline at end of file diff --git a/src/test/resources/esapi/ESAPI-test.xml b/src/test/resources/esapi/ESAPI-test.xml new file mode 100644 index 000000000..097f87eaf --- /dev/null +++ b/src/test/resources/esapi/ESAPI-test.xml @@ -0,0 +1,10 @@ + + + test_string_property + 5 + invalid int + true + yes + no + invalid boolean + \ No newline at end of file From 6de54c2c88a9bf857dc967e08bdfdbf6a7ebc712 Mon Sep 17 00:00:00 2001 From: "Kevin W. Wall" Date: Thu, 7 Jan 2016 22:37:36 -0500 Subject: [PATCH 0020/1069] updated how to create issues for ESAPI/esapi-java/legacy No one appears to be using JIRA, so until / unless the Atlassian JIRA instance is automatically synchronized with the GitHub issues (which is apparently possible; any volunteers?), I'm striking out the instructions to use if for bug tracking and enhancement requests. --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index e4c8a3e3b..96ce300c8 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,8 @@ Wiki: https://www.owasp.org/index.php/Category:OWASP_Enterprise_Security_API Nightly Build: https://esapi.ci.cloudbees.com -JIRA: https://owasp-esapi.atlassian.net/browse/ESAPILEG +~~JIRA: https://owasp-esapi.atlassian.net/browse/ESAPILEG~~
Issues: Until further notice, use the GitHub issues for reporting bugs and enhancement requests. + Documentation: https://owasp-esapi.atlassian.net/wiki/display/ESAPILEG/ESAPI+Legacy (Coming Soon) From 00cd34b8c87018d44f898d20f3608caeb7dc184d Mon Sep 17 00:00:00 2001 From: kwwall Date: Thu, 7 Jan 2016 22:56:27 -0500 Subject: [PATCH 0021/1069] Fix #354. --- .../java/org/owasp/esapi/codecs/Base64.java | 23 +++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/owasp/esapi/codecs/Base64.java b/src/main/java/org/owasp/esapi/codecs/Base64.java index 3f2d384da..e470ac6e8 100644 --- a/src/main/java/org/owasp/esapi/codecs/Base64.java +++ b/src/main/java/org/owasp/esapi/codecs/Base64.java @@ -12,7 +12,8 @@ /** *

Encodes and decodes to and from Base64 notation.

- *

Homepage: http://iharder.net/base64.

+ *

Homepage: http://iharder.net/base64 + * (based on version 2.2.2).

* *

The options parameter, which appears in a few places, is used to pass * several pieces of information to the encoder. In the "higher level" methods such as @@ -30,7 +31,7 @@ * * *

- * Change Log: + * Original change Log: *

*
    *
  • v2.2.2 - Fixed encodeFileToFile and decodeFileToFile to use the @@ -1085,10 +1086,28 @@ public static byte[] decode( String s, int options ) * Attempts to decode Base64 data and deserialize a Java * Object within. Returns null if there was an error. * + *

    + * WARNING: Using this method to decode non-validated / + * untrusted data from a string and deserialize it into + * an object can potentially result in remote command + * injection vulnerabilities. Use at your own risk! + *

    + * * @param encodedObject The Base64 data to decode * @return The decoded and deserialized object * @since 1.5 + * + * @deprecated Because of security issues, this method will be + * removed from ESAPI in a future release and no substitute + * is planned. Because as of JDK 8 (in 1Q2016) there is + * currently no way to restrict which objects + * ObjectInputStream.readObject() + * may safely deserialize in the general case. Oracle + * may decide to address this deficiency in a future Java + * release, but until they do, there is no simple way for + * a general class library like ESAPI to address this. */ + @Deprecated public static Object decodeToObject( String encodedObject ) { // Decode and gunzip if necessary From 426ea65acaecf286665428cf931b41421f4a062b Mon Sep 17 00:00:00 2001 From: kwwall Date: Thu, 7 Jan 2016 23:03:58 -0500 Subject: [PATCH 0022/1069] Fix #351 as suggested by xeno6696. This really should probably be a tunable property in the ESAPI.properties file, but saving that for a different day. --- .../java/org/owasp/esapi/filters/SecurityWrapperRequest.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/owasp/esapi/filters/SecurityWrapperRequest.java b/src/main/java/org/owasp/esapi/filters/SecurityWrapperRequest.java index 2777e4253..acf5375a2 100644 --- a/src/main/java/org/owasp/esapi/filters/SecurityWrapperRequest.java +++ b/src/main/java/org/owasp/esapi/filters/SecurityWrapperRequest.java @@ -192,7 +192,8 @@ public String getHeader(String name) { String value = getHttpServletRequest().getHeader(name); String clean = ""; try { - clean = ESAPI.validator().getValidInput("HTTP header value: " + value, value, "HTTPHeaderValue", 150, true); + clean = ESAPI.validator().getValidInput("HTTP header value: " + + value, value, "HTTPHeaderValue", 200, true); } catch (ValidationException e) { // already logged } From 068cecb6a05edefe7d4c940303f26b0057fe98ee Mon Sep 17 00:00:00 2001 From: kwwall Date: Thu, 7 Jan 2016 23:09:42 -0500 Subject: [PATCH 0023/1069] Fix #341 as per xeno6696 patch. --- src/main/assembly/dist.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/assembly/dist.xml b/src/main/assembly/dist.xml index b192b906f..3b2ee8e57 100644 --- a/src/main/assembly/dist.xml +++ b/src/main/assembly/dist.xml @@ -37,7 +37,7 @@ configuration configuration - .esapi/**/* + esapi/**/* log4j.dtd log4j.xml properties/**/* @@ -52,4 +52,4 @@ false - \ No newline at end of file + From 2f65182d3c7e46e5912514899e539c130a674075 Mon Sep 17 00:00:00 2001 From: kwwall Date: Thu, 7 Jan 2016 23:30:48 -0500 Subject: [PATCH 0024/1069] Sort of fixed #330. Changed allowed HTTP name name length from 20 to 50. --- .../java/org/owasp/esapi/filters/SecurityWrapperResponse.java | 2 +- .../java/org/owasp/esapi/reference/DefaultHTTPUtilities.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/owasp/esapi/filters/SecurityWrapperResponse.java b/src/main/java/org/owasp/esapi/filters/SecurityWrapperResponse.java index efc001372..71a9e7080 100644 --- a/src/main/java/org/owasp/esapi/filters/SecurityWrapperResponse.java +++ b/src/main/java/org/owasp/esapi/filters/SecurityWrapperResponse.java @@ -444,7 +444,7 @@ public void setHeader(String name, String value) { try { String strippedName = StringUtilities.stripControls(name); String strippedValue = StringUtilities.stripControls(value); - String safeName = ESAPI.validator().getValidInput("setHeader", strippedName, "HTTPHeaderName", 20, false); + String safeName = ESAPI.validator().getValidInput("setHeader", strippedName, "HTTPHeaderName", 50, false); String safeValue = ESAPI.validator().getValidInput("setHeader", strippedValue, "HTTPHeaderValue", ESAPI.securityConfiguration().getMaxHttpHeaderSize(), false); getHttpServletResponse().setHeader(safeName, safeValue); } catch (ValidationException e) { diff --git a/src/main/java/org/owasp/esapi/reference/DefaultHTTPUtilities.java b/src/main/java/org/owasp/esapi/reference/DefaultHTTPUtilities.java index b68c613bc..afad650cd 100644 --- a/src/main/java/org/owasp/esapi/reference/DefaultHTTPUtilities.java +++ b/src/main/java/org/owasp/esapi/reference/DefaultHTTPUtilities.java @@ -871,7 +871,7 @@ public void setHeader(HttpServletResponse response, String name, String value) { try { String strippedName = StringUtilities.replaceLinearWhiteSpace(name); String strippedValue = StringUtilities.replaceLinearWhiteSpace(value); - String safeName = ESAPI.validator().getValidInput("setHeader", strippedName, "HTTPHeaderName", 20, false); + String safeName = ESAPI.validator().getValidInput("setHeader", strippedName, "HTTPHeaderName", 50, false); String safeValue = ESAPI.validator().getValidInput("setHeader", strippedValue, "HTTPHeaderValue", 500, false); response.setHeader(safeName, safeValue); } catch (ValidationException e) { From 165a00d8ece0f5ebcaf8aa913981fd0dddc16f72 Mon Sep 17 00:00:00 2001 From: kwwall Date: Fri, 8 Jan 2016 02:35:47 -0500 Subject: [PATCH 0025/1069] Upgrade log4j from 1.2.16 6o 1.2.17. Upgrade Apache Commons Collection from 3.2.1 to 3.2.2 to pick up patch for COLLECTIONS-580 issue. --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 79f870d2f..dea265185 100644 --- a/pom.xml +++ b/pom.xml @@ -162,13 +162,13 @@ commons-collections commons-collections - 3.2.1 + 3.2.2 compile log4j log4j - 1.2.16 + 1.2.17 compile jar From b108050e5dca3552d2d78db0958ecdfa9da5f285 Mon Sep 17 00:00:00 2001 From: kwwall Date: Fri, 8 Jan 2016 03:07:04 -0500 Subject: [PATCH 0026/1069] Changed regex for Validation.HTTPHeaderName to allow 50 char header name for fix #351. --- configuration/esapi/ESAPI.properties | 5 +++-- src/test/resources/esapi/ESAPI.properties | 5 +++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/configuration/esapi/ESAPI.properties b/configuration/esapi/ESAPI.properties index 35c9d0870..6511f0e15 100644 --- a/configuration/esapi/ESAPI.properties +++ b/configuration/esapi/ESAPI.properties @@ -440,7 +440,8 @@ Validator.HTTPParameterName=^[a-zA-Z0-9_]{1,32}$ Validator.HTTPParameterValue=^[a-zA-Z0-9.\\-\\/+=@_ ]*$ Validator.HTTPCookieName=^[a-zA-Z0-9\\-_]{1,32}$ Validator.HTTPCookieValue=^[a-zA-Z0-9\\-\\/+=_ ]*$ -Validator.HTTPHeaderName=^[a-zA-Z0-9\\-_]{1,32}$ +# Note that max header name capped at 150 in SecurityRequestWrapper! +Validator.HTTPHeaderName=^[a-zA-Z0-9\\-_]{1,50}$ Validator.HTTPHeaderValue=^[a-zA-Z0-9()\\-=\\*\\.\\?;,+\\/:&_ ]*$ Validator.HTTPContextPath=^\\/?[a-zA-Z0-9.\\-\\/_]*$ Validator.HTTPServletPath=^[a-zA-Z0-9.\\-\\/_]*$ @@ -456,4 +457,4 @@ Validator.DirectoryName=^[a-zA-Z0-9:/\\\\!@#$%^&{}\\[\\]()_+\\-=,.~'` ]{1,255}$ # Validation of dates. Controls whether or not 'lenient' dates are accepted. # See DataFormat.setLenient(boolean flag) for further details. -Validator.AcceptLenientDates=false \ No newline at end of file +Validator.AcceptLenientDates=false diff --git a/src/test/resources/esapi/ESAPI.properties b/src/test/resources/esapi/ESAPI.properties index 1435b7c6d..a33b532fa 100644 --- a/src/test/resources/esapi/ESAPI.properties +++ b/src/test/resources/esapi/ESAPI.properties @@ -440,7 +440,8 @@ Validator.HTTPScheme=^(http|https)$ Validator.HTTPServerName=^[a-zA-Z0-9_.\\-]*$ Validator.HTTPCookieName=^[a-zA-Z0-9\\-_]{1,32}$ Validator.HTTPCookieValue=^[a-zA-Z0-9\\-\\/+=_ ]*$ -Validator.HTTPHeaderName=^[a-zA-Z0-9\\-_]{1,32}$ +# Note that max header name capped at 150 in SecurityRequestWrapper! +Validator.HTTPHeaderName=^[a-zA-Z0-9\\-_]{1,50}$ Validator.HTTPHeaderValue=^[a-zA-Z0-9()\\-=\\*\\.\\?;,+\\/:&_ ]*$ Validator.HTTPServletPath=^[a-zA-Z0-9.\\-\\/_]*$ Validator.HTTPPath=^[a-zA-Z0-9.\\-_]*$ @@ -462,4 +463,4 @@ Validator.DirectoryName=^[a-zA-Z0-9:/\\\\!@#$%^&{}\\[\\]()_+\\-=,.~'` ]{1,255}$ # Validation of dates. Controls whether or not 'lenient' dates are accepted. # See DataFormat.setLenient(boolean flag) for further details. -Validator.AcceptLenientDates=false \ No newline at end of file +Validator.AcceptLenientDates=false From f2dd5d7050034a8c1b7c6f76dbf5ad2edf8daa1c Mon Sep 17 00:00:00 2001 From: kwwall Date: Fri, 8 Jan 2016 03:19:49 -0500 Subject: [PATCH 0027/1069] Fix #330. Fix #351. --- .../esapi/filters/SecurityWrapperRequest.java | 15 ++++++++++++--- .../org/owasp/esapi/reference/ValidatorTest.java | 14 +++++++++----- 2 files changed, 21 insertions(+), 8 deletions(-) diff --git a/src/main/java/org/owasp/esapi/filters/SecurityWrapperRequest.java b/src/main/java/org/owasp/esapi/filters/SecurityWrapperRequest.java index acf5375a2..f9b3a7cf0 100644 --- a/src/main/java/org/owasp/esapi/filters/SecurityWrapperRequest.java +++ b/src/main/java/org/owasp/esapi/filters/SecurityWrapperRequest.java @@ -38,6 +38,12 @@ import org.owasp.esapi.errors.ValidationException; import org.owasp.esapi.errors.AccessControlException; +// TODO: Parameterize these various lengths in calls to ESAPI.validator().getValidInput() +// so that they can be placed in ESAPI.properties file (or other property file, +// such as Validator.properties) rather than being hard-coded. This is desirable +// because many of the values are well less than the commonly accepted maximum +// values. + /** * This request wrapper simply overrides unsafe methods in the * HttpServletRequest API with safe versions that return canonicalized data @@ -157,9 +163,13 @@ public Cookie[] getCookies() { n.setMaxAge(maxAge); if (domain != null) { + // kww TODO: HTTPHeaderValue seems way too liberal of a regex for cookie domain + // as it allows invalid characters for a domain name. Maybe create a new custom + // HTTPCookieDomain regex??? n.setDomain(ESAPI.validator().getValidInput("Cookie domain: " + domain, domain, "HTTPHeaderValue", 200, false)); } if (path != null) { + // kww TODO: OPEN ISSUE: Would not HTTPServletPath make more sense here??? n.setPath(ESAPI.validator().getValidInput("Cookie path: " + path, path, "HTTPHeaderValue", 200, false)); } newCookies.add(n); @@ -192,8 +202,7 @@ public String getHeader(String name) { String value = getHttpServletRequest().getHeader(name); String clean = ""; try { - clean = ESAPI.validator().getValidInput("HTTP header value: " + - value, value, "HTTPHeaderValue", 200, true); + clean = ESAPI.validator().getValidInput("HTTP header value: " + value, value, "HTTPHeaderValue", 200, true); } catch (ValidationException e) { // already logged } @@ -233,7 +242,7 @@ public Enumeration getHeaders(String name) { while (en.hasMoreElements()) { try { String value = (String) en.nextElement(); - String clean = ESAPI.validator().getValidInput("HTTP header value (" + name + "): " + value, value, "HTTPHeaderValue", 150, true); + String clean = ESAPI.validator().getValidInput("HTTP header value (" + name + "): " + value, value, "HTTPHeaderValue", 200, true); v.add(clean); } catch (ValidationException e) { // already logged diff --git a/src/test/java/org/owasp/esapi/reference/ValidatorTest.java b/src/test/java/org/owasp/esapi/reference/ValidatorTest.java index 1c3916a41..8c37835d9 100644 --- a/src/test/java/org/owasp/esapi/reference/ValidatorTest.java +++ b/src/test/java/org/owasp/esapi/reference/ValidatorTest.java @@ -1078,8 +1078,8 @@ public void testGetHeader() { SecurityWrapperRequest safeRequest = new SecurityWrapperRequest(request); request.addHeader("p1", "login"); request.addHeader("f1", "XSS"); - request.addHeader("p2", generateStringOfLength(150)); - request.addHeader("f2", generateStringOfLength(151)); + request.addHeader("p2", generateStringOfLength(200)); // Upper limit increased from 150 -> 200, GitHub issue #351 + request.addHeader("f2", generateStringOfLength(201)); assertEquals(safeRequest.getHeader("p1"), request.getHeader("p1")); assertEquals(safeRequest.getHeader("p2"), request.getHeader("p2")); assertFalse(safeRequest.getHeader("f1").equals(request.getHeader("f1"))); @@ -1093,8 +1093,11 @@ public void testGetHeaderNames() { SecurityWrapperRequest safeRequest = new SecurityWrapperRequest(request); request.addHeader("d-49653-p", "pass"); request.addHeader("= 0 : "length must be >= 0"; + StringBuilder longString = new StringBuilder(length); for (int i = 0; i < length; i++) { longString.append("a"); } From 02aa0c7e49b5cb8171e0df2a0636a25fea9050a1 Mon Sep 17 00:00:00 2001 From: kwwall Date: Wed, 13 Jan 2016 02:00:01 -0500 Subject: [PATCH 0028/1069] Comments to help w/ Unicode regex matching as mentioned in issue #334. --- configuration/esapi/ESAPI.properties | 5 +++++ src/test/resources/esapi/ESAPI.properties | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/configuration/esapi/ESAPI.properties b/configuration/esapi/ESAPI.properties index 6511f0e15..baa8e6933 100644 --- a/configuration/esapi/ESAPI.properties +++ b/configuration/esapi/ESAPI.properties @@ -421,6 +421,11 @@ IntrusionDetector.org.owasp.esapi.errors.AuthenticationHostException.actions=log # This allows enterprises to specify both organizational standards as well as application specific # validation rules. # +# Use '\p{L}' (without the quotes) within the character class to match +# any Unicode LETTER. You can also use a range, like: \u00C0-\u017F +# You can also use any of the regex flags as documented at +# https://docs.oracle.com/javase/tutorial/essential/regex/pattern.html, e.g. (?u) +# Validator.ConfigurationFile=validation.properties # Validators used by ESAPI diff --git a/src/test/resources/esapi/ESAPI.properties b/src/test/resources/esapi/ESAPI.properties index a33b532fa..1a0506ebf 100644 --- a/src/test/resources/esapi/ESAPI.properties +++ b/src/test/resources/esapi/ESAPI.properties @@ -426,6 +426,11 @@ IntrusionDetector.org.owasp.esapi.errors.AuthenticationHostException.actions=log # This allows enterprises to specify both organizational standards as well as application specific # validation rules. # +# Use '\p{L}' (without the quotes) within the character class to match +# any Unicode LETTER. You can also use a range, like: \u00C0-\u017F +# You can also use any of the regex flags as documented at +# https://docs.oracle.com/javase/tutorial/essential/regex/pattern.html, e.g. (?u) +# Validator.ConfigurationFile=validation.properties # Validators used by ESAPI From c45325d0b51da5aa5faecab20cd4b5b2caaedf13 Mon Sep 17 00:00:00 2001 From: kwwall Date: Wed, 13 Jan 2016 02:01:42 -0500 Subject: [PATCH 0029/1069] Change assertions into what should have been explicit runtime checks and throw an appropriate exception if violated. --- .../org/owasp/esapi/crypto/CipherSpec.java | 34 +++++++++++++++---- .../org/owasp/esapi/crypto/CipherText.java | 25 ++++++++++++-- 2 files changed, 51 insertions(+), 8 deletions(-) diff --git a/src/main/java/org/owasp/esapi/crypto/CipherSpec.java b/src/main/java/org/owasp/esapi/crypto/CipherSpec.java index 556ef8edb..e8836a38b 100644 --- a/src/main/java/org/owasp/esapi/crypto/CipherSpec.java +++ b/src/main/java/org/owasp/esapi/crypto/CipherSpec.java @@ -45,6 +45,8 @@ public final class CipherSpec implements Serializable { private int keySize_ = ESAPI.securityConfiguration().getEncryptionKeyLength(); // In bits private int blockSize_ = 16; // In bytes! I.e., 128 bits!!! private byte[] iv_ = null; + + private boolean blockSizeExplicitlySet = false; // Used for check in setIV(). // Cipher transformation component. Format is ALG/MODE/PADDING private enum CipherTransformationComponent { ALG, MODE, PADDING } @@ -72,7 +74,7 @@ public CipherSpec(String cipherXform, int keySize, int blockSize, final byte[] i public CipherSpec(String cipherXform, int keySize, int blockSize) { // Note: Do NOT use // this(cipherXform, keySize, blockSize, null); - // because of assertion in setIV(). + // because of checks in setIV(). // setCipherTransformation(cipherXform); setKeySize(keySize); @@ -194,7 +196,9 @@ public String getCipherTransformation() { * @return This current {@code CipherSpec} object. */ public CipherSpec setKeySize(int keySize) { - assert keySize > 0 : "keySize must be > 0; keySize=" + keySize; + if ( keySize <= 0 ) { + throw new IllegalArgumentException("keySize must be > 0; keySize=" + keySize); + } this.keySize_ = keySize; return this; } @@ -209,12 +213,16 @@ public int getKeySize() { /** * Set the block size for this {@code CipherSpec}. - * @param blockSize The block size, in bytes. Must be positive integer. + * @param blockSize The block size, in bytes. Must be positive integer appropriate + * for the specified cipher algorithm. * @return This current {@code CipherSpec} object. */ public CipherSpec setBlockSize(int blockSize) { - assert blockSize > 0 : "blockSize must be > 0; blockSize=" + blockSize; + if ( blockSize <= 0 ) { + throw new IllegalArgumentException("blockSize must be > 0; blockSize=" + blockSize); + } this.blockSize_ = blockSize; + blockSizeExplicitlySet = true; return this; } @@ -266,9 +274,23 @@ public byte[] getIV() { * @return This current {@code CipherSpec} object. */ public CipherSpec setIV(final byte[] iv) { - assert requiresIV() && (iv != null && iv.length != 0) : "Required IV cannot be null or 0 length"; - // Don't store a reference, but make a copy! + if ( ! ( requiresIV() && (iv != null && iv.length != 0) ) ) { + throw new IllegalArgumentException("Required IV cannot be null or 0 length."); + } + + // Don't store a reference, but make a copy! When an IV is provided, it generally should + // be the same length as the block size of the cipher. if ( iv != null ) { // Allow null IV for ECB mode. + // TODO: FIXME: As per email from Jeff Walton to Kevin Wall dated 12/03/2013, + // this is not always true. E.g., for CCM, the IV length is supposed + // to be 7, 8, 9, 10, 11, 12, or 13 octets because of + // it's formatting function. +/*** + if ( iv.length != this.getBlockSize() && blockSizeExplicitlySet ) { + throw new IllegalArgumentException("IV must be same length as cipher block size (" + + this.getBlockSize() + " bytes)"); + } +***/ iv_ = new byte[ iv.length ]; CryptoHelper.copyByteArray(iv, iv_); } diff --git a/src/main/java/org/owasp/esapi/crypto/CipherText.java b/src/main/java/org/owasp/esapi/crypto/CipherText.java index 47e6fc78f..66242d78f 100644 --- a/src/main/java/org/owasp/esapi/crypto/CipherText.java +++ b/src/main/java/org/owasp/esapi/crypto/CipherText.java @@ -26,6 +26,7 @@ import org.owasp.esapi.Encryptor; import org.owasp.esapi.Logger; import org.owasp.esapi.errors.EncryptionException; +import org.owasp.esapi.errors.EnterpriseSecurityRuntimeException; // CHECKME: Some of these assertions probably should be actual runtime checks // with suitable exceptions to account for cases where programmers @@ -457,7 +458,18 @@ public boolean validateMAC(SecretKey authKey) { // compare to stored value (separate_mac_). If same, then return true, // else return false. byte[] mac = computeMAC(authKey); - assert mac.length == separate_mac_.length : "MACs are of differnt lengths. Should both be the same."; + if ( mac.length != separate_mac_.length ) { + // Note: We want some type of unchecked exception + // here so this will not require code changes. + // Unfortunately, EncryptionException, which might + // make more sense here, is not a RuntimeException. + String exm = "MACs are of different lengths. " + + "Should both be the same length"; + throw new EnterpriseSecurityRuntimeException(exm, + "Possible tampering of MAC? " + exm + + "computed MAC len: " + mac.length + + ", received MAC len: " + separate_mac_.length); + } return CryptoHelper.arrayCompare(mac, separate_mac_); // Safe compare!!! } else if ( ! requiresMAC ) { // Doesn't require a MAC return true; @@ -593,6 +605,9 @@ int kdfPRFAsInt() { public void setKDF_PRF(int prfSelection) { assert prfSelection >= 0 && prfSelection <= 15 : "kdfPrf == " + prfSelection + " must be between 0 and 15."; + if ( prfSelection < 0 || prfSelection > 15 ) { + throw new IllegalArgumentException("kdfPrf == " + prfSelection + " must be between 0 and 15, inclusive."); + } kdfPrfSelection_ = prfSelection; } @@ -629,7 +644,9 @@ private void setEncryptionTimestamp() { * January 1, 1970 GMT). */ // Package level access. ESAPI jar should be sealed and signed. void setEncryptionTimestamp(long timestamp) { - assert timestamp > 0 : "Timestamp must be greater than zero."; + if ( timestamp <= 0 ) { + throw new IllegalArgumentException("Timestamp must be greater than zero."); + } if ( encryption_timestamp_ == 0 ) { // Only set it if it's not yet been set. logger.warning(Logger.EVENT_FAILURE, "Attempt to reset non-zero " + "CipherText encryption timestamp to " + new Date(timestamp) + "!"); @@ -767,6 +784,10 @@ protected boolean canEqual(Object other) { * @return The value for the MAC. */ private byte[] computeMAC(SecretKey authKey) { + // These assertions are okay and leaving them as assertions rather than + // changing the to conditional statements that throw should be all right + // because this is private method and presumably we should have already + // checked things in the public or protected methods where appropriate. assert raw_ciphertext_ != null && raw_ciphertext_.length != 0 : "Raw ciphertext may not be null or empty."; assert authKey != null && authKey.getEncoded().length != 0 : "Authenticity secret key may not be null or zero length."; try { From 79a3f593abf8dcf0b26a12b712be65e756bcde11 Mon Sep 17 00:00:00 2001 From: kwwall Date: Wed, 13 Jan 2016 02:07:25 -0500 Subject: [PATCH 0030/1069] Closes #318. --- src/main/java/org/owasp/esapi/waf/rules/RuleUtil.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/owasp/esapi/waf/rules/RuleUtil.java b/src/main/java/org/owasp/esapi/waf/rules/RuleUtil.java index 45201014f..e2a216707 100644 --- a/src/main/java/org/owasp/esapi/waf/rules/RuleUtil.java +++ b/src/main/java/org/owasp/esapi/waf/rules/RuleUtil.java @@ -76,7 +76,7 @@ public static boolean isInList(Collection c, String s) { } else if ( o instanceof Double ) { try { - if ( Double.parseDouble(s) == ((Double)o).doubleValue() ) { + if ( Double.compare(Double.parseDouble(s), ((Double)o).doubleValue()) == 0 ) { return true; } } catch (Exception e) {} @@ -121,7 +121,7 @@ public static boolean isInList(Enumeration en, String s) { } else if ( o instanceof Double ) { try { - if ( Double.parseDouble(s) == ((Double)o).doubleValue() ) { + if ( Double.compare(Double.parseDouble(s), ((Double)o).doubleValue()) == 0 ) { return true; } } catch (Exception e) {} From dffaddbb59946fb4ba46d5fb97830af277b408d7 Mon Sep 17 00:00:00 2001 From: kwwall Date: Wed, 13 Jan 2016 02:14:11 -0500 Subject: [PATCH 0031/1069] Closes issue #319. --- .../ESAPIWebApplicationFirewallFilter.java | 973 +++++++++--------- 1 file changed, 507 insertions(+), 466 deletions(-) diff --git a/src/main/java/org/owasp/esapi/waf/ESAPIWebApplicationFirewallFilter.java b/src/main/java/org/owasp/esapi/waf/ESAPIWebApplicationFirewallFilter.java index 4563c0d57..82bd7e079 100644 --- a/src/main/java/org/owasp/esapi/waf/ESAPIWebApplicationFirewallFilter.java +++ b/src/main/java/org/owasp/esapi/waf/ESAPIWebApplicationFirewallFilter.java @@ -1,466 +1,507 @@ -/** - * OWASP Enterprise Security API (ESAPI) - * - * This file is part of the Open Web Application Security Project (OWASP) - * Enterprise Security API (ESAPI) project. For details, please see - * http://www.owasp.org/index.php/ESAPI. - * - * Copyright (c) 2009 - The OWASP Foundation - * - * The ESAPI is published by OWASP under the BSD license. You should read and accept the - * LICENSE before you use, modify, and/or redistribute this software. - * - * @author Arshan Dabirsiaghi Aspect Security - * @created 2009 - */ -package org.owasp.esapi.waf; - -import java.io.File; - -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.util.List; - -import javax.servlet.Filter; -import javax.servlet.FilterChain; -import javax.servlet.FilterConfig; -import javax.servlet.ServletException; -import javax.servlet.ServletRequest; -import javax.servlet.ServletResponse; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -import org.apache.commons.fileupload.FileUploadException; -import org.apache.log4j.xml.DOMConfigurator; -import org.owasp.esapi.ESAPI; -import org.owasp.esapi.Logger; -import org.owasp.esapi.waf.actions.Action; -import org.owasp.esapi.waf.actions.BlockAction; -import org.owasp.esapi.waf.actions.DefaultAction; -import org.owasp.esapi.waf.actions.RedirectAction; -import org.owasp.esapi.waf.configuration.AppGuardianConfiguration; -import org.owasp.esapi.waf.configuration.ConfigurationParser; -import org.owasp.esapi.waf.internal.InterceptingHTTPServletRequest; -import org.owasp.esapi.waf.internal.InterceptingHTTPServletResponse; -import org.owasp.esapi.waf.rules.Rule; - -/** - * This is the main class for the ESAPI Web Application Firewall (WAF). It is a standard J2EE servlet filter - * that, in different methods, invokes the reading of the configuration file and handles the runtime processing - * and enforcing of the developer-specified rules. - * - * Ideally the filter should be configured to catch all requests (/*) in web.xml. If there are URL segments that - * need to be extremely fast and don't require any protection, the pattern may be modified with extreme caution. - * - * @author Arshan Dabirsiaghi - * - */ -public class ESAPIWebApplicationFirewallFilter implements Filter { - - private AppGuardianConfiguration appGuardConfig; - - private static final String CONFIGURATION_FILE_PARAM = "configuration"; - private static final String LOGGING_FILE_PARAM = "log_settings"; - private static final String POLLING_TIME_PARAM = "polling_time"; - - private static final int DEFAULT_POLLING_TIME = 30000; - - private String configurationFilename = null; - - private long pollingTime; - - private long lastConfigReadTime; - - //private static final String FAUX_SESSION_COOKIE = "FAUXSC"; - //private static final String SESSION_COOKIE_CANARY = "org.owasp.esapi.waf.canary"; - - private FilterConfig fc; - - private final Logger logger = ESAPI.getLogger(ESAPIWebApplicationFirewallFilter.class); - - /** - * This function is used in testing to dynamically alter the configuration. - * @param policyFilePath The path to the policy file - * @param webRootDir The root directory of the web application. - * @throws FileNotFoundException if the policy file cannot be located - */ - public void setConfiguration( String policyFilePath, String webRootDir ) throws FileNotFoundException { - try { - appGuardConfig = ConfigurationParser.readConfigurationFile(new FileInputStream(new File(policyFilePath)), webRootDir); - lastConfigReadTime = System.currentTimeMillis(); - configurationFilename = policyFilePath; - } catch (ConfigurationException e ) { - // TODO: It would be ideal if this method through the ConfigurationException rather than catching it and - // writing the error to the console. - e.printStackTrace(); - } - } - - public AppGuardianConfiguration getConfiguration() { - return appGuardConfig; - } - - - /** - * - * This function is invoked at application startup and when the configuration file - * polling period has elapsed and a change in the configuration file has been detected. - * - * It's main purpose is to read the configuration file and establish the configuration - * object model for use at runtime during the doFilter() method. - */ - public void init(FilterConfig fc) throws ServletException { - - /* - * This variable is saved so that we can retrieve it later to re-invoke this function. - */ - this.fc = fc; - - logger.debug(Logger.EVENT_SUCCESS, ">> Initializing WAF" ); - /* - * Pull logging file. - */ - - // Demoted scope to a local since this is the only place it is referenced - String logSettingsFilename = fc.getInitParameter(LOGGING_FILE_PARAM); - - String realLogSettingsFilename = fc.getServletContext().getRealPath(logSettingsFilename); - - if ( realLogSettingsFilename == null || (! new File(realLogSettingsFilename).exists()) ) { - throw new ServletException("[ESAPI WAF] Could not find log file at resolved path: " + realLogSettingsFilename); - } - - /* - * Pull main configuration file. - */ - - configurationFilename = fc.getInitParameter(CONFIGURATION_FILE_PARAM); - - configurationFilename = fc.getServletContext().getRealPath(configurationFilename); - - if ( configurationFilename == null || ! new File(configurationFilename).exists() ) { - throw new ServletException("[ESAPI WAF] Could not find configuration file at resolved path: " + configurationFilename); - } - - /* - * Find out polling time from a parameter. If none is provided, use - * the default (10 seconds). - */ - - String sPollingTime = fc.getInitParameter(POLLING_TIME_PARAM); - - if ( sPollingTime != null ) { - pollingTime = Long.parseLong(sPollingTime); - } else { - pollingTime = DEFAULT_POLLING_TIME; - } - - /* - * Open up configuration file and populate the AppGuardian configuration object. - */ - - try { - - String webRootDir = fc.getServletContext().getRealPath("/"); - appGuardConfig = ConfigurationParser.readConfigurationFile(new FileInputStream(configurationFilename),webRootDir); - - DOMConfigurator.configure(realLogSettingsFilename); - - lastConfigReadTime = System.currentTimeMillis(); - - } catch (FileNotFoundException e) { - throw new ServletException(e); - } catch (ConfigurationException e) { - throw new ServletException(e); - } - - } - - - /** - * This is the where the main interception and rule-checking logic of the WAF resides. - */ - public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, - FilterChain chain) throws IOException, ServletException { - - /* - * Check to see if polling time has elapsed. If it has, that means - * we should check to see if the config file has been changed. If - * it has, then re-read it. - * - * // TODO: Any reason this logic shouldn't be moved into a polling thread class? - */ - - if ( (System.currentTimeMillis() - lastConfigReadTime) > pollingTime ) { - File f = new File(configurationFilename); - long lastModified = f.lastModified(); - if ( lastModified > lastConfigReadTime ) { - /* - * The file has been altered since it was - * read in the last time. Must re-read it. - */ - logger.debug(Logger.EVENT_SUCCESS, ">> Re-reading WAF policy"); - init(fc); - } - } - - logger.debug(Logger.EVENT_SUCCESS, ">>In WAF doFilter"); - - HttpServletRequest httpRequest = (HttpServletRequest)servletRequest; - HttpServletResponse httpResponse = (HttpServletResponse)servletResponse; - - InterceptingHTTPServletRequest request = null; - InterceptingHTTPServletResponse response = null; - - /* - * First thing to do is create the InterceptingHTTPServletResponse, since - * we'll need that possibly before the InterceptingHTTPServletRequest. - * - * The normal HttpRequest-type objects will suffice us until we get to - * stage 2. - * - * 1st argument = the response to base the instance on - * 2nd argument = should we bother intercepting the egress response? - * 3rd argument = cookie rules because thats where they mostly get acted on - */ - - if ( appGuardConfig.getCookieRules().size() + - appGuardConfig.getBeforeResponseRules().size() > 0) { - response = new InterceptingHTTPServletResponse(httpResponse, true, appGuardConfig.getCookieRules()); - } - - /* - * Stage 1: Rules that do not need the request body. - */ - logger.debug(Logger.EVENT_SUCCESS, ">> Starting stage 1" ); - - List rules = this.appGuardConfig.getBeforeBodyRules(); - - for(int i=0;i> Starting Stage 2" ); - - rules = this.appGuardConfig.getAfterBodyRules(); - - for(int i=0;i> Calling the FilterChain: " + chain ); - chain.doFilter(request, response != null ? response : httpResponse); - - /* - * Stage 3: Before the response has been sent back to the user. - */ - logger.debug(Logger.EVENT_SUCCESS, ">> Starting Stage 3" ); - - rules = this.appGuardConfig.getBeforeResponseRules(); - - for(int i=0;i>> committing reponse" ); - response.commit(); - } - } - - /* - * Utility method to send HTTP redirects that automatically determines which response class to use. - */ - private void sendRedirect(InterceptingHTTPServletResponse response, - HttpServletResponse httpResponse, String redirectURL) throws IOException { - - if ( response != null ) { // if we've been buffering everything we clean it all out before sending back. - response.reset(); - response.resetBuffer(); - response.sendRedirect(redirectURL); - response.commit(); - } else { - httpResponse.sendRedirect(redirectURL); - } - - } - - public void destroy() { - /* - * Any cleanup necessary? - */ - } - - - private void sendRedirect(InterceptingHTTPServletResponse response, HttpServletResponse httpResponse) throws IOException { - /* [chrisisbeef] - commented out as this is not currently used. Minor performance tweak. - String finalJavaScript = AppGuardianConfiguration.JAVASCRIPT_REDIRECT; - finalJavaScript = finalJavaScript.replaceAll(AppGuardianConfiguration.JAVASCRIPT_TARGET_TOKEN, appGuardConfig.getDefaultErrorPage()); - */ - - if ( response != null ) { - response.reset(); - response.resetBuffer(); - /* - response.setStatus(appGuardConfig.getDefaultResponseCode()); - response.getOutputStream().write(finalJavaScript.getBytes()); - */ - response.sendRedirect(appGuardConfig.getDefaultErrorPage()); - - } else { - if ( ! httpResponse.isCommitted() ) { - httpResponse.sendRedirect(appGuardConfig.getDefaultErrorPage()); - } else { - /* - * Can't send redirect because response is already committed. I'm not sure - * how this could happen, but I didn't want to cause IOExceptions in case - * if it ever does. - */ - } - - } - } - -} +/** + * OWASP Enterprise Security API (ESAPI) + * + * This file is part of the Open Web Application Security Project (OWASP) + * Enterprise Security API (ESAPI) project. For details, please see + * http://www.owasp.org/index.php/ESAPI. + * + * Copyright (c) 2009 - The OWASP Foundation + * + * The ESAPI is published by OWASP under the BSD license. You should read and accept the + * LICENSE before you use, modify, and/or redistribute this software. + * + * @author Arshan Dabirsiaghi Aspect Security + * @created 2009 + */ +package org.owasp.esapi.waf; + +import java.io.File; + +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.util.List; + +import javax.servlet.Filter; +import javax.servlet.FilterChain; +import javax.servlet.FilterConfig; +import javax.servlet.ServletException; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.apache.commons.fileupload.FileUploadException; +import org.apache.log4j.xml.DOMConfigurator; +import org.owasp.esapi.ESAPI; +import org.owasp.esapi.Logger; +import org.owasp.esapi.waf.actions.Action; +import org.owasp.esapi.waf.actions.BlockAction; +import org.owasp.esapi.waf.actions.DefaultAction; +import org.owasp.esapi.waf.actions.RedirectAction; +import org.owasp.esapi.waf.configuration.AppGuardianConfiguration; +import org.owasp.esapi.waf.configuration.ConfigurationParser; +import org.owasp.esapi.waf.internal.InterceptingHTTPServletRequest; +import org.owasp.esapi.waf.internal.InterceptingHTTPServletResponse; +import org.owasp.esapi.waf.rules.Rule; + +/** + * This is the main class for the ESAPI Web Application Firewall (WAF). It is a + * standard J2EE servlet filter that, in different methods, invokes the reading + * of the configuration file and handles the runtime processing and enforcing of + * the developer-specified rules. + * + * Ideally the filter should be configured to catch all requests (/*) in + * web.xml. If there are URL segments that need to be extremely fast and don't + * require any protection, the pattern may be modified with extreme caution. + * + * @author Arshan Dabirsiaghi + * + */ +public class ESAPIWebApplicationFirewallFilter implements Filter { + + private AppGuardianConfiguration appGuardConfig; + + private static final String CONFIGURATION_FILE_PARAM = "configuration"; + private static final String LOGGING_FILE_PARAM = "log_settings"; + private static final String POLLING_TIME_PARAM = "polling_time"; + + private static final int DEFAULT_POLLING_TIME = 30000; + + private String configurationFilename = null; + + private long pollingTime; + + private long lastConfigReadTime; + + // private static final String FAUX_SESSION_COOKIE = "FAUXSC"; + // private static final String SESSION_COOKIE_CANARY = + // "org.owasp.esapi.waf.canary"; + + private FilterConfig fc; + + private final Logger logger = ESAPI.getLogger(ESAPIWebApplicationFirewallFilter.class); + + /** + * This function is used in testing to dynamically alter the configuration. + * + * @param policyFilePath + * The path to the policy file + * @param webRootDir + * The root directory of the web application. + * @throws FileNotFoundException + * if the policy file cannot be located + */ + public void setConfiguration(String policyFilePath, String webRootDir) throws FileNotFoundException { + + FileInputStream inputStream = null; + + try { + inputStream = new FileInputStream(new File(policyFilePath)); + appGuardConfig = ConfigurationParser.readConfigurationFile(inputStream, webRootDir); + lastConfigReadTime = System.currentTimeMillis(); + configurationFilename = policyFilePath; + } catch (ConfigurationException e) { + // TODO: It would be ideal if this method through the + // ConfigurationException rather than catching it and + // writing the error to the console. + e.printStackTrace(); + } finally { + if (inputStream != null) { + try { + inputStream.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + } + + public AppGuardianConfiguration getConfiguration() { + return appGuardConfig; + } + + /** + * + * This function is invoked at application startup and when the + * configuration file polling period has elapsed and a change in the + * configuration file has been detected. + * + * It's main purpose is to read the configuration file and establish the + * configuration object model for use at runtime during the + * doFilter() method. + */ + public void init(FilterConfig fc) throws ServletException { + + /* + * This variable is saved so that we can retrieve it later to re-invoke + * this function. + */ + this.fc = fc; + + logger.debug(Logger.EVENT_SUCCESS, ">> Initializing WAF"); + /* + * Pull logging file. + */ + + // Demoted scope to a local since this is the only place it is + // referenced + String logSettingsFilename = fc.getInitParameter(LOGGING_FILE_PARAM); + + String realLogSettingsFilename = fc.getServletContext().getRealPath(logSettingsFilename); + + if (realLogSettingsFilename == null || (!new File(realLogSettingsFilename).exists())) { + throw new ServletException( + "[ESAPI WAF] Could not find log file at resolved path: " + realLogSettingsFilename); + } + + /* + * Pull main configuration file. + */ + + configurationFilename = fc.getInitParameter(CONFIGURATION_FILE_PARAM); + + configurationFilename = fc.getServletContext().getRealPath(configurationFilename); + + if (configurationFilename == null || !new File(configurationFilename).exists()) { + throw new ServletException( + "[ESAPI WAF] Could not find configuration file at resolved path: " + configurationFilename); + } + + /* + * Find out polling time from a parameter. If none is provided, use the + * default (10 seconds). + */ + + String sPollingTime = fc.getInitParameter(POLLING_TIME_PARAM); + + if (sPollingTime != null) { + pollingTime = Long.parseLong(sPollingTime); + } else { + pollingTime = DEFAULT_POLLING_TIME; + } + + /* + * Open up configuration file and populate the AppGuardian configuration + * object. + */ + + FileInputStream inputStream = null; + + try { + String webRootDir = fc.getServletContext().getRealPath("/"); + inputStream = new FileInputStream(configurationFilename); + appGuardConfig = ConfigurationParser.readConfigurationFile(inputStream, webRootDir); + DOMConfigurator.configure(realLogSettingsFilename); + lastConfigReadTime = System.currentTimeMillis(); + } catch (FileNotFoundException e) { + throw new ServletException(e); + } catch (ConfigurationException e) { + throw new ServletException(e); + } finally { + if (inputStream != null) { + try { + inputStream.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + } + + /** + * This is the where the main interception and rule-checking logic of the + * WAF resides. + */ + public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain) + throws IOException, ServletException { + + /* + * Check to see if polling time has elapsed. If it has, that means we + * should check to see if the config file has been changed. If it has, + * then re-read it. + * + * // TODO: Any reason this logic shouldn't be moved into a polling + * thread class? + */ + + if ((System.currentTimeMillis() - lastConfigReadTime) > pollingTime) { + File f = new File(configurationFilename); + long lastModified = f.lastModified(); + if (lastModified > lastConfigReadTime) { + /* + * The file has been altered since it was read in the last time. + * Must re-read it. + */ + logger.debug(Logger.EVENT_SUCCESS, ">> Re-reading WAF policy"); + init(fc); + } + } + + logger.debug(Logger.EVENT_SUCCESS, ">>In WAF doFilter"); + + HttpServletRequest httpRequest = (HttpServletRequest) servletRequest; + HttpServletResponse httpResponse = (HttpServletResponse) servletResponse; + + InterceptingHTTPServletRequest request = null; + InterceptingHTTPServletResponse response = null; + + /* + * First thing to do is create the InterceptingHTTPServletResponse, + * since we'll need that possibly before the + * InterceptingHTTPServletRequest. + * + * The normal HttpRequest-type objects will suffice us until we get to + * stage 2. + * + * 1st argument = the response to base the instance on 2nd argument = + * should we bother intercepting the egress response? 3rd argument = + * cookie rules because thats where they mostly get acted on + */ + + if (appGuardConfig.getCookieRules().size() + appGuardConfig.getBeforeResponseRules().size() > 0) { + response = new InterceptingHTTPServletResponse(httpResponse, true, appGuardConfig.getCookieRules()); + } + + /* + * Stage 1: Rules that do not need the request body. + */ + logger.debug(Logger.EVENT_SUCCESS, ">> Starting stage 1"); + + List rules = this.appGuardConfig.getBeforeBodyRules(); + + for (int i = 0; i < rules.size(); i++) { + + Rule rule = rules.get(i); + logger.debug(Logger.EVENT_SUCCESS, " Applying BEFORE rule: " + rule.getClass().getName()); + + /* + * The rules execute in check(). The check() method will also log. + * All we have to do is decide what other actions to take. + */ + Action action = rule.check(httpRequest, response, httpResponse); + + if (action.isActionNecessary()) { + + if (action instanceof BlockAction) { + if (response != null) { + response.setStatus(appGuardConfig.getDefaultResponseCode()); + } else { + httpResponse.setStatus(appGuardConfig.getDefaultResponseCode()); + } + return; + + } else if (action instanceof RedirectAction) { + sendRedirect(response, httpResponse, ((RedirectAction) action).getRedirectURL()); + return; + + } else if (action instanceof DefaultAction) { + + switch (AppGuardianConfiguration.DEFAULT_FAIL_ACTION) { + case AppGuardianConfiguration.BLOCK: + if (response != null) { + response.setStatus(appGuardConfig.getDefaultResponseCode()); + } else { + httpResponse.setStatus(appGuardConfig.getDefaultResponseCode()); + } + return; + + case AppGuardianConfiguration.REDIRECT: + sendRedirect(response, httpResponse); + return; + } + } + } + } + + /* + * Create the InterceptingHTTPServletRequest. + */ + + try { + request = new InterceptingHTTPServletRequest((HttpServletRequest) servletRequest); + } catch (FileUploadException fue) { + logger.error(Logger.EVENT_SUCCESS, "Error Wrapping Request", fue); + } + + /* + * Stage 2: After the body has been read, but before the the application + * has gotten it. + */ + logger.debug(Logger.EVENT_SUCCESS, ">> Starting Stage 2"); + + rules = this.appGuardConfig.getAfterBodyRules(); + + for (int i = 0; i < rules.size(); i++) { + + Rule rule = rules.get(i); + logger.debug(Logger.EVENT_SUCCESS, " Applying BEFORE CHAIN rule: " + rule.getClass().getName()); + + /* + * The rules execute in check(). The check() method will take care + * of logging. All we have to do is decide what other actions to + * take. + */ + Action action = rule.check(request, response, httpResponse); + + if (action.isActionNecessary()) { + + if (action instanceof BlockAction) { + if (response != null) { + response.setStatus(appGuardConfig.getDefaultResponseCode()); + } else { + httpResponse.setStatus(appGuardConfig.getDefaultResponseCode()); + } + return; + + } else if (action instanceof RedirectAction) { + sendRedirect(response, httpResponse, ((RedirectAction) action).getRedirectURL()); + return; + + } else if (action instanceof DefaultAction) { + + switch (AppGuardianConfiguration.DEFAULT_FAIL_ACTION) { + case AppGuardianConfiguration.BLOCK: + if (response != null) { + response.setStatus(appGuardConfig.getDefaultResponseCode()); + } else { + httpResponse.setStatus(appGuardConfig.getDefaultResponseCode()); + } + return; + + case AppGuardianConfiguration.REDIRECT: + sendRedirect(response, httpResponse); + return; + } + } + } + } + + /* + * In between stages 2 and 3 is the application's processing of the + * input. + */ + logger.debug(Logger.EVENT_SUCCESS, ">> Calling the FilterChain: " + chain); + chain.doFilter(request, response != null ? response : httpResponse); + + /* + * Stage 3: Before the response has been sent back to the user. + */ + logger.debug(Logger.EVENT_SUCCESS, ">> Starting Stage 3"); + + rules = this.appGuardConfig.getBeforeResponseRules(); + + for (int i = 0; i < rules.size(); i++) { + + Rule rule = rules.get(i); + logger.debug(Logger.EVENT_SUCCESS, " Applying AFTER CHAIN rule: " + rule.getClass().getName()); + + /* + * The rules execute in check(). The check() method will also log. + * All we have to do is decide what other actions to take. + */ + Action action = rule.check(request, response, httpResponse); + + if (action.isActionNecessary()) { + + if (action instanceof BlockAction) { + if (response != null) { + response.setStatus(appGuardConfig.getDefaultResponseCode()); + } else { + httpResponse.setStatus(appGuardConfig.getDefaultResponseCode()); + } + return; + + } else if (action instanceof RedirectAction) { + sendRedirect(response, httpResponse, ((RedirectAction) action).getRedirectURL()); + return; + + } else if (action instanceof DefaultAction) { + + switch (AppGuardianConfiguration.DEFAULT_FAIL_ACTION) { + case AppGuardianConfiguration.BLOCK: + if (response != null) { + response.setStatus(appGuardConfig.getDefaultResponseCode()); + } else { + httpResponse.setStatus(appGuardConfig.getDefaultResponseCode()); + } + return; + + case AppGuardianConfiguration.REDIRECT: + sendRedirect(response, httpResponse); + return; + } + } + } + } + + /* + * Now that we've run our last set of rules we can allow the response to + * go through if we were intercepting. + */ + + if (response != null) { + logger.debug(Logger.EVENT_SUCCESS, ">>> committing reponse"); + response.commit(); + } + } + + /* + * Utility method to send HTTP redirects that automatically determines which + * response class to use. + */ + private void sendRedirect(InterceptingHTTPServletResponse response, HttpServletResponse httpResponse, + String redirectURL) throws IOException { + + if (response != null) { // if we've been buffering everything we clean + // it all out before sending back. + response.reset(); + response.resetBuffer(); + response.sendRedirect(redirectURL); + response.commit(); + } else { + httpResponse.sendRedirect(redirectURL); + } + + } + + public void destroy() { + /* + * Any cleanup necessary? + */ + } + + private void sendRedirect(InterceptingHTTPServletResponse response, HttpServletResponse httpResponse) + throws IOException { + /* + * [chrisisbeef] - commented out as this is not currently used. Minor + * performance tweak. String finalJavaScript = + * AppGuardianConfiguration.JAVASCRIPT_REDIRECT; finalJavaScript = + * finalJavaScript.replaceAll(AppGuardianConfiguration. + * JAVASCRIPT_TARGET_TOKEN, appGuardConfig.getDefaultErrorPage()); + */ + + if (response != null) { + response.reset(); + response.resetBuffer(); + /* + * response.setStatus(appGuardConfig.getDefaultResponseCode()); + * response.getOutputStream().write(finalJavaScript.getBytes()); + */ + response.sendRedirect(appGuardConfig.getDefaultErrorPage()); + + } else { + if (!httpResponse.isCommitted()) { + httpResponse.sendRedirect(appGuardConfig.getDefaultErrorPage()); + } else { + /* + * Can't send redirect because response is already committed. + * I'm not sure how this could happen, but I didn't want to + * cause IOExceptions in case if it ever does. + */ + } + + } + } + +} From 12ac88ccd92a606593bfb1bfe678a70b3cf0d80e Mon Sep 17 00:00:00 2001 From: kwwall Date: Sat, 16 Jan 2016 15:13:27 -0500 Subject: [PATCH 0032/1069] Make getProperty(String,String) synchronized. Closes #321. --- .../esapi/reference/crypto/ReferenceEncryptedProperties.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/owasp/esapi/reference/crypto/ReferenceEncryptedProperties.java b/src/main/java/org/owasp/esapi/reference/crypto/ReferenceEncryptedProperties.java index cffc14963..0d35b25dd 100644 --- a/src/main/java/org/owasp/esapi/reference/crypto/ReferenceEncryptedProperties.java +++ b/src/main/java/org/owasp/esapi/reference/crypto/ReferenceEncryptedProperties.java @@ -129,7 +129,7 @@ public synchronized String getProperty(String key) throws EncryptionRuntimeExcep * @throws EncryptionRuntimeException Thrown if decryption fails. */ @Override - public String getProperty(String key, String defaultValue) throws EncryptionRuntimeException { + public synchronized String getProperty(String key, String defaultValue) throws EncryptionRuntimeException { String value = getProperty(key); if (value == null) return defaultValue; From 644cd4d36be35ec756ddbce9588c65f6358d1ed6 Mon Sep 17 00:00:00 2001 From: kwwall Date: Sat, 16 Jan 2016 17:51:26 -0500 Subject: [PATCH 0033/1069] Applied patches from Eric Citaire and Matt Seil. Fixes #322. --- .../filters/RequestRateThrottleFilter.java | 43 ++++++++----------- 1 file changed, 19 insertions(+), 24 deletions(-) diff --git a/src/main/java/org/owasp/esapi/filters/RequestRateThrottleFilter.java b/src/main/java/org/owasp/esapi/filters/RequestRateThrottleFilter.java index 0ca121ec6..4ac188979 100644 --- a/src/main/java/org/owasp/esapi/filters/RequestRateThrottleFilter.java +++ b/src/main/java/org/owasp/esapi/filters/RequestRateThrottleFilter.java @@ -53,8 +53,8 @@ public class RequestRateThrottleFilter implements Filter */ public void init(FilterConfig filterConfig) { - hits = Integer.parseInt(filterConfig.getInitParameter(HITS)); - period = Integer.parseInt(filterConfig.getInitParameter(PERIOD)); + hits = filterConfig.getInitParameter(HITS) == null ? 5 : Integer.parseInt(filterConfig.getInitParameter(HITS)); + period = filterConfig.getInitParameter(PERIOD) == null ? 10 : Integer.parseInt(filterConfig.getInitParameter(PERIOD)); } /** @@ -75,27 +75,22 @@ public void doFilter(ServletRequest request, ServletResponse response, FilterCha HttpSession session = httpRequest.getSession(true); synchronized( session.getId().intern() ) { - Stack times = ESAPI.httpUtilities().getSessionAttribute("times"); - if (times == null) - { - times = new Stack(); - times.push(new Date(0)); - session.setAttribute("times", times); - } - times.push(new Date()); - if (times.size() >= hits) - { - times.removeElementAt(0); - } - Date newest = times.get(times.size() - 1); - Date oldest = times.get(0); - long elapsed = newest.getTime() - oldest.getTime(); - if (elapsed < period * 1000) // seconds - { - response.getWriter().println("Request rate too high"); - return; - } - } + List times = ESAPI.httpUtilities().getSessionAttribute("times"); + if (times == null) { + times = new LinkedList(); + session.setAttribute("times", times); + } + Long newest = System.currentTimeMillis(); + times.add(newest); + if (times.size() > hits) { + Long oldest = times.remove(0); + long elapsed = newest - oldest; + if (elapsed < period * 1000) { + response.getWriter().println("Request rate too high"); + return; + } + } + } chain.doFilter(request, response); } @@ -111,4 +106,4 @@ public void destroy() // finalize } -} \ No newline at end of file +} From 14fd61a7a29824c2bd3ee27e755ccf4253bdf141 Mon Sep 17 00:00:00 2001 From: kwwall Date: Sat, 16 Jan 2016 21:32:49 -0500 Subject: [PATCH 0034/1069] JUnit changes made because of changes to CipherSpec class and changing assertions to exlicit runtime checks resulting in IllegalArgumentExceptions. --- .../owasp/esapi/crypto/CipherSpecTest.java | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/src/test/java/org/owasp/esapi/crypto/CipherSpecTest.java b/src/test/java/org/owasp/esapi/crypto/CipherSpecTest.java index b842aaec6..8f808880f 100644 --- a/src/test/java/org/owasp/esapi/crypto/CipherSpecTest.java +++ b/src/test/java/org/owasp/esapi/crypto/CipherSpecTest.java @@ -144,14 +144,14 @@ public class CipherSpecTest extends TestCase { /** Test setBlockSize() */ @Test public void testSetBlockSize() { try { - cipherSpec.setBlockSize(0); // Throws AssertionError - } catch (AssertionError e) { - assertTrue(true); // Doesn't work w/ @Test(expected=AssertionError.class) + cipherSpec.setBlockSize(0); // Throws IllegalArgumentException + } catch (IllegalArgumentException e) { + assertTrue(true); } try { - cipherSpec.setBlockSize(-1); // Throws AssertionError - } catch (AssertionError e) { - assertTrue(true); // Doesn't work w/ @Test(expected=AssertionError.class) + cipherSpec.setBlockSize(-1); // Throws IllegalArgumentException + } catch (IllegalArgumentException e) { + assertTrue(true); } assertTrue( cipherSpec.setBlockSize(4).getBlockSize() == 4 ); } @@ -181,9 +181,10 @@ public class CipherSpecTest extends TestCase { try { // Test that ECB mode allows a null IV cipherSpec = new CipherSpec(dfltECBCipher); + assertTrue( cipherSpec.getCipherMode().equals("ECB") ); cipherSpec.setIV(null); assertTrue(true); - } catch ( AssertionError e) { + } catch ( IllegalArgumentException e) { assertFalse("Test failed; unexpected exception", false); } try { @@ -191,7 +192,7 @@ public class CipherSpecTest extends TestCase { cipherSpec = new CipherSpec(dfltAESCipher); cipherSpec.setIV(null); assertFalse("Test failed; Expected exception not thrown", false); - } catch ( AssertionError e) { + } catch ( IllegalArgumentException e) { assertTrue(true); } } @@ -270,4 +271,4 @@ public static junit.framework.Test suite() { return suite; } -} \ No newline at end of file +} From 3c8e18d726095fbc88dc42c1c7a7c5e8d5943e45 Mon Sep 17 00:00:00 2001 From: kwwall Date: Sat, 16 Jan 2016 21:34:50 -0500 Subject: [PATCH 0035/1069] Forgot to add imports for List and LinkedList. Fixes issue #322. --- .../org/owasp/esapi/filters/RequestRateThrottleFilter.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/owasp/esapi/filters/RequestRateThrottleFilter.java b/src/main/java/org/owasp/esapi/filters/RequestRateThrottleFilter.java index 4ac188979..4d78551fd 100644 --- a/src/main/java/org/owasp/esapi/filters/RequestRateThrottleFilter.java +++ b/src/main/java/org/owasp/esapi/filters/RequestRateThrottleFilter.java @@ -22,7 +22,8 @@ import javax.servlet.http.HttpSession; import java.io.IOException; import java.util.Date; -import java.util.Stack; +import java.util.List; +import java.util.LinkedList; /** * A simple servlet filter that limits the request rate to a certain threshold of requests per second. From 86c6fa54bde1aca91c7bd08b9ee96c1a5fdb99e5 Mon Sep 17 00:00:00 2001 From: kwwall Date: Sat, 16 Jan 2016 22:08:06 -0500 Subject: [PATCH 0036/1069] Ignore everything in 'target' directory. --- .gitignore | 1 + 1 file changed, 1 insertion(+) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 000000000..ea8c4bf7f --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/target From e0220902fc0e1a5ecb96587b1ae39931d11fcffb Mon Sep 17 00:00:00 2001 From: kwwall Date: Sat, 16 Jan 2016 22:17:10 -0500 Subject: [PATCH 0037/1069] Configure line endings and file types for common file extensions as discussed with Matt Seil via Gmail on 12/31/2015. --- .gitattributes | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 .gitattributes diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 000000000..596f92708 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,40 @@ +# Set the default behavior, in case people don't have core.autocrlf set. +* text=auto + +# Explicitly declare text files you want to always be normalized and converted +# to native line endings on checkout. +*.java text +*.properties text +*.xml text +*.xsd text +*.dtd text +*.MF text +*.md text +*.html text +*.tld text +*.json text + +# Declare files that will always have CRLF line endings on checkout. +*.cmd text eol=crlf +*.bat text eol=crlf +# Because *nix editors / paginators can handle either way, but braindead +# Windoze notepad, the default to open .txt files, not so much. +*.txt text eol=crlf + +# Declare files that will always have LF line endings on checkout +*.sh text eol=lf +*.bsh text eol=lf + +# Denote all files that are truly binary and should not be modified, +# or simply replaced in whole if committed. +*.jpg binary +*.JPG binary +*.jks binary +*.ser binary +*.doc binary +*.docx binary +*.xls binary +*.xlsx binary +*.pptx binary +*.odt binary +*.pdf binary From e3f09d8ec48986b5fcc9f675bb3933a0467e152c Mon Sep 17 00:00:00 2001 From: Matt Date: Mon, 18 Jan 2016 09:46:04 -0600 Subject: [PATCH 0038/1069] Commented text=auto in .gitattributes. --- .gitattributes | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitattributes b/.gitattributes index 596f92708..82f8b6ca8 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,5 +1,5 @@ # Set the default behavior, in case people don't have core.autocrlf set. -* text=auto +#* text=auto # Explicitly declare text files you want to always be normalized and converted # to native line endings on checkout. From 84dfcb75e9fb819c5a00814d81923b2c124db966 Mon Sep 17 00:00:00 2001 From: Matt Date: Mon, 18 Jan 2016 13:12:06 -0600 Subject: [PATCH 0039/1069] Removed comment from .gitattribute --- .gitattributes | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitattributes b/.gitattributes index 82f8b6ca8..596f92708 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,5 +1,5 @@ # Set the default behavior, in case people don't have core.autocrlf set. -#* text=auto +* text=auto # Explicitly declare text files you want to always be normalized and converted # to native line endings on checkout. From 2d810cebb1093a795f375e7bc947b8030624d01c Mon Sep 17 00:00:00 2001 From: Matt Seil Date: Mon, 18 Jan 2016 13:25:28 -0600 Subject: [PATCH 0040/1069] Changed java eol to 'LF' --- .gitattributes | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitattributes b/.gitattributes index 596f92708..b22d37b11 100644 --- a/.gitattributes +++ b/.gitattributes @@ -3,7 +3,7 @@ # Explicitly declare text files you want to always be normalized and converted # to native line endings on checkout. -*.java text +*.java eol=lf *.properties text *.xml text *.xsd text From 5092bbf5f7177779bbe20dd7d5931814c3a80a60 Mon Sep 17 00:00:00 2001 From: Matt Seil Date: Mon, 18 Jan 2016 13:27:50 -0600 Subject: [PATCH 0041/1069] Changed .java to crlf for testing. --- .gitattributes | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitattributes b/.gitattributes index b22d37b11..2b98a25d6 100644 --- a/.gitattributes +++ b/.gitattributes @@ -3,7 +3,7 @@ # Explicitly declare text files you want to always be normalized and converted # to native line endings on checkout. -*.java eol=lf +*.java eol=crlf *.properties text *.xml text *.xsd text From 54fca07bbb54195c94f4e95290d353436239be04 Mon Sep 17 00:00:00 2001 From: Matt Seil Date: Mon, 18 Jan 2016 14:54:42 -0600 Subject: [PATCH 0042/1069] Revert "Changed .java to crlf for testing." This reverts commit 5092bbf5f7177779bbe20dd7d5931814c3a80a60. --- .gitattributes | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitattributes b/.gitattributes index 2b98a25d6..b22d37b11 100644 --- a/.gitattributes +++ b/.gitattributes @@ -3,7 +3,7 @@ # Explicitly declare text files you want to always be normalized and converted # to native line endings on checkout. -*.java eol=crlf +*.java eol=lf *.properties text *.xml text *.xsd text From 38053aae32f648170ceada2a24b7ef4c1a324bfe Mon Sep 17 00:00:00 2001 From: kwwall Date: Mon, 18 Jan 2016 22:54:09 -0500 Subject: [PATCH 0043/1069] Try to fix the CRLF mess that we're in via .gitattributes. --- .gitattributes | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/.gitattributes b/.gitattributes index b22d37b11..a90ca1d70 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,9 +1,22 @@ -# Set the default behavior, in case people don't have core.autocrlf set. +# Autodetect text files +# +# In addition: +# Windows developers should set: +# git config --global core.autocrlf true +# UNIX / MacOS develoers should set: +# git config --global core.autocrlf input * text=auto +# And configure default EOL terminators. +# +# Force the following filetypes to have Unix eols, so Windows does not break them +# Those that are not explicitly set to use Windows EOL will use LF as EOL terminator. +# (e.g., see *.bat, below). +*.* text eol=lf + # Explicitly declare text files you want to always be normalized and converted # to native line endings on checkout. -*.java eol=lf +*.java text *.properties text *.xml text *.xsd text @@ -18,17 +31,22 @@ *.cmd text eol=crlf *.bat text eol=crlf # Because *nix editors / paginators can handle either way, but braindead -# Windoze notepad, the default to open .txt files, not so much. +# Windoze notepad which is used by default to handle text files in Windows, +# not so much, we also make the concession here to use CRLF for EOL. *.txt text eol=crlf +# Ditto for Eclipse related preferences +*.prefs text eol=crlf # Declare files that will always have LF line endings on checkout *.sh text eol=lf *.bsh text eol=lf +*.ksh text eol=lf # Denote all files that are truly binary and should not be modified, # or simply replaced in whole if committed. *.jpg binary *.JPG binary +*.png binary *.jks binary *.ser binary *.doc binary From ca5723b81c6a4ccaccd6f02c4ff5c98628096f4b Mon Sep 17 00:00:00 2001 From: kwwall Date: Mon, 18 Jan 2016 23:26:01 -0500 Subject: [PATCH 0044/1069] Test case for issue 356. If this works, will do mass updates. --- .../org/owasp/esapi/waf/WAFTestUtility.java | 182 +++++++++--------- 1 file changed, 91 insertions(+), 91 deletions(-) diff --git a/src/test/java/org/owasp/esapi/waf/WAFTestUtility.java b/src/test/java/org/owasp/esapi/waf/WAFTestUtility.java index 3a58b387c..205d0d564 100644 --- a/src/test/java/org/owasp/esapi/waf/WAFTestUtility.java +++ b/src/test/java/org/owasp/esapi/waf/WAFTestUtility.java @@ -1,91 +1,91 @@ -/** - * OWASP Enterprise Security API (ESAPI) - * - * This file is part of the Open Web Application Security Project (OWASP) - * Enterprise Security API (ESAPI) project. For details, please see - * http://www.owasp.org/index.php/ESAPI. - * - * Copyright (c) 2009 - The OWASP Foundation - * - * The ESAPI is published by OWASP under the BSD license. You should read and accept the - * LICENSE before you use, modify, and/or redistribute this software. - * - * @author Arshan Dabirsiaghi Aspect Security - * - * @created 2009 - */ -package org.owasp.esapi.waf; - -import java.io.File; -import java.io.FileInputStream; -import java.io.InputStream; -import java.util.HashMap; -import java.util.Map; - -import javax.servlet.FilterConfig; - -import org.owasp.esapi.ESAPI; -import org.owasp.esapi.http.MockFilterChain; -import org.owasp.esapi.http.MockFilterConfig; -import org.owasp.esapi.http.MockHttpServletRequest; -import org.owasp.esapi.http.MockHttpServletResponse; - -/** - * This class holds a number of static utilities to make writing WAF test cases easy. Definitely not useful - * for anything other than testing. - */ -public class WAFTestUtility { - - public static void setWAFPolicy( ESAPIWebApplicationFirewallFilter waf, String policyFile ) throws Exception { - Map map = new HashMap(); - map.put( "configuration", policyFile ); - map.put( "log_settings", "../log4j.xml"); - FilterConfig mfc = new MockWafFilterConfig( map ); - waf.init( mfc ); - } - - public static int checkWAFResult( ESAPIWebApplicationFirewallFilter waf, MockHttpServletRequest request, MockHttpServletResponse response, MockFilterChain chain ) throws Exception { - - //request.dump(); - waf.doFilter(request, response, chain); - //response.dump(); - - return response.getStatus(); - - } - - public static int createAndExecuteWAFTransaction ( ESAPIWebApplicationFirewallFilter waf, MockHttpServletRequest request, MockHttpServletResponse response ) throws Exception { - - MockFilterChain chain = new MockFilterChain(); - - return WAFTestUtility.checkWAFResult(waf, request, response, chain); - - } - - public static int createAndExecuteWAFTransaction ( ESAPIWebApplicationFirewallFilter waf, MockHttpServletRequest request, MockHttpServletResponse response, MockFilterChain filterChain ) throws Exception { - - - return WAFTestUtility.checkWAFResult(waf, request, response, filterChain); - - } - - public static int createAndExecuteWAFTransaction ( String policy, MockHttpServletRequest request, MockHttpServletResponse response ) throws Exception { - - ESAPIWebApplicationFirewallFilter waf = new ESAPIWebApplicationFirewallFilter(); - File f = ESAPI.securityConfiguration().getResourceFile(policy); - waf.setConfiguration(f.getAbsolutePath(),""); - - return createAndExecuteWAFTransaction(waf, request, response ); - - } - - public static int createAndExecuteWAFTransaction ( String policy, MockHttpServletRequest request, MockHttpServletResponse response, MockFilterChain filterChain ) throws Exception { - - ESAPIWebApplicationFirewallFilter waf = new ESAPIWebApplicationFirewallFilter(); - File f = ESAPI.securityConfiguration().getResourceFile(policy); - waf.setConfiguration(f.getAbsolutePath(),""); - - return createAndExecuteWAFTransaction(waf, request, response, filterChain ); - - } -} +/** + * OWASP Enterprise Security API (ESAPI) + * + * This file is part of the Open Web Application Security Project (OWASP) + * Enterprise Security API (ESAPI) project. For details, please see + * http://www.owasp.org/index.php/ESAPI. + * + * Copyright (c) 2009 - The OWASP Foundation + * + * The ESAPI is published by OWASP under the BSD license. You should read and accept the + * LICENSE before you use, modify, and/or redistribute this software. + * + * @author Arshan Dabirsiaghi Aspect Security + * + * @created 2009 + */ +package org.owasp.esapi.waf; + +import java.io.File; +import java.io.FileInputStream; +import java.io.InputStream; +import java.util.HashMap; +import java.util.Map; + +import javax.servlet.FilterConfig; + +import org.owasp.esapi.ESAPI; +import org.owasp.esapi.http.MockFilterChain; +import org.owasp.esapi.http.MockFilterConfig; +import org.owasp.esapi.http.MockHttpServletRequest; +import org.owasp.esapi.http.MockHttpServletResponse; + +/** + * This class holds a number of static utilities to make writing WAF test cases easy. Definitely not useful + * for anything other than testing. + */ +public class WAFTestUtility { + + public static void setWAFPolicy( ESAPIWebApplicationFirewallFilter waf, String policyFile ) throws Exception { + Map map = new HashMap(); + map.put( "configuration", policyFile ); + map.put( "log_settings", "../log4j.xml"); + FilterConfig mfc = new MockWafFilterConfig( map ); + waf.init( mfc ); + } + + public static int checkWAFResult( ESAPIWebApplicationFirewallFilter waf, MockHttpServletRequest request, MockHttpServletResponse response, MockFilterChain chain ) throws Exception { + + //request.dump(); + waf.doFilter(request, response, chain); + //response.dump(); + + return response.getStatus(); + + } + + public static int createAndExecuteWAFTransaction ( ESAPIWebApplicationFirewallFilter waf, MockHttpServletRequest request, MockHttpServletResponse response ) throws Exception { + + MockFilterChain chain = new MockFilterChain(); + + return WAFTestUtility.checkWAFResult(waf, request, response, chain); + + } + + public static int createAndExecuteWAFTransaction ( ESAPIWebApplicationFirewallFilter waf, MockHttpServletRequest request, MockHttpServletResponse response, MockFilterChain filterChain ) throws Exception { + + + return WAFTestUtility.checkWAFResult(waf, request, response, filterChain); + + } + + public static int createAndExecuteWAFTransaction ( String policy, MockHttpServletRequest request, MockHttpServletResponse response ) throws Exception { + + ESAPIWebApplicationFirewallFilter waf = new ESAPIWebApplicationFirewallFilter(); + File f = ESAPI.securityConfiguration().getResourceFile(policy); + waf.setConfiguration(f.getAbsolutePath(),""); + + return createAndExecuteWAFTransaction(waf, request, response ); + + } + + public static int createAndExecuteWAFTransaction ( String policy, MockHttpServletRequest request, MockHttpServletResponse response, MockFilterChain filterChain ) throws Exception { + + ESAPIWebApplicationFirewallFilter waf = new ESAPIWebApplicationFirewallFilter(); + File f = ESAPI.securityConfiguration().getResourceFile(policy); + waf.setConfiguration(f.getAbsolutePath(),""); + + return createAndExecuteWAFTransaction(waf, request, response, filterChain ); + + } +} From f5a66275bdd3d5be246d0bc024077ddb0d050287 Mon Sep 17 00:00:00 2001 From: kwwall Date: Mon, 18 Jan 2016 23:33:47 -0500 Subject: [PATCH 0045/1069] Change CRLF to LF for issue 356. --- .../org/owasp/esapi/AccessController.java | 702 +++++++++--------- 1 file changed, 351 insertions(+), 351 deletions(-) diff --git a/src/main/java/org/owasp/esapi/AccessController.java b/src/main/java/org/owasp/esapi/AccessController.java index e6269e1d5..2d9e4dac8 100644 --- a/src/main/java/org/owasp/esapi/AccessController.java +++ b/src/main/java/org/owasp/esapi/AccessController.java @@ -1,351 +1,351 @@ -/** - * OWASP Enterprise Security API (ESAPI) - * - * This file is part of the Open Web Application Security Project (OWASP) - * Enterprise Security API (ESAPI) project. For details, please see - * http://www.owasp.org/index.php/ESAPI. - * - * Copyright (c) 2007 - The OWASP Foundation - * - * The ESAPI is published by OWASP under the BSD license. You should read and accept the - * LICENSE before you use, modify, and/or redistribute this software. - * - * @author Jeff Williams Aspect Security - * @created 2007 - */ -package org.owasp.esapi; - -import org.owasp.esapi.errors.AccessControlException; - - - -/** - * The AccessController interface defines a set of methods that can be used in a wide variety of applications to - * enforce access control. In most applications, access control must be performed in multiple different locations across - * the various application layers. This class provides access control for URLs, business functions, data, services, and - * files. - *

    - * The implementation of this interface will need to access the current User object (from Authenticator.getCurrentUser()) - * to determine roles or permissions. In addition, the implementation - * will also need information about the resources that are being accessed. Using the user information and the resource - * information, the implementation should return an access control decision. - *

    - * Implementers are encouraged to implement the ESAPI access control rules, like assertAuthorizedForFunction() using - * existing access control mechanisms, such as methods like isUserInRole() or hasPrivilege(). While powerful, - * methods like isUserInRole() can be confusing for developers, as users may be in multiple roles or possess multiple - * overlapping privileges. Direct use of these finer grained access control methods encourages the use of complex boolean - * tests throughout the code, which can easily lead to developer mistakes. - *

    - * The point of the ESAPI access control interface is to centralize access control logic behind easy to use calls like - * assertAuthorized() so that access control is easy to use and easy to verify. Here is an example of a very - * straightforward to implement, understand, and verify ESAPI access control check: - * - *

    - * try {
    - *     ESAPI.accessController().assertAuthorized("businessFunction", runtimeData);
    - *     // execute BUSINESS_FUNCTION
    - * } catch (AccessControlException ace) {
    - * ... attack in progress
    - * }
    - * 
    - * - * Note that in the user interface layer, access control checks can be used to control whether particular controls are - * rendered or not. These checks are supposed to fail when an unauthorized user is logged in, and do not represent - * attacks. Remember that regardless of how the user interface appears, an attacker can attempt to invoke any business - * function or access any data in your application. Therefore, access control checks in the user interface should be - * repeated in both the business logic and data layers. - * - *
    - * <% if ( ESAPI.accessController().isAuthorized( "businessFunction", runtimeData ) ) { %>
    - * <a href="/doAdminFunction">ADMIN</a>
    - * <% } else { %>
    - * <a href="/doNormalFunction">NORMAL</a>
    - * <% } %>
    - * 
    - * - * @author Mike H. Fauzy (mike.fauzy@aspectsecurity.com) ESAPI v1.6- - * @author Jeff Williams (jeff.williams@aspectsecurity.com) ESAPI v0-1.5 - */ -public interface AccessController { - - /** - * isAuthorized executes the AccessControlRule - * that is identified by key and listed in the - * resources/ESAPI-AccessControlPolicy.xml file. It returns - * true if the AccessControlRule decides that the operation - * should be allowed. Otherwise, it returns false. Any exception thrown by - * the AccessControlRule must result in false. If - * key does not map to an AccessControlRule, then - * false is returned. - * - * Developers should call isAuthorized to control execution flow. For - * example, if you want to decide whether to display a UI widget in the - * browser using the same logic that you will use to enforce permissions - * on the server, then isAuthorized is the method that you want to use. - * - * Typically, assertAuthorized should be used to enforce permissions on the - * server. - * - * @param key key maps to - * <AccessControlPolicy><AccessControlRules> - * <AccessControlRule name="key" - * @param runtimeParameter runtimeParameter can contain anything that - * the AccessControlRule needs from the runtime system. - * @return Returns true if and only if the AccessControlRule specified - * by key exists and returned true. - * Otherwise returns false - */ - public boolean isAuthorized(Object key, Object runtimeParameter); - - /** - * assertAuthorized executes the AccessControlRule - * that is identified by key and listed in the - * resources/ESAPI-AccessControlPolicy.xml file. It does - * nothing if the AccessControlRule decides that the operation - * should be allowed. Otherwise, it throws an - * org.owasp.esapi.errors.AccessControlException. Any exception - * thrown by the AccessControlRule will also result in an - * AccesControlException. If key does not map to - * an AccessControlRule, then an AccessControlException - * is thrown. - * - * Developers should call {@code assertAuthorized} to enforce privileged access to - * the system. It should be used to answer the question: "Should execution - * continue." Ideally, the call to assertAuthorized should - * be integrated into the application framework so that it is called - * automatically. - * - * @param key key maps to - * <AccessControlPolicy><AccessControlRules> - * <AccessControlRule name="key" - * @param runtimeParameter runtimeParameter can contain anything that - * the AccessControlRule needs from the runtime system. - */ - public void assertAuthorized(Object key, Object runtimeParameter) - throws AccessControlException; - - - - - /*** Below this line has been deprecated as of ESAPI 1.6 ***/ - - - - - /** - * Checks if the current user is authorized to access the referenced URL. Generally, this method should be invoked in the - * application's controller or a filter as follows: - *
    ESAPI.accessController().isAuthorizedForURL(request.getRequestURI().toString());
    - * - * The implementation of this method should call assertAuthorizedForURL(String url), and if an AccessControlException is - * not thrown, this method should return true. This way, if the user is not authorized, false would be returned, and the - * exception would be logged. - * - * @param url - * the URL as returned by request.getRequestURI().toString() - * - * @return - * true, if is authorized for URL - */ - boolean isAuthorizedForURL(String url); - - /** - * Checks if the current user is authorized to access the referenced function. - * - * The implementation of this method should call assertAuthorizedForFunction(String functionName), and if an - * AccessControlException is not thrown, this method should return true. - * - * @param functionName - * the name of the function - * - * @return - * true, if is authorized for function - */ - boolean isAuthorizedForFunction(String functionName); - - - /** - * Checks if the current user is authorized to access the referenced data, represented as an Object. - * - * The implementation of this method should call assertAuthorizedForData(String action, Object data), and if an - * AccessControlException is not thrown, this method should return true. - * - * @param action - * The action to verify for an access control decision, such as a role, or an action being performed on the object - * (e.g., Read, Write, etc.), or the name of the function the data is being passed to. - * - * @param data - * The actual object or object identifier being accessed or a reference to the object being accessed. - * - * @return - * true, if is authorized for the data - */ - boolean isAuthorizedForData(String action, Object data); - - /** - * Checks if the current user is authorized to access the referenced file. - * - * The implementation of this method should call assertAuthorizedForFile(String filepath), and if an AccessControlException - * is not thrown, this method should return true. - * - * @param filepath - * the path of the file to be checked, including filename - * - * @return - * true, if is authorized for the file - */ - boolean isAuthorizedForFile(String filepath); - - /** - * Checks if the current user is authorized to access the referenced service. This can be used in applications that - * provide access to a variety of back end services. - * - * The implementation of this method should call assertAuthorizedForService(String serviceName), and if an - * AccessControlException is not thrown, this method should return true. - * - * @param serviceName - * the service name - * - * @return - * true, if is authorized for the service - */ - boolean isAuthorizedForService(String serviceName); - - /** - * Checks if the current user is authorized to access the referenced URL. The implementation should allow - * access to be granted to any part of the URL. Generally, this method should be invoked in the - * application's controller or a filter as follows: - *
    ESAPI.accessController().assertAuthorizedForURL(request.getRequestURI().toString());
    - * - * This method throws an AccessControlException if access is not authorized, or if the referenced URL does not exist. - * If the User is authorized, this method simply returns. - *

    - * Specification: The implementation should do the following: - *

      - *
    1. Check to see if the resource exists and if not, throw an AccessControlException
    2. - *
    3. Use available information to make an access control decision
    4. - *
        - *
      1. Ideally, this policy would be data driven
      2. - *
      3. You can use the current User, roles, data type, data name, time of day, etc.
      4. - *
      5. Access control decisions must deny by default
      6. - *
      - *
    5. If access is not permitted, throw an AccessControlException with details
    6. - *
    - * @param url - * the URL as returned by request.getRequestURI().toString() - * - * @throws AccessControlException - * if access is not permitted - */ - void assertAuthorizedForURL(String url) throws AccessControlException; - - /** - * Checks if the current user is authorized to access the referenced function. The implementation should define the - * function "namespace" to be enforced. Choosing something simple like the class name of action classes or menu item - * names will make this implementation easier to use. - *

    - * This method throws an AccessControlException if access is not authorized, or if the referenced function does not exist. - * If the User is authorized, this method simply returns. - *

    - * Specification: The implementation should do the following: - *

      - *
    1. Check to see if the function exists and if not, throw an AccessControlException
    2. - *
    3. Use available information to make an access control decision
    4. - *
        - *
      1. Ideally, this policy would be data driven
      2. - *
      3. You can use the current User, roles, data type, data name, time of day, etc.
      4. - *
      5. Access control decisions must deny by default
      6. - *
      - *
    5. If access is not permitted, throw an AccessControlException with details
    6. - *
    - * - * @param functionName - * the function name - * - * @throws AccessControlException - * if access is not permitted - */ - void assertAuthorizedForFunction(String functionName) throws AccessControlException; - - - /** - * Checks if the current user is authorized to access the referenced data. This method simply returns if access is authorized. - * It throws an AccessControlException if access is not authorized, or if the referenced data does not exist. - *

    - * Specification: The implementation should do the following: - *

      - *
    1. Check to see if the resource exists and if not, throw an AccessControlException
    2. - *
    3. Use available information to make an access control decision
    4. - *
        - *
      1. Ideally, this policy would be data driven
      2. - *
      3. You can use the current User, roles, data type, data name, time of day, etc.
      4. - *
      5. Access control decisions must deny by default
      6. - *
      - *
    5. If access is not permitted, throw an AccessControlException with details
    6. - *
    - * - * @param action - * The action to verify for an access control decision, such as a role, or an action being performed on the object - * (e.g., Read, Write, etc.), or the name of the function the data is being passed to. - * - * @param data - * The actual object or object identifier being accessed or a reference to the object being accessed. - * - * @throws AccessControlException - * if access is not permitted - */ - void assertAuthorizedForData(String action, Object data) throws AccessControlException; - - /** - * Checks if the current user is authorized to access the referenced file. The implementation should validate and canonicalize the - * input to be sure the filepath is not malicious. - *

    - * This method throws an AccessControlException if access is not authorized, or if the referenced File does not exist. - * If the User is authorized, this method simply returns. - *

    - * Specification: The implementation should do the following: - *

      - *
    1. Check to see if the File exists and if not, throw an AccessControlException
    2. - *
    3. Use available information to make an access control decision
    4. - *
        - *
      1. Ideally, this policy would be data driven
      2. - *
      3. You can use the current User, roles, data type, data name, time of day, etc.
      4. - *
      5. Access control decisions must deny by default
      6. - *
      - *
    5. If access is not permitted, throw an AccessControlException with details
    6. - *
    - * - * @param filepath - * Path to the file to be checked - * @throws AccessControlException if access is denied - */ - void assertAuthorizedForFile(String filepath) throws AccessControlException; - - /** - * Checks if the current user is authorized to access the referenced service. This can be used in applications that - * provide access to a variety of backend services. - *

    - * This method throws an AccessControlException if access is not authorized, or if the referenced service does not exist. - * If the User is authorized, this method simply returns. - *

    - * Specification: The implementation should do the following: - *

      - *
    1. Check to see if the service exists and if not, throw an AccessControlException
    2. - *
    3. Use available information to make an access control decision
    4. - *
        - *
      1. Ideally, this policy would be data driven
      2. - *
      3. You can use the current User, roles, data type, data name, time of day, etc.
      4. - *
      5. Access control decisions must deny by default
      6. - *
      - *
    5. If access is not permitted, throw an AccessControlException with details
    6. - *
    - * - * @param serviceName - * the service name - * - * @throws AccessControlException - * if access is not permitted - */ - void assertAuthorizedForService(String serviceName) throws AccessControlException; - -} +/** + * OWASP Enterprise Security API (ESAPI) + * + * This file is part of the Open Web Application Security Project (OWASP) + * Enterprise Security API (ESAPI) project. For details, please see + * http://www.owasp.org/index.php/ESAPI. + * + * Copyright (c) 2007 - The OWASP Foundation + * + * The ESAPI is published by OWASP under the BSD license. You should read and accept the + * LICENSE before you use, modify, and/or redistribute this software. + * + * @author Jeff Williams Aspect Security + * @created 2007 + */ +package org.owasp.esapi; + +import org.owasp.esapi.errors.AccessControlException; + + + +/** + * The AccessController interface defines a set of methods that can be used in a wide variety of applications to + * enforce access control. In most applications, access control must be performed in multiple different locations across + * the various application layers. This class provides access control for URLs, business functions, data, services, and + * files. + *

    + * The implementation of this interface will need to access the current User object (from Authenticator.getCurrentUser()) + * to determine roles or permissions. In addition, the implementation + * will also need information about the resources that are being accessed. Using the user information and the resource + * information, the implementation should return an access control decision. + *

    + * Implementers are encouraged to implement the ESAPI access control rules, like assertAuthorizedForFunction() using + * existing access control mechanisms, such as methods like isUserInRole() or hasPrivilege(). While powerful, + * methods like isUserInRole() can be confusing for developers, as users may be in multiple roles or possess multiple + * overlapping privileges. Direct use of these finer grained access control methods encourages the use of complex boolean + * tests throughout the code, which can easily lead to developer mistakes. + *

    + * The point of the ESAPI access control interface is to centralize access control logic behind easy to use calls like + * assertAuthorized() so that access control is easy to use and easy to verify. Here is an example of a very + * straightforward to implement, understand, and verify ESAPI access control check: + * + *

    + * try {
    + *     ESAPI.accessController().assertAuthorized("businessFunction", runtimeData);
    + *     // execute BUSINESS_FUNCTION
    + * } catch (AccessControlException ace) {
    + * ... attack in progress
    + * }
    + * 
    + * + * Note that in the user interface layer, access control checks can be used to control whether particular controls are + * rendered or not. These checks are supposed to fail when an unauthorized user is logged in, and do not represent + * attacks. Remember that regardless of how the user interface appears, an attacker can attempt to invoke any business + * function or access any data in your application. Therefore, access control checks in the user interface should be + * repeated in both the business logic and data layers. + * + *
    + * <% if ( ESAPI.accessController().isAuthorized( "businessFunction", runtimeData ) ) { %>
    + * <a href="/doAdminFunction">ADMIN</a>
    + * <% } else { %>
    + * <a href="/doNormalFunction">NORMAL</a>
    + * <% } %>
    + * 
    + * + * @author Mike H. Fauzy (mike.fauzy@aspectsecurity.com) ESAPI v1.6- + * @author Jeff Williams (jeff.williams@aspectsecurity.com) ESAPI v0-1.5 + */ +public interface AccessController { + + /** + * isAuthorized executes the AccessControlRule + * that is identified by key and listed in the + * resources/ESAPI-AccessControlPolicy.xml file. It returns + * true if the AccessControlRule decides that the operation + * should be allowed. Otherwise, it returns false. Any exception thrown by + * the AccessControlRule must result in false. If + * key does not map to an AccessControlRule, then + * false is returned. + * + * Developers should call isAuthorized to control execution flow. For + * example, if you want to decide whether to display a UI widget in the + * browser using the same logic that you will use to enforce permissions + * on the server, then isAuthorized is the method that you want to use. + * + * Typically, assertAuthorized should be used to enforce permissions on the + * server. + * + * @param key key maps to + * <AccessControlPolicy><AccessControlRules> + * <AccessControlRule name="key" + * @param runtimeParameter runtimeParameter can contain anything that + * the AccessControlRule needs from the runtime system. + * @return Returns true if and only if the AccessControlRule specified + * by key exists and returned true. + * Otherwise returns false + */ + public boolean isAuthorized(Object key, Object runtimeParameter); + + /** + * assertAuthorized executes the AccessControlRule + * that is identified by key and listed in the + * resources/ESAPI-AccessControlPolicy.xml file. It does + * nothing if the AccessControlRule decides that the operation + * should be allowed. Otherwise, it throws an + * org.owasp.esapi.errors.AccessControlException. Any exception + * thrown by the AccessControlRule will also result in an + * AccesControlException. If key does not map to + * an AccessControlRule, then an AccessControlException + * is thrown. + * + * Developers should call {@code assertAuthorized} to enforce privileged access to + * the system. It should be used to answer the question: "Should execution + * continue." Ideally, the call to assertAuthorized should + * be integrated into the application framework so that it is called + * automatically. + * + * @param key key maps to + * <AccessControlPolicy><AccessControlRules> + * <AccessControlRule name="key" + * @param runtimeParameter runtimeParameter can contain anything that + * the AccessControlRule needs from the runtime system. + */ + public void assertAuthorized(Object key, Object runtimeParameter) + throws AccessControlException; + + + + + /*** Below this line has been deprecated as of ESAPI 1.6 ***/ + + + + + /** + * Checks if the current user is authorized to access the referenced URL. Generally, this method should be invoked in the + * application's controller or a filter as follows: + *
    ESAPI.accessController().isAuthorizedForURL(request.getRequestURI().toString());
    + * + * The implementation of this method should call assertAuthorizedForURL(String url), and if an AccessControlException is + * not thrown, this method should return true. This way, if the user is not authorized, false would be returned, and the + * exception would be logged. + * + * @param url + * the URL as returned by request.getRequestURI().toString() + * + * @return + * true, if is authorized for URL + */ + boolean isAuthorizedForURL(String url); + + /** + * Checks if the current user is authorized to access the referenced function. + * + * The implementation of this method should call assertAuthorizedForFunction(String functionName), and if an + * AccessControlException is not thrown, this method should return true. + * + * @param functionName + * the name of the function + * + * @return + * true, if is authorized for function + */ + boolean isAuthorizedForFunction(String functionName); + + + /** + * Checks if the current user is authorized to access the referenced data, represented as an Object. + * + * The implementation of this method should call assertAuthorizedForData(String action, Object data), and if an + * AccessControlException is not thrown, this method should return true. + * + * @param action + * The action to verify for an access control decision, such as a role, or an action being performed on the object + * (e.g., Read, Write, etc.), or the name of the function the data is being passed to. + * + * @param data + * The actual object or object identifier being accessed or a reference to the object being accessed. + * + * @return + * true, if is authorized for the data + */ + boolean isAuthorizedForData(String action, Object data); + + /** + * Checks if the current user is authorized to access the referenced file. + * + * The implementation of this method should call assertAuthorizedForFile(String filepath), and if an AccessControlException + * is not thrown, this method should return true. + * + * @param filepath + * the path of the file to be checked, including filename + * + * @return + * true, if is authorized for the file + */ + boolean isAuthorizedForFile(String filepath); + + /** + * Checks if the current user is authorized to access the referenced service. This can be used in applications that + * provide access to a variety of back end services. + * + * The implementation of this method should call assertAuthorizedForService(String serviceName), and if an + * AccessControlException is not thrown, this method should return true. + * + * @param serviceName + * the service name + * + * @return + * true, if is authorized for the service + */ + boolean isAuthorizedForService(String serviceName); + + /** + * Checks if the current user is authorized to access the referenced URL. The implementation should allow + * access to be granted to any part of the URL. Generally, this method should be invoked in the + * application's controller or a filter as follows: + *
    ESAPI.accessController().assertAuthorizedForURL(request.getRequestURI().toString());
    + * + * This method throws an AccessControlException if access is not authorized, or if the referenced URL does not exist. + * If the User is authorized, this method simply returns. + *

    + * Specification: The implementation should do the following: + *

      + *
    1. Check to see if the resource exists and if not, throw an AccessControlException
    2. + *
    3. Use available information to make an access control decision
    4. + *
        + *
      1. Ideally, this policy would be data driven
      2. + *
      3. You can use the current User, roles, data type, data name, time of day, etc.
      4. + *
      5. Access control decisions must deny by default
      6. + *
      + *
    5. If access is not permitted, throw an AccessControlException with details
    6. + *
    + * @param url + * the URL as returned by request.getRequestURI().toString() + * + * @throws AccessControlException + * if access is not permitted + */ + void assertAuthorizedForURL(String url) throws AccessControlException; + + /** + * Checks if the current user is authorized to access the referenced function. The implementation should define the + * function "namespace" to be enforced. Choosing something simple like the class name of action classes or menu item + * names will make this implementation easier to use. + *

    + * This method throws an AccessControlException if access is not authorized, or if the referenced function does not exist. + * If the User is authorized, this method simply returns. + *

    + * Specification: The implementation should do the following: + *

      + *
    1. Check to see if the function exists and if not, throw an AccessControlException
    2. + *
    3. Use available information to make an access control decision
    4. + *
        + *
      1. Ideally, this policy would be data driven
      2. + *
      3. You can use the current User, roles, data type, data name, time of day, etc.
      4. + *
      5. Access control decisions must deny by default
      6. + *
      + *
    5. If access is not permitted, throw an AccessControlException with details
    6. + *
    + * + * @param functionName + * the function name + * + * @throws AccessControlException + * if access is not permitted + */ + void assertAuthorizedForFunction(String functionName) throws AccessControlException; + + + /** + * Checks if the current user is authorized to access the referenced data. This method simply returns if access is authorized. + * It throws an AccessControlException if access is not authorized, or if the referenced data does not exist. + *

    + * Specification: The implementation should do the following: + *

      + *
    1. Check to see if the resource exists and if not, throw an AccessControlException
    2. + *
    3. Use available information to make an access control decision
    4. + *
        + *
      1. Ideally, this policy would be data driven
      2. + *
      3. You can use the current User, roles, data type, data name, time of day, etc.
      4. + *
      5. Access control decisions must deny by default
      6. + *
      + *
    5. If access is not permitted, throw an AccessControlException with details
    6. + *
    + * + * @param action + * The action to verify for an access control decision, such as a role, or an action being performed on the object + * (e.g., Read, Write, etc.), or the name of the function the data is being passed to. + * + * @param data + * The actual object or object identifier being accessed or a reference to the object being accessed. + * + * @throws AccessControlException + * if access is not permitted + */ + void assertAuthorizedForData(String action, Object data) throws AccessControlException; + + /** + * Checks if the current user is authorized to access the referenced file. The implementation should validate and canonicalize the + * input to be sure the filepath is not malicious. + *

    + * This method throws an AccessControlException if access is not authorized, or if the referenced File does not exist. + * If the User is authorized, this method simply returns. + *

    + * Specification: The implementation should do the following: + *

      + *
    1. Check to see if the File exists and if not, throw an AccessControlException
    2. + *
    3. Use available information to make an access control decision
    4. + *
        + *
      1. Ideally, this policy would be data driven
      2. + *
      3. You can use the current User, roles, data type, data name, time of day, etc.
      4. + *
      5. Access control decisions must deny by default
      6. + *
      + *
    5. If access is not permitted, throw an AccessControlException with details
    6. + *
    + * + * @param filepath + * Path to the file to be checked + * @throws AccessControlException if access is denied + */ + void assertAuthorizedForFile(String filepath) throws AccessControlException; + + /** + * Checks if the current user is authorized to access the referenced service. This can be used in applications that + * provide access to a variety of backend services. + *

    + * This method throws an AccessControlException if access is not authorized, or if the referenced service does not exist. + * If the User is authorized, this method simply returns. + *

    + * Specification: The implementation should do the following: + *

      + *
    1. Check to see if the service exists and if not, throw an AccessControlException
    2. + *
    3. Use available information to make an access control decision
    4. + *
        + *
      1. Ideally, this policy would be data driven
      2. + *
      3. You can use the current User, roles, data type, data name, time of day, etc.
      4. + *
      5. Access control decisions must deny by default
      6. + *
      + *
    5. If access is not permitted, throw an AccessControlException with details
    6. + *
    + * + * @param serviceName + * the service name + * + * @throws AccessControlException + * if access is not permitted + */ + void assertAuthorizedForService(String serviceName) throws AccessControlException; + +} From b7859f80e540b37d8ada9967eb47abe4645ec3ce Mon Sep 17 00:00:00 2001 From: kwwall Date: Mon, 18 Jan 2016 23:33:47 -0500 Subject: [PATCH 0046/1069] Change CRLF to LF for issue 356. --- .../java/org/owasp/esapi/AccessControlRule.java | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/main/java/org/owasp/esapi/AccessControlRule.java b/src/main/java/org/owasp/esapi/AccessControlRule.java index becdb9971..b10940369 100644 --- a/src/main/java/org/owasp/esapi/AccessControlRule.java +++ b/src/main/java/org/owasp/esapi/AccessControlRule.java @@ -1,8 +1,8 @@ -package org.owasp.esapi; - - -public interface AccessControlRule { - public void setPolicyParameters(P policyParameter); - public P getPolicyParameters(); - public boolean isAuthorized(R runtimeParameter) throws Exception; -} +package org.owasp.esapi; + + +public interface AccessControlRule { + public void setPolicyParameters(P policyParameter); + public P getPolicyParameters(); + public boolean isAuthorized(R runtimeParameter) throws Exception; +} From 4c731447a5481207b8ff6c7a09debb91cef6351c Mon Sep 17 00:00:00 2001 From: kwwall Date: Mon, 18 Jan 2016 23:33:47 -0500 Subject: [PATCH 0047/1069] Change CRLF to LF for issue 356. --- .../org/owasp/esapi/AccessReferenceMap.java | 352 +++++++++--------- 1 file changed, 176 insertions(+), 176 deletions(-) diff --git a/src/main/java/org/owasp/esapi/AccessReferenceMap.java b/src/main/java/org/owasp/esapi/AccessReferenceMap.java index a20338e18..4fd74e01e 100644 --- a/src/main/java/org/owasp/esapi/AccessReferenceMap.java +++ b/src/main/java/org/owasp/esapi/AccessReferenceMap.java @@ -1,176 +1,176 @@ -/** - * OWASP Enterprise Security API (ESAPI) - * - * This file is part of the Open Web Application Security Project (OWASP) - * Enterprise Security API (ESAPI) project. For details, please see - * http://www.owasp.org/index.php/ESAPI. - * - * Copyright (c) 2007 - The OWASP Foundation - * - * The ESAPI is published by OWASP under the BSD license. You should read and accept the - * LICENSE before you use, modify, and/or redistribute this software. - * - * @author Jeff Williams Aspect Security - * @created 2007 - */ -package org.owasp.esapi; - -import org.owasp.esapi.errors.AccessControlException; - -import java.io.Serializable; -import java.util.Iterator; -import java.util.Set; - - -/** - * The AccessReferenceMap interface is used to map from a set of internal - * direct object references to a set of indirect references that are safe to - * disclose publicly. This can be used to help protect database keys, - * filenames, and other types of direct object references. As a rule, developers - * should not expose their direct object references as it enables attackers to - * attempt to manipulate them. - *

    - * Indirect references are handled as strings, to facilitate their use in HTML. - * Implementations can generate simple integers or more complicated random - * character strings as indirect references. Implementations should probably add - * a constructor that takes a list of direct references. - *

    - * Note that in addition to defeating all forms of parameter tampering attacks, - * there is a side benefit of the AccessReferenceMap. Using random strings as indirect object - * references, as opposed to simple integers makes it impossible for an attacker to - * guess valid identifiers. So if per-user AccessReferenceMaps are used, then request - * forgery (CSRF) attacks will also be prevented. - * - *

    - * Set fileSet = new HashSet();
    - * fileSet.addAll(...); // add direct references (e.g. File objects)
    - * AccessReferenceMap map = new AccessReferenceMap( fileSet );
    - * // store the map somewhere safe - like the session!
    - * String indRef = map.getIndirectReference( file1 );
    - * String href = "http://www.aspectsecurity.com/esapi?file=" + indRef );
    - * ...
    - * // if the indirect reference doesn't exist, it's likely an attack
    - * // getDirectReference throws an AccessControlException
    - * // you should handle as appropriate
    - * String indref = request.getParameter( "file" );
    - * File file = (File)map.getDirectReference( indref );
    - * 
    - * - *

    - * - * @author Jeff Williams (jeff.williams@aspectsecurity.com) - * @author Chris Schmidt (chrisisbeef@gmail.com) - */ -public interface AccessReferenceMap extends Serializable { - - /** - * Get an iterator through the direct object references. No guarantee is made as - * to the order of items returned. - * - * @return the iterator - */ - Iterator iterator(); - - /** - * Get a safe indirect reference to use in place of a potentially sensitive - * direct object reference. Developers should use this call when building - * URL's, form fields, hidden fields, etc... to help protect their private - * implementation information. - * - * @param directReference - * the direct reference - * - * @return - * the indirect reference - */ - K getIndirectReference(T directReference); - - /** - * Get the original direct object reference from an indirect reference. - * Developers should use this when they get an indirect reference from a - * request to translate it back into the real direct reference. If an - * invalid indirect reference is requested, then an AccessControlException is - * thrown. - * - * If a type is implied the requested object will be cast to that type, if the - * object is not of the requested type, a AccessControlException will be thrown to - * the caller. - * - * For example: - *

    -    * UserProfile profile = arm.getDirectReference( indirectRef );
    -    * 
    - * - * Will throw a AccessControlException if the object stored in memory is not of type - * UserProfile. - * - * However, - *
    -    * Object uncastObject = arm.getDirectReference( indirectRef );
    -    * 
    - * - * Will never throw a AccessControlException as long as the object exists. If you are - * unsure of the object type of that an indirect reference references you should get - * the uncast object and test for type in the calling code. - *
    -    * Object uncastProfile = arm.getDirectReference( indirectRef );
    -    * if ( uncastProfile instanceof UserProfile ) {
    -    *     UserProfile userProfile = (UserProfile) uncastProfile;
    -    *     // ...
    -    * } else {
    -    *     EmployeeProfile employeeProfile = (EmployeeProfile) uncastProfile;
    -    *     // ...
    -    * }
    -    * 
    - * - * @param indirectReference - * the indirect reference - * - * @return - * the direct reference - * - * @throws AccessControlException - * if no direct reference exists for the specified indirect reference - * @throws ClassCastException - * if the implied type is not the same as the referenced object type - */ - T getDirectReference(K indirectReference) throws AccessControlException; - - /** - * Adds a direct reference to the AccessReferenceMap, then generates and returns - * an associated indirect reference. - * - * @param direct - * the direct reference - * - * @return - * the corresponding indirect reference - */ - K addDirectReference(T direct); - - /** - * Removes a direct reference and its associated indirect reference from the AccessReferenceMap. - * - * @param direct - * the direct reference to remove - * - * @return - * the corresponding indirect reference - * - * @throws AccessControlException - * if the reference does not exist. - */ - K removeDirectReference(T direct) throws AccessControlException; - - /** - * Updates the access reference map with a new set of direct references, maintaining - * any existing indirect references associated with items that are in the new list. - * New indirect references could be generated every time, but that - * might mess up anything that previously used an indirect reference, such - * as a URL parameter. - * - * @param directReferences - * a Set of direct references to add - */ - void update(Set directReferences); -} +/** + * OWASP Enterprise Security API (ESAPI) + * + * This file is part of the Open Web Application Security Project (OWASP) + * Enterprise Security API (ESAPI) project. For details, please see + * http://www.owasp.org/index.php/ESAPI. + * + * Copyright (c) 2007 - The OWASP Foundation + * + * The ESAPI is published by OWASP under the BSD license. You should read and accept the + * LICENSE before you use, modify, and/or redistribute this software. + * + * @author Jeff Williams Aspect Security + * @created 2007 + */ +package org.owasp.esapi; + +import org.owasp.esapi.errors.AccessControlException; + +import java.io.Serializable; +import java.util.Iterator; +import java.util.Set; + + +/** + * The AccessReferenceMap interface is used to map from a set of internal + * direct object references to a set of indirect references that are safe to + * disclose publicly. This can be used to help protect database keys, + * filenames, and other types of direct object references. As a rule, developers + * should not expose their direct object references as it enables attackers to + * attempt to manipulate them. + *

    + * Indirect references are handled as strings, to facilitate their use in HTML. + * Implementations can generate simple integers or more complicated random + * character strings as indirect references. Implementations should probably add + * a constructor that takes a list of direct references. + *

    + * Note that in addition to defeating all forms of parameter tampering attacks, + * there is a side benefit of the AccessReferenceMap. Using random strings as indirect object + * references, as opposed to simple integers makes it impossible for an attacker to + * guess valid identifiers. So if per-user AccessReferenceMaps are used, then request + * forgery (CSRF) attacks will also be prevented. + * + *

    + * Set fileSet = new HashSet();
    + * fileSet.addAll(...); // add direct references (e.g. File objects)
    + * AccessReferenceMap map = new AccessReferenceMap( fileSet );
    + * // store the map somewhere safe - like the session!
    + * String indRef = map.getIndirectReference( file1 );
    + * String href = "http://www.aspectsecurity.com/esapi?file=" + indRef );
    + * ...
    + * // if the indirect reference doesn't exist, it's likely an attack
    + * // getDirectReference throws an AccessControlException
    + * // you should handle as appropriate
    + * String indref = request.getParameter( "file" );
    + * File file = (File)map.getDirectReference( indref );
    + * 
    + * + *

    + * + * @author Jeff Williams (jeff.williams@aspectsecurity.com) + * @author Chris Schmidt (chrisisbeef@gmail.com) + */ +public interface AccessReferenceMap extends Serializable { + + /** + * Get an iterator through the direct object references. No guarantee is made as + * to the order of items returned. + * + * @return the iterator + */ + Iterator iterator(); + + /** + * Get a safe indirect reference to use in place of a potentially sensitive + * direct object reference. Developers should use this call when building + * URL's, form fields, hidden fields, etc... to help protect their private + * implementation information. + * + * @param directReference + * the direct reference + * + * @return + * the indirect reference + */ + K getIndirectReference(T directReference); + + /** + * Get the original direct object reference from an indirect reference. + * Developers should use this when they get an indirect reference from a + * request to translate it back into the real direct reference. If an + * invalid indirect reference is requested, then an AccessControlException is + * thrown. + * + * If a type is implied the requested object will be cast to that type, if the + * object is not of the requested type, a AccessControlException will be thrown to + * the caller. + * + * For example: + *

    +    * UserProfile profile = arm.getDirectReference( indirectRef );
    +    * 
    + * + * Will throw a AccessControlException if the object stored in memory is not of type + * UserProfile. + * + * However, + *
    +    * Object uncastObject = arm.getDirectReference( indirectRef );
    +    * 
    + * + * Will never throw a AccessControlException as long as the object exists. If you are + * unsure of the object type of that an indirect reference references you should get + * the uncast object and test for type in the calling code. + *
    +    * Object uncastProfile = arm.getDirectReference( indirectRef );
    +    * if ( uncastProfile instanceof UserProfile ) {
    +    *     UserProfile userProfile = (UserProfile) uncastProfile;
    +    *     // ...
    +    * } else {
    +    *     EmployeeProfile employeeProfile = (EmployeeProfile) uncastProfile;
    +    *     // ...
    +    * }
    +    * 
    + * + * @param indirectReference + * the indirect reference + * + * @return + * the direct reference + * + * @throws AccessControlException + * if no direct reference exists for the specified indirect reference + * @throws ClassCastException + * if the implied type is not the same as the referenced object type + */ + T getDirectReference(K indirectReference) throws AccessControlException; + + /** + * Adds a direct reference to the AccessReferenceMap, then generates and returns + * an associated indirect reference. + * + * @param direct + * the direct reference + * + * @return + * the corresponding indirect reference + */ + K addDirectReference(T direct); + + /** + * Removes a direct reference and its associated indirect reference from the AccessReferenceMap. + * + * @param direct + * the direct reference to remove + * + * @return + * the corresponding indirect reference + * + * @throws AccessControlException + * if the reference does not exist. + */ + K removeDirectReference(T direct) throws AccessControlException; + + /** + * Updates the access reference map with a new set of direct references, maintaining + * any existing indirect references associated with items that are in the new list. + * New indirect references could be generated every time, but that + * might mess up anything that previously used an indirect reference, such + * as a URL parameter. + * + * @param directReferences + * a Set of direct references to add + */ + void update(Set directReferences); +} From cb65cf0d83cca62a011f33cc1b2de730529182e4 Mon Sep 17 00:00:00 2001 From: kwwall Date: Mon, 18 Jan 2016 23:33:47 -0500 Subject: [PATCH 0048/1069] Change CRLF to LF for issue 356. --- .../java/org/owasp/esapi/Authenticator.java | 648 +++++++++--------- 1 file changed, 324 insertions(+), 324 deletions(-) diff --git a/src/main/java/org/owasp/esapi/Authenticator.java b/src/main/java/org/owasp/esapi/Authenticator.java index 9cb401345..907739db1 100644 --- a/src/main/java/org/owasp/esapi/Authenticator.java +++ b/src/main/java/org/owasp/esapi/Authenticator.java @@ -1,324 +1,324 @@ -/** - * OWASP Enterprise Security API (ESAPI) - * - * This file is part of the Open Web Application Security Project (OWASP) - * Enterprise Security API (ESAPI) project. For details, please see - * http://www.owasp.org/index.php/ESAPI. - * - * Copyright (c) 2007 - The OWASP Foundation - * - * The ESAPI is published by OWASP under the BSD license. You should read and accept the - * LICENSE before you use, modify, and/or redistribute this software. - * - * @author Jeff Williams Aspect Security - * @created 2007 - */ -package org.owasp.esapi; - -import org.owasp.esapi.errors.AuthenticationException; -import org.owasp.esapi.errors.EncryptionException; - -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import java.util.Set; - - -/** - * The Authenticator interface defines a set of methods for generating and - * handling account credentials and session identifiers. The goal of this - * interface is to encourage developers to protect credentials from disclosure - * to the maximum extent possible. - *

    - * One possible implementation relies on the use of a thread local variable to - * store the current user's identity. The application is responsible for calling - * setCurrentUser() as soon as possible after each HTTP request is received. The - * value of getCurrentUser() is used in several other places in this API. This - * eliminates the need to pass a user object to methods throughout the library. - * For example, all of the logging, access control, and exception calls need - * access to the currently logged in user. - *

    - * The goal is to minimize the responsibility of the developer for - * authentication. In this example, the user simply calls authenticate with the - * current request and the name of the parameters containing the username and - * password. The implementation should verify the password if necessary, create - * a session if necessary, and set the user as the current user. - * - *

    - * public void doPost(ServletRequest request, ServletResponse response) {
    - * try {
    - * User user = ESAPI.authenticator().login(request, response);
    - * // continue with authenticated user
    - * } catch (AuthenticationException e) {
    - * // handle failed authentication (it's already been logged)
    - * }
    - * 
    - * - * @author Jeff Williams (jeff.williams .at. aspectsecurity.com) Aspect Security - * @since June 1, 2007 - */ -public interface Authenticator { - - /** - * Clears the current User. This allows the thread to be reused safely. - * - * This clears all threadlocal variables from the thread. This should ONLY be called after - * all possible ESAPI operations have concluded. If you clear too early, many calls will - * fail, including logging, which requires the user identity. - */ - void clearCurrent(); - - /** - * Calls login with the *current* request and response. - * @return Authenticated {@code User} if login is successful. - * @see HTTPUtilities#setCurrentHTTP(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse) - */ - User login() throws AuthenticationException; - - /** - * This method should be called for every HTTP request, to login the current user either from the session of HTTP - * request. This method will set the current user so that getCurrentUser() will work properly. - * - * Authenticates the user's credentials from the HttpServletRequest if - * necessary, creates a session if necessary, and sets the user as the - * current user. - * - * Specification: The implementation should do the following: - * 1) Check if the User is already stored in the session - * a. If so, check that session absolute and inactivity timeout have not expired - * b. Step 2 may not be required if 1a has been satisfied - * 2) Verify User credentials - * a. It is recommended that you use - * loginWithUsernameAndPassword(HttpServletRequest, HttpServletResponse) to verify credentials - * 3) Set the last host of the User (ex. user.setLastHostAddress(address) ) - * 4) Verify that the request is secure (ex. over SSL) - * 5) Verify the User account is allowed to be logged in - * a. Verify the User is not disabled, expired or locked - * 6) Assign User to session variable - * - * @param request - * the current HTTP request - * @param response - * the HTTP response - * - * @return - * the User - * - * @throws AuthenticationException - * if the credentials are not verified, or if the account is disabled, locked, expired, or timed out - */ - User login(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException; - - /** - * Verify that the supplied password matches the password for this user. Password should - * be stored as a hash. It is recommended you use the hashPassword(password, accountName) method - * in this class. - * This method is typically used for "reauthentication" for the most sensitive functions, such - * as transactions, changing email address, and changing other account information. - * - * @param user - * the user who requires verification - * @param password - * the hashed user-supplied password - * - * @return - * true, if the password is correct for the specified user - */ - boolean verifyPassword(User user, String password); - - /** - * Logs out the current user. - * - * This is usually done by calling User.logout on the current User. - */ - void logout(); - - /** - * Creates a new User with the information provided. Implementations should check - * accountName and password for proper format and strength against brute force - * attacks ( verifyAccountNameStrength(String), verifyPasswordStrength(String, String) ). - * - * Two copies of the new password are required to encourage user interface designers to - * include a "re-type password" field in their forms. Implementations should verify that - * both are the same. - * - * @param accountName - * the account name of the new user - * @param password1 - * the password of the new user - * @param password2 - * the password of the new user. This field is to encourage user interface designers to include two password fields in their forms. - * - * @return - * the User that has been created - * - * @throws AuthenticationException - * if user creation fails due to any of the qualifications listed in this method's description - */ - User createUser(String accountName, String password1, String password2) throws AuthenticationException; - - /** - * Generate a strong password. Implementations should use a large character set that does not - * include confusing characters, such as i I 1 l 0 o and O. There are many algorithms to - * generate strong memorable passwords that have been studied in the past. - * - * @return - * a password with strong password strength - */ - String generateStrongPassword(); - - /** - * Generate strong password that takes into account the user's information and old password. Implementations - * should verify that the new password does not include information such as the username, fragments of the - * old password, and other information that could be used to weaken the strength of the password. - * - * @param user - * the user whose information to use when generating password - * @param oldPassword - * the old password to use when verifying strength of new password. The new password may be checked for fragments of oldPassword. - * - * @return - * a password with strong password strength - */ - String generateStrongPassword(User user, String oldPassword); - - /** - * Changes the password for the specified user. This requires the current password, as well as - * the password to replace it with. The new password should be checked against old hashes to be sure the new password does not closely resemble or equal any recent passwords for that User. - * Password strength should also be verified. This new password must be repeated to ensure that the user has typed it in correctly. - * - * @param user - * the user to change the password for - * @param currentPassword - * the current password for the specified user - * @param newPassword - * the new password to use - * @param newPassword2 - * a verification copy of the new password - * - * @throws AuthenticationException - * if any errors occur - */ - void changePassword(User user, String currentPassword, String newPassword, String newPassword2) throws AuthenticationException; - - /** - * Returns the User matching the provided accountId. If the accoundId is not found, an Anonymous - * User or null may be returned. - * - * @param accountId - * the account id - * - * @return - * the matching User object, or the Anonymous User if no match exists - */ - User getUser(long accountId); - - /** - * Returns the User matching the provided accountName. If the accoundId is not found, an Anonymous - * User or null may be returned. - * - * @param accountName - * the account name - * - * @return - * the matching User object, or the Anonymous User if no match exists - */ - User getUser(String accountName); - - /** - * Gets a collection containing all the existing user names. - * - * @return - * a set of all user names - */ - Set getUserNames(); - - /** - * Returns the currently logged in User. - * - * @return - * the matching User object, or the Anonymous User if no match - * exists - */ - User getCurrentUser(); - - /** - * Sets the currently logged in User. - * - * @param user - * the user to set as the current user - */ - void setCurrentUser(User user); - - /** - * Returns a string representation of the hashed password, using the - * accountName as the salt. The salt helps to prevent against "rainbow" - * table attacks where the attacker pre-calculates hashes for known strings. - * This method specifies the use of the user's account name as the "salt" - * value. The Encryptor.hash method can be used if a different salt is - * required. - * - * @param password - * the password to hash - * @param accountName - * the account name to use as the salt - * - * @return - * the hashed password - * @throws EncryptionException - */ - String hashPassword(String password, String accountName) throws EncryptionException; - - /** - * Removes the account of the specified accountName. - * - * @param accountName - * the account name to remove - * - * @throws AuthenticationException - * the authentication exception if user does not exist - */ - void removeUser(String accountName) throws AuthenticationException; - - /** - * Ensures that the account name passes site-specific complexity requirements, like minimum length. - * - * @param accountName - * the account name - * - * @throws AuthenticationException - * if account name does not meet complexity requirements - */ - void verifyAccountNameStrength(String accountName) throws AuthenticationException; - - /** - * Ensures that the password meets site-specific complexity requirements, like length or number - * of character sets. This method takes the old password so that the algorithm can analyze the - * new password to see if it is too similar to the old password. Note that this has to be - * invoked when the user has entered the old password, as the list of old - * credentials stored by ESAPI is all hashed. - * Additionally, the user object is taken in order to verify the password and account name differ. - * - * @param oldPassword - * the old password - * @param newPassword - * the new password - * @param user - * the user - * - * @throws AuthenticationException - * if newPassword is too similar to oldPassword or if newPassword does not meet complexity requirements - */ - void verifyPasswordStrength(String oldPassword, String newPassword, User user) throws AuthenticationException; - - /** - * Determine if the account exists. - * - * @param accountName - * the account name - * - * @return true, if the account exists - */ - boolean exists(String accountName); - -} +/** + * OWASP Enterprise Security API (ESAPI) + * + * This file is part of the Open Web Application Security Project (OWASP) + * Enterprise Security API (ESAPI) project. For details, please see + * http://www.owasp.org/index.php/ESAPI. + * + * Copyright (c) 2007 - The OWASP Foundation + * + * The ESAPI is published by OWASP under the BSD license. You should read and accept the + * LICENSE before you use, modify, and/or redistribute this software. + * + * @author Jeff Williams Aspect Security + * @created 2007 + */ +package org.owasp.esapi; + +import org.owasp.esapi.errors.AuthenticationException; +import org.owasp.esapi.errors.EncryptionException; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.util.Set; + + +/** + * The Authenticator interface defines a set of methods for generating and + * handling account credentials and session identifiers. The goal of this + * interface is to encourage developers to protect credentials from disclosure + * to the maximum extent possible. + *

    + * One possible implementation relies on the use of a thread local variable to + * store the current user's identity. The application is responsible for calling + * setCurrentUser() as soon as possible after each HTTP request is received. The + * value of getCurrentUser() is used in several other places in this API. This + * eliminates the need to pass a user object to methods throughout the library. + * For example, all of the logging, access control, and exception calls need + * access to the currently logged in user. + *

    + * The goal is to minimize the responsibility of the developer for + * authentication. In this example, the user simply calls authenticate with the + * current request and the name of the parameters containing the username and + * password. The implementation should verify the password if necessary, create + * a session if necessary, and set the user as the current user. + * + *

    + * public void doPost(ServletRequest request, ServletResponse response) {
    + * try {
    + * User user = ESAPI.authenticator().login(request, response);
    + * // continue with authenticated user
    + * } catch (AuthenticationException e) {
    + * // handle failed authentication (it's already been logged)
    + * }
    + * 
    + * + * @author Jeff Williams (jeff.williams .at. aspectsecurity.com) Aspect Security + * @since June 1, 2007 + */ +public interface Authenticator { + + /** + * Clears the current User. This allows the thread to be reused safely. + * + * This clears all threadlocal variables from the thread. This should ONLY be called after + * all possible ESAPI operations have concluded. If you clear too early, many calls will + * fail, including logging, which requires the user identity. + */ + void clearCurrent(); + + /** + * Calls login with the *current* request and response. + * @return Authenticated {@code User} if login is successful. + * @see HTTPUtilities#setCurrentHTTP(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse) + */ + User login() throws AuthenticationException; + + /** + * This method should be called for every HTTP request, to login the current user either from the session of HTTP + * request. This method will set the current user so that getCurrentUser() will work properly. + * + * Authenticates the user's credentials from the HttpServletRequest if + * necessary, creates a session if necessary, and sets the user as the + * current user. + * + * Specification: The implementation should do the following: + * 1) Check if the User is already stored in the session + * a. If so, check that session absolute and inactivity timeout have not expired + * b. Step 2 may not be required if 1a has been satisfied + * 2) Verify User credentials + * a. It is recommended that you use + * loginWithUsernameAndPassword(HttpServletRequest, HttpServletResponse) to verify credentials + * 3) Set the last host of the User (ex. user.setLastHostAddress(address) ) + * 4) Verify that the request is secure (ex. over SSL) + * 5) Verify the User account is allowed to be logged in + * a. Verify the User is not disabled, expired or locked + * 6) Assign User to session variable + * + * @param request + * the current HTTP request + * @param response + * the HTTP response + * + * @return + * the User + * + * @throws AuthenticationException + * if the credentials are not verified, or if the account is disabled, locked, expired, or timed out + */ + User login(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException; + + /** + * Verify that the supplied password matches the password for this user. Password should + * be stored as a hash. It is recommended you use the hashPassword(password, accountName) method + * in this class. + * This method is typically used for "reauthentication" for the most sensitive functions, such + * as transactions, changing email address, and changing other account information. + * + * @param user + * the user who requires verification + * @param password + * the hashed user-supplied password + * + * @return + * true, if the password is correct for the specified user + */ + boolean verifyPassword(User user, String password); + + /** + * Logs out the current user. + * + * This is usually done by calling User.logout on the current User. + */ + void logout(); + + /** + * Creates a new User with the information provided. Implementations should check + * accountName and password for proper format and strength against brute force + * attacks ( verifyAccountNameStrength(String), verifyPasswordStrength(String, String) ). + * + * Two copies of the new password are required to encourage user interface designers to + * include a "re-type password" field in their forms. Implementations should verify that + * both are the same. + * + * @param accountName + * the account name of the new user + * @param password1 + * the password of the new user + * @param password2 + * the password of the new user. This field is to encourage user interface designers to include two password fields in their forms. + * + * @return + * the User that has been created + * + * @throws AuthenticationException + * if user creation fails due to any of the qualifications listed in this method's description + */ + User createUser(String accountName, String password1, String password2) throws AuthenticationException; + + /** + * Generate a strong password. Implementations should use a large character set that does not + * include confusing characters, such as i I 1 l 0 o and O. There are many algorithms to + * generate strong memorable passwords that have been studied in the past. + * + * @return + * a password with strong password strength + */ + String generateStrongPassword(); + + /** + * Generate strong password that takes into account the user's information and old password. Implementations + * should verify that the new password does not include information such as the username, fragments of the + * old password, and other information that could be used to weaken the strength of the password. + * + * @param user + * the user whose information to use when generating password + * @param oldPassword + * the old password to use when verifying strength of new password. The new password may be checked for fragments of oldPassword. + * + * @return + * a password with strong password strength + */ + String generateStrongPassword(User user, String oldPassword); + + /** + * Changes the password for the specified user. This requires the current password, as well as + * the password to replace it with. The new password should be checked against old hashes to be sure the new password does not closely resemble or equal any recent passwords for that User. + * Password strength should also be verified. This new password must be repeated to ensure that the user has typed it in correctly. + * + * @param user + * the user to change the password for + * @param currentPassword + * the current password for the specified user + * @param newPassword + * the new password to use + * @param newPassword2 + * a verification copy of the new password + * + * @throws AuthenticationException + * if any errors occur + */ + void changePassword(User user, String currentPassword, String newPassword, String newPassword2) throws AuthenticationException; + + /** + * Returns the User matching the provided accountId. If the accoundId is not found, an Anonymous + * User or null may be returned. + * + * @param accountId + * the account id + * + * @return + * the matching User object, or the Anonymous User if no match exists + */ + User getUser(long accountId); + + /** + * Returns the User matching the provided accountName. If the accoundId is not found, an Anonymous + * User or null may be returned. + * + * @param accountName + * the account name + * + * @return + * the matching User object, or the Anonymous User if no match exists + */ + User getUser(String accountName); + + /** + * Gets a collection containing all the existing user names. + * + * @return + * a set of all user names + */ + Set getUserNames(); + + /** + * Returns the currently logged in User. + * + * @return + * the matching User object, or the Anonymous User if no match + * exists + */ + User getCurrentUser(); + + /** + * Sets the currently logged in User. + * + * @param user + * the user to set as the current user + */ + void setCurrentUser(User user); + + /** + * Returns a string representation of the hashed password, using the + * accountName as the salt. The salt helps to prevent against "rainbow" + * table attacks where the attacker pre-calculates hashes for known strings. + * This method specifies the use of the user's account name as the "salt" + * value. The Encryptor.hash method can be used if a different salt is + * required. + * + * @param password + * the password to hash + * @param accountName + * the account name to use as the salt + * + * @return + * the hashed password + * @throws EncryptionException + */ + String hashPassword(String password, String accountName) throws EncryptionException; + + /** + * Removes the account of the specified accountName. + * + * @param accountName + * the account name to remove + * + * @throws AuthenticationException + * the authentication exception if user does not exist + */ + void removeUser(String accountName) throws AuthenticationException; + + /** + * Ensures that the account name passes site-specific complexity requirements, like minimum length. + * + * @param accountName + * the account name + * + * @throws AuthenticationException + * if account name does not meet complexity requirements + */ + void verifyAccountNameStrength(String accountName) throws AuthenticationException; + + /** + * Ensures that the password meets site-specific complexity requirements, like length or number + * of character sets. This method takes the old password so that the algorithm can analyze the + * new password to see if it is too similar to the old password. Note that this has to be + * invoked when the user has entered the old password, as the list of old + * credentials stored by ESAPI is all hashed. + * Additionally, the user object is taken in order to verify the password and account name differ. + * + * @param oldPassword + * the old password + * @param newPassword + * the new password + * @param user + * the user + * + * @throws AuthenticationException + * if newPassword is too similar to oldPassword or if newPassword does not meet complexity requirements + */ + void verifyPasswordStrength(String oldPassword, String newPassword, User user) throws AuthenticationException; + + /** + * Determine if the account exists. + * + * @param accountName + * the account name + * + * @return true, if the account exists + */ + boolean exists(String accountName); + +} From 5762feafcab321a134f71765827405596c8c3282 Mon Sep 17 00:00:00 2001 From: kwwall Date: Mon, 18 Jan 2016 23:33:47 -0500 Subject: [PATCH 0049/1069] Change CRLF to LF for issue 356. --- .../java/org/owasp/esapi/codecs/Base64.java | 3748 ++++++++--------- 1 file changed, 1874 insertions(+), 1874 deletions(-) diff --git a/src/main/java/org/owasp/esapi/codecs/Base64.java b/src/main/java/org/owasp/esapi/codecs/Base64.java index e470ac6e8..624d23c3d 100644 --- a/src/main/java/org/owasp/esapi/codecs/Base64.java +++ b/src/main/java/org/owasp/esapi/codecs/Base64.java @@ -1,1874 +1,1874 @@ -package org.owasp.esapi.codecs; - -import org.owasp.esapi.ESAPI; -import org.owasp.esapi.Logger; - -// CHECKME: Version at http://iharder.net/base64 is up to v2.3.3. Some semantic changes -// starting with v2.3. Should we upgrade and then add ESAPI logging or stay at 2.2.2 base? -// I think that really depends on how much OWASP ESAPI plans on tracking changes to this -// version vs. if the plan was just to fork from it and maintain OWASP's own version. -// At this point, I think I prefer split from tracking Harder's original, but I'm easily -// persuaded otherwise. - Kevin Wall - -/** - *

    Encodes and decodes to and from Base64 notation.

    - *

    Homepage: http://iharder.net/base64 - * (based on version 2.2.2).

    - * - *

    The options parameter, which appears in a few places, is used to pass - * several pieces of information to the encoder. In the "higher level" methods such as - * encodeBytes( bytes, options ) the options parameter can be used to indicate such - * things as first gzipping the bytes before encoding them, not inserting linefeeds - * (though that breaks strict Base64 compatibility), and encoding using the URL-safe - * and Ordered dialects.

    - * - *

    The constants defined in Base64 can be OR-ed together to combine options, so you - * might make a call like this:

    - * - * String encoded = Base64.encodeBytes( mybytes, Base64.GZIP | Base64.DONT_BREAK_LINES ); - * - *

    to compress the data before encoding it and then making the output have no newline characters.

    - * - * - *

    - * Original change Log: - *

    - *
      - *
    • v2.2.2 - Fixed encodeFileToFile and decodeFileToFile to use the - * Base64.InputStream class to encode and decode on the fly which uses - * less memory than encoding/decoding an entire file into memory before writing.
    • - *
    • v2.2.1 - Fixed bug using URL_SAFE and ORDERED encodings. Fixed bug - * when using very small files (~< 40 bytes).
    • - *
    • v2.2 - Added some helper methods for encoding/decoding directly from - * one file to the next. Also added a main() method to support command line - * encoding/decoding from one file to the next. Also added these Base64 dialects: - *
        - *
      1. The default is RFC3548 format.
      2. - *
      3. Calling Base64.setFormat(Base64.BASE64_FORMAT.URLSAFE_FORMAT) generates - * URL and file name friendly format as described in Section 4 of RFC3548. - * http://www.faqs.org/rfcs/rfc3548.html
      4. - *
      5. Calling Base64.setFormat(Base64.BASE64_FORMAT.ORDERED_FORMAT) generates - * URL and file name friendly format that preserves lexical ordering as described - * in http://www.faqs.org/qa/rfcc-1940.html
      6. - *
      - * Special thanks to Jim Kellerman at http://www.powerset.com/ - * for contributing the new Base64 dialects. - *
    • - * - *
    • v2.1 - Cleaned up javadoc comments and unused variables and methods. Added - * some convenience methods for reading and writing to and from files.
    • - *
    • v2.0.2 - Now specifies UTF-8 encoding in places where the code fails on systems - * with other encodings (like EBCDIC).
    • - *
    • v2.0.1 - Fixed an error when decoding a single byte, that is, when the - * encoded data was a single byte.
    • - *
    • v2.0 - I got rid of methods that used booleans to set options. - * Now everything is more consolidated and cleaner. The code now detects - * when data that's being decoded is gzip-compressed and will decompress it - * automatically. Generally things are cleaner. You'll probably have to - * change some method calls that you were making to support the new - * options format (ints that you "OR" together).
    • - *
    • v1.5.1 - Fixed bug when decompressing and decoding to a - * byte[] using decode( String s, boolean gzipCompressed ). - * Added the ability to "suspend" encoding in the Output Stream so - * you can turn on and off the encoding if you need to embed base64 - * data in an otherwise "normal" stream (like an XML file).
    • - *
    • v1.5 - Output stream pases on flush() command but doesn't do anything itself. - * This helps when using GZIP streams. - * Added the ability to GZip-compress objects before encoding them.
    • - *
    • v1.4 - Added helper methods to read/write files.
    • - *
    • v1.3.6 - Fixed OutputStream.flush() so that 'position' is reset.
    • - *
    • v1.3.5 - Added flag to turn on and off line breaks. Fixed bug in input stream - * where last buffer being read, if not completely full, was not returned.
    • - *
    • v1.3.4 - Fixed when "improperly padded stream" error was thrown at the wrong time.
    • - *
    • v1.3.3 - Fixed I/O streams which were totally messed up.
    • - *
    - * - *

    - * I am placing this code in the Public Domain. Do with it as you will. - * This software comes with no guarantees or warranties but with - * plenty of well-wishing instead! - * Please visit http://iharder.net/base64 - * periodically to check for updates or to contribute improvements. - *

    - * - * @author Robert Harder - * @author rob@iharder.net - * @version 2.2.2 - */ -public class Base64 -{ - -/* ******** P U B L I C F I E L D S ******** */ - - - /** No options specified. Value is zero. */ - public final static int NO_OPTIONS = 0; - - /** Specify encoding. */ - public final static int ENCODE = 1; - - - /** Specify decoding. */ - public final static int DECODE = 0; - - - /** Specify that data should be gzip-compressed. */ - public final static int GZIP = 2; - - - /** Don't break lines when encoding (violates strict Base64 specification) */ - public final static int DONT_BREAK_LINES = 8; - - /** - * Encode using Base64-like encoding that is URL- and Filename-safe as described - * in Section 4 of RFC3548: - * http://www.faqs.org/rfcs/rfc3548.html. - * It is important to note that data encoded this way is not officially valid Base64, - * or at the very least should not be called Base64 without also specifying that is - * was encoded using the URL- and Filename-safe dialect. - */ - public final static int URL_SAFE = 16; - - - /** - * Encode using the special "ordered" dialect of Base64 described here: - * http://www.faqs.org/qa/rfcc-1940.html. - */ - public final static int ORDERED = 32; - - -/* ******** P R I V A T E F I E L D S ******** */ - - - /** Maximum line length (76) of Base64 output. */ - private final static int MAX_LINE_LENGTH = 76; - - - /** The equals sign (=) as a byte. */ - private final static byte EQUALS_SIGN = (byte)'='; - - - /** The new line character (\n) as a byte. */ - private final static byte NEW_LINE = (byte)'\n'; - - - /** Preferred encoding. */ - private final static String PREFERRED_ENCODING = "UTF-8"; - - /** End of line character. */ - private final static String EOL = System.getProperty("line.separator", "\n"); - - - // I think I end up not using the BAD_ENCODING indicator. - //private final static byte BAD_ENCODING = -9; // Indicates error in encoding - private final static byte WHITE_SPACE_ENC = -5; // Indicates white space in encoding - private final static byte EQUALS_SIGN_ENC = -1; // Indicates equals sign in encoding - - private static final Logger logger = ESAPI.getLogger("Base64"); - - -/* ******** S T A N D A R D B A S E 6 4 A L P H A B E T ******** */ - - /** The 64 valid Base64 values. */ - //private final static byte[] ALPHABET; - /* Host platform me be something funny like EBCDIC, so we hard code these values. */ - private final static byte[] _STANDARD_ALPHABET = - { - (byte)'A', (byte)'B', (byte)'C', (byte)'D', (byte)'E', (byte)'F', (byte)'G', - (byte)'H', (byte)'I', (byte)'J', (byte)'K', (byte)'L', (byte)'M', (byte)'N', - (byte)'O', (byte)'P', (byte)'Q', (byte)'R', (byte)'S', (byte)'T', (byte)'U', - (byte)'V', (byte)'W', (byte)'X', (byte)'Y', (byte)'Z', - (byte)'a', (byte)'b', (byte)'c', (byte)'d', (byte)'e', (byte)'f', (byte)'g', - (byte)'h', (byte)'i', (byte)'j', (byte)'k', (byte)'l', (byte)'m', (byte)'n', - (byte)'o', (byte)'p', (byte)'q', (byte)'r', (byte)'s', (byte)'t', (byte)'u', - (byte)'v', (byte)'w', (byte)'x', (byte)'y', (byte)'z', - (byte)'0', (byte)'1', (byte)'2', (byte)'3', (byte)'4', (byte)'5', - (byte)'6', (byte)'7', (byte)'8', (byte)'9', (byte)'+', (byte)'/' - }; - - - /** - * Translates a Base64 value to either its 6-bit reconstruction value - * or a negative number indicating some other meaning. - **/ - private final static byte[] _STANDARD_DECODABET = - { - -9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 0 - 8 - -5,-5, // Whitespace: Tab and Linefeed - -9,-9, // Decimal 11 - 12 - -5, // Whitespace: Carriage Return - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 14 - 26 - -9,-9,-9,-9,-9, // Decimal 27 - 31 - -5, // Whitespace: Space - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 33 - 42 - 62, // Plus sign at decimal 43 - -9,-9,-9, // Decimal 44 - 46 - 63, // Slash at decimal 47 - 52,53,54,55,56,57,58,59,60,61, // Numbers zero through nine - -9,-9,-9, // Decimal 58 - 60 - -1, // Equals sign at decimal 61 - -9,-9,-9, // Decimal 62 - 64 - 0,1,2,3,4,5,6,7,8,9,10,11,12,13, // Letters 'A' through 'N' - 14,15,16,17,18,19,20,21,22,23,24,25, // Letters 'O' through 'Z' - -9,-9,-9,-9,-9,-9, // Decimal 91 - 96 - 26,27,28,29,30,31,32,33,34,35,36,37,38, // Letters 'a' through 'm' - 39,40,41,42,43,44,45,46,47,48,49,50,51, // Letters 'n' through 'z' - -9,-9,-9,-9 // Decimal 123 - 126 - /*,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 127 - 139 - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 140 - 152 - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 153 - 165 - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 166 - 178 - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 179 - 191 - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 192 - 204 - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 205 - 217 - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 218 - 230 - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 231 - 243 - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9 // Decimal 244 - 255 */ - }; - - -/* ******** U R L S A F E B A S E 6 4 A L P H A B E T ******** */ - - /** - * Used in the URL- and Filename-safe dialect described in Section 4 of RFC3548: - * http://www.faqs.org/rfcs/rfc3548.html. - * Notice that the last two bytes become "hyphen" and "underscore" instead of "plus" and "slash." - */ - private final static byte[] _URL_SAFE_ALPHABET = - { - (byte)'A', (byte)'B', (byte)'C', (byte)'D', (byte)'E', (byte)'F', (byte)'G', - (byte)'H', (byte)'I', (byte)'J', (byte)'K', (byte)'L', (byte)'M', (byte)'N', - (byte)'O', (byte)'P', (byte)'Q', (byte)'R', (byte)'S', (byte)'T', (byte)'U', - (byte)'V', (byte)'W', (byte)'X', (byte)'Y', (byte)'Z', - (byte)'a', (byte)'b', (byte)'c', (byte)'d', (byte)'e', (byte)'f', (byte)'g', - (byte)'h', (byte)'i', (byte)'j', (byte)'k', (byte)'l', (byte)'m', (byte)'n', - (byte)'o', (byte)'p', (byte)'q', (byte)'r', (byte)'s', (byte)'t', (byte)'u', - (byte)'v', (byte)'w', (byte)'x', (byte)'y', (byte)'z', - (byte)'0', (byte)'1', (byte)'2', (byte)'3', (byte)'4', (byte)'5', - (byte)'6', (byte)'7', (byte)'8', (byte)'9', (byte)'-', (byte)'_' - }; - - /** - * Used in decoding URL- and Filename-safe dialects of Base64. - */ - private final static byte[] _URL_SAFE_DECODABET = - { - -9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 0 - 8 - -5,-5, // Whitespace: Tab and Linefeed - -9,-9, // Decimal 11 - 12 - -5, // Whitespace: Carriage Return - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 14 - 26 - -9,-9,-9,-9,-9, // Decimal 27 - 31 - -5, // Whitespace: Space - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 33 - 42 - -9, // Plus sign at decimal 43 - -9, // Decimal 44 - 62, // Minus sign at decimal 45 - -9, // Decimal 46 - -9, // Slash at decimal 47 - 52,53,54,55,56,57,58,59,60,61, // Numbers zero through nine - -9,-9,-9, // Decimal 58 - 60 - -1, // Equals sign at decimal 61 - -9,-9,-9, // Decimal 62 - 64 - 0,1,2,3,4,5,6,7,8,9,10,11,12,13, // Letters 'A' through 'N' - 14,15,16,17,18,19,20,21,22,23,24,25, // Letters 'O' through 'Z' - -9,-9,-9,-9, // Decimal 91 - 94 - 63, // Underscore at decimal 95 - -9, // Decimal 96 - 26,27,28,29,30,31,32,33,34,35,36,37,38, // Letters 'a' through 'm' - 39,40,41,42,43,44,45,46,47,48,49,50,51, // Letters 'n' through 'z' - -9,-9,-9,-9 // Decimal 123 - 126 - /*,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 127 - 139 - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 140 - 152 - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 153 - 165 - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 166 - 178 - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 179 - 191 - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 192 - 204 - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 205 - 217 - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 218 - 230 - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 231 - 243 - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9 // Decimal 244 - 255 */ - }; - - - -/* ******** O R D E R E D B A S E 6 4 A L P H A B E T ******** */ - - /** - * I don't get the point of this technique, but it is described here: - * http://www.faqs.org/qa/rfcc-1940.html. - */ - private final static byte[] _ORDERED_ALPHABET = - { - (byte)'-', - (byte)'0', (byte)'1', (byte)'2', (byte)'3', (byte)'4', - (byte)'5', (byte)'6', (byte)'7', (byte)'8', (byte)'9', - (byte)'A', (byte)'B', (byte)'C', (byte)'D', (byte)'E', (byte)'F', (byte)'G', - (byte)'H', (byte)'I', (byte)'J', (byte)'K', (byte)'L', (byte)'M', (byte)'N', - (byte)'O', (byte)'P', (byte)'Q', (byte)'R', (byte)'S', (byte)'T', (byte)'U', - (byte)'V', (byte)'W', (byte)'X', (byte)'Y', (byte)'Z', - (byte)'_', - (byte)'a', (byte)'b', (byte)'c', (byte)'d', (byte)'e', (byte)'f', (byte)'g', - (byte)'h', (byte)'i', (byte)'j', (byte)'k', (byte)'l', (byte)'m', (byte)'n', - (byte)'o', (byte)'p', (byte)'q', (byte)'r', (byte)'s', (byte)'t', (byte)'u', - (byte)'v', (byte)'w', (byte)'x', (byte)'y', (byte)'z' - }; - - /** - * Used in decoding the "ordered" dialect of Base64. - */ - private final static byte[] _ORDERED_DECODABET = - { - -9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 0 - 8 - -5,-5, // Whitespace: Tab and Linefeed - -9,-9, // Decimal 11 - 12 - -5, // Whitespace: Carriage Return - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 14 - 26 - -9,-9,-9,-9,-9, // Decimal 27 - 31 - -5, // Whitespace: Space - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 33 - 42 - -9, // Plus sign at decimal 43 - -9, // Decimal 44 - 0, // Minus sign at decimal 45 - -9, // Decimal 46 - -9, // Slash at decimal 47 - 1,2,3,4,5,6,7,8,9,10, // Numbers zero through nine - -9,-9,-9, // Decimal 58 - 60 - -1, // Equals sign at decimal 61 - -9,-9,-9, // Decimal 62 - 64 - 11,12,13,14,15,16,17,18,19,20,21,22,23, // Letters 'A' through 'M' - 24,25,26,27,28,29,30,31,32,33,34,35,36, // Letters 'N' through 'Z' - -9,-9,-9,-9, // Decimal 91 - 94 - 37, // Underscore at decimal 95 - -9, // Decimal 96 - 38,39,40,41,42,43,44,45,46,47,48,49,50, // Letters 'a' through 'm' - 51,52,53,54,55,56,57,58,59,60,61,62,63, // Letters 'n' through 'z' - -9,-9,-9,-9 // Decimal 123 - 126 - /*,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 127 - 139 - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 140 - 152 - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 153 - 165 - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 166 - 178 - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 179 - 191 - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 192 - 204 - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 205 - 217 - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 218 - 230 - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 231 - 243 - -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9 // Decimal 244 - 255 */ - }; - - -/* ******** D E T E R M I N E W H I C H A L H A B E T ******** */ - - - /** - * Returns one of the _SOMETHING_ALPHABET byte arrays depending on - * the options specified. - * It's possible, though silly, to specify ORDERED and URLSAFE - * in which case one of them will be picked, though there is - * no guarantee as to which one will be picked. - */ - private final static byte[] getAlphabet( int options ) - { - if( (options & URL_SAFE) == URL_SAFE ) return _URL_SAFE_ALPHABET; - else if( (options & ORDERED) == ORDERED ) return _ORDERED_ALPHABET; - else return _STANDARD_ALPHABET; - - } // end getAlphabet - - - /** - * Returns one of the _SOMETHING_DECODABET byte arrays depending on - * the options specified. - * It's possible, though silly, to specify ORDERED and URL_SAFE - * in which case one of them will be picked, though there is - * no guarantee as to which one will be picked. - */ - private final static byte[] getDecodabet( int options ) - { - if( (options & URL_SAFE) == URL_SAFE ) return _URL_SAFE_DECODABET; - else if( (options & ORDERED) == ORDERED ) return _ORDERED_DECODABET; - else return _STANDARD_DECODABET; - - } // end getAlphabet - - - - /** Defeats instantiation. */ - private Base64(){} - - - /** - * Encodes or decodes two files from the command line; - * feel free to delete this method (in fact you probably should) - * if you're embedding this code into a larger program. - * @param args - */ - public final static void main( String[] args ) - { - if( args.length < 3 ){ - usage("Not enough arguments."); - } // end if: args.length < 3 - else { - String flag = args[0]; - String infile = args[1]; - String outfile = args[2]; - if( flag.equals( "-e" ) ){ - Base64.encodeFileToFile( infile, outfile ); - } // end if: encode - else if( flag.equals( "-d" ) ) { - Base64.decodeFileToFile( infile, outfile ); - } // end else if: decode - else { - usage( "Unknown flag: " + flag ); - } // end else - } // end else - } // end main - - /** - * Prints command line usage. - * - * @param msg A message to include with usage info. - */ - private final static void usage( String msg ) - { - System.err.println( msg ); - System.err.println( "Usage: java Base64 -e|-d inputfile outputfile" ); - } // end usage - - -/* ******** E N C O D I N G M E T H O D S ******** */ - - - /** - * Encodes up to the first three bytes of array threeBytes - * and returns a four-byte array in Base64 notation. - * The actual number of significant bytes in your array is - * given by numSigBytes. - * The array threeBytes needs only be as big as - * numSigBytes. - * Code can reuse a byte array by passing a four-byte array as b4. - * - * @param b4 A reusable byte array to reduce array instantiation - * @param threeBytes the array to convert - * @param numSigBytes the number of significant bytes in your array - * @return four byte array in Base64 notation. - * @since 1.5.1 - */ - private static byte[] encode3to4( byte[] b4, byte[] threeBytes, int numSigBytes, int options ) - { - encode3to4( threeBytes, 0, numSigBytes, b4, 0, options ); - return b4; - } // end encode3to4 - - - /** - *

    Encodes up to three bytes of the array source - * and writes the resulting four Base64 bytes to destination. - * The source and destination arrays can be manipulated - * anywhere along their length by specifying - * srcOffset and destOffset. - * This method does not check to make sure your arrays - * are large enough to accomodate srcOffset + 3 for - * the source array or destOffset + 4 for - * the destination array. - * The actual number of significant bytes in your array is - * given by numSigBytes.

    - *

    This is the lowest level of the encoding methods with - * all possible parameters.

    - * - * @param source the array to convert - * @param srcOffset the index where conversion begins - * @param numSigBytes the number of significant bytes in your array - * @param destination the array to hold the conversion - * @param destOffset the index where output will be put - * @return the destination array - * @since 1.3 - */ - private static byte[] encode3to4( - byte[] source, int srcOffset, int numSigBytes, - byte[] destination, int destOffset, int options ) - { - byte[] ALPHABET = getAlphabet( options ); - - // 1 2 3 - // 01234567890123456789012345678901 Bit position - // --------000000001111111122222222 Array position from threeBytes - // --------| || || || | Six bit groups to index ALPHABET - // >>18 >>12 >> 6 >> 0 Right shift necessary - // 0x3f 0x3f 0x3f Additional AND - - // Create buffer with zero-padding if there are only one or two - // significant bytes passed in the array. - // We have to shift left 24 in order to flush out the 1's that appear - // when Java treats a value as negative that is cast from a byte to an int. - int inBuff = ( numSigBytes > 0 ? ((source[ srcOffset ] << 24) >>> 8) : 0 ) - | ( numSigBytes > 1 ? ((source[ srcOffset + 1 ] << 24) >>> 16) : 0 ) - | ( numSigBytes > 2 ? ((source[ srcOffset + 2 ] << 24) >>> 24) : 0 ); - - switch( numSigBytes ) - { - case 3: - destination[ destOffset ] = ALPHABET[ (inBuff >>> 18) ]; - destination[ destOffset + 1 ] = ALPHABET[ (inBuff >>> 12) & 0x3f ]; - destination[ destOffset + 2 ] = ALPHABET[ (inBuff >>> 6) & 0x3f ]; - destination[ destOffset + 3 ] = ALPHABET[ (inBuff ) & 0x3f ]; - return destination; - - case 2: - destination[ destOffset ] = ALPHABET[ (inBuff >>> 18) ]; - destination[ destOffset + 1 ] = ALPHABET[ (inBuff >>> 12) & 0x3f ]; - destination[ destOffset + 2 ] = ALPHABET[ (inBuff >>> 6) & 0x3f ]; - destination[ destOffset + 3 ] = EQUALS_SIGN; - return destination; - - case 1: - destination[ destOffset ] = ALPHABET[ (inBuff >>> 18) ]; - destination[ destOffset + 1 ] = ALPHABET[ (inBuff >>> 12) & 0x3f ]; - destination[ destOffset + 2 ] = EQUALS_SIGN; - destination[ destOffset + 3 ] = EQUALS_SIGN; - return destination; - - default: - return destination; - } // end switch - } // end encode3to4 - - - - /** - * Serializes an object and returns the Base64-encoded - * version of that serialized object. If the object - * cannot be serialized or there is another error, - * the method will return null. - * The object is not GZip-compressed before being encoded. - * - * @param serializableObject The object to encode - * @return The Base64-encoded object - * @since 1.4 - */ - public static String encodeObject( java.io.Serializable serializableObject ) - { - return encodeObject( serializableObject, NO_OPTIONS ); - } // end encodeObject - - - - /** - * Serializes an object and returns the Base64-encoded - * version of that serialized object. If the object - * cannot be serialized or there is another error, - * the method will return null. - *

    - * Valid options:

    -     *   GZIP: gzip-compresses object before encoding it.
    -     *   DONT_BREAK_LINES: don't break lines at 76 characters
    -     *     Note: Technically, this makes your encoding non-compliant.
    -     * 
    - *

    - * Example: encodeObject( myObj, Base64.GZIP ) or - *

    - * Example: encodeObject( myObj, Base64.GZIP | Base64.DONT_BREAK_LINES ) - * - * @param serializableObject The object to encode - * @param options Specified options - * @return The Base64-encoded object - * @see Base64#GZIP - * @see Base64#DONT_BREAK_LINES - * @since 2.0 - */ - public static String encodeObject( java.io.Serializable serializableObject, int options ) - { - // Streams - java.io.ByteArrayOutputStream baos = null; - java.io.OutputStream b64os = null; - java.io.ObjectOutputStream oos = null; - java.util.zip.GZIPOutputStream gzos = null; - - // Isolate options - int gzip = (options & GZIP); - //int dontBreakLines = (options & DONT_BREAK_LINES); - - try - { - // ObjectOutputStream -> (GZIP) -> Base64 -> ByteArrayOutputStream - baos = new java.io.ByteArrayOutputStream(); - b64os = new Base64.OutputStream( baos, ENCODE | options ); - - // GZip? - if( gzip == GZIP ) - { - gzos = new java.util.zip.GZIPOutputStream( b64os ); - oos = new java.io.ObjectOutputStream( gzos ); - } // end if: gzip - else - oos = new java.io.ObjectOutputStream( b64os ); - - oos.writeObject( serializableObject ); - } // end try - catch( java.io.IOException e ) - { - logger.error( Logger.SECURITY_FAILURE, "Problem writing object", e ); - return null; - } // end catch - finally - { - try{ oos.close(); } catch( Exception e ){} - try{ gzos.close(); } catch( Exception e ){} - try{ b64os.close(); } catch( Exception e ){} - try{ baos.close(); } catch( Exception e ){} - } // end finally - - // Return value according to relevant encoding. - try - { - return new String( baos.toByteArray(), PREFERRED_ENCODING ); - } // end try - catch (java.io.UnsupportedEncodingException uue) - { - return new String( baos.toByteArray() ); - } // end catch - - } // end encode - - - - /** - * Encodes a byte array into Base64 notation. - * Does not GZip-compress data. - * - * @param source The data to convert - * @return The Base64-encoded resulting string - * @since 1.4 - */ - public static String encodeBytes( byte[] source ) - { - return encodeBytes( source, 0, source.length, NO_OPTIONS ); - } // end encodeBytes - - - - /** - * Encodes a byte array into Base64 notation. - *

    - * Valid options:

    -     *   GZIP: gzip-compresses object before encoding it.
    -     *   DONT_BREAK_LINES: don't break lines at 76 characters
    -     *     Note: Technically, this makes your encoding non-compliant.
    -     * 
    - *

    - * Example: encodeBytes( myData, Base64.GZIP ) or - *

    - * Example: encodeBytes( myData, Base64.GZIP | Base64.DONT_BREAK_LINES ) - * - * - * @param source The data to convert - * @param options Specified options - * @return The Base64-encoded resulting string - * @see Base64#GZIP - * @see Base64#DONT_BREAK_LINES - * @since 2.0 - */ - public static String encodeBytes( byte[] source, int options ) - { - return encodeBytes( source, 0, source.length, options ); - } // end encodeBytes - - - /** - * Encodes a byte array into Base64 notation. - * Does not GZip-compress data. - * - * @param source The data to convert - * @param off Offset in array where conversion should begin - * @param len Length of data to convert - * @return The Base64-encoded resulting string - * @since 1.4 - */ - public static String encodeBytes( byte[] source, int off, int len ) - { - return encodeBytes( source, off, len, NO_OPTIONS ); - } // end encodeBytes - - - - /** - * Encodes a byte array into Base64 notation. - *

    - * Valid options:

    -     *   GZIP: gzip-compresses object before encoding it.
    -     *   DONT_BREAK_LINES: don't break lines at 76 characters
    -     *     Note: Technically, this makes your encoding non-compliant.
    -     * 
    - *

    - * Example: encodeBytes( myData, Base64.GZIP ) or - *

    - * Example: encodeBytes( myData, Base64.GZIP | Base64.DONT_BREAK_LINES ) - * - * - * @param source The data to convert - * @param off Offset in array where conversion should begin - * @param len Length of data to convert - * @param options alphabet type is pulled from this (standard, url-safe, ordered) - * @return The Base64-encoded resulting string - * @see Base64#GZIP - * @see Base64#DONT_BREAK_LINES - * @since 2.0 - */ - public static String encodeBytes( byte[] source, int off, int len, int options ) - { - // Isolate options - int dontBreakLines = ( options & DONT_BREAK_LINES ); - int gzip = ( options & GZIP ); - - // Compress? - if( gzip == GZIP ) - { - java.io.ByteArrayOutputStream baos = null; - java.util.zip.GZIPOutputStream gzos = null; - Base64.OutputStream b64os = null; - - - try - { - // GZip -> Base64 -> ByteArray - baos = new java.io.ByteArrayOutputStream(); - b64os = new Base64.OutputStream( baos, ENCODE | options ); - gzos = new java.util.zip.GZIPOutputStream( b64os ); - - gzos.write( source, off, len ); - gzos.close(); - } // end try - catch( java.io.IOException e ) - { - logger.error( Logger.SECURITY_FAILURE, "Problem writing gzip stream", e ); - return null; - } // end catch - finally - { - try{ gzos.close(); } catch( Exception e ){} - try{ b64os.close(); } catch( Exception e ){} - try{ baos.close(); } catch( Exception e ){} - } // end finally - - // Return value according to relevant encoding. - try - { - return new String( baos.toByteArray(), PREFERRED_ENCODING ); - } // end try - catch (java.io.UnsupportedEncodingException uue) - { - return new String( baos.toByteArray() ); - } // end catch - } // end if: compress - - // Else, don't compress. Better not to use streams at all then. - else - { - // Convert option to boolean in way that code likes it. - boolean breakLines = dontBreakLines == 0; - - int len43 = len * 4 / 3; - byte[] outBuff = new byte[ ( len43 ) // Main 4:3 - + ( (len % 3) > 0 ? 4 : 0 ) // Account for padding - + (breakLines ? ( len43 / MAX_LINE_LENGTH ) : 0) ]; // New lines - int d = 0; - int e = 0; - int len2 = len - 2; - int lineLength = 0; - for( ; d < len2; d+=3, e+=4 ) - { - encode3to4( source, d+off, 3, outBuff, e, options ); - - lineLength += 4; - if( breakLines && lineLength == MAX_LINE_LENGTH ) - { - outBuff[e+4] = NEW_LINE; - e++; - lineLength = 0; - } // end if: end of line - } // en dfor: each piece of array - - if( d < len ) - { - encode3to4( source, d+off, len - d, outBuff, e, options ); - e += 4; - } // end if: some padding needed - - - // Return value according to relevant encoding. - try - { - return new String( outBuff, 0, e, PREFERRED_ENCODING ); - } // end try - catch (java.io.UnsupportedEncodingException uue) - { - return new String( outBuff, 0, e ); - } // end catch - - } // end else: don't compress - - } // end encodeBytes - - - - - -/* ******** D E C O D I N G M E T H O D S ******** */ - - - /** - * Decodes four bytes from array source - * and writes the resulting bytes (up to three of them) - * to destination. - * The source and destination arrays can be manipulated - * anywhere along their length by specifying - * srcOffset and destOffset. - * This method does not check to make sure your arrays - * are large enough to accomodate srcOffset + 4 for - * the source array or destOffset + 3 for - * the destination array. - * This method returns the actual number of bytes that - * were converted from the Base64 encoding. - *

    This is the lowest level of the decoding methods with - * all possible parameters.

    - * - * - * @param source the array to convert - * @param srcOffset the index where conversion begins - * @param destination the array to hold the conversion - * @param destOffset the index where output will be put - * @param options alphabet type is pulled from this (standard, url-safe, ordered) - * @return the number of decoded bytes converted - * @since 1.3 - */ - private static int decode4to3( byte[] source, int srcOffset, byte[] destination, int destOffset, int options ) - { - byte[] DECODABET = getDecodabet( options ); - - // Example: Dk== - if( source[ srcOffset + 2] == EQUALS_SIGN ) - { - // Two ways to do the same thing. Don't know which way I like best. - //int outBuff = ( ( DECODABET[ source[ srcOffset ] ] << 24 ) >>> 6 ) - // | ( ( DECODABET[ source[ srcOffset + 1] ] << 24 ) >>> 12 ); - int outBuff = ( ( DECODABET[ source[ srcOffset ] ] & 0xFF ) << 18 ) - | ( ( DECODABET[ source[ srcOffset + 1] ] & 0xFF ) << 12 ); - - destination[ destOffset ] = (byte)( outBuff >>> 16 ); - return 1; - } - - // Example: DkL= - else if( source[ srcOffset + 3 ] == EQUALS_SIGN ) - { - // Two ways to do the same thing. Don't know which way I like best. - //int outBuff = ( ( DECODABET[ source[ srcOffset ] ] << 24 ) >>> 6 ) - // | ( ( DECODABET[ source[ srcOffset + 1 ] ] << 24 ) >>> 12 ) - // | ( ( DECODABET[ source[ srcOffset + 2 ] ] << 24 ) >>> 18 ); - int outBuff = ( ( DECODABET[ source[ srcOffset ] ] & 0xFF ) << 18 ) - | ( ( DECODABET[ source[ srcOffset + 1 ] ] & 0xFF ) << 12 ) - | ( ( DECODABET[ source[ srcOffset + 2 ] ] & 0xFF ) << 6 ); - - destination[ destOffset ] = (byte)( outBuff >>> 16 ); - destination[ destOffset + 1 ] = (byte)( outBuff >>> 8 ); - return 2; - } - - // Example: DkLE - else - { - try{ - // Two ways to do the same thing. Don't know which way I like best. - //int outBuff = ( ( DECODABET[ source[ srcOffset ] ] << 24 ) >>> 6 ) - // | ( ( DECODABET[ source[ srcOffset + 1 ] ] << 24 ) >>> 12 ) - // | ( ( DECODABET[ source[ srcOffset + 2 ] ] << 24 ) >>> 18 ) - // | ( ( DECODABET[ source[ srcOffset + 3 ] ] << 24 ) >>> 24 ); - int outBuff = ( ( DECODABET[ source[ srcOffset ] ] & 0xFF ) << 18 ) - | ( ( DECODABET[ source[ srcOffset + 1 ] ] & 0xFF ) << 12 ) - | ( ( DECODABET[ source[ srcOffset + 2 ] ] & 0xFF ) << 6) - | ( ( DECODABET[ source[ srcOffset + 3 ] ] & 0xFF ) ); - - - destination[ destOffset ] = (byte)( outBuff >> 16 ); - destination[ destOffset + 1 ] = (byte)( outBuff >> 8 ); - destination[ destOffset + 2 ] = (byte)( outBuff ); - - return 3; - }catch( Exception e){ - - // Remove these after checking -- for context only. - // logger.error( Logger.SECURITY_FAILURE, "Problem writing object", e ); - // logger.error( Logger.SECURITY_FAILURE, ""+source[srcOffset]+ ": " + ( DECODABET[ source[ srcOffset ] ] ) ); - // logger.error( Logger.SECURITY_FAILURE, ""+source[srcOffset+1]+ ": " + ( DECODABET[ source[ srcOffset + 1 ] ] ) ); - // logger.error( Logger.SECURITY_FAILURE, ""+source[srcOffset+2]+ ": " + ( DECODABET[ source[ srcOffset + 2 ] ] ) ); - // logger.error( Logger.SECURITY_FAILURE, ""+source[srcOffset+3]+ ": " + ( DECODABET[ source[ srcOffset + 3 ] ] ) ); - - // CHECKME: I replaced the 5 separate logger.error() calls above with a single logger.error() call so they can't - // become interleaved with other log entries from other threads. Normally this would have placed log entries - // on separate lines, so I also added line terminators here as well. (Probably don't want it all on one single - // really long log entry, do we?) Anyhow, somebody should check the formatting to ensure that it's - // esthetically pleasing, etc. But this works for me. I'm also OK if you want to remove all the line terminators - // in which case the declaration for EOL should be removed as well. - Kevin Wall - - StringBuilder sb = new StringBuilder("Problem writing object:"); - sb.append(EOL); - sb.append( source[srcOffset] ).append(": ").append( ( DECODABET[ source[ srcOffset ] ] ) ).append(EOL); - sb.append( source[srcOffset+1] ).append(": ").append( ( DECODABET[ source[ srcOffset + 1 ] ] ) ).append(EOL); - sb.append( source[srcOffset+2] ).append(": ").append( ( DECODABET[ source[ srcOffset + 2 ] ] ) ).append(EOL); - sb.append( source[srcOffset+3] ).append(": ").append( ( DECODABET[ source[ srcOffset + 3 ] ] ) ).append(EOL); - - logger.error( Logger.SECURITY_FAILURE, sb.toString(), e ); - return -1; - } // end catch - } - } // end decodeToBytes - - - - - /** - * Very low-level access to decoding ASCII characters in - * the form of a byte array. Does not support automatically - * gunzipping or any other "fancy" features. - * - * @param source The Base64 encoded data - * @param off The offset of where to begin decoding - * @param len The length of characters to decode - * @param options - * @return decoded data - * @since 1.3 - */ - public static byte[] decode( byte[] source, int off, int len, int options ) - { - byte[] DECODABET = getDecodabet( options ); - - int len34 = len * 3 / 4; - byte[] outBuff = new byte[ len34 ]; // Upper limit on size of output - int outBuffPosn = 0; - - byte[] b4 = new byte[4]; - int b4Posn = 0; - int i = 0; - byte sbiCrop = 0; - byte sbiDecode = 0; - for( i = off; i < off+len; i++ ) - { - sbiCrop = (byte)(source[i] & 0x7f); // Only the low seven bits - sbiDecode = DECODABET[ sbiCrop ]; - - if( sbiDecode >= WHITE_SPACE_ENC ) // White space, Equals sign or better - { - if( sbiDecode >= EQUALS_SIGN_ENC ) - { - b4[ b4Posn++ ] = sbiCrop; - if( b4Posn > 3 ) - { - outBuffPosn += decode4to3( b4, 0, outBuff, outBuffPosn, options ); - b4Posn = 0; - - // If that was the equals sign, break out of 'for' loop - if( sbiCrop == EQUALS_SIGN ) - break; - } // end if: quartet built - - } // end if: equals sign or better - - } // end if: white space, equals sign or better - else - { - logger.error( Logger.SECURITY_FAILURE, "Bad Base64 input character at " + i + ": " + source[i] + "(decimal)" ); - return null; - } // end else: - } // each input character - - byte[] out = new byte[ outBuffPosn ]; - System.arraycopy( outBuff, 0, out, 0, outBuffPosn ); - return out; - } // end decode - - - - - /** - * Decodes data from Base64 notation, automatically - * detecting gzip-compressed data and decompressing it. - * - * @param s the string to decode - * @return the decoded data - * @since 1.4 - */ - public static byte[] decode( String s ) - { - return decode( s, NO_OPTIONS ); - } - - - /** - * Decodes data from Base64 notation, automatically - * detecting gzip-compressed data and decompressing it. - * - * @param s the string to decode - * @param options encode options such as URL_SAFE - * @return the decoded data - * @since 1.4 - */ - public static byte[] decode( String s, int options ) - { - byte[] bytes; - try - { - bytes = s.getBytes( PREFERRED_ENCODING ); - } - catch( java.io.UnsupportedEncodingException uee ) - { - bytes = s.getBytes(); // Uses native encoding - // CHECKME: Is this correct? I think it should be a warning instead of an error since nothing - // is re-thrown. I do think that *some* sort of logging is in order here especially since UTF-8 should - // always be available on all platforms. If it's not, then all bets are off on your runtime env. - Kevin Wall - logger.warning( Logger.SECURITY_FAILURE, "Problem decoding string using " + - PREFERRED_ENCODING + "; substituting native platform encoding instead", uee ); - } - - // Decode - bytes = decode( bytes, 0, bytes.length, options ); - - - // Check to see if it's gzip-compressed - // GZIP Magic Two-Byte Number: 0x8b1f (35615) - if( bytes != null && bytes.length >= 4 ) - { - - int head = ((int)bytes[0] & 0xff) | ((bytes[1] << 8) & 0xff00); - if( java.util.zip.GZIPInputStream.GZIP_MAGIC == head ) - { - java.io.ByteArrayInputStream bais = null; - java.util.zip.GZIPInputStream gzis = null; - java.io.ByteArrayOutputStream baos = null; - byte[] buffer = new byte[2048]; - int length = 0; - - try - { - baos = new java.io.ByteArrayOutputStream(); - bais = new java.io.ByteArrayInputStream( bytes ); - gzis = new java.util.zip.GZIPInputStream( bais ); - - while( ( length = gzis.read( buffer ) ) >= 0 ) - { - baos.write(buffer,0,length); - } // end while: reading input - - // No error? Get new bytes. - bytes = baos.toByteArray(); - - } // end try - catch( java.io.IOException e ) - { - // Just return originally-decoded bytes - } // end catch - finally - { - try{ baos.close(); } catch( Exception e ){} - try{ gzis.close(); } catch( Exception e ){} - try{ bais.close(); } catch( Exception e ){} - } // end finally - - } // end if: gzipped - } // end if: bytes.length >= 2 - - return bytes; - } // end decode - - - - - /** - * Attempts to decode Base64 data and deserialize a Java - * Object within. Returns null if there was an error. - * - *

    - * WARNING: Using this method to decode non-validated / - * untrusted data from a string and deserialize it into - * an object can potentially result in remote command - * injection vulnerabilities. Use at your own risk! - *

    - * - * @param encodedObject The Base64 data to decode - * @return The decoded and deserialized object - * @since 1.5 - * - * @deprecated Because of security issues, this method will be - * removed from ESAPI in a future release and no substitute - * is planned. Because as of JDK 8 (in 1Q2016) there is - * currently no way to restrict which objects - * ObjectInputStream.readObject() - * may safely deserialize in the general case. Oracle - * may decide to address this deficiency in a future Java - * release, but until they do, there is no simple way for - * a general class library like ESAPI to address this. - */ - @Deprecated - public static Object decodeToObject( String encodedObject ) - { - // Decode and gunzip if necessary - byte[] objBytes = decode( encodedObject ); - - java.io.ByteArrayInputStream bais = null; - java.io.ObjectInputStream ois = null; - Object obj = null; - - try - { - bais = new java.io.ByteArrayInputStream( objBytes ); - ois = new java.io.ObjectInputStream( bais ); - - obj = ois.readObject(); - } // end try - catch( java.io.IOException e ) - { - logger.error( Logger.SECURITY_FAILURE, "Problem reading object", e ); - obj = null; - } // end catch - catch( java.lang.ClassNotFoundException e ) - { - logger.error( Logger.SECURITY_FAILURE, "Problem reading object", e ); - obj = null; - } // end catch - finally - { - try{ bais.close(); } catch( Exception e ){} - try{ ois.close(); } catch( Exception e ){} - } // end finally - - return obj; - } // end decodeObject - - - - /** - * Convenience method for encoding data to a file. - * - * @param dataToEncode byte array of data to encode in base64 form - * @param filename Filename for saving encoded data - * @return true if successful, false otherwise - * - * @since 2.1 - */ - public static boolean encodeToFile( byte[] dataToEncode, String filename ) - { - boolean success = false; - Base64.OutputStream bos = null; - try - { - bos = new Base64.OutputStream( - new java.io.FileOutputStream( filename ), Base64.ENCODE ); - bos.write( dataToEncode ); - success = true; - } // end try - catch( java.io.IOException e ) - { - - success = false; - } // end catch: IOException - finally - { - try{ bos.close(); } catch( Exception e ){} - } // end finally - - return success; - } // end encodeToFile - - - /** - * Convenience method for decoding data to a file. - * - * @param dataToDecode Base64-encoded data as a string - * @param filename Filename for saving decoded data - * @return true if successful, false otherwise - * - * @since 2.1 - */ - public static boolean decodeToFile( String dataToDecode, String filename ) - { - boolean success = false; - Base64.OutputStream bos = null; - try - { - bos = new Base64.OutputStream( - new java.io.FileOutputStream( filename ), Base64.DECODE ); - bos.write( dataToDecode.getBytes( PREFERRED_ENCODING ) ); - success = true; - } // end try - catch( java.io.IOException e ) - { - success = false; - } // end catch: IOException - finally - { - try{ bos.close(); } catch( Exception e ){} - } // end finally - - return success; - } // end decodeToFile - - - - - /** - * Convenience method for reading a base64-encoded - * file and decoding it. - * - * @param filename Filename for reading encoded data - * @return decoded byte array or null if unsuccessful - * - * @since 2.1 - */ - public static byte[] decodeFromFile( String filename ) - { - byte[] decodedData = null; - Base64.InputStream bis = null; - try - { - // Set up some useful variables - java.io.File file = new java.io.File( filename ); - byte[] buffer = null; - int length = 0; - int numBytes = 0; - - // Check for size of file - if( file.length() > Integer.MAX_VALUE ) - { - logger.error( Logger.SECURITY_FAILURE, "File is too big for this convenience method (" + file.length() + " bytes)." ); - return null; - } // end if: file too big for int index - buffer = new byte[ (int)file.length() ]; - - // Open a stream - bis = new Base64.InputStream( - new java.io.BufferedInputStream( - new java.io.FileInputStream( file ) ), Base64.DECODE ); - - // Read until done - while( ( numBytes = bis.read( buffer, length, 4096 ) ) >= 0 ) - length += numBytes; - - // Save in a variable to return - decodedData = new byte[ length ]; - System.arraycopy( buffer, 0, decodedData, 0, length ); - - } // end try - catch( java.io.IOException e ) - { - logger.error( Logger.SECURITY_FAILURE, "Error decoding from file " + filename, e ); - } // end catch: IOException - finally - { - try{ if (bis != null ) bis.close(); } catch( Exception e) {} - } // end finally - - return decodedData; - } // end decodeFromFile - - - - /** - * Convenience method for reading a binary file - * and base64-encoding it. - * - * @param filename Filename for reading binary data - * @return base64-encoded string or null if unsuccessful - * - * @since 2.1 - */ - public static String encodeFromFile( String filename ) - { - String encodedData = null; - Base64.InputStream bis = null; - try - { - // Set up some useful variables - java.io.File file = new java.io.File( filename ); - byte[] buffer = new byte[ Math.max((int)(file.length() * 1.4),40) ]; // Need max() for math on small files (v2.2.1) - int length = 0; - int numBytes = 0; - - // Open a stream - bis = new Base64.InputStream( - new java.io.BufferedInputStream( - new java.io.FileInputStream( file ) ), Base64.ENCODE ); - - // Read until done - while( ( numBytes = bis.read( buffer, length, 4096 ) ) >= 0 ) - length += numBytes; - - // Save in a variable to return - encodedData = new String( buffer, 0, length, Base64.PREFERRED_ENCODING ); - - } // end try - catch( java.io.IOException e ) - { - logger.error( Logger.SECURITY_FAILURE, "Error encoding from file " + filename, e ); - } // end catch: IOException - finally - { - try{ bis.close(); } catch( Exception e) {} - } // end finally - - return encodedData; - } // end encodeFromFile - - - - - /** - * Reads infile and encodes it to outfile. - * - * @param infile Input file - * @param outfile Output file - * @return true if the operation is successful - * @since 2.2 - */ - public static boolean encodeFileToFile( String infile, String outfile ) - { - boolean success = false; - java.io.InputStream in = null; - java.io.OutputStream out = null; - try{ - in = new Base64.InputStream( - new java.io.BufferedInputStream( - new java.io.FileInputStream( infile ) ), - Base64.ENCODE ); - out = new java.io.BufferedOutputStream( new java.io.FileOutputStream( outfile ) ); - byte[] buffer = new byte[65536]; // 64K - int read = -1; - while( ( read = in.read(buffer) ) >= 0 ){ - out.write( buffer,0,read ); - } // end while: through file - success = true; - } catch( java.io.IOException exc ){ - logger.error( Logger.SECURITY_FAILURE, "Problem encoding file to file", exc ); - } finally{ - try{ in.close(); } catch( Exception exc ){} - try{ out.close(); } catch( Exception exc ){} - } // end finally - - return success; - } // end encodeFileToFile - - - - /** - * Reads infile and decodes it to outfile. - * - * @param infile Input file - * @param outfile Output file - * @return true if the operation is successful - * @since 2.2 - */ - public static boolean decodeFileToFile( String infile, String outfile ) - { - boolean success = false; - java.io.InputStream in = null; - java.io.OutputStream out = null; - try{ - in = new Base64.InputStream( - new java.io.BufferedInputStream( - new java.io.FileInputStream( infile ) ), - Base64.DECODE ); - out = new java.io.BufferedOutputStream( new java.io.FileOutputStream( outfile ) ); - byte[] buffer = new byte[65536]; // 64K - int read = -1; - while( ( read = in.read(buffer) ) >= 0 ){ - out.write( buffer,0,read ); - } // end while: through file - success = true; - } catch( java.io.IOException exc ){ - logger.error( Logger.SECURITY_FAILURE, "Problem decoding file to file", exc ); - } finally{ - try{ in.close(); } catch( Exception exc ){} - try{ out.close(); } catch( Exception exc ){} - } // end finally - - return success; - } // end decodeFileToFile - - - /* ******** I N N E R C L A S S I N P U T S T R E A M ******** */ - - - - /** - * A {@link Base64.InputStream} will read data from another - * java.io.InputStream, given in the constructor, - * and encode/decode to/from Base64 notation on the fly. - * - * @see Base64 - * @since 1.3 - */ - public static class InputStream extends java.io.FilterInputStream - { - private boolean encode; // Encoding or decoding - private int position; // Current position in the buffer - private byte[] buffer; // Small buffer holding converted data - private int bufferLength; // Length of buffer (3 or 4) - private int numSigBytes; // Number of meaningful bytes in the buffer - private int lineLength; - private boolean breakLines; // Break lines at less than 80 characters - private int options; // Record options used to create the stream. - private byte[] decodabet; // Local copies to avoid extra method calls - - - /** - * Constructs a {@link Base64.InputStream} in DECODE mode. - * - * @param in the java.io.InputStream from which to read data. - * @since 1.3 - */ - public InputStream( java.io.InputStream in ) - { - this( in, DECODE ); - } // end constructor - - - /** - * Constructs a {@link Base64.InputStream} in - * either ENCODE or DECODE mode. - *

    - * Valid options:

    -         *   ENCODE or DECODE: Encode or Decode as data is read.
    -         *   DONT_BREAK_LINES: don't break lines at 76 characters
    -         *     (only meaningful when encoding)
    -         *     Note: Technically, this makes your encoding non-compliant.
    -         * 
    - *

    - * Example: new Base64.InputStream( in, Base64.DECODE ) - * - * - * @param in the java.io.InputStream from which to read data. - * @param options Specified options - * @see Base64#ENCODE - * @see Base64#DECODE - * @see Base64#DONT_BREAK_LINES - * @since 2.0 - */ - public InputStream( java.io.InputStream in, int options ) - { - super( in ); - this.breakLines = (options & DONT_BREAK_LINES) != DONT_BREAK_LINES; - this.encode = (options & ENCODE) == ENCODE; - this.bufferLength = encode ? 4 : 3; - this.buffer = new byte[ bufferLength ]; - this.position = -1; - this.lineLength = 0; - this.options = options; // Record for later, mostly to determine which alphabet to use - this.decodabet = getDecodabet(options); - } // end constructor - - /** - * Reads enough of the input stream to convert - * to/from Base64 and returns the next byte. - * - * @return next byte - * @throws java.io.IOException - * @since 1.3 - */ - public int read() throws java.io.IOException - { - // Do we need to get data? - if( position < 0 ) - { - if( encode ) - { - byte[] b3 = new byte[3]; - int numBinaryBytes = 0; - for( int i = 0; i < 3; i++ ) - { - try - { - int b = in.read(); - - // If end of stream, b is -1. - if( b >= 0 ) - { - b3[i] = (byte)b; - numBinaryBytes++; - } // end if: not end of stream - - } // end try: read - catch( java.io.IOException e ) - { - // Only a problem if we got no data at all. - if( i == 0 ) - throw e; - - } // end catch - } // end for: each needed input byte - - if( numBinaryBytes > 0 ) - { - encode3to4( b3, 0, numBinaryBytes, buffer, 0, options ); - position = 0; - numSigBytes = 4; - } // end if: got data - else - { - return -1; - } // end else - } // end if: encoding - - // Else decoding - else - { - byte[] b4 = new byte[4]; - int i = 0; - for( i = 0; i < 4; i++ ) - { - // Read four "meaningful" bytes: - int b = 0; - do{ b = in.read(); } - while( b >= 0 && decodabet[ b & 0x7f ] <= WHITE_SPACE_ENC ); - - if( b < 0 ) - break; // Reads a -1 if end of stream - - b4[i] = (byte)b; - } // end for: each needed input byte - - if( i == 4 ) - { - numSigBytes = decode4to3( b4, 0, buffer, 0, options ); - position = 0; - } // end if: got four characters - else if( i == 0 ){ - return -1; - } // end else if: also padded correctly - else - { - // Must have broken out from above. - throw new java.io.IOException( "Improperly padded Base64 input." ); - } // end - - } // end else: decode - } // end else: get data - - // Got data? - if( position >= 0 ) - { - // End of relevant data? - if( /*!encode &&*/ position >= numSigBytes ) - return -1; - - if( encode && breakLines && lineLength >= MAX_LINE_LENGTH ) - { - lineLength = 0; - return '\n'; - } // end if - else - { - lineLength++; // This isn't important when decoding - // but throwing an extra "if" seems - // just as wasteful. - - int b = buffer[ position++ ]; - - if( position >= bufferLength ) - position = -1; - - return b & 0xFF; // This is how you "cast" a byte that's - // intended to be unsigned. - } // end else - } // end if: position >= 0 - - // Else error - else - { - // When JDK1.4 is more accepted, use an assertion here. - throw new java.io.IOException( "Error in Base64 code reading stream." ); - } // end else - } // end read - - - /** - * Calls {@link #read()} repeatedly until the end of stream - * is reached or len bytes are read. - * Returns number of bytes read into array or -1 if - * end of stream is encountered. - * - * @param dest array to hold values - * @param off offset for array - * @param len max number of bytes to read into array - * @return bytes read into array or -1 if end of stream is encountered. - * @throws java.io.IOException - * @since 1.3 - */ - public int read( byte[] dest, int off, int len ) throws java.io.IOException - { - int i; - int b; - for( i = 0; i < len; i++ ) - { - b = read(); - - //if( b < 0 && i == 0 ) - // return -1; - - if( b >= 0 ) - dest[off + i] = (byte)b; - else if( i == 0 ) - return -1; - else - break; // Out of 'for' loop - } // end for: each byte read - return i; - } // end read - - } // end inner class InputStream - - - - - - - /* ******** I N N E R C L A S S O U T P U T S T R E A M ******** */ - - - - /** - * A {@link Base64.OutputStream} will write data to another - * java.io.OutputStream, given in the constructor, - * and encode/decode to/from Base64 notation on the fly. - * - * @see Base64 - * @since 1.3 - */ - public static class OutputStream extends java.io.FilterOutputStream - { - private boolean encode; - private int position; - private byte[] buffer; - private int bufferLength; - private int lineLength; - private boolean breakLines; - private byte[] b4; // Scratch used in a few places - private boolean suspendEncoding; - private int options; // Record for later - private byte[] decodabet; // Local copies to avoid extra method calls - - /** - * Constructs a {@link Base64.OutputStream} in ENCODE mode. - * - * @param out the java.io.OutputStream to which data will be written. - * @since 1.3 - */ - public OutputStream( java.io.OutputStream out ) - { - this( out, ENCODE ); - } // end constructor - - - /** - * Constructs a {@link Base64.OutputStream} in - * either ENCODE or DECODE mode. - *

    - * Valid options:

    -         *   ENCODE or DECODE: Encode or Decode as data is read.
    -         *   DONT_BREAK_LINES: don't break lines at 76 characters
    -         *     (only meaningful when encoding)
    -         *     Note: Technically, this makes your encoding non-compliant.
    -         * 
    - *

    - * Example: new Base64.OutputStream( out, Base64.ENCODE ) - * - * @param out the java.io.OutputStream to which data will be written. - * @param options Specified options. - * @see Base64#ENCODE - * @see Base64#DECODE - * @see Base64#DONT_BREAK_LINES - * @since 1.3 - */ - public OutputStream( java.io.OutputStream out, int options ) - { - super( out ); - this.breakLines = (options & DONT_BREAK_LINES) != DONT_BREAK_LINES; - this.encode = (options & ENCODE) == ENCODE; - this.bufferLength = encode ? 3 : 4; - this.buffer = new byte[ bufferLength ]; - this.position = 0; - this.lineLength = 0; - this.suspendEncoding = false; - this.b4 = new byte[4]; - this.options = options; - this.decodabet = getDecodabet(options); - } // end constructor - - - /** - * Writes the byte to the output stream after - * converting to/from Base64 notation. - * When encoding, bytes are buffered three - * at a time before the output stream actually - * gets a write() call. - * When decoding, bytes are buffered four - * at a time. - * - * @param theByte the byte to write - * @throws java.io.IOException - * @since 1.3 - */ - public void write(int theByte) throws java.io.IOException - { - // Encoding suspended? - if( suspendEncoding ) - { - super.out.write( theByte ); - return; - } // end if: supsended - - // Encode? - if( encode ) - { - buffer[ position++ ] = (byte)theByte; - if( position >= bufferLength ) // Enough to encode. - { - out.write( encode3to4( b4, buffer, bufferLength, options ) ); - - lineLength += 4; - if( breakLines && lineLength >= MAX_LINE_LENGTH ) - { - out.write( NEW_LINE ); - lineLength = 0; - } // end if: end of line - - position = 0; - } // end if: enough to output - } // end if: encoding - - // Else, Decoding - else - { - // Meaningful Base64 character? - if( decodabet[ theByte & 0x7f ] > WHITE_SPACE_ENC ) - { - buffer[ position++ ] = (byte)theByte; - if( position >= bufferLength ) // Enough to output. - { - int len = Base64.decode4to3( buffer, 0, b4, 0, options ); - out.write( b4, 0, len ); - //out.write( Base64.decode4to3( buffer ) ); - position = 0; - } // end if: enough to output - } // end if: meaningful base64 character - else if( decodabet[ theByte & 0x7f ] != WHITE_SPACE_ENC ) - { - throw new java.io.IOException( "Invalid character in Base64 data." ); - } // end else: not white space either - } // end else: decoding - } // end write - - - - /** - * Calls {@link #write(int)} repeatedly until len - * bytes are written. - * - * @param theBytes array from which to read bytes - * @param off offset for array - * @param len max number of bytes to read into array - * @throws java.io.IOException - * @since 1.3 - */ - public void write( byte[] theBytes, int off, int len ) throws java.io.IOException - { - // Encoding suspended? - if( suspendEncoding ) - { - super.out.write( theBytes, off, len ); - return; - } // end if: supsended - - for( int i = 0; i < len; i++ ) - { - write( theBytes[ off + i ] ); - } // end for: each byte written - - } // end write - - - - /** - * Method added by PHIL. [Thanks, PHIL. -Rob] - * This pads the buffer without closing the stream. - * @throws java.io.IOException - */ - public void flushBase64() throws java.io.IOException - { - if( position > 0 ) - { - if( encode ) - { - out.write( encode3to4( b4, buffer, position, options ) ); - position = 0; - } // end if: encoding - else - { - throw new java.io.IOException( "Base64 input not properly padded." ); - } // end else: decoding - } // end if: buffer partially full - - } // end flush - - - /** - * Flushes and closes (I think, in the superclass) the stream. - * - * @throws java.io.IOException - * @since 1.3 - */ - public void close() throws java.io.IOException - { - // 1. Ensure that pending characters are written - flushBase64(); - - // 2. Actually close the stream - // Base class both flushes and closes. - super.close(); - - buffer = null; - out = null; - } // end close - - - - /** - * Suspends encoding of the stream. - * May be helpful if you need to embed a piece of - * base640-encoded data in a stream. - * - * @throws java.io.IOException - * @since 1.5.1 - */ - public void suspendEncoding() throws java.io.IOException - { - flushBase64(); - this.suspendEncoding = true; - } // end suspendEncoding - - - /** - * Resumes encoding of the stream. - * May be helpful if you need to embed a piece of - * base640-encoded data in a stream. - * - * @since 1.5.1 - */ - public void resumeEncoding() - { - this.suspendEncoding = false; - } // end resumeEncoding - - - - } // end inner class OutputStream - - -} // end class Base64 +package org.owasp.esapi.codecs; + +import org.owasp.esapi.ESAPI; +import org.owasp.esapi.Logger; + +// CHECKME: Version at http://iharder.net/base64 is up to v2.3.3. Some semantic changes +// starting with v2.3. Should we upgrade and then add ESAPI logging or stay at 2.2.2 base? +// I think that really depends on how much OWASP ESAPI plans on tracking changes to this +// version vs. if the plan was just to fork from it and maintain OWASP's own version. +// At this point, I think I prefer split from tracking Harder's original, but I'm easily +// persuaded otherwise. - Kevin Wall + +/** + *

    Encodes and decodes to and from Base64 notation.

    + *

    Homepage: http://iharder.net/base64 + * (based on version 2.2.2).

    + * + *

    The options parameter, which appears in a few places, is used to pass + * several pieces of information to the encoder. In the "higher level" methods such as + * encodeBytes( bytes, options ) the options parameter can be used to indicate such + * things as first gzipping the bytes before encoding them, not inserting linefeeds + * (though that breaks strict Base64 compatibility), and encoding using the URL-safe + * and Ordered dialects.

    + * + *

    The constants defined in Base64 can be OR-ed together to combine options, so you + * might make a call like this:

    + * + * String encoded = Base64.encodeBytes( mybytes, Base64.GZIP | Base64.DONT_BREAK_LINES ); + * + *

    to compress the data before encoding it and then making the output have no newline characters.

    + * + * + *

    + * Original change Log: + *

    + *
      + *
    • v2.2.2 - Fixed encodeFileToFile and decodeFileToFile to use the + * Base64.InputStream class to encode and decode on the fly which uses + * less memory than encoding/decoding an entire file into memory before writing.
    • + *
    • v2.2.1 - Fixed bug using URL_SAFE and ORDERED encodings. Fixed bug + * when using very small files (~< 40 bytes).
    • + *
    • v2.2 - Added some helper methods for encoding/decoding directly from + * one file to the next. Also added a main() method to support command line + * encoding/decoding from one file to the next. Also added these Base64 dialects: + *
        + *
      1. The default is RFC3548 format.
      2. + *
      3. Calling Base64.setFormat(Base64.BASE64_FORMAT.URLSAFE_FORMAT) generates + * URL and file name friendly format as described in Section 4 of RFC3548. + * http://www.faqs.org/rfcs/rfc3548.html
      4. + *
      5. Calling Base64.setFormat(Base64.BASE64_FORMAT.ORDERED_FORMAT) generates + * URL and file name friendly format that preserves lexical ordering as described + * in http://www.faqs.org/qa/rfcc-1940.html
      6. + *
      + * Special thanks to Jim Kellerman at http://www.powerset.com/ + * for contributing the new Base64 dialects. + *
    • + * + *
    • v2.1 - Cleaned up javadoc comments and unused variables and methods. Added + * some convenience methods for reading and writing to and from files.
    • + *
    • v2.0.2 - Now specifies UTF-8 encoding in places where the code fails on systems + * with other encodings (like EBCDIC).
    • + *
    • v2.0.1 - Fixed an error when decoding a single byte, that is, when the + * encoded data was a single byte.
    • + *
    • v2.0 - I got rid of methods that used booleans to set options. + * Now everything is more consolidated and cleaner. The code now detects + * when data that's being decoded is gzip-compressed and will decompress it + * automatically. Generally things are cleaner. You'll probably have to + * change some method calls that you were making to support the new + * options format (ints that you "OR" together).
    • + *
    • v1.5.1 - Fixed bug when decompressing and decoding to a + * byte[] using decode( String s, boolean gzipCompressed ). + * Added the ability to "suspend" encoding in the Output Stream so + * you can turn on and off the encoding if you need to embed base64 + * data in an otherwise "normal" stream (like an XML file).
    • + *
    • v1.5 - Output stream pases on flush() command but doesn't do anything itself. + * This helps when using GZIP streams. + * Added the ability to GZip-compress objects before encoding them.
    • + *
    • v1.4 - Added helper methods to read/write files.
    • + *
    • v1.3.6 - Fixed OutputStream.flush() so that 'position' is reset.
    • + *
    • v1.3.5 - Added flag to turn on and off line breaks. Fixed bug in input stream + * where last buffer being read, if not completely full, was not returned.
    • + *
    • v1.3.4 - Fixed when "improperly padded stream" error was thrown at the wrong time.
    • + *
    • v1.3.3 - Fixed I/O streams which were totally messed up.
    • + *
    + * + *

    + * I am placing this code in the Public Domain. Do with it as you will. + * This software comes with no guarantees or warranties but with + * plenty of well-wishing instead! + * Please visit http://iharder.net/base64 + * periodically to check for updates or to contribute improvements. + *

    + * + * @author Robert Harder + * @author rob@iharder.net + * @version 2.2.2 + */ +public class Base64 +{ + +/* ******** P U B L I C F I E L D S ******** */ + + + /** No options specified. Value is zero. */ + public final static int NO_OPTIONS = 0; + + /** Specify encoding. */ + public final static int ENCODE = 1; + + + /** Specify decoding. */ + public final static int DECODE = 0; + + + /** Specify that data should be gzip-compressed. */ + public final static int GZIP = 2; + + + /** Don't break lines when encoding (violates strict Base64 specification) */ + public final static int DONT_BREAK_LINES = 8; + + /** + * Encode using Base64-like encoding that is URL- and Filename-safe as described + * in Section 4 of RFC3548: + * http://www.faqs.org/rfcs/rfc3548.html. + * It is important to note that data encoded this way is not officially valid Base64, + * or at the very least should not be called Base64 without also specifying that is + * was encoded using the URL- and Filename-safe dialect. + */ + public final static int URL_SAFE = 16; + + + /** + * Encode using the special "ordered" dialect of Base64 described here: + * http://www.faqs.org/qa/rfcc-1940.html. + */ + public final static int ORDERED = 32; + + +/* ******** P R I V A T E F I E L D S ******** */ + + + /** Maximum line length (76) of Base64 output. */ + private final static int MAX_LINE_LENGTH = 76; + + + /** The equals sign (=) as a byte. */ + private final static byte EQUALS_SIGN = (byte)'='; + + + /** The new line character (\n) as a byte. */ + private final static byte NEW_LINE = (byte)'\n'; + + + /** Preferred encoding. */ + private final static String PREFERRED_ENCODING = "UTF-8"; + + /** End of line character. */ + private final static String EOL = System.getProperty("line.separator", "\n"); + + + // I think I end up not using the BAD_ENCODING indicator. + //private final static byte BAD_ENCODING = -9; // Indicates error in encoding + private final static byte WHITE_SPACE_ENC = -5; // Indicates white space in encoding + private final static byte EQUALS_SIGN_ENC = -1; // Indicates equals sign in encoding + + private static final Logger logger = ESAPI.getLogger("Base64"); + + +/* ******** S T A N D A R D B A S E 6 4 A L P H A B E T ******** */ + + /** The 64 valid Base64 values. */ + //private final static byte[] ALPHABET; + /* Host platform me be something funny like EBCDIC, so we hard code these values. */ + private final static byte[] _STANDARD_ALPHABET = + { + (byte)'A', (byte)'B', (byte)'C', (byte)'D', (byte)'E', (byte)'F', (byte)'G', + (byte)'H', (byte)'I', (byte)'J', (byte)'K', (byte)'L', (byte)'M', (byte)'N', + (byte)'O', (byte)'P', (byte)'Q', (byte)'R', (byte)'S', (byte)'T', (byte)'U', + (byte)'V', (byte)'W', (byte)'X', (byte)'Y', (byte)'Z', + (byte)'a', (byte)'b', (byte)'c', (byte)'d', (byte)'e', (byte)'f', (byte)'g', + (byte)'h', (byte)'i', (byte)'j', (byte)'k', (byte)'l', (byte)'m', (byte)'n', + (byte)'o', (byte)'p', (byte)'q', (byte)'r', (byte)'s', (byte)'t', (byte)'u', + (byte)'v', (byte)'w', (byte)'x', (byte)'y', (byte)'z', + (byte)'0', (byte)'1', (byte)'2', (byte)'3', (byte)'4', (byte)'5', + (byte)'6', (byte)'7', (byte)'8', (byte)'9', (byte)'+', (byte)'/' + }; + + + /** + * Translates a Base64 value to either its 6-bit reconstruction value + * or a negative number indicating some other meaning. + **/ + private final static byte[] _STANDARD_DECODABET = + { + -9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 0 - 8 + -5,-5, // Whitespace: Tab and Linefeed + -9,-9, // Decimal 11 - 12 + -5, // Whitespace: Carriage Return + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 14 - 26 + -9,-9,-9,-9,-9, // Decimal 27 - 31 + -5, // Whitespace: Space + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 33 - 42 + 62, // Plus sign at decimal 43 + -9,-9,-9, // Decimal 44 - 46 + 63, // Slash at decimal 47 + 52,53,54,55,56,57,58,59,60,61, // Numbers zero through nine + -9,-9,-9, // Decimal 58 - 60 + -1, // Equals sign at decimal 61 + -9,-9,-9, // Decimal 62 - 64 + 0,1,2,3,4,5,6,7,8,9,10,11,12,13, // Letters 'A' through 'N' + 14,15,16,17,18,19,20,21,22,23,24,25, // Letters 'O' through 'Z' + -9,-9,-9,-9,-9,-9, // Decimal 91 - 96 + 26,27,28,29,30,31,32,33,34,35,36,37,38, // Letters 'a' through 'm' + 39,40,41,42,43,44,45,46,47,48,49,50,51, // Letters 'n' through 'z' + -9,-9,-9,-9 // Decimal 123 - 126 + /*,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 127 - 139 + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 140 - 152 + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 153 - 165 + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 166 - 178 + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 179 - 191 + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 192 - 204 + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 205 - 217 + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 218 - 230 + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 231 - 243 + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9 // Decimal 244 - 255 */ + }; + + +/* ******** U R L S A F E B A S E 6 4 A L P H A B E T ******** */ + + /** + * Used in the URL- and Filename-safe dialect described in Section 4 of RFC3548: + * http://www.faqs.org/rfcs/rfc3548.html. + * Notice that the last two bytes become "hyphen" and "underscore" instead of "plus" and "slash." + */ + private final static byte[] _URL_SAFE_ALPHABET = + { + (byte)'A', (byte)'B', (byte)'C', (byte)'D', (byte)'E', (byte)'F', (byte)'G', + (byte)'H', (byte)'I', (byte)'J', (byte)'K', (byte)'L', (byte)'M', (byte)'N', + (byte)'O', (byte)'P', (byte)'Q', (byte)'R', (byte)'S', (byte)'T', (byte)'U', + (byte)'V', (byte)'W', (byte)'X', (byte)'Y', (byte)'Z', + (byte)'a', (byte)'b', (byte)'c', (byte)'d', (byte)'e', (byte)'f', (byte)'g', + (byte)'h', (byte)'i', (byte)'j', (byte)'k', (byte)'l', (byte)'m', (byte)'n', + (byte)'o', (byte)'p', (byte)'q', (byte)'r', (byte)'s', (byte)'t', (byte)'u', + (byte)'v', (byte)'w', (byte)'x', (byte)'y', (byte)'z', + (byte)'0', (byte)'1', (byte)'2', (byte)'3', (byte)'4', (byte)'5', + (byte)'6', (byte)'7', (byte)'8', (byte)'9', (byte)'-', (byte)'_' + }; + + /** + * Used in decoding URL- and Filename-safe dialects of Base64. + */ + private final static byte[] _URL_SAFE_DECODABET = + { + -9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 0 - 8 + -5,-5, // Whitespace: Tab and Linefeed + -9,-9, // Decimal 11 - 12 + -5, // Whitespace: Carriage Return + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 14 - 26 + -9,-9,-9,-9,-9, // Decimal 27 - 31 + -5, // Whitespace: Space + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 33 - 42 + -9, // Plus sign at decimal 43 + -9, // Decimal 44 + 62, // Minus sign at decimal 45 + -9, // Decimal 46 + -9, // Slash at decimal 47 + 52,53,54,55,56,57,58,59,60,61, // Numbers zero through nine + -9,-9,-9, // Decimal 58 - 60 + -1, // Equals sign at decimal 61 + -9,-9,-9, // Decimal 62 - 64 + 0,1,2,3,4,5,6,7,8,9,10,11,12,13, // Letters 'A' through 'N' + 14,15,16,17,18,19,20,21,22,23,24,25, // Letters 'O' through 'Z' + -9,-9,-9,-9, // Decimal 91 - 94 + 63, // Underscore at decimal 95 + -9, // Decimal 96 + 26,27,28,29,30,31,32,33,34,35,36,37,38, // Letters 'a' through 'm' + 39,40,41,42,43,44,45,46,47,48,49,50,51, // Letters 'n' through 'z' + -9,-9,-9,-9 // Decimal 123 - 126 + /*,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 127 - 139 + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 140 - 152 + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 153 - 165 + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 166 - 178 + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 179 - 191 + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 192 - 204 + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 205 - 217 + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 218 - 230 + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 231 - 243 + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9 // Decimal 244 - 255 */ + }; + + + +/* ******** O R D E R E D B A S E 6 4 A L P H A B E T ******** */ + + /** + * I don't get the point of this technique, but it is described here: + * http://www.faqs.org/qa/rfcc-1940.html. + */ + private final static byte[] _ORDERED_ALPHABET = + { + (byte)'-', + (byte)'0', (byte)'1', (byte)'2', (byte)'3', (byte)'4', + (byte)'5', (byte)'6', (byte)'7', (byte)'8', (byte)'9', + (byte)'A', (byte)'B', (byte)'C', (byte)'D', (byte)'E', (byte)'F', (byte)'G', + (byte)'H', (byte)'I', (byte)'J', (byte)'K', (byte)'L', (byte)'M', (byte)'N', + (byte)'O', (byte)'P', (byte)'Q', (byte)'R', (byte)'S', (byte)'T', (byte)'U', + (byte)'V', (byte)'W', (byte)'X', (byte)'Y', (byte)'Z', + (byte)'_', + (byte)'a', (byte)'b', (byte)'c', (byte)'d', (byte)'e', (byte)'f', (byte)'g', + (byte)'h', (byte)'i', (byte)'j', (byte)'k', (byte)'l', (byte)'m', (byte)'n', + (byte)'o', (byte)'p', (byte)'q', (byte)'r', (byte)'s', (byte)'t', (byte)'u', + (byte)'v', (byte)'w', (byte)'x', (byte)'y', (byte)'z' + }; + + /** + * Used in decoding the "ordered" dialect of Base64. + */ + private final static byte[] _ORDERED_DECODABET = + { + -9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 0 - 8 + -5,-5, // Whitespace: Tab and Linefeed + -9,-9, // Decimal 11 - 12 + -5, // Whitespace: Carriage Return + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 14 - 26 + -9,-9,-9,-9,-9, // Decimal 27 - 31 + -5, // Whitespace: Space + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 33 - 42 + -9, // Plus sign at decimal 43 + -9, // Decimal 44 + 0, // Minus sign at decimal 45 + -9, // Decimal 46 + -9, // Slash at decimal 47 + 1,2,3,4,5,6,7,8,9,10, // Numbers zero through nine + -9,-9,-9, // Decimal 58 - 60 + -1, // Equals sign at decimal 61 + -9,-9,-9, // Decimal 62 - 64 + 11,12,13,14,15,16,17,18,19,20,21,22,23, // Letters 'A' through 'M' + 24,25,26,27,28,29,30,31,32,33,34,35,36, // Letters 'N' through 'Z' + -9,-9,-9,-9, // Decimal 91 - 94 + 37, // Underscore at decimal 95 + -9, // Decimal 96 + 38,39,40,41,42,43,44,45,46,47,48,49,50, // Letters 'a' through 'm' + 51,52,53,54,55,56,57,58,59,60,61,62,63, // Letters 'n' through 'z' + -9,-9,-9,-9 // Decimal 123 - 126 + /*,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 127 - 139 + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 140 - 152 + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 153 - 165 + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 166 - 178 + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 179 - 191 + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 192 - 204 + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 205 - 217 + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 218 - 230 + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 231 - 243 + -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9 // Decimal 244 - 255 */ + }; + + +/* ******** D E T E R M I N E W H I C H A L H A B E T ******** */ + + + /** + * Returns one of the _SOMETHING_ALPHABET byte arrays depending on + * the options specified. + * It's possible, though silly, to specify ORDERED and URLSAFE + * in which case one of them will be picked, though there is + * no guarantee as to which one will be picked. + */ + private final static byte[] getAlphabet( int options ) + { + if( (options & URL_SAFE) == URL_SAFE ) return _URL_SAFE_ALPHABET; + else if( (options & ORDERED) == ORDERED ) return _ORDERED_ALPHABET; + else return _STANDARD_ALPHABET; + + } // end getAlphabet + + + /** + * Returns one of the _SOMETHING_DECODABET byte arrays depending on + * the options specified. + * It's possible, though silly, to specify ORDERED and URL_SAFE + * in which case one of them will be picked, though there is + * no guarantee as to which one will be picked. + */ + private final static byte[] getDecodabet( int options ) + { + if( (options & URL_SAFE) == URL_SAFE ) return _URL_SAFE_DECODABET; + else if( (options & ORDERED) == ORDERED ) return _ORDERED_DECODABET; + else return _STANDARD_DECODABET; + + } // end getAlphabet + + + + /** Defeats instantiation. */ + private Base64(){} + + + /** + * Encodes or decodes two files from the command line; + * feel free to delete this method (in fact you probably should) + * if you're embedding this code into a larger program. + * @param args + */ + public final static void main( String[] args ) + { + if( args.length < 3 ){ + usage("Not enough arguments."); + } // end if: args.length < 3 + else { + String flag = args[0]; + String infile = args[1]; + String outfile = args[2]; + if( flag.equals( "-e" ) ){ + Base64.encodeFileToFile( infile, outfile ); + } // end if: encode + else if( flag.equals( "-d" ) ) { + Base64.decodeFileToFile( infile, outfile ); + } // end else if: decode + else { + usage( "Unknown flag: " + flag ); + } // end else + } // end else + } // end main + + /** + * Prints command line usage. + * + * @param msg A message to include with usage info. + */ + private final static void usage( String msg ) + { + System.err.println( msg ); + System.err.println( "Usage: java Base64 -e|-d inputfile outputfile" ); + } // end usage + + +/* ******** E N C O D I N G M E T H O D S ******** */ + + + /** + * Encodes up to the first three bytes of array threeBytes + * and returns a four-byte array in Base64 notation. + * The actual number of significant bytes in your array is + * given by numSigBytes. + * The array threeBytes needs only be as big as + * numSigBytes. + * Code can reuse a byte array by passing a four-byte array as b4. + * + * @param b4 A reusable byte array to reduce array instantiation + * @param threeBytes the array to convert + * @param numSigBytes the number of significant bytes in your array + * @return four byte array in Base64 notation. + * @since 1.5.1 + */ + private static byte[] encode3to4( byte[] b4, byte[] threeBytes, int numSigBytes, int options ) + { + encode3to4( threeBytes, 0, numSigBytes, b4, 0, options ); + return b4; + } // end encode3to4 + + + /** + *

    Encodes up to three bytes of the array source + * and writes the resulting four Base64 bytes to destination. + * The source and destination arrays can be manipulated + * anywhere along their length by specifying + * srcOffset and destOffset. + * This method does not check to make sure your arrays + * are large enough to accomodate srcOffset + 3 for + * the source array or destOffset + 4 for + * the destination array. + * The actual number of significant bytes in your array is + * given by numSigBytes.

    + *

    This is the lowest level of the encoding methods with + * all possible parameters.

    + * + * @param source the array to convert + * @param srcOffset the index where conversion begins + * @param numSigBytes the number of significant bytes in your array + * @param destination the array to hold the conversion + * @param destOffset the index where output will be put + * @return the destination array + * @since 1.3 + */ + private static byte[] encode3to4( + byte[] source, int srcOffset, int numSigBytes, + byte[] destination, int destOffset, int options ) + { + byte[] ALPHABET = getAlphabet( options ); + + // 1 2 3 + // 01234567890123456789012345678901 Bit position + // --------000000001111111122222222 Array position from threeBytes + // --------| || || || | Six bit groups to index ALPHABET + // >>18 >>12 >> 6 >> 0 Right shift necessary + // 0x3f 0x3f 0x3f Additional AND + + // Create buffer with zero-padding if there are only one or two + // significant bytes passed in the array. + // We have to shift left 24 in order to flush out the 1's that appear + // when Java treats a value as negative that is cast from a byte to an int. + int inBuff = ( numSigBytes > 0 ? ((source[ srcOffset ] << 24) >>> 8) : 0 ) + | ( numSigBytes > 1 ? ((source[ srcOffset + 1 ] << 24) >>> 16) : 0 ) + | ( numSigBytes > 2 ? ((source[ srcOffset + 2 ] << 24) >>> 24) : 0 ); + + switch( numSigBytes ) + { + case 3: + destination[ destOffset ] = ALPHABET[ (inBuff >>> 18) ]; + destination[ destOffset + 1 ] = ALPHABET[ (inBuff >>> 12) & 0x3f ]; + destination[ destOffset + 2 ] = ALPHABET[ (inBuff >>> 6) & 0x3f ]; + destination[ destOffset + 3 ] = ALPHABET[ (inBuff ) & 0x3f ]; + return destination; + + case 2: + destination[ destOffset ] = ALPHABET[ (inBuff >>> 18) ]; + destination[ destOffset + 1 ] = ALPHABET[ (inBuff >>> 12) & 0x3f ]; + destination[ destOffset + 2 ] = ALPHABET[ (inBuff >>> 6) & 0x3f ]; + destination[ destOffset + 3 ] = EQUALS_SIGN; + return destination; + + case 1: + destination[ destOffset ] = ALPHABET[ (inBuff >>> 18) ]; + destination[ destOffset + 1 ] = ALPHABET[ (inBuff >>> 12) & 0x3f ]; + destination[ destOffset + 2 ] = EQUALS_SIGN; + destination[ destOffset + 3 ] = EQUALS_SIGN; + return destination; + + default: + return destination; + } // end switch + } // end encode3to4 + + + + /** + * Serializes an object and returns the Base64-encoded + * version of that serialized object. If the object + * cannot be serialized or there is another error, + * the method will return null. + * The object is not GZip-compressed before being encoded. + * + * @param serializableObject The object to encode + * @return The Base64-encoded object + * @since 1.4 + */ + public static String encodeObject( java.io.Serializable serializableObject ) + { + return encodeObject( serializableObject, NO_OPTIONS ); + } // end encodeObject + + + + /** + * Serializes an object and returns the Base64-encoded + * version of that serialized object. If the object + * cannot be serialized or there is another error, + * the method will return null. + *

    + * Valid options:

    +     *   GZIP: gzip-compresses object before encoding it.
    +     *   DONT_BREAK_LINES: don't break lines at 76 characters
    +     *     Note: Technically, this makes your encoding non-compliant.
    +     * 
    + *

    + * Example: encodeObject( myObj, Base64.GZIP ) or + *

    + * Example: encodeObject( myObj, Base64.GZIP | Base64.DONT_BREAK_LINES ) + * + * @param serializableObject The object to encode + * @param options Specified options + * @return The Base64-encoded object + * @see Base64#GZIP + * @see Base64#DONT_BREAK_LINES + * @since 2.0 + */ + public static String encodeObject( java.io.Serializable serializableObject, int options ) + { + // Streams + java.io.ByteArrayOutputStream baos = null; + java.io.OutputStream b64os = null; + java.io.ObjectOutputStream oos = null; + java.util.zip.GZIPOutputStream gzos = null; + + // Isolate options + int gzip = (options & GZIP); + //int dontBreakLines = (options & DONT_BREAK_LINES); + + try + { + // ObjectOutputStream -> (GZIP) -> Base64 -> ByteArrayOutputStream + baos = new java.io.ByteArrayOutputStream(); + b64os = new Base64.OutputStream( baos, ENCODE | options ); + + // GZip? + if( gzip == GZIP ) + { + gzos = new java.util.zip.GZIPOutputStream( b64os ); + oos = new java.io.ObjectOutputStream( gzos ); + } // end if: gzip + else + oos = new java.io.ObjectOutputStream( b64os ); + + oos.writeObject( serializableObject ); + } // end try + catch( java.io.IOException e ) + { + logger.error( Logger.SECURITY_FAILURE, "Problem writing object", e ); + return null; + } // end catch + finally + { + try{ oos.close(); } catch( Exception e ){} + try{ gzos.close(); } catch( Exception e ){} + try{ b64os.close(); } catch( Exception e ){} + try{ baos.close(); } catch( Exception e ){} + } // end finally + + // Return value according to relevant encoding. + try + { + return new String( baos.toByteArray(), PREFERRED_ENCODING ); + } // end try + catch (java.io.UnsupportedEncodingException uue) + { + return new String( baos.toByteArray() ); + } // end catch + + } // end encode + + + + /** + * Encodes a byte array into Base64 notation. + * Does not GZip-compress data. + * + * @param source The data to convert + * @return The Base64-encoded resulting string + * @since 1.4 + */ + public static String encodeBytes( byte[] source ) + { + return encodeBytes( source, 0, source.length, NO_OPTIONS ); + } // end encodeBytes + + + + /** + * Encodes a byte array into Base64 notation. + *

    + * Valid options:

    +     *   GZIP: gzip-compresses object before encoding it.
    +     *   DONT_BREAK_LINES: don't break lines at 76 characters
    +     *     Note: Technically, this makes your encoding non-compliant.
    +     * 
    + *

    + * Example: encodeBytes( myData, Base64.GZIP ) or + *

    + * Example: encodeBytes( myData, Base64.GZIP | Base64.DONT_BREAK_LINES ) + * + * + * @param source The data to convert + * @param options Specified options + * @return The Base64-encoded resulting string + * @see Base64#GZIP + * @see Base64#DONT_BREAK_LINES + * @since 2.0 + */ + public static String encodeBytes( byte[] source, int options ) + { + return encodeBytes( source, 0, source.length, options ); + } // end encodeBytes + + + /** + * Encodes a byte array into Base64 notation. + * Does not GZip-compress data. + * + * @param source The data to convert + * @param off Offset in array where conversion should begin + * @param len Length of data to convert + * @return The Base64-encoded resulting string + * @since 1.4 + */ + public static String encodeBytes( byte[] source, int off, int len ) + { + return encodeBytes( source, off, len, NO_OPTIONS ); + } // end encodeBytes + + + + /** + * Encodes a byte array into Base64 notation. + *

    + * Valid options:

    +     *   GZIP: gzip-compresses object before encoding it.
    +     *   DONT_BREAK_LINES: don't break lines at 76 characters
    +     *     Note: Technically, this makes your encoding non-compliant.
    +     * 
    + *

    + * Example: encodeBytes( myData, Base64.GZIP ) or + *

    + * Example: encodeBytes( myData, Base64.GZIP | Base64.DONT_BREAK_LINES ) + * + * + * @param source The data to convert + * @param off Offset in array where conversion should begin + * @param len Length of data to convert + * @param options alphabet type is pulled from this (standard, url-safe, ordered) + * @return The Base64-encoded resulting string + * @see Base64#GZIP + * @see Base64#DONT_BREAK_LINES + * @since 2.0 + */ + public static String encodeBytes( byte[] source, int off, int len, int options ) + { + // Isolate options + int dontBreakLines = ( options & DONT_BREAK_LINES ); + int gzip = ( options & GZIP ); + + // Compress? + if( gzip == GZIP ) + { + java.io.ByteArrayOutputStream baos = null; + java.util.zip.GZIPOutputStream gzos = null; + Base64.OutputStream b64os = null; + + + try + { + // GZip -> Base64 -> ByteArray + baos = new java.io.ByteArrayOutputStream(); + b64os = new Base64.OutputStream( baos, ENCODE | options ); + gzos = new java.util.zip.GZIPOutputStream( b64os ); + + gzos.write( source, off, len ); + gzos.close(); + } // end try + catch( java.io.IOException e ) + { + logger.error( Logger.SECURITY_FAILURE, "Problem writing gzip stream", e ); + return null; + } // end catch + finally + { + try{ gzos.close(); } catch( Exception e ){} + try{ b64os.close(); } catch( Exception e ){} + try{ baos.close(); } catch( Exception e ){} + } // end finally + + // Return value according to relevant encoding. + try + { + return new String( baos.toByteArray(), PREFERRED_ENCODING ); + } // end try + catch (java.io.UnsupportedEncodingException uue) + { + return new String( baos.toByteArray() ); + } // end catch + } // end if: compress + + // Else, don't compress. Better not to use streams at all then. + else + { + // Convert option to boolean in way that code likes it. + boolean breakLines = dontBreakLines == 0; + + int len43 = len * 4 / 3; + byte[] outBuff = new byte[ ( len43 ) // Main 4:3 + + ( (len % 3) > 0 ? 4 : 0 ) // Account for padding + + (breakLines ? ( len43 / MAX_LINE_LENGTH ) : 0) ]; // New lines + int d = 0; + int e = 0; + int len2 = len - 2; + int lineLength = 0; + for( ; d < len2; d+=3, e+=4 ) + { + encode3to4( source, d+off, 3, outBuff, e, options ); + + lineLength += 4; + if( breakLines && lineLength == MAX_LINE_LENGTH ) + { + outBuff[e+4] = NEW_LINE; + e++; + lineLength = 0; + } // end if: end of line + } // en dfor: each piece of array + + if( d < len ) + { + encode3to4( source, d+off, len - d, outBuff, e, options ); + e += 4; + } // end if: some padding needed + + + // Return value according to relevant encoding. + try + { + return new String( outBuff, 0, e, PREFERRED_ENCODING ); + } // end try + catch (java.io.UnsupportedEncodingException uue) + { + return new String( outBuff, 0, e ); + } // end catch + + } // end else: don't compress + + } // end encodeBytes + + + + + +/* ******** D E C O D I N G M E T H O D S ******** */ + + + /** + * Decodes four bytes from array source + * and writes the resulting bytes (up to three of them) + * to destination. + * The source and destination arrays can be manipulated + * anywhere along their length by specifying + * srcOffset and destOffset. + * This method does not check to make sure your arrays + * are large enough to accomodate srcOffset + 4 for + * the source array or destOffset + 3 for + * the destination array. + * This method returns the actual number of bytes that + * were converted from the Base64 encoding. + *

    This is the lowest level of the decoding methods with + * all possible parameters.

    + * + * + * @param source the array to convert + * @param srcOffset the index where conversion begins + * @param destination the array to hold the conversion + * @param destOffset the index where output will be put + * @param options alphabet type is pulled from this (standard, url-safe, ordered) + * @return the number of decoded bytes converted + * @since 1.3 + */ + private static int decode4to3( byte[] source, int srcOffset, byte[] destination, int destOffset, int options ) + { + byte[] DECODABET = getDecodabet( options ); + + // Example: Dk== + if( source[ srcOffset + 2] == EQUALS_SIGN ) + { + // Two ways to do the same thing. Don't know which way I like best. + //int outBuff = ( ( DECODABET[ source[ srcOffset ] ] << 24 ) >>> 6 ) + // | ( ( DECODABET[ source[ srcOffset + 1] ] << 24 ) >>> 12 ); + int outBuff = ( ( DECODABET[ source[ srcOffset ] ] & 0xFF ) << 18 ) + | ( ( DECODABET[ source[ srcOffset + 1] ] & 0xFF ) << 12 ); + + destination[ destOffset ] = (byte)( outBuff >>> 16 ); + return 1; + } + + // Example: DkL= + else if( source[ srcOffset + 3 ] == EQUALS_SIGN ) + { + // Two ways to do the same thing. Don't know which way I like best. + //int outBuff = ( ( DECODABET[ source[ srcOffset ] ] << 24 ) >>> 6 ) + // | ( ( DECODABET[ source[ srcOffset + 1 ] ] << 24 ) >>> 12 ) + // | ( ( DECODABET[ source[ srcOffset + 2 ] ] << 24 ) >>> 18 ); + int outBuff = ( ( DECODABET[ source[ srcOffset ] ] & 0xFF ) << 18 ) + | ( ( DECODABET[ source[ srcOffset + 1 ] ] & 0xFF ) << 12 ) + | ( ( DECODABET[ source[ srcOffset + 2 ] ] & 0xFF ) << 6 ); + + destination[ destOffset ] = (byte)( outBuff >>> 16 ); + destination[ destOffset + 1 ] = (byte)( outBuff >>> 8 ); + return 2; + } + + // Example: DkLE + else + { + try{ + // Two ways to do the same thing. Don't know which way I like best. + //int outBuff = ( ( DECODABET[ source[ srcOffset ] ] << 24 ) >>> 6 ) + // | ( ( DECODABET[ source[ srcOffset + 1 ] ] << 24 ) >>> 12 ) + // | ( ( DECODABET[ source[ srcOffset + 2 ] ] << 24 ) >>> 18 ) + // | ( ( DECODABET[ source[ srcOffset + 3 ] ] << 24 ) >>> 24 ); + int outBuff = ( ( DECODABET[ source[ srcOffset ] ] & 0xFF ) << 18 ) + | ( ( DECODABET[ source[ srcOffset + 1 ] ] & 0xFF ) << 12 ) + | ( ( DECODABET[ source[ srcOffset + 2 ] ] & 0xFF ) << 6) + | ( ( DECODABET[ source[ srcOffset + 3 ] ] & 0xFF ) ); + + + destination[ destOffset ] = (byte)( outBuff >> 16 ); + destination[ destOffset + 1 ] = (byte)( outBuff >> 8 ); + destination[ destOffset + 2 ] = (byte)( outBuff ); + + return 3; + }catch( Exception e){ + + // Remove these after checking -- for context only. + // logger.error( Logger.SECURITY_FAILURE, "Problem writing object", e ); + // logger.error( Logger.SECURITY_FAILURE, ""+source[srcOffset]+ ": " + ( DECODABET[ source[ srcOffset ] ] ) ); + // logger.error( Logger.SECURITY_FAILURE, ""+source[srcOffset+1]+ ": " + ( DECODABET[ source[ srcOffset + 1 ] ] ) ); + // logger.error( Logger.SECURITY_FAILURE, ""+source[srcOffset+2]+ ": " + ( DECODABET[ source[ srcOffset + 2 ] ] ) ); + // logger.error( Logger.SECURITY_FAILURE, ""+source[srcOffset+3]+ ": " + ( DECODABET[ source[ srcOffset + 3 ] ] ) ); + + // CHECKME: I replaced the 5 separate logger.error() calls above with a single logger.error() call so they can't + // become interleaved with other log entries from other threads. Normally this would have placed log entries + // on separate lines, so I also added line terminators here as well. (Probably don't want it all on one single + // really long log entry, do we?) Anyhow, somebody should check the formatting to ensure that it's + // esthetically pleasing, etc. But this works for me. I'm also OK if you want to remove all the line terminators + // in which case the declaration for EOL should be removed as well. - Kevin Wall + + StringBuilder sb = new StringBuilder("Problem writing object:"); + sb.append(EOL); + sb.append( source[srcOffset] ).append(": ").append( ( DECODABET[ source[ srcOffset ] ] ) ).append(EOL); + sb.append( source[srcOffset+1] ).append(": ").append( ( DECODABET[ source[ srcOffset + 1 ] ] ) ).append(EOL); + sb.append( source[srcOffset+2] ).append(": ").append( ( DECODABET[ source[ srcOffset + 2 ] ] ) ).append(EOL); + sb.append( source[srcOffset+3] ).append(": ").append( ( DECODABET[ source[ srcOffset + 3 ] ] ) ).append(EOL); + + logger.error( Logger.SECURITY_FAILURE, sb.toString(), e ); + return -1; + } // end catch + } + } // end decodeToBytes + + + + + /** + * Very low-level access to decoding ASCII characters in + * the form of a byte array. Does not support automatically + * gunzipping or any other "fancy" features. + * + * @param source The Base64 encoded data + * @param off The offset of where to begin decoding + * @param len The length of characters to decode + * @param options + * @return decoded data + * @since 1.3 + */ + public static byte[] decode( byte[] source, int off, int len, int options ) + { + byte[] DECODABET = getDecodabet( options ); + + int len34 = len * 3 / 4; + byte[] outBuff = new byte[ len34 ]; // Upper limit on size of output + int outBuffPosn = 0; + + byte[] b4 = new byte[4]; + int b4Posn = 0; + int i = 0; + byte sbiCrop = 0; + byte sbiDecode = 0; + for( i = off; i < off+len; i++ ) + { + sbiCrop = (byte)(source[i] & 0x7f); // Only the low seven bits + sbiDecode = DECODABET[ sbiCrop ]; + + if( sbiDecode >= WHITE_SPACE_ENC ) // White space, Equals sign or better + { + if( sbiDecode >= EQUALS_SIGN_ENC ) + { + b4[ b4Posn++ ] = sbiCrop; + if( b4Posn > 3 ) + { + outBuffPosn += decode4to3( b4, 0, outBuff, outBuffPosn, options ); + b4Posn = 0; + + // If that was the equals sign, break out of 'for' loop + if( sbiCrop == EQUALS_SIGN ) + break; + } // end if: quartet built + + } // end if: equals sign or better + + } // end if: white space, equals sign or better + else + { + logger.error( Logger.SECURITY_FAILURE, "Bad Base64 input character at " + i + ": " + source[i] + "(decimal)" ); + return null; + } // end else: + } // each input character + + byte[] out = new byte[ outBuffPosn ]; + System.arraycopy( outBuff, 0, out, 0, outBuffPosn ); + return out; + } // end decode + + + + + /** + * Decodes data from Base64 notation, automatically + * detecting gzip-compressed data and decompressing it. + * + * @param s the string to decode + * @return the decoded data + * @since 1.4 + */ + public static byte[] decode( String s ) + { + return decode( s, NO_OPTIONS ); + } + + + /** + * Decodes data from Base64 notation, automatically + * detecting gzip-compressed data and decompressing it. + * + * @param s the string to decode + * @param options encode options such as URL_SAFE + * @return the decoded data + * @since 1.4 + */ + public static byte[] decode( String s, int options ) + { + byte[] bytes; + try + { + bytes = s.getBytes( PREFERRED_ENCODING ); + } + catch( java.io.UnsupportedEncodingException uee ) + { + bytes = s.getBytes(); // Uses native encoding + // CHECKME: Is this correct? I think it should be a warning instead of an error since nothing + // is re-thrown. I do think that *some* sort of logging is in order here especially since UTF-8 should + // always be available on all platforms. If it's not, then all bets are off on your runtime env. - Kevin Wall + logger.warning( Logger.SECURITY_FAILURE, "Problem decoding string using " + + PREFERRED_ENCODING + "; substituting native platform encoding instead", uee ); + } + + // Decode + bytes = decode( bytes, 0, bytes.length, options ); + + + // Check to see if it's gzip-compressed + // GZIP Magic Two-Byte Number: 0x8b1f (35615) + if( bytes != null && bytes.length >= 4 ) + { + + int head = ((int)bytes[0] & 0xff) | ((bytes[1] << 8) & 0xff00); + if( java.util.zip.GZIPInputStream.GZIP_MAGIC == head ) + { + java.io.ByteArrayInputStream bais = null; + java.util.zip.GZIPInputStream gzis = null; + java.io.ByteArrayOutputStream baos = null; + byte[] buffer = new byte[2048]; + int length = 0; + + try + { + baos = new java.io.ByteArrayOutputStream(); + bais = new java.io.ByteArrayInputStream( bytes ); + gzis = new java.util.zip.GZIPInputStream( bais ); + + while( ( length = gzis.read( buffer ) ) >= 0 ) + { + baos.write(buffer,0,length); + } // end while: reading input + + // No error? Get new bytes. + bytes = baos.toByteArray(); + + } // end try + catch( java.io.IOException e ) + { + // Just return originally-decoded bytes + } // end catch + finally + { + try{ baos.close(); } catch( Exception e ){} + try{ gzis.close(); } catch( Exception e ){} + try{ bais.close(); } catch( Exception e ){} + } // end finally + + } // end if: gzipped + } // end if: bytes.length >= 2 + + return bytes; + } // end decode + + + + + /** + * Attempts to decode Base64 data and deserialize a Java + * Object within. Returns null if there was an error. + * + *

    + * WARNING: Using this method to decode non-validated / + * untrusted data from a string and deserialize it into + * an object can potentially result in remote command + * injection vulnerabilities. Use at your own risk! + *

    + * + * @param encodedObject The Base64 data to decode + * @return The decoded and deserialized object + * @since 1.5 + * + * @deprecated Because of security issues, this method will be + * removed from ESAPI in a future release and no substitute + * is planned. Because as of JDK 8 (in 1Q2016) there is + * currently no way to restrict which objects + * ObjectInputStream.readObject() + * may safely deserialize in the general case. Oracle + * may decide to address this deficiency in a future Java + * release, but until they do, there is no simple way for + * a general class library like ESAPI to address this. + */ + @Deprecated + public static Object decodeToObject( String encodedObject ) + { + // Decode and gunzip if necessary + byte[] objBytes = decode( encodedObject ); + + java.io.ByteArrayInputStream bais = null; + java.io.ObjectInputStream ois = null; + Object obj = null; + + try + { + bais = new java.io.ByteArrayInputStream( objBytes ); + ois = new java.io.ObjectInputStream( bais ); + + obj = ois.readObject(); + } // end try + catch( java.io.IOException e ) + { + logger.error( Logger.SECURITY_FAILURE, "Problem reading object", e ); + obj = null; + } // end catch + catch( java.lang.ClassNotFoundException e ) + { + logger.error( Logger.SECURITY_FAILURE, "Problem reading object", e ); + obj = null; + } // end catch + finally + { + try{ bais.close(); } catch( Exception e ){} + try{ ois.close(); } catch( Exception e ){} + } // end finally + + return obj; + } // end decodeObject + + + + /** + * Convenience method for encoding data to a file. + * + * @param dataToEncode byte array of data to encode in base64 form + * @param filename Filename for saving encoded data + * @return true if successful, false otherwise + * + * @since 2.1 + */ + public static boolean encodeToFile( byte[] dataToEncode, String filename ) + { + boolean success = false; + Base64.OutputStream bos = null; + try + { + bos = new Base64.OutputStream( + new java.io.FileOutputStream( filename ), Base64.ENCODE ); + bos.write( dataToEncode ); + success = true; + } // end try + catch( java.io.IOException e ) + { + + success = false; + } // end catch: IOException + finally + { + try{ bos.close(); } catch( Exception e ){} + } // end finally + + return success; + } // end encodeToFile + + + /** + * Convenience method for decoding data to a file. + * + * @param dataToDecode Base64-encoded data as a string + * @param filename Filename for saving decoded data + * @return true if successful, false otherwise + * + * @since 2.1 + */ + public static boolean decodeToFile( String dataToDecode, String filename ) + { + boolean success = false; + Base64.OutputStream bos = null; + try + { + bos = new Base64.OutputStream( + new java.io.FileOutputStream( filename ), Base64.DECODE ); + bos.write( dataToDecode.getBytes( PREFERRED_ENCODING ) ); + success = true; + } // end try + catch( java.io.IOException e ) + { + success = false; + } // end catch: IOException + finally + { + try{ bos.close(); } catch( Exception e ){} + } // end finally + + return success; + } // end decodeToFile + + + + + /** + * Convenience method for reading a base64-encoded + * file and decoding it. + * + * @param filename Filename for reading encoded data + * @return decoded byte array or null if unsuccessful + * + * @since 2.1 + */ + public static byte[] decodeFromFile( String filename ) + { + byte[] decodedData = null; + Base64.InputStream bis = null; + try + { + // Set up some useful variables + java.io.File file = new java.io.File( filename ); + byte[] buffer = null; + int length = 0; + int numBytes = 0; + + // Check for size of file + if( file.length() > Integer.MAX_VALUE ) + { + logger.error( Logger.SECURITY_FAILURE, "File is too big for this convenience method (" + file.length() + " bytes)." ); + return null; + } // end if: file too big for int index + buffer = new byte[ (int)file.length() ]; + + // Open a stream + bis = new Base64.InputStream( + new java.io.BufferedInputStream( + new java.io.FileInputStream( file ) ), Base64.DECODE ); + + // Read until done + while( ( numBytes = bis.read( buffer, length, 4096 ) ) >= 0 ) + length += numBytes; + + // Save in a variable to return + decodedData = new byte[ length ]; + System.arraycopy( buffer, 0, decodedData, 0, length ); + + } // end try + catch( java.io.IOException e ) + { + logger.error( Logger.SECURITY_FAILURE, "Error decoding from file " + filename, e ); + } // end catch: IOException + finally + { + try{ if (bis != null ) bis.close(); } catch( Exception e) {} + } // end finally + + return decodedData; + } // end decodeFromFile + + + + /** + * Convenience method for reading a binary file + * and base64-encoding it. + * + * @param filename Filename for reading binary data + * @return base64-encoded string or null if unsuccessful + * + * @since 2.1 + */ + public static String encodeFromFile( String filename ) + { + String encodedData = null; + Base64.InputStream bis = null; + try + { + // Set up some useful variables + java.io.File file = new java.io.File( filename ); + byte[] buffer = new byte[ Math.max((int)(file.length() * 1.4),40) ]; // Need max() for math on small files (v2.2.1) + int length = 0; + int numBytes = 0; + + // Open a stream + bis = new Base64.InputStream( + new java.io.BufferedInputStream( + new java.io.FileInputStream( file ) ), Base64.ENCODE ); + + // Read until done + while( ( numBytes = bis.read( buffer, length, 4096 ) ) >= 0 ) + length += numBytes; + + // Save in a variable to return + encodedData = new String( buffer, 0, length, Base64.PREFERRED_ENCODING ); + + } // end try + catch( java.io.IOException e ) + { + logger.error( Logger.SECURITY_FAILURE, "Error encoding from file " + filename, e ); + } // end catch: IOException + finally + { + try{ bis.close(); } catch( Exception e) {} + } // end finally + + return encodedData; + } // end encodeFromFile + + + + + /** + * Reads infile and encodes it to outfile. + * + * @param infile Input file + * @param outfile Output file + * @return true if the operation is successful + * @since 2.2 + */ + public static boolean encodeFileToFile( String infile, String outfile ) + { + boolean success = false; + java.io.InputStream in = null; + java.io.OutputStream out = null; + try{ + in = new Base64.InputStream( + new java.io.BufferedInputStream( + new java.io.FileInputStream( infile ) ), + Base64.ENCODE ); + out = new java.io.BufferedOutputStream( new java.io.FileOutputStream( outfile ) ); + byte[] buffer = new byte[65536]; // 64K + int read = -1; + while( ( read = in.read(buffer) ) >= 0 ){ + out.write( buffer,0,read ); + } // end while: through file + success = true; + } catch( java.io.IOException exc ){ + logger.error( Logger.SECURITY_FAILURE, "Problem encoding file to file", exc ); + } finally{ + try{ in.close(); } catch( Exception exc ){} + try{ out.close(); } catch( Exception exc ){} + } // end finally + + return success; + } // end encodeFileToFile + + + + /** + * Reads infile and decodes it to outfile. + * + * @param infile Input file + * @param outfile Output file + * @return true if the operation is successful + * @since 2.2 + */ + public static boolean decodeFileToFile( String infile, String outfile ) + { + boolean success = false; + java.io.InputStream in = null; + java.io.OutputStream out = null; + try{ + in = new Base64.InputStream( + new java.io.BufferedInputStream( + new java.io.FileInputStream( infile ) ), + Base64.DECODE ); + out = new java.io.BufferedOutputStream( new java.io.FileOutputStream( outfile ) ); + byte[] buffer = new byte[65536]; // 64K + int read = -1; + while( ( read = in.read(buffer) ) >= 0 ){ + out.write( buffer,0,read ); + } // end while: through file + success = true; + } catch( java.io.IOException exc ){ + logger.error( Logger.SECURITY_FAILURE, "Problem decoding file to file", exc ); + } finally{ + try{ in.close(); } catch( Exception exc ){} + try{ out.close(); } catch( Exception exc ){} + } // end finally + + return success; + } // end decodeFileToFile + + + /* ******** I N N E R C L A S S I N P U T S T R E A M ******** */ + + + + /** + * A {@link Base64.InputStream} will read data from another + * java.io.InputStream, given in the constructor, + * and encode/decode to/from Base64 notation on the fly. + * + * @see Base64 + * @since 1.3 + */ + public static class InputStream extends java.io.FilterInputStream + { + private boolean encode; // Encoding or decoding + private int position; // Current position in the buffer + private byte[] buffer; // Small buffer holding converted data + private int bufferLength; // Length of buffer (3 or 4) + private int numSigBytes; // Number of meaningful bytes in the buffer + private int lineLength; + private boolean breakLines; // Break lines at less than 80 characters + private int options; // Record options used to create the stream. + private byte[] decodabet; // Local copies to avoid extra method calls + + + /** + * Constructs a {@link Base64.InputStream} in DECODE mode. + * + * @param in the java.io.InputStream from which to read data. + * @since 1.3 + */ + public InputStream( java.io.InputStream in ) + { + this( in, DECODE ); + } // end constructor + + + /** + * Constructs a {@link Base64.InputStream} in + * either ENCODE or DECODE mode. + *

    + * Valid options:

    +         *   ENCODE or DECODE: Encode or Decode as data is read.
    +         *   DONT_BREAK_LINES: don't break lines at 76 characters
    +         *     (only meaningful when encoding)
    +         *     Note: Technically, this makes your encoding non-compliant.
    +         * 
    + *

    + * Example: new Base64.InputStream( in, Base64.DECODE ) + * + * + * @param in the java.io.InputStream from which to read data. + * @param options Specified options + * @see Base64#ENCODE + * @see Base64#DECODE + * @see Base64#DONT_BREAK_LINES + * @since 2.0 + */ + public InputStream( java.io.InputStream in, int options ) + { + super( in ); + this.breakLines = (options & DONT_BREAK_LINES) != DONT_BREAK_LINES; + this.encode = (options & ENCODE) == ENCODE; + this.bufferLength = encode ? 4 : 3; + this.buffer = new byte[ bufferLength ]; + this.position = -1; + this.lineLength = 0; + this.options = options; // Record for later, mostly to determine which alphabet to use + this.decodabet = getDecodabet(options); + } // end constructor + + /** + * Reads enough of the input stream to convert + * to/from Base64 and returns the next byte. + * + * @return next byte + * @throws java.io.IOException + * @since 1.3 + */ + public int read() throws java.io.IOException + { + // Do we need to get data? + if( position < 0 ) + { + if( encode ) + { + byte[] b3 = new byte[3]; + int numBinaryBytes = 0; + for( int i = 0; i < 3; i++ ) + { + try + { + int b = in.read(); + + // If end of stream, b is -1. + if( b >= 0 ) + { + b3[i] = (byte)b; + numBinaryBytes++; + } // end if: not end of stream + + } // end try: read + catch( java.io.IOException e ) + { + // Only a problem if we got no data at all. + if( i == 0 ) + throw e; + + } // end catch + } // end for: each needed input byte + + if( numBinaryBytes > 0 ) + { + encode3to4( b3, 0, numBinaryBytes, buffer, 0, options ); + position = 0; + numSigBytes = 4; + } // end if: got data + else + { + return -1; + } // end else + } // end if: encoding + + // Else decoding + else + { + byte[] b4 = new byte[4]; + int i = 0; + for( i = 0; i < 4; i++ ) + { + // Read four "meaningful" bytes: + int b = 0; + do{ b = in.read(); } + while( b >= 0 && decodabet[ b & 0x7f ] <= WHITE_SPACE_ENC ); + + if( b < 0 ) + break; // Reads a -1 if end of stream + + b4[i] = (byte)b; + } // end for: each needed input byte + + if( i == 4 ) + { + numSigBytes = decode4to3( b4, 0, buffer, 0, options ); + position = 0; + } // end if: got four characters + else if( i == 0 ){ + return -1; + } // end else if: also padded correctly + else + { + // Must have broken out from above. + throw new java.io.IOException( "Improperly padded Base64 input." ); + } // end + + } // end else: decode + } // end else: get data + + // Got data? + if( position >= 0 ) + { + // End of relevant data? + if( /*!encode &&*/ position >= numSigBytes ) + return -1; + + if( encode && breakLines && lineLength >= MAX_LINE_LENGTH ) + { + lineLength = 0; + return '\n'; + } // end if + else + { + lineLength++; // This isn't important when decoding + // but throwing an extra "if" seems + // just as wasteful. + + int b = buffer[ position++ ]; + + if( position >= bufferLength ) + position = -1; + + return b & 0xFF; // This is how you "cast" a byte that's + // intended to be unsigned. + } // end else + } // end if: position >= 0 + + // Else error + else + { + // When JDK1.4 is more accepted, use an assertion here. + throw new java.io.IOException( "Error in Base64 code reading stream." ); + } // end else + } // end read + + + /** + * Calls {@link #read()} repeatedly until the end of stream + * is reached or len bytes are read. + * Returns number of bytes read into array or -1 if + * end of stream is encountered. + * + * @param dest array to hold values + * @param off offset for array + * @param len max number of bytes to read into array + * @return bytes read into array or -1 if end of stream is encountered. + * @throws java.io.IOException + * @since 1.3 + */ + public int read( byte[] dest, int off, int len ) throws java.io.IOException + { + int i; + int b; + for( i = 0; i < len; i++ ) + { + b = read(); + + //if( b < 0 && i == 0 ) + // return -1; + + if( b >= 0 ) + dest[off + i] = (byte)b; + else if( i == 0 ) + return -1; + else + break; // Out of 'for' loop + } // end for: each byte read + return i; + } // end read + + } // end inner class InputStream + + + + + + + /* ******** I N N E R C L A S S O U T P U T S T R E A M ******** */ + + + + /** + * A {@link Base64.OutputStream} will write data to another + * java.io.OutputStream, given in the constructor, + * and encode/decode to/from Base64 notation on the fly. + * + * @see Base64 + * @since 1.3 + */ + public static class OutputStream extends java.io.FilterOutputStream + { + private boolean encode; + private int position; + private byte[] buffer; + private int bufferLength; + private int lineLength; + private boolean breakLines; + private byte[] b4; // Scratch used in a few places + private boolean suspendEncoding; + private int options; // Record for later + private byte[] decodabet; // Local copies to avoid extra method calls + + /** + * Constructs a {@link Base64.OutputStream} in ENCODE mode. + * + * @param out the java.io.OutputStream to which data will be written. + * @since 1.3 + */ + public OutputStream( java.io.OutputStream out ) + { + this( out, ENCODE ); + } // end constructor + + + /** + * Constructs a {@link Base64.OutputStream} in + * either ENCODE or DECODE mode. + *

    + * Valid options:

    +         *   ENCODE or DECODE: Encode or Decode as data is read.
    +         *   DONT_BREAK_LINES: don't break lines at 76 characters
    +         *     (only meaningful when encoding)
    +         *     Note: Technically, this makes your encoding non-compliant.
    +         * 
    + *

    + * Example: new Base64.OutputStream( out, Base64.ENCODE ) + * + * @param out the java.io.OutputStream to which data will be written. + * @param options Specified options. + * @see Base64#ENCODE + * @see Base64#DECODE + * @see Base64#DONT_BREAK_LINES + * @since 1.3 + */ + public OutputStream( java.io.OutputStream out, int options ) + { + super( out ); + this.breakLines = (options & DONT_BREAK_LINES) != DONT_BREAK_LINES; + this.encode = (options & ENCODE) == ENCODE; + this.bufferLength = encode ? 3 : 4; + this.buffer = new byte[ bufferLength ]; + this.position = 0; + this.lineLength = 0; + this.suspendEncoding = false; + this.b4 = new byte[4]; + this.options = options; + this.decodabet = getDecodabet(options); + } // end constructor + + + /** + * Writes the byte to the output stream after + * converting to/from Base64 notation. + * When encoding, bytes are buffered three + * at a time before the output stream actually + * gets a write() call. + * When decoding, bytes are buffered four + * at a time. + * + * @param theByte the byte to write + * @throws java.io.IOException + * @since 1.3 + */ + public void write(int theByte) throws java.io.IOException + { + // Encoding suspended? + if( suspendEncoding ) + { + super.out.write( theByte ); + return; + } // end if: supsended + + // Encode? + if( encode ) + { + buffer[ position++ ] = (byte)theByte; + if( position >= bufferLength ) // Enough to encode. + { + out.write( encode3to4( b4, buffer, bufferLength, options ) ); + + lineLength += 4; + if( breakLines && lineLength >= MAX_LINE_LENGTH ) + { + out.write( NEW_LINE ); + lineLength = 0; + } // end if: end of line + + position = 0; + } // end if: enough to output + } // end if: encoding + + // Else, Decoding + else + { + // Meaningful Base64 character? + if( decodabet[ theByte & 0x7f ] > WHITE_SPACE_ENC ) + { + buffer[ position++ ] = (byte)theByte; + if( position >= bufferLength ) // Enough to output. + { + int len = Base64.decode4to3( buffer, 0, b4, 0, options ); + out.write( b4, 0, len ); + //out.write( Base64.decode4to3( buffer ) ); + position = 0; + } // end if: enough to output + } // end if: meaningful base64 character + else if( decodabet[ theByte & 0x7f ] != WHITE_SPACE_ENC ) + { + throw new java.io.IOException( "Invalid character in Base64 data." ); + } // end else: not white space either + } // end else: decoding + } // end write + + + + /** + * Calls {@link #write(int)} repeatedly until len + * bytes are written. + * + * @param theBytes array from which to read bytes + * @param off offset for array + * @param len max number of bytes to read into array + * @throws java.io.IOException + * @since 1.3 + */ + public void write( byte[] theBytes, int off, int len ) throws java.io.IOException + { + // Encoding suspended? + if( suspendEncoding ) + { + super.out.write( theBytes, off, len ); + return; + } // end if: supsended + + for( int i = 0; i < len; i++ ) + { + write( theBytes[ off + i ] ); + } // end for: each byte written + + } // end write + + + + /** + * Method added by PHIL. [Thanks, PHIL. -Rob] + * This pads the buffer without closing the stream. + * @throws java.io.IOException + */ + public void flushBase64() throws java.io.IOException + { + if( position > 0 ) + { + if( encode ) + { + out.write( encode3to4( b4, buffer, position, options ) ); + position = 0; + } // end if: encoding + else + { + throw new java.io.IOException( "Base64 input not properly padded." ); + } // end else: decoding + } // end if: buffer partially full + + } // end flush + + + /** + * Flushes and closes (I think, in the superclass) the stream. + * + * @throws java.io.IOException + * @since 1.3 + */ + public void close() throws java.io.IOException + { + // 1. Ensure that pending characters are written + flushBase64(); + + // 2. Actually close the stream + // Base class both flushes and closes. + super.close(); + + buffer = null; + out = null; + } // end close + + + + /** + * Suspends encoding of the stream. + * May be helpful if you need to embed a piece of + * base640-encoded data in a stream. + * + * @throws java.io.IOException + * @since 1.5.1 + */ + public void suspendEncoding() throws java.io.IOException + { + flushBase64(); + this.suspendEncoding = true; + } // end suspendEncoding + + + /** + * Resumes encoding of the stream. + * May be helpful if you need to embed a piece of + * base640-encoded data in a stream. + * + * @since 1.5.1 + */ + public void resumeEncoding() + { + this.suspendEncoding = false; + } // end resumeEncoding + + + + } // end inner class OutputStream + + +} // end class Base64 From 9bb8d205a0c28ece62a045dae24fd93d29d75585 Mon Sep 17 00:00:00 2001 From: kwwall Date: Mon, 18 Jan 2016 23:33:47 -0500 Subject: [PATCH 0050/1069] Change CRLF to LF for issue 356. --- .../java/org/owasp/esapi/codecs/Codec.java | 318 +++++++++--------- 1 file changed, 159 insertions(+), 159 deletions(-) diff --git a/src/main/java/org/owasp/esapi/codecs/Codec.java b/src/main/java/org/owasp/esapi/codecs/Codec.java index 8e5fac8a3..ec02a806a 100644 --- a/src/main/java/org/owasp/esapi/codecs/Codec.java +++ b/src/main/java/org/owasp/esapi/codecs/Codec.java @@ -1,159 +1,159 @@ -/** - * OWASP Enterprise Security API (ESAPI) - * - * This file is part of the Open Web Application Security Project (OWASP) - * Enterprise Security API (ESAPI) project. For details, please see - * http://www.owasp.org/index.php/ESAPI. - * - * Copyright (c) 2007 - The OWASP Foundation - * - * The ESAPI is published by OWASP under the BSD license. You should read and accept the - * LICENSE before you use, modify, and/or redistribute this software. - * - * @author Jeff Williams Aspect Security - * @created 2007 - */ -package org.owasp.esapi.codecs; - - -/** - * The Codec interface defines a set of methods for encoding and decoding application level encoding schemes, - * such as HTML entity encoding and percent encoding (aka URL encoding). Codecs are used in output encoding - * and canonicalization. The design of these codecs allows for character-by-character decoding, which is - * necessary to detect double-encoding and the use of multiple encoding schemes, both of which are techniques - * used by attackers to bypass validation and bury encoded attacks in data. - * - * @author Jeff Williams (jeff.williams .at. aspectsecurity.com) Aspect Security - * @since June 1, 2007 - * @see org.owasp.esapi.Encoder - */ -public abstract class Codec { - - /** - * Initialize an array to mark which characters are to be encoded. Store the hex - * string for that character to save time later. If the character shouldn't be - * encoded, then store null. - */ - private static final String[] hex = new String[256]; - - static { - for ( char c = 0; c < 0xFF; c++ ) { - if ( c >= 0x30 && c <= 0x39 || c >= 0x41 && c <= 0x5A || c >= 0x61 && c <= 0x7A ) { - hex[c] = null; - } else { - hex[c] = toHex(c).intern(); - } - } - } - - - /** - * Default constructor - */ - public Codec() { - } - - /** - * Encode a String so that it can be safely used in a specific context. - * - * @param immune - * @param input - * the String to encode - * @return the encoded String - */ - public String encode(char[] immune, String input) { - StringBuilder sb = new StringBuilder(); - for (int i = 0; i < input.length(); i++) { - char c = input.charAt(i); - sb.append(encodeCharacter(immune, c)); - } - return sb.toString(); - } - - /** - * Default implementation that should be overridden in specific codecs. - * - * @param immune - * @param c - * the Character to encode - * @return - * the encoded Character - */ - public String encodeCharacter( char[] immune, Character c ) { - return ""+c; - } - - /** - * Decode a String that was encoded using the encode method in this Class - * - * @param input - * the String to decode - * @return - * the decoded String - */ - public String decode(String input) { - StringBuilder sb = new StringBuilder(); - PushbackString pbs = new PushbackString(input); - while (pbs.hasNext()) { - Character c = decodeCharacter(pbs); - if (c != null) { - sb.append(c); - } else { - sb.append(pbs.next()); - } - } - return sb.toString(); - } - - /** - * Returns the decoded version of the next character from the input string and advances the - * current character in the PushbackString. If the current character is not encoded, this - * method MUST reset the PushbackString. - * - * @param input the Character to decode - * - * @return the decoded Character - */ - public Character decodeCharacter( PushbackString input ) { - return input.next(); - } - - /** - * Lookup the hex value of any character that is not alphanumeric. - * @param c The character to lookup. - * @return, return null if alphanumeric or the character code - * in hex. - */ - public static String getHexForNonAlphanumeric(char c) - { - if(c<0xFF) - return hex[c]; - return toHex(c); - } - - public static String toOctal(char c) - { - return Integer.toOctalString(c); - } - - public static String toHex(char c) - { - return Integer.toHexString(c); - } - - /** - * Utility to search a char[] for a specific char. - * - * @param c - * @param array - * @return - */ - public static boolean containsCharacter( char c, char[] array ) { - for (char ch : array) { - if (c == ch) return true; - } - return false; - } - -} +/** + * OWASP Enterprise Security API (ESAPI) + * + * This file is part of the Open Web Application Security Project (OWASP) + * Enterprise Security API (ESAPI) project. For details, please see + * http://www.owasp.org/index.php/ESAPI. + * + * Copyright (c) 2007 - The OWASP Foundation + * + * The ESAPI is published by OWASP under the BSD license. You should read and accept the + * LICENSE before you use, modify, and/or redistribute this software. + * + * @author Jeff Williams Aspect Security + * @created 2007 + */ +package org.owasp.esapi.codecs; + + +/** + * The Codec interface defines a set of methods for encoding and decoding application level encoding schemes, + * such as HTML entity encoding and percent encoding (aka URL encoding). Codecs are used in output encoding + * and canonicalization. The design of these codecs allows for character-by-character decoding, which is + * necessary to detect double-encoding and the use of multiple encoding schemes, both of which are techniques + * used by attackers to bypass validation and bury encoded attacks in data. + * + * @author Jeff Williams (jeff.williams .at. aspectsecurity.com) Aspect Security + * @since June 1, 2007 + * @see org.owasp.esapi.Encoder + */ +public abstract class Codec { + + /** + * Initialize an array to mark which characters are to be encoded. Store the hex + * string for that character to save time later. If the character shouldn't be + * encoded, then store null. + */ + private static final String[] hex = new String[256]; + + static { + for ( char c = 0; c < 0xFF; c++ ) { + if ( c >= 0x30 && c <= 0x39 || c >= 0x41 && c <= 0x5A || c >= 0x61 && c <= 0x7A ) { + hex[c] = null; + } else { + hex[c] = toHex(c).intern(); + } + } + } + + + /** + * Default constructor + */ + public Codec() { + } + + /** + * Encode a String so that it can be safely used in a specific context. + * + * @param immune + * @param input + * the String to encode + * @return the encoded String + */ + public String encode(char[] immune, String input) { + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < input.length(); i++) { + char c = input.charAt(i); + sb.append(encodeCharacter(immune, c)); + } + return sb.toString(); + } + + /** + * Default implementation that should be overridden in specific codecs. + * + * @param immune + * @param c + * the Character to encode + * @return + * the encoded Character + */ + public String encodeCharacter( char[] immune, Character c ) { + return ""+c; + } + + /** + * Decode a String that was encoded using the encode method in this Class + * + * @param input + * the String to decode + * @return + * the decoded String + */ + public String decode(String input) { + StringBuilder sb = new StringBuilder(); + PushbackString pbs = new PushbackString(input); + while (pbs.hasNext()) { + Character c = decodeCharacter(pbs); + if (c != null) { + sb.append(c); + } else { + sb.append(pbs.next()); + } + } + return sb.toString(); + } + + /** + * Returns the decoded version of the next character from the input string and advances the + * current character in the PushbackString. If the current character is not encoded, this + * method MUST reset the PushbackString. + * + * @param input the Character to decode + * + * @return the decoded Character + */ + public Character decodeCharacter( PushbackString input ) { + return input.next(); + } + + /** + * Lookup the hex value of any character that is not alphanumeric. + * @param c The character to lookup. + * @return, return null if alphanumeric or the character code + * in hex. + */ + public static String getHexForNonAlphanumeric(char c) + { + if(c<0xFF) + return hex[c]; + return toHex(c); + } + + public static String toOctal(char c) + { + return Integer.toOctalString(c); + } + + public static String toHex(char c) + { + return Integer.toHexString(c); + } + + /** + * Utility to search a char[] for a specific char. + * + * @param c + * @param array + * @return + */ + public static boolean containsCharacter( char c, char[] array ) { + for (char ch : array) { + if (c == ch) return true; + } + return false; + } + +} From 53d1d2c32d75745f319eff01a86b6ac09d5c9f2f Mon Sep 17 00:00:00 2001 From: kwwall Date: Mon, 18 Jan 2016 23:33:47 -0500 Subject: [PATCH 0051/1069] Change CRLF to LF for issue 356. --- .../java/org/owasp/esapi/codecs/CSSCodec.java | 362 +++++++++--------- 1 file changed, 181 insertions(+), 181 deletions(-) diff --git a/src/main/java/org/owasp/esapi/codecs/CSSCodec.java b/src/main/java/org/owasp/esapi/codecs/CSSCodec.java index 9e5d0af19..cee77ccaf 100644 --- a/src/main/java/org/owasp/esapi/codecs/CSSCodec.java +++ b/src/main/java/org/owasp/esapi/codecs/CSSCodec.java @@ -1,181 +1,181 @@ -/** - * OWASP Enterprise Security API (ESAPI) - * - * This file is part of the Open Web Application Security Project (OWASP) Enterprise Security API - * (ESAPI) project. For details, please see http://www.owasp.org/index.php/ESAPI. - * - * Copyright (c) 2007 - The OWASP Foundation - * - * The ESAPI is published by OWASP under the BSD license. You should read and accept the LICENSE - * before you use, modify, and/or redistribute this software. - * - * @author Jeff Williams Aspect Security - * @created 2007 - */ -package org.owasp.esapi.codecs; - -/** - * Implementation of the Codec interface for backslash encoding used in CSS. - * - * @author Jeff Williams (jeff.williams .at. aspectsecurity.com) Aspect Security - * @since June 1, 2007 - * @see org.owasp.esapi.Encoder - */ -public class CSSCodec extends Codec -{ - private static final Character REPLACEMENT = '\ufffd'; - - - /** - * {@inheritDoc} - * - * Returns backslash encoded character. - * - * @param immune - */ - public String encodeCharacter(char[] immune, Character c) { - // check for immune characters - if ( containsCharacter(c, immune ) ) { - return ""+c; - } - - // check for alphanumeric characters - String hex = Codec.getHexForNonAlphanumeric(c); - if ( hex == null ) { - return ""+c; - } - - // return the hex and end in whitespace to terminate - return "\\" + hex + " "; - } - - - /** - * {@inheritDoc} - * - * Returns the decoded version of the character starting at index, - * or null if no decoding is possible. - */ - public Character decodeCharacter(PushbackString input) - { - input.mark(); - Character first = input.next(); - if (first == null || first != '\\') - { - input.reset(); - return null; - } - - Character second = input.next(); - if (second == null) { - input.reset(); - return null; - } - - /* From css 2.1 spec: - * http://www.w3.org/TR/CSS21/syndata.html#characters - * - * First, inside a string, a backslash followed by a - * newline is ignored (i.e., the string is deemed not - * to contain either the backslash or the newline). - * - * Second, it cancels the meaning of special CSS - * characters. Except within CSS comments, any character - * (except a hexadecimal digit, linefeed, carriage return, - * or form feed) can be escaped with a backslash to - * remove its special meaning. For example, "\"" is a string - * consisting of one double quote. Style sheet - * preprocessors must not remove these backslashes - * from a style sheet since that would change the style - * sheet's meaning. - * - * Third, backslash escapes allow authors to refer to - * characters they cannot easily put in a document. In - * this case, the backslash is followed by at most six - * hexadecimal digits (0..9A..F), which stand for the ISO - * 10646 ([ISO10646]) character with that number, which - * must not be zero. (It is undefined in CSS 2.1 what - * happens if a style sheet does contain a character with - * Unicode codepoint zero.) If a character in the range - * [0-9a-fA-F] follows the hexadecimal number, the end - * of the number needs to be made clear. There are two - * ways to do that: - * - * 1. with a space (or other white space character): - * "\26 B" ("&B"). In this case, user agents should - * treat a "CR/LF" pair (U+000D/U+000A) as a single - * white space character. - * - * 2. by providing exactly 6 hexadecimal digits: - * "\000026B" ("&B") - * - * In fact, these two methods may be combined. Only one - * white space character is ignored after a hexadecimal - * escape. Note that this means that a "real" space - * after the escape sequence must itself either be - * escaped or doubled. - * - * If the number is outside the range allowed by Unicode - * (e.g., "\110000" is above the maximum 10FFFF allowed in - * current Unicode), the UA may replace the escape with - * the "replacement character" (U+FFFD). If the character - * is to be displayed, the UA should show a visible - * symbol, such as a "missing character" glyph (cf. 15.2, - * point 5). - */ - - switch(second) - { // special whitespace cases. I assume they mean - // for all of these to qualify as a "new - // line." Otherwise there is no specification - // of what to do for \f - case '\r': - if(input.peek('\n')) - input.next(); - // fall through - case '\n': - case '\f': - // bs follwed by new line replaced by nothing - case '\u0000': // skip NUL for now too - return decodeCharacter(input); - } - - if (!PushbackString.isHexDigit(second)) - { // non hex digit - return second; - } - - // Search for up to 6 hex digits following until a space - StringBuilder sb = new StringBuilder(); - sb.append(second); - for (int i = 0; i < 5; i++) - { - Character c = input.next(); - if(c == null || Character.isWhitespace(c)) - break; - if(PushbackString.isHexDigit(c)) - sb.append(c); - else - { - input.pushback(c); - break; - } - } - try - { - // parse the hex digit and create a character - int i = Integer.parseInt(sb.toString(), 16); - - if (Character.isValidCodePoint(i)) - return (char)i; - return REPLACEMENT; - } - catch (NumberFormatException e) - { - throw new IllegalStateException("Received a NumberFormateException parsing a string verified to be hex", e); - } - } - -} +/** + * OWASP Enterprise Security API (ESAPI) + * + * This file is part of the Open Web Application Security Project (OWASP) Enterprise Security API + * (ESAPI) project. For details, please see http://www.owasp.org/index.php/ESAPI. + * + * Copyright (c) 2007 - The OWASP Foundation + * + * The ESAPI is published by OWASP under the BSD license. You should read and accept the LICENSE + * before you use, modify, and/or redistribute this software. + * + * @author Jeff Williams Aspect Security + * @created 2007 + */ +package org.owasp.esapi.codecs; + +/** + * Implementation of the Codec interface for backslash encoding used in CSS. + * + * @author Jeff Williams (jeff.williams .at. aspectsecurity.com) Aspect Security + * @since June 1, 2007 + * @see org.owasp.esapi.Encoder + */ +public class CSSCodec extends Codec +{ + private static final Character REPLACEMENT = '\ufffd'; + + + /** + * {@inheritDoc} + * + * Returns backslash encoded character. + * + * @param immune + */ + public String encodeCharacter(char[] immune, Character c) { + // check for immune characters + if ( containsCharacter(c, immune ) ) { + return ""+c; + } + + // check for alphanumeric characters + String hex = Codec.getHexForNonAlphanumeric(c); + if ( hex == null ) { + return ""+c; + } + + // return the hex and end in whitespace to terminate + return "\\" + hex + " "; + } + + + /** + * {@inheritDoc} + * + * Returns the decoded version of the character starting at index, + * or null if no decoding is possible. + */ + public Character decodeCharacter(PushbackString input) + { + input.mark(); + Character first = input.next(); + if (first == null || first != '\\') + { + input.reset(); + return null; + } + + Character second = input.next(); + if (second == null) { + input.reset(); + return null; + } + + /* From css 2.1 spec: + * http://www.w3.org/TR/CSS21/syndata.html#characters + * + * First, inside a string, a backslash followed by a + * newline is ignored (i.e., the string is deemed not + * to contain either the backslash or the newline). + * + * Second, it cancels the meaning of special CSS + * characters. Except within CSS comments, any character + * (except a hexadecimal digit, linefeed, carriage return, + * or form feed) can be escaped with a backslash to + * remove its special meaning. For example, "\"" is a string + * consisting of one double quote. Style sheet + * preprocessors must not remove these backslashes + * from a style sheet since that would change the style + * sheet's meaning. + * + * Third, backslash escapes allow authors to refer to + * characters they cannot easily put in a document. In + * this case, the backslash is followed by at most six + * hexadecimal digits (0..9A..F), which stand for the ISO + * 10646 ([ISO10646]) character with that number, which + * must not be zero. (It is undefined in CSS 2.1 what + * happens if a style sheet does contain a character with + * Unicode codepoint zero.) If a character in the range + * [0-9a-fA-F] follows the hexadecimal number, the end + * of the number needs to be made clear. There are two + * ways to do that: + * + * 1. with a space (or other white space character): + * "\26 B" ("&B"). In this case, user agents should + * treat a "CR/LF" pair (U+000D/U+000A) as a single + * white space character. + * + * 2. by providing exactly 6 hexadecimal digits: + * "\000026B" ("&B") + * + * In fact, these two methods may be combined. Only one + * white space character is ignored after a hexadecimal + * escape. Note that this means that a "real" space + * after the escape sequence must itself either be + * escaped or doubled. + * + * If the number is outside the range allowed by Unicode + * (e.g., "\110000" is above the maximum 10FFFF allowed in + * current Unicode), the UA may replace the escape with + * the "replacement character" (U+FFFD). If the character + * is to be displayed, the UA should show a visible + * symbol, such as a "missing character" glyph (cf. 15.2, + * point 5). + */ + + switch(second) + { // special whitespace cases. I assume they mean + // for all of these to qualify as a "new + // line." Otherwise there is no specification + // of what to do for \f + case '\r': + if(input.peek('\n')) + input.next(); + // fall through + case '\n': + case '\f': + // bs follwed by new line replaced by nothing + case '\u0000': // skip NUL for now too + return decodeCharacter(input); + } + + if (!PushbackString.isHexDigit(second)) + { // non hex digit + return second; + } + + // Search for up to 6 hex digits following until a space + StringBuilder sb = new StringBuilder(); + sb.append(second); + for (int i = 0; i < 5; i++) + { + Character c = input.next(); + if(c == null || Character.isWhitespace(c)) + break; + if(PushbackString.isHexDigit(c)) + sb.append(c); + else + { + input.pushback(c); + break; + } + } + try + { + // parse the hex digit and create a character + int i = Integer.parseInt(sb.toString(), 16); + + if (Character.isValidCodePoint(i)) + return (char)i; + return REPLACEMENT; + } + catch (NumberFormatException e) + { + throw new IllegalStateException("Received a NumberFormateException parsing a string verified to be hex", e); + } + } + +} From 4a984b0e7fca75fb0667fc03c16f11a193a53872 Mon Sep 17 00:00:00 2001 From: kwwall Date: Mon, 18 Jan 2016 23:33:47 -0500 Subject: [PATCH 0052/1069] Change CRLF to LF for issue 356. --- .../java/org/owasp/esapi/codecs/DB2Codec.java | 134 +++++++++--------- 1 file changed, 67 insertions(+), 67 deletions(-) diff --git a/src/main/java/org/owasp/esapi/codecs/DB2Codec.java b/src/main/java/org/owasp/esapi/codecs/DB2Codec.java index a99818c27..4ff63ea1a 100644 --- a/src/main/java/org/owasp/esapi/codecs/DB2Codec.java +++ b/src/main/java/org/owasp/esapi/codecs/DB2Codec.java @@ -1,68 +1,68 @@ -/** - * OWASP Enterprise Security API (ESAPI) - * - * This file is part of the Open Web Application Security Project (OWASP) - * Enterprise Security API (ESAPI) project. For details, please see - * http://www.owasp.org/index.php/ESAPI. - * - * Copyright (c) 2007 - The OWASP Foundation - * - * The ESAPI is published by OWASP under the BSD license. You should read and accept the - * LICENSE before you use, modify, and/or redistribute this software. - */ -package org.owasp.esapi.codecs; - - -/** - * Implementation of the Codec interface for DB2 strings. This function will only protect you from SQLi in limited situations. - * - * @author Sivasankar Tanakala (stanakal@TRS.NYC.NY.US) - * @since October 26, 2010 - * @see org.owasp.esapi.Encoder - */ -public class DB2Codec extends Codec { - - public String encodeCharacter(char[] immune, Character c) { - - if (c.charValue() == '\'') - return "\'\'"; - - if (c.charValue() == ';') - return "."; - - return "" + c; - } - - public Character decodeCharacter(PushbackString input) { - - input.mark(); - Character first = input.next(); - - if (first == null) { - input.reset(); - return null; - } - - // if this is not an encoded character, return null - - if (first.charValue() != '\'') { - input.reset(); - return null; - } - - Character second = input.next(); - - if (second == null) { - input.reset(); - return null; - } - - // if this is not an encoded character, return null - if (second.charValue() != '\'') { - input.reset(); - return null; - } - - return (Character.valueOf('\'')); - } +/** + * OWASP Enterprise Security API (ESAPI) + * + * This file is part of the Open Web Application Security Project (OWASP) + * Enterprise Security API (ESAPI) project. For details, please see + * http://www.owasp.org/index.php/ESAPI. + * + * Copyright (c) 2007 - The OWASP Foundation + * + * The ESAPI is published by OWASP under the BSD license. You should read and accept the + * LICENSE before you use, modify, and/or redistribute this software. + */ +package org.owasp.esapi.codecs; + + +/** + * Implementation of the Codec interface for DB2 strings. This function will only protect you from SQLi in limited situations. + * + * @author Sivasankar Tanakala (stanakal@TRS.NYC.NY.US) + * @since October 26, 2010 + * @see org.owasp.esapi.Encoder + */ +public class DB2Codec extends Codec { + + public String encodeCharacter(char[] immune, Character c) { + + if (c.charValue() == '\'') + return "\'\'"; + + if (c.charValue() == ';') + return "."; + + return "" + c; + } + + public Character decodeCharacter(PushbackString input) { + + input.mark(); + Character first = input.next(); + + if (first == null) { + input.reset(); + return null; + } + + // if this is not an encoded character, return null + + if (first.charValue() != '\'') { + input.reset(); + return null; + } + + Character second = input.next(); + + if (second == null) { + input.reset(); + return null; + } + + // if this is not an encoded character, return null + if (second.charValue() != '\'') { + input.reset(); + return null; + } + + return (Character.valueOf('\'')); + } } \ No newline at end of file From 68db04bb6e323f3597e196e18e4b9909c60f8efd Mon Sep 17 00:00:00 2001 From: kwwall Date: Mon, 18 Jan 2016 23:33:47 -0500 Subject: [PATCH 0053/1069] Change CRLF to LF for issue 356. --- .../owasp/esapi/codecs/HTMLEntityCodec.java | 1100 ++++++++--------- 1 file changed, 550 insertions(+), 550 deletions(-) diff --git a/src/main/java/org/owasp/esapi/codecs/HTMLEntityCodec.java b/src/main/java/org/owasp/esapi/codecs/HTMLEntityCodec.java index 36d5876a9..b4337968a 100644 --- a/src/main/java/org/owasp/esapi/codecs/HTMLEntityCodec.java +++ b/src/main/java/org/owasp/esapi/codecs/HTMLEntityCodec.java @@ -1,550 +1,550 @@ -/** - * OWASP Enterprise Security API (ESAPI) - * - * This file is part of the Open Web Application Security Project (OWASP) - * Enterprise Security API (ESAPI) project. For details, please see - * http://www.owasp.org/index.php/ESAPI. - * - * Copyright (c) 2007 - The OWASP Foundation - * - * The ESAPI is published by OWASP under the BSD license. You should read and accept the - * LICENSE before you use, modify, and/or redistribute this software. - * - * @author Jeff Williams Aspect Security - * @created 2007 - */ -package org.owasp.esapi.codecs; - -import java.util.HashMap; -import java.util.Collections; -import java.util.Map; - -/** - * Implementation of the Codec interface for HTML entity encoding. - * - * @author Jeff Williams (jeff.williams .at. aspectsecurity.com) Aspect Security - * @since June 1, 2007 - * @see org.owasp.esapi.Encoder - */ -public class HTMLEntityCodec extends Codec -{ - private static final char REPLACEMENT_CHAR = '\ufffd'; - private static final String REPLACEMENT_HEX = "fffd"; - private static final String REPLACEMENT_STR = "" + REPLACEMENT_CHAR; - private static final Map characterToEntityMap = mkCharacterToEntityMap(); - - private static final Trie entityToCharacterTrie = mkEntityToCharacterTrie(); - - /** - * - */ - public HTMLEntityCodec() { - } - - /** - * {@inheritDoc} - * - * Encodes a Character for safe use in an HTML entity field. - * @param immune - */ - public String encodeCharacter( char[] immune, Character c ) { - - // check for immune characters - if ( containsCharacter(c, immune ) ) { - return ""+c; - } - - // check for alphanumeric characters - String hex = Codec.getHexForNonAlphanumeric(c); - if ( hex == null ) { - return ""+c; - } - - // check for illegal characters - if ( ( c <= 0x1f && c != '\t' && c != '\n' && c != '\r' ) || ( c >= 0x7f && c <= 0x9f ) ) - { - hex = REPLACEMENT_HEX; // Let's entity encode this instead of returning it - c = REPLACEMENT_CHAR; - } - - // check if there's a defined entity - String entityName = (String) characterToEntityMap.get(c); - if (entityName != null) { - return "&" + entityName + ";"; - } - - // return the hex entity as suggested in the spec - return "&#x" + hex + ";"; - } - - /** - * {@inheritDoc} - * - * Returns the decoded version of the character starting at index, or - * null if no decoding is possible. - * - * Formats all are legal both with and without semi-colon, upper/lower case: - * &#dddd; - * &#xhhhh; - * &name; - */ - public Character decodeCharacter( PushbackString input ) { - input.mark(); - Character first = input.next(); - if ( first == null ) { - input.reset(); - return null; - } - - // if this is not an encoded character, return null - if (first != '&' ) { - input.reset(); - return null; - } - - // test for numeric encodings - Character second = input.next(); - if ( second == null ) { - input.reset(); - return null; - } - - if (second == '#' ) { - // handle numbers - Character c = getNumericEntity( input ); - if ( c != null ) return c; - } else if ( Character.isLetter( second.charValue() ) ) { - // handle entities - input.pushback( second ); - Character c = getNamedEntity( input ); - if ( c != null ) return c; - } - input.reset(); - return null; - } - - /** - * getNumericEntry checks input to see if it is a numeric entity - * - * @param input - * The input to test for being a numeric entity - * - * @return - * null if input is null, the character of input after decoding - */ - private Character getNumericEntity( PushbackString input ) { - Character first = input.peek(); - if ( first == null ) return null; - - if (first == 'x' || first == 'X' ) { - input.next(); - return parseHex( input ); - } - return parseNumber( input ); - } - - /** - * Parse a decimal number, such as those from JavaScript's String.fromCharCode(value) - * - * @param input - * decimal encoded string, such as 65 - * @return - * character representation of this decimal value, e.g. A - * @throws NumberFormatException - */ - private Character parseNumber( PushbackString input ) { - StringBuilder sb = new StringBuilder(); - while( input.hasNext() ) { - Character c = input.peek(); - - // if character is a digit then add it on and keep going - if ( Character.isDigit( c.charValue() ) ) { - sb.append( c ); - input.next(); - - // if character is a semi-colon, eat it and quit - } else if (c == ';' ) { - input.next(); - break; - - // otherwise just quit - } else { - break; - } - } - try { - int i = Integer.parseInt(sb.toString()); - if (Character.isValidCodePoint(i)) { - return (char) i; - } - } catch( NumberFormatException e ) { - // throw an exception for malformed entity? - } - return null; - } - - /** - * Parse a hex encoded entity - * - * @param input - * Hex encoded input (such as 437ae;) - * @return - * A single character from the string - * @throws NumberFormatException - */ - private Character parseHex( PushbackString input ) { - StringBuilder sb = new StringBuilder(); - while( input.hasNext() ) { - Character c = input.peek(); - - // if character is a hex digit then add it on and keep going - if ( "0123456789ABCDEFabcdef".indexOf(c) != -1 ) { - sb.append( c ); - input.next(); - - // if character is a semi-colon, eat it and quit - } else if (c == ';' ) { - input.next(); - break; - - // otherwise just quit - } else { - break; - } - } - try { - int i = Integer.parseInt(sb.toString(), 16); - if (Character.isValidCodePoint(i)) { - return (char) i; - } - } catch( NumberFormatException e ) { - // throw an exception for malformed entity? - } - return null; - } - - /** - * - * Returns the decoded version of the character starting at index, or - * null if no decoding is possible. - * - * Formats all are legal both with and without semi-colon, upper/lower case: - * &aa; - * &aaa; - * &aaaa; - * &aaaaa; - * &aaaaaa; - * &aaaaaaa; - * - * @param input - * A string containing a named entity like " - * @return - * Returns the decoded version of the character starting at index, or null if no decoding is possible. - */ - private Character getNamedEntity( PushbackString input ) { - StringBuilder possible = new StringBuilder(); - Map.Entry entry; - int len; - - // kludge around PushbackString.... - len = Math.min(input.remainder().length(), entityToCharacterTrie.getMaxKeyLength()); - for(int i=0;i mkCharacterToEntityMap() - { - Map map = new HashMap(252); - - map.put((char)34, "quot"); /* quotation mark */ - map.put((char)38, "amp"); /* ampersand */ - map.put((char)60, "lt"); /* less-than sign */ - map.put((char)62, "gt"); /* greater-than sign */ - map.put((char)160, "nbsp"); /* no-break space */ - map.put((char)161, "iexcl"); /* inverted exclamation mark */ - map.put((char)162, "cent"); /* cent sign */ - map.put((char)163, "pound"); /* pound sign */ - map.put((char)164, "curren"); /* currency sign */ - map.put((char)165, "yen"); /* yen sign */ - map.put((char)166, "brvbar"); /* broken bar */ - map.put((char)167, "sect"); /* section sign */ - map.put((char)168, "uml"); /* diaeresis */ - map.put((char)169, "copy"); /* copyright sign */ - map.put((char)170, "ordf"); /* feminine ordinal indicator */ - map.put((char)171, "laquo"); /* left-pointing double angle quotation mark */ - map.put((char)172, "not"); /* not sign */ - map.put((char)173, "shy"); /* soft hyphen */ - map.put((char)174, "reg"); /* registered sign */ - map.put((char)175, "macr"); /* macron */ - map.put((char)176, "deg"); /* degree sign */ - map.put((char)177, "plusmn"); /* plus-minus sign */ - map.put((char)178, "sup2"); /* superscript two */ - map.put((char)179, "sup3"); /* superscript three */ - map.put((char)180, "acute"); /* acute accent */ - map.put((char)181, "micro"); /* micro sign */ - map.put((char)182, "para"); /* pilcrow sign */ - map.put((char)183, "middot"); /* middle dot */ - map.put((char)184, "cedil"); /* cedilla */ - map.put((char)185, "sup1"); /* superscript one */ - map.put((char)186, "ordm"); /* masculine ordinal indicator */ - map.put((char)187, "raquo"); /* right-pointing double angle quotation mark */ - map.put((char)188, "frac14"); /* vulgar fraction one quarter */ - map.put((char)189, "frac12"); /* vulgar fraction one half */ - map.put((char)190, "frac34"); /* vulgar fraction three quarters */ - map.put((char)191, "iquest"); /* inverted question mark */ - map.put((char)192, "Agrave"); /* Latin capital letter a with grave */ - map.put((char)193, "Aacute"); /* Latin capital letter a with acute */ - map.put((char)194, "Acirc"); /* Latin capital letter a with circumflex */ - map.put((char)195, "Atilde"); /* Latin capital letter a with tilde */ - map.put((char)196, "Auml"); /* Latin capital letter a with diaeresis */ - map.put((char)197, "Aring"); /* Latin capital letter a with ring above */ - map.put((char)198, "AElig"); /* Latin capital letter ae */ - map.put((char)199, "Ccedil"); /* Latin capital letter c with cedilla */ - map.put((char)200, "Egrave"); /* Latin capital letter e with grave */ - map.put((char)201, "Eacute"); /* Latin capital letter e with acute */ - map.put((char)202, "Ecirc"); /* Latin capital letter e with circumflex */ - map.put((char)203, "Euml"); /* Latin capital letter e with diaeresis */ - map.put((char)204, "Igrave"); /* Latin capital letter i with grave */ - map.put((char)205, "Iacute"); /* Latin capital letter i with acute */ - map.put((char)206, "Icirc"); /* Latin capital letter i with circumflex */ - map.put((char)207, "Iuml"); /* Latin capital letter i with diaeresis */ - map.put((char)208, "ETH"); /* Latin capital letter eth */ - map.put((char)209, "Ntilde"); /* Latin capital letter n with tilde */ - map.put((char)210, "Ograve"); /* Latin capital letter o with grave */ - map.put((char)211, "Oacute"); /* Latin capital letter o with acute */ - map.put((char)212, "Ocirc"); /* Latin capital letter o with circumflex */ - map.put((char)213, "Otilde"); /* Latin capital letter o with tilde */ - map.put((char)214, "Ouml"); /* Latin capital letter o with diaeresis */ - map.put((char)215, "times"); /* multiplication sign */ - map.put((char)216, "Oslash"); /* Latin capital letter o with stroke */ - map.put((char)217, "Ugrave"); /* Latin capital letter u with grave */ - map.put((char)218, "Uacute"); /* Latin capital letter u with acute */ - map.put((char)219, "Ucirc"); /* Latin capital letter u with circumflex */ - map.put((char)220, "Uuml"); /* Latin capital letter u with diaeresis */ - map.put((char)221, "Yacute"); /* Latin capital letter y with acute */ - map.put((char)222, "THORN"); /* Latin capital letter thorn */ - map.put((char)223, "szlig"); /* Latin small letter sharp sXCOMMAX German Eszett */ - map.put((char)224, "agrave"); /* Latin small letter a with grave */ - map.put((char)225, "aacute"); /* Latin small letter a with acute */ - map.put((char)226, "acirc"); /* Latin small letter a with circumflex */ - map.put((char)227, "atilde"); /* Latin small letter a with tilde */ - map.put((char)228, "auml"); /* Latin small letter a with diaeresis */ - map.put((char)229, "aring"); /* Latin small letter a with ring above */ - map.put((char)230, "aelig"); /* Latin lowercase ligature ae */ - map.put((char)231, "ccedil"); /* Latin small letter c with cedilla */ - map.put((char)232, "egrave"); /* Latin small letter e with grave */ - map.put((char)233, "eacute"); /* Latin small letter e with acute */ - map.put((char)234, "ecirc"); /* Latin small letter e with circumflex */ - map.put((char)235, "euml"); /* Latin small letter e with diaeresis */ - map.put((char)236, "igrave"); /* Latin small letter i with grave */ - map.put((char)237, "iacute"); /* Latin small letter i with acute */ - map.put((char)238, "icirc"); /* Latin small letter i with circumflex */ - map.put((char)239, "iuml"); /* Latin small letter i with diaeresis */ - map.put((char)240, "eth"); /* Latin small letter eth */ - map.put((char)241, "ntilde"); /* Latin small letter n with tilde */ - map.put((char)242, "ograve"); /* Latin small letter o with grave */ - map.put((char)243, "oacute"); /* Latin small letter o with acute */ - map.put((char)244, "ocirc"); /* Latin small letter o with circumflex */ - map.put((char)245, "otilde"); /* Latin small letter o with tilde */ - map.put((char)246, "ouml"); /* Latin small letter o with diaeresis */ - map.put((char)247, "divide"); /* division sign */ - map.put((char)248, "oslash"); /* Latin small letter o with stroke */ - map.put((char)249, "ugrave"); /* Latin small letter u with grave */ - map.put((char)250, "uacute"); /* Latin small letter u with acute */ - map.put((char)251, "ucirc"); /* Latin small letter u with circumflex */ - map.put((char)252, "uuml"); /* Latin small letter u with diaeresis */ - map.put((char)253, "yacute"); /* Latin small letter y with acute */ - map.put((char)254, "thorn"); /* Latin small letter thorn */ - map.put((char)255, "yuml"); /* Latin small letter y with diaeresis */ - map.put((char)338, "OElig"); /* Latin capital ligature oe */ - map.put((char)339, "oelig"); /* Latin small ligature oe */ - map.put((char)352, "Scaron"); /* Latin capital letter s with caron */ - map.put((char)353, "scaron"); /* Latin small letter s with caron */ - map.put((char)376, "Yuml"); /* Latin capital letter y with diaeresis */ - map.put((char)402, "fnof"); /* Latin small letter f with hook */ - map.put((char)710, "circ"); /* modifier letter circumflex accent */ - map.put((char)732, "tilde"); /* small tilde */ - map.put((char)913, "Alpha"); /* Greek capital letter alpha */ - map.put((char)914, "Beta"); /* Greek capital letter beta */ - map.put((char)915, "Gamma"); /* Greek capital letter gamma */ - map.put((char)916, "Delta"); /* Greek capital letter delta */ - map.put((char)917, "Epsilon"); /* Greek capital letter epsilon */ - map.put((char)918, "Zeta"); /* Greek capital letter zeta */ - map.put((char)919, "Eta"); /* Greek capital letter eta */ - map.put((char)920, "Theta"); /* Greek capital letter theta */ - map.put((char)921, "Iota"); /* Greek capital letter iota */ - map.put((char)922, "Kappa"); /* Greek capital letter kappa */ - map.put((char)923, "Lambda"); /* Greek capital letter lambda */ - map.put((char)924, "Mu"); /* Greek capital letter mu */ - map.put((char)925, "Nu"); /* Greek capital letter nu */ - map.put((char)926, "Xi"); /* Greek capital letter xi */ - map.put((char)927, "Omicron"); /* Greek capital letter omicron */ - map.put((char)928, "Pi"); /* Greek capital letter pi */ - map.put((char)929, "Rho"); /* Greek capital letter rho */ - map.put((char)931, "Sigma"); /* Greek capital letter sigma */ - map.put((char)932, "Tau"); /* Greek capital letter tau */ - map.put((char)933, "Upsilon"); /* Greek capital letter upsilon */ - map.put((char)934, "Phi"); /* Greek capital letter phi */ - map.put((char)935, "Chi"); /* Greek capital letter chi */ - map.put((char)936, "Psi"); /* Greek capital letter psi */ - map.put((char)937, "Omega"); /* Greek capital letter omega */ - map.put((char)945, "alpha"); /* Greek small letter alpha */ - map.put((char)946, "beta"); /* Greek small letter beta */ - map.put((char)947, "gamma"); /* Greek small letter gamma */ - map.put((char)948, "delta"); /* Greek small letter delta */ - map.put((char)949, "epsilon"); /* Greek small letter epsilon */ - map.put((char)950, "zeta"); /* Greek small letter zeta */ - map.put((char)951, "eta"); /* Greek small letter eta */ - map.put((char)952, "theta"); /* Greek small letter theta */ - map.put((char)953, "iota"); /* Greek small letter iota */ - map.put((char)954, "kappa"); /* Greek small letter kappa */ - map.put((char)955, "lambda"); /* Greek small letter lambda */ - map.put((char)956, "mu"); /* Greek small letter mu */ - map.put((char)957, "nu"); /* Greek small letter nu */ - map.put((char)958, "xi"); /* Greek small letter xi */ - map.put((char)959, "omicron"); /* Greek small letter omicron */ - map.put((char)960, "pi"); /* Greek small letter pi */ - map.put((char)961, "rho"); /* Greek small letter rho */ - map.put((char)962, "sigmaf"); /* Greek small letter final sigma */ - map.put((char)963, "sigma"); /* Greek small letter sigma */ - map.put((char)964, "tau"); /* Greek small letter tau */ - map.put((char)965, "upsilon"); /* Greek small letter upsilon */ - map.put((char)966, "phi"); /* Greek small letter phi */ - map.put((char)967, "chi"); /* Greek small letter chi */ - map.put((char)968, "psi"); /* Greek small letter psi */ - map.put((char)969, "omega"); /* Greek small letter omega */ - map.put((char)977, "thetasym"); /* Greek theta symbol */ - map.put((char)978, "upsih"); /* Greek upsilon with hook symbol */ - map.put((char)982, "piv"); /* Greek pi symbol */ - map.put((char)8194, "ensp"); /* en space */ - map.put((char)8195, "emsp"); /* em space */ - map.put((char)8201, "thinsp"); /* thin space */ - map.put((char)8204, "zwnj"); /* zero width non-joiner */ - map.put((char)8205, "zwj"); /* zero width joiner */ - map.put((char)8206, "lrm"); /* left-to-right mark */ - map.put((char)8207, "rlm"); /* right-to-left mark */ - map.put((char)8211, "ndash"); /* en dash */ - map.put((char)8212, "mdash"); /* em dash */ - map.put((char)8216, "lsquo"); /* left single quotation mark */ - map.put((char)8217, "rsquo"); /* right single quotation mark */ - map.put((char)8218, "sbquo"); /* single low-9 quotation mark */ - map.put((char)8220, "ldquo"); /* left double quotation mark */ - map.put((char)8221, "rdquo"); /* right double quotation mark */ - map.put((char)8222, "bdquo"); /* double low-9 quotation mark */ - map.put((char)8224, "dagger"); /* dagger */ - map.put((char)8225, "Dagger"); /* double dagger */ - map.put((char)8226, "bull"); /* bullet */ - map.put((char)8230, "hellip"); /* horizontal ellipsis */ - map.put((char)8240, "permil"); /* per mille sign */ - map.put((char)8242, "prime"); /* prime */ - map.put((char)8243, "Prime"); /* double prime */ - map.put((char)8249, "lsaquo"); /* single left-pointing angle quotation mark */ - map.put((char)8250, "rsaquo"); /* single right-pointing angle quotation mark */ - map.put((char)8254, "oline"); /* overline */ - map.put((char)8260, "frasl"); /* fraction slash */ - map.put((char)8364, "euro"); /* euro sign */ - map.put((char)8465, "image"); /* black-letter capital i */ - map.put((char)8472, "weierp"); /* script capital pXCOMMAX Weierstrass p */ - map.put((char)8476, "real"); /* black-letter capital r */ - map.put((char)8482, "trade"); /* trademark sign */ - map.put((char)8501, "alefsym"); /* alef symbol */ - map.put((char)8592, "larr"); /* leftwards arrow */ - map.put((char)8593, "uarr"); /* upwards arrow */ - map.put((char)8594, "rarr"); /* rightwards arrow */ - map.put((char)8595, "darr"); /* downwards arrow */ - map.put((char)8596, "harr"); /* left right arrow */ - map.put((char)8629, "crarr"); /* downwards arrow with corner leftwards */ - map.put((char)8656, "lArr"); /* leftwards double arrow */ - map.put((char)8657, "uArr"); /* upwards double arrow */ - map.put((char)8658, "rArr"); /* rightwards double arrow */ - map.put((char)8659, "dArr"); /* downwards double arrow */ - map.put((char)8660, "hArr"); /* left right double arrow */ - map.put((char)8704, "forall"); /* for all */ - map.put((char)8706, "part"); /* partial differential */ - map.put((char)8707, "exist"); /* there exists */ - map.put((char)8709, "empty"); /* empty set */ - map.put((char)8711, "nabla"); /* nabla */ - map.put((char)8712, "isin"); /* element of */ - map.put((char)8713, "notin"); /* not an element of */ - map.put((char)8715, "ni"); /* contains as member */ - map.put((char)8719, "prod"); /* n-ary product */ - map.put((char)8721, "sum"); /* n-ary summation */ - map.put((char)8722, "minus"); /* minus sign */ - map.put((char)8727, "lowast"); /* asterisk operator */ - map.put((char)8730, "radic"); /* square root */ - map.put((char)8733, "prop"); /* proportional to */ - map.put((char)8734, "infin"); /* infinity */ - map.put((char)8736, "ang"); /* angle */ - map.put((char)8743, "and"); /* logical and */ - map.put((char)8744, "or"); /* logical or */ - map.put((char)8745, "cap"); /* intersection */ - map.put((char)8746, "cup"); /* union */ - map.put((char)8747, "int"); /* integral */ - map.put((char)8756, "there4"); /* therefore */ - map.put((char)8764, "sim"); /* tilde operator */ - map.put((char)8773, "cong"); /* congruent to */ - map.put((char)8776, "asymp"); /* almost equal to */ - map.put((char)8800, "ne"); /* not equal to */ - map.put((char)8801, "equiv"); /* identical toXCOMMAX equivalent to */ - map.put((char)8804, "le"); /* less-than or equal to */ - map.put((char)8805, "ge"); /* greater-than or equal to */ - map.put((char)8834, "sub"); /* subset of */ - map.put((char)8835, "sup"); /* superset of */ - map.put((char)8836, "nsub"); /* not a subset of */ - map.put((char)8838, "sube"); /* subset of or equal to */ - map.put((char)8839, "supe"); /* superset of or equal to */ - map.put((char)8853, "oplus"); /* circled plus */ - map.put((char)8855, "otimes"); /* circled times */ - map.put((char)8869, "perp"); /* up tack */ - map.put((char)8901, "sdot"); /* dot operator */ - map.put((char)8968, "lceil"); /* left ceiling */ - map.put((char)8969, "rceil"); /* right ceiling */ - map.put((char)8970, "lfloor"); /* left floor */ - map.put((char)8971, "rfloor"); /* right floor */ - map.put((char)9001, "lang"); /* left-pointing angle bracket */ - map.put((char)9002, "rang"); /* right-pointing angle bracket */ - map.put((char)9674, "loz"); /* lozenge */ - map.put((char)9824, "spades"); /* black spade suit */ - map.put((char)9827, "clubs"); /* black club suit */ - map.put((char)9829, "hearts"); /* black heart suit */ - map.put((char)9830, "diams"); /* black diamond suit */ - - return Collections.unmodifiableMap(map); - } - - /** - * Build a unmodifiable Trie from entitiy Name to Character - * @return Unmodifiable trie. - */ - private static synchronized Trie mkEntityToCharacterTrie() - { - Trie trie = new HashTrie(); - - for(Map.Entry entry : characterToEntityMap.entrySet()) - trie.put(entry.getValue(),entry.getKey()); - return Trie.Util.unmodifiable(trie); - } -} +/** + * OWASP Enterprise Security API (ESAPI) + * + * This file is part of the Open Web Application Security Project (OWASP) + * Enterprise Security API (ESAPI) project. For details, please see + * http://www.owasp.org/index.php/ESAPI. + * + * Copyright (c) 2007 - The OWASP Foundation + * + * The ESAPI is published by OWASP under the BSD license. You should read and accept the + * LICENSE before you use, modify, and/or redistribute this software. + * + * @author Jeff Williams Aspect Security + * @created 2007 + */ +package org.owasp.esapi.codecs; + +import java.util.HashMap; +import java.util.Collections; +import java.util.Map; + +/** + * Implementation of the Codec interface for HTML entity encoding. + * + * @author Jeff Williams (jeff.williams .at. aspectsecurity.com) Aspect Security + * @since June 1, 2007 + * @see org.owasp.esapi.Encoder + */ +public class HTMLEntityCodec extends Codec +{ + private static final char REPLACEMENT_CHAR = '\ufffd'; + private static final String REPLACEMENT_HEX = "fffd"; + private static final String REPLACEMENT_STR = "" + REPLACEMENT_CHAR; + private static final Map characterToEntityMap = mkCharacterToEntityMap(); + + private static final Trie entityToCharacterTrie = mkEntityToCharacterTrie(); + + /** + * + */ + public HTMLEntityCodec() { + } + + /** + * {@inheritDoc} + * + * Encodes a Character for safe use in an HTML entity field. + * @param immune + */ + public String encodeCharacter( char[] immune, Character c ) { + + // check for immune characters + if ( containsCharacter(c, immune ) ) { + return ""+c; + } + + // check for alphanumeric characters + String hex = Codec.getHexForNonAlphanumeric(c); + if ( hex == null ) { + return ""+c; + } + + // check for illegal characters + if ( ( c <= 0x1f && c != '\t' && c != '\n' && c != '\r' ) || ( c >= 0x7f && c <= 0x9f ) ) + { + hex = REPLACEMENT_HEX; // Let's entity encode this instead of returning it + c = REPLACEMENT_CHAR; + } + + // check if there's a defined entity + String entityName = (String) characterToEntityMap.get(c); + if (entityName != null) { + return "&" + entityName + ";"; + } + + // return the hex entity as suggested in the spec + return "&#x" + hex + ";"; + } + + /** + * {@inheritDoc} + * + * Returns the decoded version of the character starting at index, or + * null if no decoding is possible. + * + * Formats all are legal both with and without semi-colon, upper/lower case: + * &#dddd; + * &#xhhhh; + * &name; + */ + public Character decodeCharacter( PushbackString input ) { + input.mark(); + Character first = input.next(); + if ( first == null ) { + input.reset(); + return null; + } + + // if this is not an encoded character, return null + if (first != '&' ) { + input.reset(); + return null; + } + + // test for numeric encodings + Character second = input.next(); + if ( second == null ) { + input.reset(); + return null; + } + + if (second == '#' ) { + // handle numbers + Character c = getNumericEntity( input ); + if ( c != null ) return c; + } else if ( Character.isLetter( second.charValue() ) ) { + // handle entities + input.pushback( second ); + Character c = getNamedEntity( input ); + if ( c != null ) return c; + } + input.reset(); + return null; + } + + /** + * getNumericEntry checks input to see if it is a numeric entity + * + * @param input + * The input to test for being a numeric entity + * + * @return + * null if input is null, the character of input after decoding + */ + private Character getNumericEntity( PushbackString input ) { + Character first = input.peek(); + if ( first == null ) return null; + + if (first == 'x' || first == 'X' ) { + input.next(); + return parseHex( input ); + } + return parseNumber( input ); + } + + /** + * Parse a decimal number, such as those from JavaScript's String.fromCharCode(value) + * + * @param input + * decimal encoded string, such as 65 + * @return + * character representation of this decimal value, e.g. A + * @throws NumberFormatException + */ + private Character parseNumber( PushbackString input ) { + StringBuilder sb = new StringBuilder(); + while( input.hasNext() ) { + Character c = input.peek(); + + // if character is a digit then add it on and keep going + if ( Character.isDigit( c.charValue() ) ) { + sb.append( c ); + input.next(); + + // if character is a semi-colon, eat it and quit + } else if (c == ';' ) { + input.next(); + break; + + // otherwise just quit + } else { + break; + } + } + try { + int i = Integer.parseInt(sb.toString()); + if (Character.isValidCodePoint(i)) { + return (char) i; + } + } catch( NumberFormatException e ) { + // throw an exception for malformed entity? + } + return null; + } + + /** + * Parse a hex encoded entity + * + * @param input + * Hex encoded input (such as 437ae;) + * @return + * A single character from the string + * @throws NumberFormatException + */ + private Character parseHex( PushbackString input ) { + StringBuilder sb = new StringBuilder(); + while( input.hasNext() ) { + Character c = input.peek(); + + // if character is a hex digit then add it on and keep going + if ( "0123456789ABCDEFabcdef".indexOf(c) != -1 ) { + sb.append( c ); + input.next(); + + // if character is a semi-colon, eat it and quit + } else if (c == ';' ) { + input.next(); + break; + + // otherwise just quit + } else { + break; + } + } + try { + int i = Integer.parseInt(sb.toString(), 16); + if (Character.isValidCodePoint(i)) { + return (char) i; + } + } catch( NumberFormatException e ) { + // throw an exception for malformed entity? + } + return null; + } + + /** + * + * Returns the decoded version of the character starting at index, or + * null if no decoding is possible. + * + * Formats all are legal both with and without semi-colon, upper/lower case: + * &aa; + * &aaa; + * &aaaa; + * &aaaaa; + * &aaaaaa; + * &aaaaaaa; + * + * @param input + * A string containing a named entity like " + * @return + * Returns the decoded version of the character starting at index, or null if no decoding is possible. + */ + private Character getNamedEntity( PushbackString input ) { + StringBuilder possible = new StringBuilder(); + Map.Entry entry; + int len; + + // kludge around PushbackString.... + len = Math.min(input.remainder().length(), entityToCharacterTrie.getMaxKeyLength()); + for(int i=0;i mkCharacterToEntityMap() + { + Map map = new HashMap(252); + + map.put((char)34, "quot"); /* quotation mark */ + map.put((char)38, "amp"); /* ampersand */ + map.put((char)60, "lt"); /* less-than sign */ + map.put((char)62, "gt"); /* greater-than sign */ + map.put((char)160, "nbsp"); /* no-break space */ + map.put((char)161, "iexcl"); /* inverted exclamation mark */ + map.put((char)162, "cent"); /* cent sign */ + map.put((char)163, "pound"); /* pound sign */ + map.put((char)164, "curren"); /* currency sign */ + map.put((char)165, "yen"); /* yen sign */ + map.put((char)166, "brvbar"); /* broken bar */ + map.put((char)167, "sect"); /* section sign */ + map.put((char)168, "uml"); /* diaeresis */ + map.put((char)169, "copy"); /* copyright sign */ + map.put((char)170, "ordf"); /* feminine ordinal indicator */ + map.put((char)171, "laquo"); /* left-pointing double angle quotation mark */ + map.put((char)172, "not"); /* not sign */ + map.put((char)173, "shy"); /* soft hyphen */ + map.put((char)174, "reg"); /* registered sign */ + map.put((char)175, "macr"); /* macron */ + map.put((char)176, "deg"); /* degree sign */ + map.put((char)177, "plusmn"); /* plus-minus sign */ + map.put((char)178, "sup2"); /* superscript two */ + map.put((char)179, "sup3"); /* superscript three */ + map.put((char)180, "acute"); /* acute accent */ + map.put((char)181, "micro"); /* micro sign */ + map.put((char)182, "para"); /* pilcrow sign */ + map.put((char)183, "middot"); /* middle dot */ + map.put((char)184, "cedil"); /* cedilla */ + map.put((char)185, "sup1"); /* superscript one */ + map.put((char)186, "ordm"); /* masculine ordinal indicator */ + map.put((char)187, "raquo"); /* right-pointing double angle quotation mark */ + map.put((char)188, "frac14"); /* vulgar fraction one quarter */ + map.put((char)189, "frac12"); /* vulgar fraction one half */ + map.put((char)190, "frac34"); /* vulgar fraction three quarters */ + map.put((char)191, "iquest"); /* inverted question mark */ + map.put((char)192, "Agrave"); /* Latin capital letter a with grave */ + map.put((char)193, "Aacute"); /* Latin capital letter a with acute */ + map.put((char)194, "Acirc"); /* Latin capital letter a with circumflex */ + map.put((char)195, "Atilde"); /* Latin capital letter a with tilde */ + map.put((char)196, "Auml"); /* Latin capital letter a with diaeresis */ + map.put((char)197, "Aring"); /* Latin capital letter a with ring above */ + map.put((char)198, "AElig"); /* Latin capital letter ae */ + map.put((char)199, "Ccedil"); /* Latin capital letter c with cedilla */ + map.put((char)200, "Egrave"); /* Latin capital letter e with grave */ + map.put((char)201, "Eacute"); /* Latin capital letter e with acute */ + map.put((char)202, "Ecirc"); /* Latin capital letter e with circumflex */ + map.put((char)203, "Euml"); /* Latin capital letter e with diaeresis */ + map.put((char)204, "Igrave"); /* Latin capital letter i with grave */ + map.put((char)205, "Iacute"); /* Latin capital letter i with acute */ + map.put((char)206, "Icirc"); /* Latin capital letter i with circumflex */ + map.put((char)207, "Iuml"); /* Latin capital letter i with diaeresis */ + map.put((char)208, "ETH"); /* Latin capital letter eth */ + map.put((char)209, "Ntilde"); /* Latin capital letter n with tilde */ + map.put((char)210, "Ograve"); /* Latin capital letter o with grave */ + map.put((char)211, "Oacute"); /* Latin capital letter o with acute */ + map.put((char)212, "Ocirc"); /* Latin capital letter o with circumflex */ + map.put((char)213, "Otilde"); /* Latin capital letter o with tilde */ + map.put((char)214, "Ouml"); /* Latin capital letter o with diaeresis */ + map.put((char)215, "times"); /* multiplication sign */ + map.put((char)216, "Oslash"); /* Latin capital letter o with stroke */ + map.put((char)217, "Ugrave"); /* Latin capital letter u with grave */ + map.put((char)218, "Uacute"); /* Latin capital letter u with acute */ + map.put((char)219, "Ucirc"); /* Latin capital letter u with circumflex */ + map.put((char)220, "Uuml"); /* Latin capital letter u with diaeresis */ + map.put((char)221, "Yacute"); /* Latin capital letter y with acute */ + map.put((char)222, "THORN"); /* Latin capital letter thorn */ + map.put((char)223, "szlig"); /* Latin small letter sharp sXCOMMAX German Eszett */ + map.put((char)224, "agrave"); /* Latin small letter a with grave */ + map.put((char)225, "aacute"); /* Latin small letter a with acute */ + map.put((char)226, "acirc"); /* Latin small letter a with circumflex */ + map.put((char)227, "atilde"); /* Latin small letter a with tilde */ + map.put((char)228, "auml"); /* Latin small letter a with diaeresis */ + map.put((char)229, "aring"); /* Latin small letter a with ring above */ + map.put((char)230, "aelig"); /* Latin lowercase ligature ae */ + map.put((char)231, "ccedil"); /* Latin small letter c with cedilla */ + map.put((char)232, "egrave"); /* Latin small letter e with grave */ + map.put((char)233, "eacute"); /* Latin small letter e with acute */ + map.put((char)234, "ecirc"); /* Latin small letter e with circumflex */ + map.put((char)235, "euml"); /* Latin small letter e with diaeresis */ + map.put((char)236, "igrave"); /* Latin small letter i with grave */ + map.put((char)237, "iacute"); /* Latin small letter i with acute */ + map.put((char)238, "icirc"); /* Latin small letter i with circumflex */ + map.put((char)239, "iuml"); /* Latin small letter i with diaeresis */ + map.put((char)240, "eth"); /* Latin small letter eth */ + map.put((char)241, "ntilde"); /* Latin small letter n with tilde */ + map.put((char)242, "ograve"); /* Latin small letter o with grave */ + map.put((char)243, "oacute"); /* Latin small letter o with acute */ + map.put((char)244, "ocirc"); /* Latin small letter o with circumflex */ + map.put((char)245, "otilde"); /* Latin small letter o with tilde */ + map.put((char)246, "ouml"); /* Latin small letter o with diaeresis */ + map.put((char)247, "divide"); /* division sign */ + map.put((char)248, "oslash"); /* Latin small letter o with stroke */ + map.put((char)249, "ugrave"); /* Latin small letter u with grave */ + map.put((char)250, "uacute"); /* Latin small letter u with acute */ + map.put((char)251, "ucirc"); /* Latin small letter u with circumflex */ + map.put((char)252, "uuml"); /* Latin small letter u with diaeresis */ + map.put((char)253, "yacute"); /* Latin small letter y with acute */ + map.put((char)254, "thorn"); /* Latin small letter thorn */ + map.put((char)255, "yuml"); /* Latin small letter y with diaeresis */ + map.put((char)338, "OElig"); /* Latin capital ligature oe */ + map.put((char)339, "oelig"); /* Latin small ligature oe */ + map.put((char)352, "Scaron"); /* Latin capital letter s with caron */ + map.put((char)353, "scaron"); /* Latin small letter s with caron */ + map.put((char)376, "Yuml"); /* Latin capital letter y with diaeresis */ + map.put((char)402, "fnof"); /* Latin small letter f with hook */ + map.put((char)710, "circ"); /* modifier letter circumflex accent */ + map.put((char)732, "tilde"); /* small tilde */ + map.put((char)913, "Alpha"); /* Greek capital letter alpha */ + map.put((char)914, "Beta"); /* Greek capital letter beta */ + map.put((char)915, "Gamma"); /* Greek capital letter gamma */ + map.put((char)916, "Delta"); /* Greek capital letter delta */ + map.put((char)917, "Epsilon"); /* Greek capital letter epsilon */ + map.put((char)918, "Zeta"); /* Greek capital letter zeta */ + map.put((char)919, "Eta"); /* Greek capital letter eta */ + map.put((char)920, "Theta"); /* Greek capital letter theta */ + map.put((char)921, "Iota"); /* Greek capital letter iota */ + map.put((char)922, "Kappa"); /* Greek capital letter kappa */ + map.put((char)923, "Lambda"); /* Greek capital letter lambda */ + map.put((char)924, "Mu"); /* Greek capital letter mu */ + map.put((char)925, "Nu"); /* Greek capital letter nu */ + map.put((char)926, "Xi"); /* Greek capital letter xi */ + map.put((char)927, "Omicron"); /* Greek capital letter omicron */ + map.put((char)928, "Pi"); /* Greek capital letter pi */ + map.put((char)929, "Rho"); /* Greek capital letter rho */ + map.put((char)931, "Sigma"); /* Greek capital letter sigma */ + map.put((char)932, "Tau"); /* Greek capital letter tau */ + map.put((char)933, "Upsilon"); /* Greek capital letter upsilon */ + map.put((char)934, "Phi"); /* Greek capital letter phi */ + map.put((char)935, "Chi"); /* Greek capital letter chi */ + map.put((char)936, "Psi"); /* Greek capital letter psi */ + map.put((char)937, "Omega"); /* Greek capital letter omega */ + map.put((char)945, "alpha"); /* Greek small letter alpha */ + map.put((char)946, "beta"); /* Greek small letter beta */ + map.put((char)947, "gamma"); /* Greek small letter gamma */ + map.put((char)948, "delta"); /* Greek small letter delta */ + map.put((char)949, "epsilon"); /* Greek small letter epsilon */ + map.put((char)950, "zeta"); /* Greek small letter zeta */ + map.put((char)951, "eta"); /* Greek small letter eta */ + map.put((char)952, "theta"); /* Greek small letter theta */ + map.put((char)953, "iota"); /* Greek small letter iota */ + map.put((char)954, "kappa"); /* Greek small letter kappa */ + map.put((char)955, "lambda"); /* Greek small letter lambda */ + map.put((char)956, "mu"); /* Greek small letter mu */ + map.put((char)957, "nu"); /* Greek small letter nu */ + map.put((char)958, "xi"); /* Greek small letter xi */ + map.put((char)959, "omicron"); /* Greek small letter omicron */ + map.put((char)960, "pi"); /* Greek small letter pi */ + map.put((char)961, "rho"); /* Greek small letter rho */ + map.put((char)962, "sigmaf"); /* Greek small letter final sigma */ + map.put((char)963, "sigma"); /* Greek small letter sigma */ + map.put((char)964, "tau"); /* Greek small letter tau */ + map.put((char)965, "upsilon"); /* Greek small letter upsilon */ + map.put((char)966, "phi"); /* Greek small letter phi */ + map.put((char)967, "chi"); /* Greek small letter chi */ + map.put((char)968, "psi"); /* Greek small letter psi */ + map.put((char)969, "omega"); /* Greek small letter omega */ + map.put((char)977, "thetasym"); /* Greek theta symbol */ + map.put((char)978, "upsih"); /* Greek upsilon with hook symbol */ + map.put((char)982, "piv"); /* Greek pi symbol */ + map.put((char)8194, "ensp"); /* en space */ + map.put((char)8195, "emsp"); /* em space */ + map.put((char)8201, "thinsp"); /* thin space */ + map.put((char)8204, "zwnj"); /* zero width non-joiner */ + map.put((char)8205, "zwj"); /* zero width joiner */ + map.put((char)8206, "lrm"); /* left-to-right mark */ + map.put((char)8207, "rlm"); /* right-to-left mark */ + map.put((char)8211, "ndash"); /* en dash */ + map.put((char)8212, "mdash"); /* em dash */ + map.put((char)8216, "lsquo"); /* left single quotation mark */ + map.put((char)8217, "rsquo"); /* right single quotation mark */ + map.put((char)8218, "sbquo"); /* single low-9 quotation mark */ + map.put((char)8220, "ldquo"); /* left double quotation mark */ + map.put((char)8221, "rdquo"); /* right double quotation mark */ + map.put((char)8222, "bdquo"); /* double low-9 quotation mark */ + map.put((char)8224, "dagger"); /* dagger */ + map.put((char)8225, "Dagger"); /* double dagger */ + map.put((char)8226, "bull"); /* bullet */ + map.put((char)8230, "hellip"); /* horizontal ellipsis */ + map.put((char)8240, "permil"); /* per mille sign */ + map.put((char)8242, "prime"); /* prime */ + map.put((char)8243, "Prime"); /* double prime */ + map.put((char)8249, "lsaquo"); /* single left-pointing angle quotation mark */ + map.put((char)8250, "rsaquo"); /* single right-pointing angle quotation mark */ + map.put((char)8254, "oline"); /* overline */ + map.put((char)8260, "frasl"); /* fraction slash */ + map.put((char)8364, "euro"); /* euro sign */ + map.put((char)8465, "image"); /* black-letter capital i */ + map.put((char)8472, "weierp"); /* script capital pXCOMMAX Weierstrass p */ + map.put((char)8476, "real"); /* black-letter capital r */ + map.put((char)8482, "trade"); /* trademark sign */ + map.put((char)8501, "alefsym"); /* alef symbol */ + map.put((char)8592, "larr"); /* leftwards arrow */ + map.put((char)8593, "uarr"); /* upwards arrow */ + map.put((char)8594, "rarr"); /* rightwards arrow */ + map.put((char)8595, "darr"); /* downwards arrow */ + map.put((char)8596, "harr"); /* left right arrow */ + map.put((char)8629, "crarr"); /* downwards arrow with corner leftwards */ + map.put((char)8656, "lArr"); /* leftwards double arrow */ + map.put((char)8657, "uArr"); /* upwards double arrow */ + map.put((char)8658, "rArr"); /* rightwards double arrow */ + map.put((char)8659, "dArr"); /* downwards double arrow */ + map.put((char)8660, "hArr"); /* left right double arrow */ + map.put((char)8704, "forall"); /* for all */ + map.put((char)8706, "part"); /* partial differential */ + map.put((char)8707, "exist"); /* there exists */ + map.put((char)8709, "empty"); /* empty set */ + map.put((char)8711, "nabla"); /* nabla */ + map.put((char)8712, "isin"); /* element of */ + map.put((char)8713, "notin"); /* not an element of */ + map.put((char)8715, "ni"); /* contains as member */ + map.put((char)8719, "prod"); /* n-ary product */ + map.put((char)8721, "sum"); /* n-ary summation */ + map.put((char)8722, "minus"); /* minus sign */ + map.put((char)8727, "lowast"); /* asterisk operator */ + map.put((char)8730, "radic"); /* square root */ + map.put((char)8733, "prop"); /* proportional to */ + map.put((char)8734, "infin"); /* infinity */ + map.put((char)8736, "ang"); /* angle */ + map.put((char)8743, "and"); /* logical and */ + map.put((char)8744, "or"); /* logical or */ + map.put((char)8745, "cap"); /* intersection */ + map.put((char)8746, "cup"); /* union */ + map.put((char)8747, "int"); /* integral */ + map.put((char)8756, "there4"); /* therefore */ + map.put((char)8764, "sim"); /* tilde operator */ + map.put((char)8773, "cong"); /* congruent to */ + map.put((char)8776, "asymp"); /* almost equal to */ + map.put((char)8800, "ne"); /* not equal to */ + map.put((char)8801, "equiv"); /* identical toXCOMMAX equivalent to */ + map.put((char)8804, "le"); /* less-than or equal to */ + map.put((char)8805, "ge"); /* greater-than or equal to */ + map.put((char)8834, "sub"); /* subset of */ + map.put((char)8835, "sup"); /* superset of */ + map.put((char)8836, "nsub"); /* not a subset of */ + map.put((char)8838, "sube"); /* subset of or equal to */ + map.put((char)8839, "supe"); /* superset of or equal to */ + map.put((char)8853, "oplus"); /* circled plus */ + map.put((char)8855, "otimes"); /* circled times */ + map.put((char)8869, "perp"); /* up tack */ + map.put((char)8901, "sdot"); /* dot operator */ + map.put((char)8968, "lceil"); /* left ceiling */ + map.put((char)8969, "rceil"); /* right ceiling */ + map.put((char)8970, "lfloor"); /* left floor */ + map.put((char)8971, "rfloor"); /* right floor */ + map.put((char)9001, "lang"); /* left-pointing angle bracket */ + map.put((char)9002, "rang"); /* right-pointing angle bracket */ + map.put((char)9674, "loz"); /* lozenge */ + map.put((char)9824, "spades"); /* black spade suit */ + map.put((char)9827, "clubs"); /* black club suit */ + map.put((char)9829, "hearts"); /* black heart suit */ + map.put((char)9830, "diams"); /* black diamond suit */ + + return Collections.unmodifiableMap(map); + } + + /** + * Build a unmodifiable Trie from entitiy Name to Character + * @return Unmodifiable trie. + */ + private static synchronized Trie mkEntityToCharacterTrie() + { + Trie trie = new HashTrie(); + + for(Map.Entry entry : characterToEntityMap.entrySet()) + trie.put(entry.getValue(),entry.getKey()); + return Trie.Util.unmodifiable(trie); + } +} From d87f8dbb8f97a6cc28f488f4206264ea937735a6 Mon Sep 17 00:00:00 2001 From: kwwall Date: Mon, 18 Jan 2016 23:33:47 -0500 Subject: [PATCH 0054/1069] Change CRLF to LF for issue 356. --- .../owasp/esapi/codecs/JavaScriptCodec.java | 432 +++++++++--------- 1 file changed, 216 insertions(+), 216 deletions(-) diff --git a/src/main/java/org/owasp/esapi/codecs/JavaScriptCodec.java b/src/main/java/org/owasp/esapi/codecs/JavaScriptCodec.java index 6d128e462..7980155e1 100644 --- a/src/main/java/org/owasp/esapi/codecs/JavaScriptCodec.java +++ b/src/main/java/org/owasp/esapi/codecs/JavaScriptCodec.java @@ -1,217 +1,217 @@ -/** - * OWASP Enterprise Security API (ESAPI) - * - * This file is part of the Open Web Application Security Project (OWASP) - * Enterprise Security API (ESAPI) project. For details, please see - * http://www.owasp.org/index.php/ESAPI. - * - * Copyright (c) 2007 - The OWASP Foundation - * - * The ESAPI is published by OWASP under the BSD license. You should read and accept the - * LICENSE before you use, modify, and/or redistribute this software. - * - * @author Jeff Williams Aspect Security - * @created 2007 - */ -package org.owasp.esapi.codecs; - - -/** - * Implementation of the Codec interface for backslash encoding in JavaScript. - * - * @author Jeff Williams (jeff.williams .at. aspectsecurity.com) Aspect Security - * @since June 1, 2007 - * @see org.owasp.esapi.Encoder - */ -public class JavaScriptCodec extends Codec { - - - /** - * {@inheritDoc} - * - * Returns backslash encoded numeric format. Does not use backslash character escapes - * such as, \" or \' as these may cause parsing problems. For example, if a javascript - * attribute, such as onmouseover, contains a \" that will close the entire attribute and - * allow an attacker to inject another script attribute. - * - * @param immune - */ - public String encodeCharacter( char[] immune, Character c ) { - - // check for immune characters - if ( containsCharacter(c, immune ) ) { - return ""+c; - } - - // check for alphanumeric characters - String hex = Codec.getHexForNonAlphanumeric(c); - if ( hex == null ) { - return ""+c; - } - - // Do not use these shortcuts as they can be used to break out of a context - // if ( ch == 0x00 ) return "\\0"; - // if ( ch == 0x08 ) return "\\b"; - // if ( ch == 0x09 ) return "\\t"; - // if ( ch == 0x0a ) return "\\n"; - // if ( ch == 0x0b ) return "\\v"; - // if ( ch == 0x0c ) return "\\f"; - // if ( ch == 0x0d ) return "\\r"; - // if ( ch == 0x22 ) return "\\\""; - // if ( ch == 0x27 ) return "\\'"; - // if ( ch == 0x5c ) return "\\\\"; - - // encode up to 256 with \\xHH - String temp = Integer.toHexString(c); - if ( c < 256 ) { - String pad = "00".substring(temp.length() ); - return "\\x" + pad + temp.toUpperCase(); - } - - // otherwise encode with \\uHHHH - String pad = "0000".substring(temp.length() ); - return "\\u" + pad + temp.toUpperCase(); - } - - - /** - * {@inheritDoc} - * - * Returns the decoded version of the character starting at index, or - * null if no decoding is possible. - * See http://www.planetpdf.com/codecuts/pdfs/tutorial/jsspec.pdf - * Formats all are legal both upper/lower case: - * \\a - special characters - * \\xHH - * \\uHHHH - * \\OOO (1, 2, or 3 digits) - */ - public Character decodeCharacter( PushbackString input ) { - input.mark(); - Character first = input.next(); - if ( first == null ) { - input.reset(); - return null; - } - - // if this is not an encoded character, return null - if (first != '\\' ) { - input.reset(); - return null; - } - - Character second = input.next(); - if ( second == null ) { - input.reset(); - return null; - } - - // \0 collides with the octal decoder and is non-standard - // if ( second.charValue() == '0' ) { - // return Character.valueOf( (char)0x00 ); - if (second == 'b' ) { - return 0x08; - } else if (second == 't' ) { - return 0x09; - } else if (second == 'n' ) { - return 0x0a; - } else if (second == 'v' ) { - return 0x0b; - } else if (second == 'f' ) { - return 0x0c; - } else if (second == 'r' ) { - return 0x0d; - } else if (second == '\"' ) { - return 0x22; - } else if (second == '\'' ) { - return 0x27; - } else if (second == '\\' ) { - return 0x5c; - - // look for \\xXX format - } else if ( Character.toLowerCase( second.charValue() ) == 'x' ) { - // Search for exactly 2 hex digits following - StringBuilder sb = new StringBuilder(); - for ( int i=0; i<2; i++ ) { - Character c = input.nextHex(); - if ( c != null ) sb.append( c ); - else { - input.reset(); - return null; - } - } - try { - // parse the hex digit and create a character - int i = Integer.parseInt(sb.toString(), 16); - if (Character.isValidCodePoint(i)) { - return (char) i; - } - } catch( NumberFormatException e ) { - // throw an exception for malformed entity? - input.reset(); - return null; - } - - // look for \\uXXXX format - } else if ( Character.toLowerCase( second.charValue() ) == 'u') { - // Search for exactly 4 hex digits following - StringBuilder sb = new StringBuilder(); - for ( int i=0; i<4; i++ ) { - Character c = input.nextHex(); - if ( c != null ) sb.append( c ); - else { - input.reset(); - return null; - } - } - try { - // parse the hex string and create a character - int i = Integer.parseInt(sb.toString(), 16); - if (Character.isValidCodePoint(i)) { - return (char) i; - } - } catch( NumberFormatException e ) { - // throw an exception for malformed entity? - input.reset(); - return null; - } - - // look for one, two, or three octal digits - } else if ( PushbackString.isOctalDigit(second) ) { - StringBuilder sb = new StringBuilder(); - // get digit 1 - sb.append(second); - - // get digit 2 if present - Character c2 = input.next(); - if ( !PushbackString.isOctalDigit(c2) ) { - input.pushback( c2 ); - } else { - sb.append( c2 ); - // get digit 3 if present - Character c3 = input.next(); - if ( !PushbackString.isOctalDigit(c3) ) { - input.pushback( c3 ); - } else { - sb.append( c3 ); - } - } - try { - // parse the octal string and create a character - int i = Integer.parseInt(sb.toString(), 8); - if (Character.isValidCodePoint(i)) { - return (char) i; - } - } catch( NumberFormatException e ) { - // throw an exception for malformed entity? - input.reset(); - return null; - } - } - - // ignore the backslash and return the character - return second; - } - +/** + * OWASP Enterprise Security API (ESAPI) + * + * This file is part of the Open Web Application Security Project (OWASP) + * Enterprise Security API (ESAPI) project. For details, please see + * http://www.owasp.org/index.php/ESAPI. + * + * Copyright (c) 2007 - The OWASP Foundation + * + * The ESAPI is published by OWASP under the BSD license. You should read and accept the + * LICENSE before you use, modify, and/or redistribute this software. + * + * @author Jeff Williams Aspect Security + * @created 2007 + */ +package org.owasp.esapi.codecs; + + +/** + * Implementation of the Codec interface for backslash encoding in JavaScript. + * + * @author Jeff Williams (jeff.williams .at. aspectsecurity.com) Aspect Security + * @since June 1, 2007 + * @see org.owasp.esapi.Encoder + */ +public class JavaScriptCodec extends Codec { + + + /** + * {@inheritDoc} + * + * Returns backslash encoded numeric format. Does not use backslash character escapes + * such as, \" or \' as these may cause parsing problems. For example, if a javascript + * attribute, such as onmouseover, contains a \" that will close the entire attribute and + * allow an attacker to inject another script attribute. + * + * @param immune + */ + public String encodeCharacter( char[] immune, Character c ) { + + // check for immune characters + if ( containsCharacter(c, immune ) ) { + return ""+c; + } + + // check for alphanumeric characters + String hex = Codec.getHexForNonAlphanumeric(c); + if ( hex == null ) { + return ""+c; + } + + // Do not use these shortcuts as they can be used to break out of a context + // if ( ch == 0x00 ) return "\\0"; + // if ( ch == 0x08 ) return "\\b"; + // if ( ch == 0x09 ) return "\\t"; + // if ( ch == 0x0a ) return "\\n"; + // if ( ch == 0x0b ) return "\\v"; + // if ( ch == 0x0c ) return "\\f"; + // if ( ch == 0x0d ) return "\\r"; + // if ( ch == 0x22 ) return "\\\""; + // if ( ch == 0x27 ) return "\\'"; + // if ( ch == 0x5c ) return "\\\\"; + + // encode up to 256 with \\xHH + String temp = Integer.toHexString(c); + if ( c < 256 ) { + String pad = "00".substring(temp.length() ); + return "\\x" + pad + temp.toUpperCase(); + } + + // otherwise encode with \\uHHHH + String pad = "0000".substring(temp.length() ); + return "\\u" + pad + temp.toUpperCase(); + } + + + /** + * {@inheritDoc} + * + * Returns the decoded version of the character starting at index, or + * null if no decoding is possible. + * See http://www.planetpdf.com/codecuts/pdfs/tutorial/jsspec.pdf + * Formats all are legal both upper/lower case: + * \\a - special characters + * \\xHH + * \\uHHHH + * \\OOO (1, 2, or 3 digits) + */ + public Character decodeCharacter( PushbackString input ) { + input.mark(); + Character first = input.next(); + if ( first == null ) { + input.reset(); + return null; + } + + // if this is not an encoded character, return null + if (first != '\\' ) { + input.reset(); + return null; + } + + Character second = input.next(); + if ( second == null ) { + input.reset(); + return null; + } + + // \0 collides with the octal decoder and is non-standard + // if ( second.charValue() == '0' ) { + // return Character.valueOf( (char)0x00 ); + if (second == 'b' ) { + return 0x08; + } else if (second == 't' ) { + return 0x09; + } else if (second == 'n' ) { + return 0x0a; + } else if (second == 'v' ) { + return 0x0b; + } else if (second == 'f' ) { + return 0x0c; + } else if (second == 'r' ) { + return 0x0d; + } else if (second == '\"' ) { + return 0x22; + } else if (second == '\'' ) { + return 0x27; + } else if (second == '\\' ) { + return 0x5c; + + // look for \\xXX format + } else if ( Character.toLowerCase( second.charValue() ) == 'x' ) { + // Search for exactly 2 hex digits following + StringBuilder sb = new StringBuilder(); + for ( int i=0; i<2; i++ ) { + Character c = input.nextHex(); + if ( c != null ) sb.append( c ); + else { + input.reset(); + return null; + } + } + try { + // parse the hex digit and create a character + int i = Integer.parseInt(sb.toString(), 16); + if (Character.isValidCodePoint(i)) { + return (char) i; + } + } catch( NumberFormatException e ) { + // throw an exception for malformed entity? + input.reset(); + return null; + } + + // look for \\uXXXX format + } else if ( Character.toLowerCase( second.charValue() ) == 'u') { + // Search for exactly 4 hex digits following + StringBuilder sb = new StringBuilder(); + for ( int i=0; i<4; i++ ) { + Character c = input.nextHex(); + if ( c != null ) sb.append( c ); + else { + input.reset(); + return null; + } + } + try { + // parse the hex string and create a character + int i = Integer.parseInt(sb.toString(), 16); + if (Character.isValidCodePoint(i)) { + return (char) i; + } + } catch( NumberFormatException e ) { + // throw an exception for malformed entity? + input.reset(); + return null; + } + + // look for one, two, or three octal digits + } else if ( PushbackString.isOctalDigit(second) ) { + StringBuilder sb = new StringBuilder(); + // get digit 1 + sb.append(second); + + // get digit 2 if present + Character c2 = input.next(); + if ( !PushbackString.isOctalDigit(c2) ) { + input.pushback( c2 ); + } else { + sb.append( c2 ); + // get digit 3 if present + Character c3 = input.next(); + if ( !PushbackString.isOctalDigit(c3) ) { + input.pushback( c3 ); + } else { + sb.append( c3 ); + } + } + try { + // parse the octal string and create a character + int i = Integer.parseInt(sb.toString(), 8); + if (Character.isValidCodePoint(i)) { + return (char) i; + } + } catch( NumberFormatException e ) { + // throw an exception for malformed entity? + input.reset(); + return null; + } + } + + // ignore the backslash and return the character + return second; + } + } \ No newline at end of file From eae6bba8583221ea4eee72f26fdae0a0c68c2430 Mon Sep 17 00:00:00 2001 From: kwwall Date: Mon, 18 Jan 2016 23:33:48 -0500 Subject: [PATCH 0055/1069] Change CRLF to LF for issue 356. --- .../org/owasp/esapi/codecs/MySQLCodec.java | 526 +++++++++--------- 1 file changed, 263 insertions(+), 263 deletions(-) diff --git a/src/main/java/org/owasp/esapi/codecs/MySQLCodec.java b/src/main/java/org/owasp/esapi/codecs/MySQLCodec.java index 01db4faf5..7d2aacbaa 100644 --- a/src/main/java/org/owasp/esapi/codecs/MySQLCodec.java +++ b/src/main/java/org/owasp/esapi/codecs/MySQLCodec.java @@ -1,264 +1,264 @@ -/** - * OWASP Enterprise Security API (ESAPI) - * - * This file is part of the Open Web Application Security Project (OWASP) - * Enterprise Security API (ESAPI) project. For details, please see - * http://www.owasp.org/index.php/ESAPI. - * - * Copyright (c) 2007 - The OWASP Foundation - * - * The ESAPI is published by OWASP under the BSD license. You should read and accept the - * LICENSE before you use, modify, and/or redistribute this software. - * - * @author Jeff Williams Aspect Security - * @created 2007 - */ -package org.owasp.esapi.codecs; - - - -/** - * Implementation of the Codec interface for MySQL strings. See http://mirror.yandex.ru/mirrors/ftp.mysql.com/doc/refman/5.0/en/string-syntax.html - * for more information. - * - * @author Jeff Williams (jeff.williams .at. aspectsecurity.com) Aspect Security - * @since June 1, 2007 - * @see org.owasp.esapi.Encoder - */ -public class MySQLCodec extends Codec { - /** - * Specifies the SQL Mode the target MySQL Server is running with. For details about MySQL Server Modes - * please see the Manual at {@link http://dev.mysql.com/doc/refman/5.0/en/server-sql-mode.html#sqlmode_ansi} - * - * Currently the only supported modes are: - * ANSI - * STANDARD - */ - public static enum Mode { - ANSI(1),STANDARD(0); - - private int key; - private Mode(int key) { this.key = key; } - - static Mode findByKey(int key) { - for ( Mode m : values() ) { - if ( m.key == key ) - return m; - } - return null; - } - } - - /** Target MySQL Server is running in Standard MySQL (Default) mode. */ - public static final int MYSQL_MODE = 0; - /** Target MySQL Server is running in {@link "http://dev.mysql.com/doc/refman/5.0/en/ansi-mode.html"} ANSI Mode */ - public static final int ANSI_MODE = 1; - - //private int mode = 0; - private Mode mode; - - /** - * Instantiate the MySQL codec - * - * @param mode - * Mode has to be one of {MYSQL_MODE|ANSI_MODE} to allow correct encoding - * @deprecated - * @see #MySQLCodec(org.owasp.esapi.codecs.MySQLCodec.Mode) - */ - public MySQLCodec( int mode ) { - this.mode = Mode.findByKey(mode); - } - - /** - * Instantiate the MySQL Codec with the given SQL {@link Mode}. - * @param mode The mode the target server is running in - */ - public MySQLCodec( Mode mode ) { - this.mode = mode; - } - - - /** - * {@inheritDoc} - * - * Returns quote-encoded character - * - * @param immune - */ - public String encodeCharacter( char[] immune, Character c ) { - char ch = c.charValue(); - - // check for immune characters - if ( containsCharacter( ch, immune ) ) { - return ""+ch; - } - - // check for alphanumeric characters - String hex = Codec.getHexForNonAlphanumeric( ch ); - if ( hex == null ) { - return ""+ch; - } - - switch( mode ) { - case ANSI: return encodeCharacterANSI( c ); - case STANDARD: return encodeCharacterMySQL( c ); - } - return null; - } - - /** - * encodeCharacterANSI encodes for ANSI SQL. - * - * Apostrophe is encoded - * - * Bug ###: In ANSI Mode Strings can also be passed in using the quotation. In ANSI_QUOTES mode a quotation - * is considered to be an identifier, thus cannot be used at all in a value and will be dropped completely. - * - * @param c - * character to encode - * @return - * String encoded to standards of MySQL running in ANSI mode - */ - private String encodeCharacterANSI( Character c ) { - if ( c == '\'' ) - return "\'\'"; - if ( c == '\"' ) - return ""; - return ""+c; - } - - /** - * Encode a character suitable for MySQL - * - * @param c - * Character to encode - * @return - * Encoded Character - */ - private String encodeCharacterMySQL( Character c ) { - char ch = c.charValue(); - if ( ch == 0x00 ) return "\\0"; - if ( ch == 0x08 ) return "\\b"; - if ( ch == 0x09 ) return "\\t"; - if ( ch == 0x0a ) return "\\n"; - if ( ch == 0x0d ) return "\\r"; - if ( ch == 0x1a ) return "\\Z"; - if ( ch == 0x22 ) return "\\\""; - if ( ch == 0x25 ) return "\\%"; - if ( ch == 0x27 ) return "\\'"; - if ( ch == 0x5c ) return "\\\\"; - if ( ch == 0x5f ) return "\\_"; - return "\\" + c; - } - - /** - * {@inheritDoc} - * - * Returns the decoded version of the character starting at index, or - * null if no decoding is possible. - * - * Formats all are legal (case sensitive) - * In ANSI_MODE '' decodes to ' - * In MYSQL_MODE \x decodes to x (or a small list of specials) - */ - public Character decodeCharacter( PushbackString input ) { - switch( mode ) { - case ANSI: return decodeCharacterANSI( input ); - case STANDARD: return decodeCharacterMySQL( input ); - } - return null; - } - - /** - * decodeCharacterANSI decodes the next character from ANSI SQL escaping - * - * @param input - * A PushBackString containing characters you'd like decoded - * @return - * A single character, decoded - */ - private Character decodeCharacterANSI( PushbackString input ) { - input.mark(); - Character first = input.next(); - if ( first == null ) { - input.reset(); - return null; - } - - // if this is not an encoded character, return null - if ( first.charValue() != '\'' ) { - input.reset(); - return null; - } - - Character second = input.next(); - if ( second == null ) { - input.reset(); - return null; - } - - // if this is not an encoded character, return null - if ( second.charValue() != '\'' ) { - input.reset(); - return null; - } - return( Character.valueOf( '\'' ) ); - } - - /** - * decodeCharacterMySQL decodes all the potential escaped characters that MySQL is prepared to escape - * - * @param input - * A string you'd like to be decoded - * @return - * A single character from that string, decoded. - */ - private Character decodeCharacterMySQL( PushbackString input ) { - input.mark(); - Character first = input.next(); - if ( first == null ) { - input.reset(); - return null; - } - - // if this is not an encoded character, return null - if ( first.charValue() != '\\' ) { - input.reset(); - return null; - } - - Character second = input.next(); - if ( second == null ) { - input.reset(); - return null; - } - - if ( second.charValue() == '0' ) { - return Character.valueOf( (char)0x00 ); - } else if ( second.charValue() == 'b' ) { - return Character.valueOf( (char)0x08 ); - } else if ( second.charValue() == 't' ) { - return Character.valueOf( (char)0x09 ); - } else if ( second.charValue() == 'n' ) { - return Character.valueOf( (char)0x0a ); - } else if ( second.charValue() == 'r' ) { - return Character.valueOf( (char)0x0d ); - } else if ( second.charValue() == 'z' ) { - return Character.valueOf( (char)0x1a ); - } else if ( second.charValue() == '\"' ) { - return Character.valueOf( (char)0x22 ); - } else if ( second.charValue() == '%' ) { - return Character.valueOf( (char)0x25 ); - } else if ( second.charValue() == '\'' ) { - return Character.valueOf( (char)0x27 ); - } else if ( second.charValue() == '\\' ) { - return Character.valueOf( (char)0x5c ); - } else if ( second.charValue() == '_' ) { - return Character.valueOf( (char)0x5f ); - } else { - return second; - } - } - +/** + * OWASP Enterprise Security API (ESAPI) + * + * This file is part of the Open Web Application Security Project (OWASP) + * Enterprise Security API (ESAPI) project. For details, please see + * http://www.owasp.org/index.php/ESAPI. + * + * Copyright (c) 2007 - The OWASP Foundation + * + * The ESAPI is published by OWASP under the BSD license. You should read and accept the + * LICENSE before you use, modify, and/or redistribute this software. + * + * @author Jeff Williams Aspect Security + * @created 2007 + */ +package org.owasp.esapi.codecs; + + + +/** + * Implementation of the Codec interface for MySQL strings. See http://mirror.yandex.ru/mirrors/ftp.mysql.com/doc/refman/5.0/en/string-syntax.html + * for more information. + * + * @author Jeff Williams (jeff.williams .at. aspectsecurity.com) Aspect Security + * @since June 1, 2007 + * @see org.owasp.esapi.Encoder + */ +public class MySQLCodec extends Codec { + /** + * Specifies the SQL Mode the target MySQL Server is running with. For details about MySQL Server Modes + * please see the Manual at {@link http://dev.mysql.com/doc/refman/5.0/en/server-sql-mode.html#sqlmode_ansi} + * + * Currently the only supported modes are: + * ANSI + * STANDARD + */ + public static enum Mode { + ANSI(1),STANDARD(0); + + private int key; + private Mode(int key) { this.key = key; } + + static Mode findByKey(int key) { + for ( Mode m : values() ) { + if ( m.key == key ) + return m; + } + return null; + } + } + + /** Target MySQL Server is running in Standard MySQL (Default) mode. */ + public static final int MYSQL_MODE = 0; + /** Target MySQL Server is running in {@link "http://dev.mysql.com/doc/refman/5.0/en/ansi-mode.html"} ANSI Mode */ + public static final int ANSI_MODE = 1; + + //private int mode = 0; + private Mode mode; + + /** + * Instantiate the MySQL codec + * + * @param mode + * Mode has to be one of {MYSQL_MODE|ANSI_MODE} to allow correct encoding + * @deprecated + * @see #MySQLCodec(org.owasp.esapi.codecs.MySQLCodec.Mode) + */ + public MySQLCodec( int mode ) { + this.mode = Mode.findByKey(mode); + } + + /** + * Instantiate the MySQL Codec with the given SQL {@link Mode}. + * @param mode The mode the target server is running in + */ + public MySQLCodec( Mode mode ) { + this.mode = mode; + } + + + /** + * {@inheritDoc} + * + * Returns quote-encoded character + * + * @param immune + */ + public String encodeCharacter( char[] immune, Character c ) { + char ch = c.charValue(); + + // check for immune characters + if ( containsCharacter( ch, immune ) ) { + return ""+ch; + } + + // check for alphanumeric characters + String hex = Codec.getHexForNonAlphanumeric( ch ); + if ( hex == null ) { + return ""+ch; + } + + switch( mode ) { + case ANSI: return encodeCharacterANSI( c ); + case STANDARD: return encodeCharacterMySQL( c ); + } + return null; + } + + /** + * encodeCharacterANSI encodes for ANSI SQL. + * + * Apostrophe is encoded + * + * Bug ###: In ANSI Mode Strings can also be passed in using the quotation. In ANSI_QUOTES mode a quotation + * is considered to be an identifier, thus cannot be used at all in a value and will be dropped completely. + * + * @param c + * character to encode + * @return + * String encoded to standards of MySQL running in ANSI mode + */ + private String encodeCharacterANSI( Character c ) { + if ( c == '\'' ) + return "\'\'"; + if ( c == '\"' ) + return ""; + return ""+c; + } + + /** + * Encode a character suitable for MySQL + * + * @param c + * Character to encode + * @return + * Encoded Character + */ + private String encodeCharacterMySQL( Character c ) { + char ch = c.charValue(); + if ( ch == 0x00 ) return "\\0"; + if ( ch == 0x08 ) return "\\b"; + if ( ch == 0x09 ) return "\\t"; + if ( ch == 0x0a ) return "\\n"; + if ( ch == 0x0d ) return "\\r"; + if ( ch == 0x1a ) return "\\Z"; + if ( ch == 0x22 ) return "\\\""; + if ( ch == 0x25 ) return "\\%"; + if ( ch == 0x27 ) return "\\'"; + if ( ch == 0x5c ) return "\\\\"; + if ( ch == 0x5f ) return "\\_"; + return "\\" + c; + } + + /** + * {@inheritDoc} + * + * Returns the decoded version of the character starting at index, or + * null if no decoding is possible. + * + * Formats all are legal (case sensitive) + * In ANSI_MODE '' decodes to ' + * In MYSQL_MODE \x decodes to x (or a small list of specials) + */ + public Character decodeCharacter( PushbackString input ) { + switch( mode ) { + case ANSI: return decodeCharacterANSI( input ); + case STANDARD: return decodeCharacterMySQL( input ); + } + return null; + } + + /** + * decodeCharacterANSI decodes the next character from ANSI SQL escaping + * + * @param input + * A PushBackString containing characters you'd like decoded + * @return + * A single character, decoded + */ + private Character decodeCharacterANSI( PushbackString input ) { + input.mark(); + Character first = input.next(); + if ( first == null ) { + input.reset(); + return null; + } + + // if this is not an encoded character, return null + if ( first.charValue() != '\'' ) { + input.reset(); + return null; + } + + Character second = input.next(); + if ( second == null ) { + input.reset(); + return null; + } + + // if this is not an encoded character, return null + if ( second.charValue() != '\'' ) { + input.reset(); + return null; + } + return( Character.valueOf( '\'' ) ); + } + + /** + * decodeCharacterMySQL decodes all the potential escaped characters that MySQL is prepared to escape + * + * @param input + * A string you'd like to be decoded + * @return + * A single character from that string, decoded. + */ + private Character decodeCharacterMySQL( PushbackString input ) { + input.mark(); + Character first = input.next(); + if ( first == null ) { + input.reset(); + return null; + } + + // if this is not an encoded character, return null + if ( first.charValue() != '\\' ) { + input.reset(); + return null; + } + + Character second = input.next(); + if ( second == null ) { + input.reset(); + return null; + } + + if ( second.charValue() == '0' ) { + return Character.valueOf( (char)0x00 ); + } else if ( second.charValue() == 'b' ) { + return Character.valueOf( (char)0x08 ); + } else if ( second.charValue() == 't' ) { + return Character.valueOf( (char)0x09 ); + } else if ( second.charValue() == 'n' ) { + return Character.valueOf( (char)0x0a ); + } else if ( second.charValue() == 'r' ) { + return Character.valueOf( (char)0x0d ); + } else if ( second.charValue() == 'z' ) { + return Character.valueOf( (char)0x1a ); + } else if ( second.charValue() == '\"' ) { + return Character.valueOf( (char)0x22 ); + } else if ( second.charValue() == '%' ) { + return Character.valueOf( (char)0x25 ); + } else if ( second.charValue() == '\'' ) { + return Character.valueOf( (char)0x27 ); + } else if ( second.charValue() == '\\' ) { + return Character.valueOf( (char)0x5c ); + } else if ( second.charValue() == '_' ) { + return Character.valueOf( (char)0x5f ); + } else { + return second; + } + } + } \ No newline at end of file From 0911b97f567a804d15c3b537e6d208abc747a364 Mon Sep 17 00:00:00 2001 From: kwwall Date: Mon, 18 Jan 2016 23:33:48 -0500 Subject: [PATCH 0056/1069] Change CRLF to LF for issue 356. --- .../org/owasp/esapi/codecs/OracleCodec.java | 178 +++++++++--------- 1 file changed, 89 insertions(+), 89 deletions(-) diff --git a/src/main/java/org/owasp/esapi/codecs/OracleCodec.java b/src/main/java/org/owasp/esapi/codecs/OracleCodec.java index 953fece04..540920d24 100644 --- a/src/main/java/org/owasp/esapi/codecs/OracleCodec.java +++ b/src/main/java/org/owasp/esapi/codecs/OracleCodec.java @@ -1,90 +1,90 @@ -/** - * OWASP Enterprise Security API (ESAPI) - * - * This file is part of the Open Web Application Security Project (OWASP) - * Enterprise Security API (ESAPI) project. For details, please see - * http://www.owasp.org/index.php/ESAPI. - * - * Copyright (c) 2007 - The OWASP Foundation - * - * The ESAPI is published by OWASP under the BSD license. You should read and accept the - * LICENSE before you use, modify, and/or redistribute this software. - * - * @author Jeff Williams Aspect Security - * @created 2007 - */ -package org.owasp.esapi.codecs; - - - -/** - * Implementation of the Codec interface for Oracle strings. This function will only protect you from SQLi in the case of user data - * bring placed within an Oracle quoted string such as: - * - * select * from table where user_name=' USERDATA '; - * - * @see how-to-escape-single-quotes-in-strings - * - * @author Jeff Williams (jeff.williams .at. aspectsecurity.com) Aspect Security - * @author Jim Manico (jim@manico.net) Manico.net - * @since June 1, 2007 - * @see org.owasp.esapi.Encoder - */ -public class OracleCodec extends Codec { - - - /** - * {@inheritDoc} - * - * Encodes ' to '' - * - * Encodes ' to '' - * - * @param immune - */ - public String encodeCharacter( char[] immune, Character c ) { - if ( c.charValue() == '\'' ) - return "\'\'"; - return ""+c; - } - - - - /** - * {@inheritDoc} - * - * Returns the decoded version of the character starting at index, or - * null if no decoding is possible. - * - * Formats all are legal - * '' decodes to ' - */ - public Character decodeCharacter( PushbackString input ) { - input.mark(); - Character first = input.next(); - if ( first == null ) { - input.reset(); - return null; - } - - // if this is not an encoded character, return null - if ( first.charValue() != '\'' ) { - input.reset(); - return null; - } - - Character second = input.next(); - if ( second == null ) { - input.reset(); - return null; - } - - // if this is not an encoded character, return null - if ( second.charValue() != '\'' ) { - input.reset(); - return null; - } - return( Character.valueOf( '\'' ) ); - } - +/** + * OWASP Enterprise Security API (ESAPI) + * + * This file is part of the Open Web Application Security Project (OWASP) + * Enterprise Security API (ESAPI) project. For details, please see + * http://www.owasp.org/index.php/ESAPI. + * + * Copyright (c) 2007 - The OWASP Foundation + * + * The ESAPI is published by OWASP under the BSD license. You should read and accept the + * LICENSE before you use, modify, and/or redistribute this software. + * + * @author Jeff Williams Aspect Security + * @created 2007 + */ +package org.owasp.esapi.codecs; + + + +/** + * Implementation of the Codec interface for Oracle strings. This function will only protect you from SQLi in the case of user data + * bring placed within an Oracle quoted string such as: + * + * select * from table where user_name=' USERDATA '; + * + * @see how-to-escape-single-quotes-in-strings + * + * @author Jeff Williams (jeff.williams .at. aspectsecurity.com) Aspect Security + * @author Jim Manico (jim@manico.net) Manico.net + * @since June 1, 2007 + * @see org.owasp.esapi.Encoder + */ +public class OracleCodec extends Codec { + + + /** + * {@inheritDoc} + * + * Encodes ' to '' + * + * Encodes ' to '' + * + * @param immune + */ + public String encodeCharacter( char[] immune, Character c ) { + if ( c.charValue() == '\'' ) + return "\'\'"; + return ""+c; + } + + + + /** + * {@inheritDoc} + * + * Returns the decoded version of the character starting at index, or + * null if no decoding is possible. + * + * Formats all are legal + * '' decodes to ' + */ + public Character decodeCharacter( PushbackString input ) { + input.mark(); + Character first = input.next(); + if ( first == null ) { + input.reset(); + return null; + } + + // if this is not an encoded character, return null + if ( first.charValue() != '\'' ) { + input.reset(); + return null; + } + + Character second = input.next(); + if ( second == null ) { + input.reset(); + return null; + } + + // if this is not an encoded character, return null + if ( second.charValue() != '\'' ) { + input.reset(); + return null; + } + return( Character.valueOf( '\'' ) ); + } + } \ No newline at end of file From 6fa2265647a8b56f86f39c55c118234d79c24ac3 Mon Sep 17 00:00:00 2001 From: kwwall Date: Mon, 18 Jan 2016 23:33:48 -0500 Subject: [PATCH 0057/1069] Change CRLF to LF for issue 356. --- .../org/owasp/esapi/codecs/PercentCodec.java | 308 +++++++++--------- 1 file changed, 154 insertions(+), 154 deletions(-) diff --git a/src/main/java/org/owasp/esapi/codecs/PercentCodec.java b/src/main/java/org/owasp/esapi/codecs/PercentCodec.java index 307da471a..3d1e76a86 100644 --- a/src/main/java/org/owasp/esapi/codecs/PercentCodec.java +++ b/src/main/java/org/owasp/esapi/codecs/PercentCodec.java @@ -1,154 +1,154 @@ -/** - * OWASP Enterprise Security API (ESAPI) - * - * This file is part of the Open Web Application Security Project (OWASP) - * Enterprise Security API (ESAPI) project. For details, please see - * http://www.owasp.org/index.php/ESAPI. - * - * Copyright (c) 2007 - The OWASP Foundation - * - * The ESAPI is published by OWASP under the BSD license. You should read and accept the - * LICENSE before you use, modify, and/or redistribute this software. - * - * @author Jeff Williams Aspect Security - * @created 2007 - */ -package org.owasp.esapi.codecs; - -import java.io.UnsupportedEncodingException; -import java.util.Set; - -import org.owasp.esapi.util.CollectionsUtil; - -/** - * Implementation of the Codec interface for percent encoding (aka URL encoding). - * - * @author Jeff Williams (jeff.williams .at. aspectsecurity.com) Aspect Security - * @since June 1, 2007 - * @see org.owasp.esapi.Encoder - */ -public class PercentCodec extends Codec -{ - private static final String ALPHA_NUMERIC_STR = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; - @SuppressWarnings("unused") - private static final String RFC3986_RESERVED_STR = ":/?#[]@!$&'()*+,;="; - private static final String RFC3986_NON_ALPHANUMERIC_UNRESERVED_STR = "-._~"; - // rfc3986 2.3: For consistency, percent-encoded octets - // in the ranges of ALPHA (%41-%5A and %61-%7A), DIGIT - // (%30-%39), hyphen (%2D), period (%2E), underscore - // (%5F), or tilde (%7E) should not be created by URI - // producers - private static final boolean ENCODED_NON_ALPHA_NUMERIC_UNRESERVED = true; - private static final String UNENCODED_STR = ALPHA_NUMERIC_STR + - (ENCODED_NON_ALPHA_NUMERIC_UNRESERVED ? "" : RFC3986_NON_ALPHANUMERIC_UNRESERVED_STR); - private static final Set UNENCODED_SET = CollectionsUtil.strToUnmodifiableSet(UNENCODED_STR); - - /** - * Convinence method to encode a string into UTF-8. This - * wraps the {@link UnsupportedEncodingException} that - * {@link String#getBytes(String)} throws in a - * {@link IllegalStateException} as UTF-8 support is required - * by the Java spec and should never throw this exception. - * @param str the string to encode - * @return str encoded in UTF-8 as bytes. - * @throws IllegalStateException wrapped {@link - * UnsupportedEncodingException} if - * {@link String.getBytes(String)} throws it. - */ - private static byte[] toUtf8Bytes(String str) - { - try - { - return str.getBytes("UTF-8"); - } - catch(UnsupportedEncodingException e) - { - throw new IllegalStateException("The Java spec requires UTF-8 support.", e); - } - } - - /** - * Append the two upper case hex characters for a byte. - * @param sb The string buffer to append to. - * @param b The byte to hexify - * @return sb with the hex characters appended. - */ - // rfc3986 2.1: For consistency, URI producers - // should use uppercase hexadecimal digits for all percent- - // encodings. - private static StringBuilder appendTwoUpperHex(StringBuilder sb, int b) - { - if(b < Byte.MIN_VALUE || b > Byte.MAX_VALUE) - throw new IllegalArgumentException("b is not a byte (was " + b + ')'); - b &= 0xFF; - if(b<0x10) - sb.append('0'); - return sb.append(Integer.toHexString(b).toUpperCase()); - } - - /** - * Encode a character for URLs - * @param immune characters not to encode - * @param c character to encode - * @return the encoded string representing c - */ - public String encodeCharacter( char[] immune, Character c ) - { - String cStr = String.valueOf(c.charValue()); - byte[] bytes; - StringBuilder sb; - - if(UNENCODED_SET.contains(c)) - return cStr; - - bytes = toUtf8Bytes(cStr); - sb = new StringBuilder(bytes.length * 3); - for(byte b : bytes) - appendTwoUpperHex(sb.append('%'), b); - return sb.toString(); - } - - /** - * {@inheritDoc} - * - * Formats all are legal both upper/lower case: - * %hh; - * - * @param input - * encoded character using percent characters (such as URL encoding) - */ - public Character decodeCharacter( PushbackString input ) { - input.mark(); - Character first = input.next(); - if ( first == null ) { - input.reset(); - return null; - } - - // if this is not an encoded character, return null - if (first != '%' ) { - input.reset(); - return null; - } - - // Search for exactly 2 hex digits following - StringBuilder sb = new StringBuilder(); - for ( int i=0; i<2; i++ ) { - Character c = input.nextHex(); - if ( c != null ) sb.append( c ); - } - if ( sb.length() == 2 ) { - try { - // parse the hex digit and create a character - int i = Integer.parseInt(sb.toString(), 16); - if (Character.isValidCodePoint(i)) { - return (char) i; - } - } catch( NumberFormatException ignored ) { } - } - input.reset(); - return null; - } - -} +/** + * OWASP Enterprise Security API (ESAPI) + * + * This file is part of the Open Web Application Security Project (OWASP) + * Enterprise Security API (ESAPI) project. For details, please see + * http://www.owasp.org/index.php/ESAPI. + * + * Copyright (c) 2007 - The OWASP Foundation + * + * The ESAPI is published by OWASP under the BSD license. You should read and accept the + * LICENSE before you use, modify, and/or redistribute this software. + * + * @author Jeff Williams Aspect Security + * @created 2007 + */ +package org.owasp.esapi.codecs; + +import java.io.UnsupportedEncodingException; +import java.util.Set; + +import org.owasp.esapi.util.CollectionsUtil; + +/** + * Implementation of the Codec interface for percent encoding (aka URL encoding). + * + * @author Jeff Williams (jeff.williams .at. aspectsecurity.com) Aspect Security + * @since June 1, 2007 + * @see org.owasp.esapi.Encoder + */ +public class PercentCodec extends Codec +{ + private static final String ALPHA_NUMERIC_STR = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; + @SuppressWarnings("unused") + private static final String RFC3986_RESERVED_STR = ":/?#[]@!$&'()*+,;="; + private static final String RFC3986_NON_ALPHANUMERIC_UNRESERVED_STR = "-._~"; + // rfc3986 2.3: For consistency, percent-encoded octets + // in the ranges of ALPHA (%41-%5A and %61-%7A), DIGIT + // (%30-%39), hyphen (%2D), period (%2E), underscore + // (%5F), or tilde (%7E) should not be created by URI + // producers + private static final boolean ENCODED_NON_ALPHA_NUMERIC_UNRESERVED = true; + private static final String UNENCODED_STR = ALPHA_NUMERIC_STR + + (ENCODED_NON_ALPHA_NUMERIC_UNRESERVED ? "" : RFC3986_NON_ALPHANUMERIC_UNRESERVED_STR); + private static final Set UNENCODED_SET = CollectionsUtil.strToUnmodifiableSet(UNENCODED_STR); + + /** + * Convinence method to encode a string into UTF-8. This + * wraps the {@link UnsupportedEncodingException} that + * {@link String#getBytes(String)} throws in a + * {@link IllegalStateException} as UTF-8 support is required + * by the Java spec and should never throw this exception. + * @param str the string to encode + * @return str encoded in UTF-8 as bytes. + * @throws IllegalStateException wrapped {@link + * UnsupportedEncodingException} if + * {@link String.getBytes(String)} throws it. + */ + private static byte[] toUtf8Bytes(String str) + { + try + { + return str.getBytes("UTF-8"); + } + catch(UnsupportedEncodingException e) + { + throw new IllegalStateException("The Java spec requires UTF-8 support.", e); + } + } + + /** + * Append the two upper case hex characters for a byte. + * @param sb The string buffer to append to. + * @param b The byte to hexify + * @return sb with the hex characters appended. + */ + // rfc3986 2.1: For consistency, URI producers + // should use uppercase hexadecimal digits for all percent- + // encodings. + private static StringBuilder appendTwoUpperHex(StringBuilder sb, int b) + { + if(b < Byte.MIN_VALUE || b > Byte.MAX_VALUE) + throw new IllegalArgumentException("b is not a byte (was " + b + ')'); + b &= 0xFF; + if(b<0x10) + sb.append('0'); + return sb.append(Integer.toHexString(b).toUpperCase()); + } + + /** + * Encode a character for URLs + * @param immune characters not to encode + * @param c character to encode + * @return the encoded string representing c + */ + public String encodeCharacter( char[] immune, Character c ) + { + String cStr = String.valueOf(c.charValue()); + byte[] bytes; + StringBuilder sb; + + if(UNENCODED_SET.contains(c)) + return cStr; + + bytes = toUtf8Bytes(cStr); + sb = new StringBuilder(bytes.length * 3); + for(byte b : bytes) + appendTwoUpperHex(sb.append('%'), b); + return sb.toString(); + } + + /** + * {@inheritDoc} + * + * Formats all are legal both upper/lower case: + * %hh; + * + * @param input + * encoded character using percent characters (such as URL encoding) + */ + public Character decodeCharacter( PushbackString input ) { + input.mark(); + Character first = input.next(); + if ( first == null ) { + input.reset(); + return null; + } + + // if this is not an encoded character, return null + if (first != '%' ) { + input.reset(); + return null; + } + + // Search for exactly 2 hex digits following + StringBuilder sb = new StringBuilder(); + for ( int i=0; i<2; i++ ) { + Character c = input.nextHex(); + if ( c != null ) sb.append( c ); + } + if ( sb.length() == 2 ) { + try { + // parse the hex digit and create a character + int i = Integer.parseInt(sb.toString(), 16); + if (Character.isValidCodePoint(i)) { + return (char) i; + } + } catch( NumberFormatException ignored ) { } + } + input.reset(); + return null; + } + +} From 327964c8b1433a959c534b7507cefdc38973776d Mon Sep 17 00:00:00 2001 From: kwwall Date: Mon, 18 Jan 2016 23:33:48 -0500 Subject: [PATCH 0058/1069] Change CRLF to LF for issue 356. --- .../owasp/esapi/codecs/PushbackString.java | 368 +++++++++--------- 1 file changed, 184 insertions(+), 184 deletions(-) diff --git a/src/main/java/org/owasp/esapi/codecs/PushbackString.java b/src/main/java/org/owasp/esapi/codecs/PushbackString.java index d218eb932..1119b223c 100644 --- a/src/main/java/org/owasp/esapi/codecs/PushbackString.java +++ b/src/main/java/org/owasp/esapi/codecs/PushbackString.java @@ -1,185 +1,185 @@ -/** - * OWASP Enterprise Security API (ESAPI) - * - * This file is part of the Open Web Application Security Project (OWASP) - * Enterprise Security API (ESAPI) project. For details, please see - * http://www.owasp.org/index.php/ESAPI. - * - * Copyright (c) 2007 - The OWASP Foundation - * - * The ESAPI is published by OWASP under the BSD license. You should read and accept the - * LICENSE before you use, modify, and/or redistribute this software. - * - * @author Jeff Williams Aspect Security - * @created 2007 - */ -package org.owasp.esapi.codecs; - - -/** - * The pushback string is used by Codecs to allow them to push decoded characters back onto a string - * for further decoding. This is necessary to detect double-encoding. - * - * @author Jeff Williams (jeff.williams .at. aspectsecurity.com) Aspect Security - * @since June 1, 2007 - * @see org.owasp.esapi.Encoder - */ -public class PushbackString { - - private String input; - private Character pushback; - private Character temp; - private int index = 0; - private int mark = 0; - - /** - * - * @param input - */ - public PushbackString( String input ) { - this.input = input; - } - - /** - * - * @param c - */ - public void pushback( Character c ) { - pushback = c; - } - - - /** - * Get the current index of the PushbackString. Typically used in error messages. - * @return The current index of the PushbackString. - */ - public int index() { - return index; - } - - /** - * - * @return - */ - public boolean hasNext() { - if ( pushback != null ) return true; - if ( input == null ) return false; - if ( input.length() == 0 ) return false; - if ( index >= input.length() ) return false; - return true; - } - - /** - * - * @return - */ - public Character next() { - if ( pushback != null ) { - Character save = pushback; - pushback = null; - return save; - } - if ( input == null ) return null; - if ( input.length() == 0 ) return null; - if ( index >= input.length() ) return null; - return Character.valueOf( input.charAt(index++) ); - } - - /** - * - * @return - */ - public Character nextHex() { - Character c = next(); - if ( c == null ) return null; - if ( isHexDigit( c ) ) return c; - return null; - } - - /** - * - * @return - */ - public Character nextOctal() { - Character c = next(); - if ( c == null ) return null; - if ( isOctalDigit( c ) ) return c; - return null; - } - - /** - * Returns true if the parameter character is a hexidecimal digit 0 through 9, a through f, or A through F. - * @param c - * @return - */ - public static boolean isHexDigit( Character c ) { - if ( c == null ) return false; - char ch = c.charValue(); - return (ch >= '0' && ch <= '9' ) || (ch >= 'a' && ch <= 'f' ) || (ch >= 'A' && ch <= 'F' ); - } - - /** - * Returns true if the parameter character is an octal digit 0 through 7. - * @param c - * @return - */ -public static boolean isOctalDigit( Character c ) { - if ( c == null ) return false; - char ch = c.charValue(); - return ch >= '0' && ch <= '7'; -} - - /** - * Return the next character without affecting the current index. - * @return - */ - public Character peek() { - if ( pushback != null ) return pushback; - if ( input == null ) return null; - if ( input.length() == 0 ) return null; - if ( index >= input.length() ) return null; - return Character.valueOf( input.charAt(index) ); - } - - /** - * Test to see if the next character is a particular value without affecting the current index. - * @param c - * @return - */ - public boolean peek( char c ) { - if ( pushback != null && pushback.charValue() == c ) return true; - if ( input == null ) return false; - if ( input.length() == 0 ) return false; - if ( index >= input.length() ) return false; - return input.charAt(index) == c; - } - - /** - * - */ - public void mark() { - temp = pushback; - mark = index; - } - - /** - * - */ - public void reset() { - pushback = temp; - index = mark; - } - - /** - * - * @return - */ - protected String remainder() { - String output = input.substring( index ); - if ( pushback != null ) { - output = pushback + output; - } - return output; - } +/** + * OWASP Enterprise Security API (ESAPI) + * + * This file is part of the Open Web Application Security Project (OWASP) + * Enterprise Security API (ESAPI) project. For details, please see + * http://www.owasp.org/index.php/ESAPI. + * + * Copyright (c) 2007 - The OWASP Foundation + * + * The ESAPI is published by OWASP under the BSD license. You should read and accept the + * LICENSE before you use, modify, and/or redistribute this software. + * + * @author Jeff Williams Aspect Security + * @created 2007 + */ +package org.owasp.esapi.codecs; + + +/** + * The pushback string is used by Codecs to allow them to push decoded characters back onto a string + * for further decoding. This is necessary to detect double-encoding. + * + * @author Jeff Williams (jeff.williams .at. aspectsecurity.com) Aspect Security + * @since June 1, 2007 + * @see org.owasp.esapi.Encoder + */ +public class PushbackString { + + private String input; + private Character pushback; + private Character temp; + private int index = 0; + private int mark = 0; + + /** + * + * @param input + */ + public PushbackString( String input ) { + this.input = input; + } + + /** + * + * @param c + */ + public void pushback( Character c ) { + pushback = c; + } + + + /** + * Get the current index of the PushbackString. Typically used in error messages. + * @return The current index of the PushbackString. + */ + public int index() { + return index; + } + + /** + * + * @return + */ + public boolean hasNext() { + if ( pushback != null ) return true; + if ( input == null ) return false; + if ( input.length() == 0 ) return false; + if ( index >= input.length() ) return false; + return true; + } + + /** + * + * @return + */ + public Character next() { + if ( pushback != null ) { + Character save = pushback; + pushback = null; + return save; + } + if ( input == null ) return null; + if ( input.length() == 0 ) return null; + if ( index >= input.length() ) return null; + return Character.valueOf( input.charAt(index++) ); + } + + /** + * + * @return + */ + public Character nextHex() { + Character c = next(); + if ( c == null ) return null; + if ( isHexDigit( c ) ) return c; + return null; + } + + /** + * + * @return + */ + public Character nextOctal() { + Character c = next(); + if ( c == null ) return null; + if ( isOctalDigit( c ) ) return c; + return null; + } + + /** + * Returns true if the parameter character is a hexidecimal digit 0 through 9, a through f, or A through F. + * @param c + * @return + */ + public static boolean isHexDigit( Character c ) { + if ( c == null ) return false; + char ch = c.charValue(); + return (ch >= '0' && ch <= '9' ) || (ch >= 'a' && ch <= 'f' ) || (ch >= 'A' && ch <= 'F' ); + } + + /** + * Returns true if the parameter character is an octal digit 0 through 7. + * @param c + * @return + */ +public static boolean isOctalDigit( Character c ) { + if ( c == null ) return false; + char ch = c.charValue(); + return ch >= '0' && ch <= '7'; +} + + /** + * Return the next character without affecting the current index. + * @return + */ + public Character peek() { + if ( pushback != null ) return pushback; + if ( input == null ) return null; + if ( input.length() == 0 ) return null; + if ( index >= input.length() ) return null; + return Character.valueOf( input.charAt(index) ); + } + + /** + * Test to see if the next character is a particular value without affecting the current index. + * @param c + * @return + */ + public boolean peek( char c ) { + if ( pushback != null && pushback.charValue() == c ) return true; + if ( input == null ) return false; + if ( input.length() == 0 ) return false; + if ( index >= input.length() ) return false; + return input.charAt(index) == c; + } + + /** + * + */ + public void mark() { + temp = pushback; + mark = index; + } + + /** + * + */ + public void reset() { + pushback = temp; + index = mark; + } + + /** + * + * @return + */ + protected String remainder() { + String output = input.substring( index ); + if ( pushback != null ) { + output = pushback + output; + } + return output; + } } \ No newline at end of file From a1ebee77067751fa133ef764f4f7147749c4fb69 Mon Sep 17 00:00:00 2001 From: kwwall Date: Mon, 18 Jan 2016 23:33:48 -0500 Subject: [PATCH 0059/1069] Change CRLF to LF for issue 356. --- .../org/owasp/esapi/codecs/UnixCodec.java | 162 +++++++++--------- 1 file changed, 81 insertions(+), 81 deletions(-) diff --git a/src/main/java/org/owasp/esapi/codecs/UnixCodec.java b/src/main/java/org/owasp/esapi/codecs/UnixCodec.java index 19ecc80ca..489cf073b 100644 --- a/src/main/java/org/owasp/esapi/codecs/UnixCodec.java +++ b/src/main/java/org/owasp/esapi/codecs/UnixCodec.java @@ -1,82 +1,82 @@ -/** - * OWASP Enterprise Security API (ESAPI) - * - * This file is part of the Open Web Application Security Project (OWASP) - * Enterprise Security API (ESAPI) project. For details, please see - * http://www.owasp.org/index.php/ESAPI. - * - * Copyright (c) 2007 - The OWASP Foundation - * - * The ESAPI is published by OWASP under the BSD license. You should read and accept the - * LICENSE before you use, modify, and/or redistribute this software. - * - * @author Jeff Williams Aspect Security - * @created 2007 - */ -package org.owasp.esapi.codecs; - - -/** - * Implementation of the Codec interface for '\' encoding from Unix command shell. - * - * @author Jeff Williams (jeff.williams .at. aspectsecurity.com) Aspect Security - * @since June 1, 2007 - * @see org.owasp.esapi.Encoder - */ -public class UnixCodec extends Codec { - - /** - * {@inheritDoc} - * - * Returns backslash-encoded character - * - * @param immune - */ - public String encodeCharacter( char[] immune, Character c ) { - char ch = c.charValue(); - - // check for immune characters - if ( containsCharacter( ch, immune ) ) { - return ""+ch; - } - - // check for alphanumeric characters - String hex = Codec.getHexForNonAlphanumeric( ch ); - if ( hex == null ) { - return ""+ch; - } - - return "\\" + c; - } - - - /** - * {@inheritDoc} - * - * Returns the decoded version of the character starting at index, or - * null if no decoding is possible. - *

    - * Formats all are legal both upper/lower case: - * \x - all special characters - * - */ - public Character decodeCharacter( PushbackString input ) { - input.mark(); - Character first = input.next(); - if ( first == null ) { - input.reset(); - return null; - } - - // if this is not an encoded character, return null - if ( first.charValue() != '\\' ) { - input.reset(); - return null; - } - - Character second = input.next(); - return second; - } - +/** + * OWASP Enterprise Security API (ESAPI) + * + * This file is part of the Open Web Application Security Project (OWASP) + * Enterprise Security API (ESAPI) project. For details, please see + * http://www.owasp.org/index.php/ESAPI. + * + * Copyright (c) 2007 - The OWASP Foundation + * + * The ESAPI is published by OWASP under the BSD license. You should read and accept the + * LICENSE before you use, modify, and/or redistribute this software. + * + * @author Jeff Williams Aspect Security + * @created 2007 + */ +package org.owasp.esapi.codecs; + + +/** + * Implementation of the Codec interface for '\' encoding from Unix command shell. + * + * @author Jeff Williams (jeff.williams .at. aspectsecurity.com) Aspect Security + * @since June 1, 2007 + * @see org.owasp.esapi.Encoder + */ +public class UnixCodec extends Codec { + + /** + * {@inheritDoc} + * + * Returns backslash-encoded character + * + * @param immune + */ + public String encodeCharacter( char[] immune, Character c ) { + char ch = c.charValue(); + + // check for immune characters + if ( containsCharacter( ch, immune ) ) { + return ""+ch; + } + + // check for alphanumeric characters + String hex = Codec.getHexForNonAlphanumeric( ch ); + if ( hex == null ) { + return ""+ch; + } + + return "\\" + c; + } + + + /** + * {@inheritDoc} + * + * Returns the decoded version of the character starting at index, or + * null if no decoding is possible. + *

    + * Formats all are legal both upper/lower case: + * \x - all special characters + * + */ + public Character decodeCharacter( PushbackString input ) { + input.mark(); + Character first = input.next(); + if ( first == null ) { + input.reset(); + return null; + } + + // if this is not an encoded character, return null + if ( first.charValue() != '\\' ) { + input.reset(); + return null; + } + + Character second = input.next(); + return second; + } + } \ No newline at end of file From 2ee8a833875a63a7c8d4f745ab9c32e16e551bc3 Mon Sep 17 00:00:00 2001 From: kwwall Date: Mon, 18 Jan 2016 23:33:48 -0500 Subject: [PATCH 0060/1069] Change CRLF to LF for issue 356. --- .../org/owasp/esapi/codecs/VBScriptCodec.java | 232 +++++++++--------- 1 file changed, 116 insertions(+), 116 deletions(-) diff --git a/src/main/java/org/owasp/esapi/codecs/VBScriptCodec.java b/src/main/java/org/owasp/esapi/codecs/VBScriptCodec.java index 2dccbbd81..31442127a 100644 --- a/src/main/java/org/owasp/esapi/codecs/VBScriptCodec.java +++ b/src/main/java/org/owasp/esapi/codecs/VBScriptCodec.java @@ -1,117 +1,117 @@ -/** - * OWASP Enterprise Security API (ESAPI) - * - * This file is part of the Open Web Application Security Project (OWASP) - * Enterprise Security API (ESAPI) project. For details, please see - * http://www.owasp.org/index.php/ESAPI. - * - * Copyright (c) 2007 - The OWASP Foundation - * - * The ESAPI is published by OWASP under the BSD license. You should read and accept the - * LICENSE before you use, modify, and/or redistribute this software. - * - * @author Jeff Williams Aspect Security - * @created 2007 - */ -package org.owasp.esapi.codecs; - -import org.owasp.esapi.EncoderConstants; - - -/** - * Implementation of the Codec interface for 'quote' encoding from VBScript. - * - * @author Jeff Williams (jeff.williams .at. aspectsecurity.com) Aspect Security - * @since June 1, 2007 - * @see org.owasp.esapi.Encoder - */ -public class VBScriptCodec extends Codec { - - /** - * Encode a String so that it can be safely used in a specific context. - * - * @param immune - * @param input - * the String to encode - * @return the encoded String - */ - public String encode(char[] immune, String input) { - StringBuilder sb = new StringBuilder(); - boolean encoding = false; - boolean inquotes = false; - for ( int i=0; i 0 ) sb.append( "&" ); - if ( !inquotes && i > 0 ) sb.append( "\"" ); - sb.append( c ); - inquotes = true; - encoding = false; - - // handle characters that need encoding - } else { - if ( inquotes && i < input.length() ) sb.append( "\"" ); - if ( i > 0 ) sb.append( "&" ); - sb.append( encodeCharacter( immune, Character.valueOf( c ) ) ); - inquotes = false; - encoding = true; - } - } - return sb.toString(); - } - - - /** - * Returns quote-encoded character - * - * @param immune - */ - public String encodeCharacter( char[] immune, Character c ) { - char ch = c.charValue(); - - // check for immune characters - if ( containsCharacter( ch, immune ) ) { - return ""+ch; - } - - // check for alphanumeric characters - String hex = Codec.getHexForNonAlphanumeric( ch ); - if ( hex == null ) { - return ""+ch; - } - - return "chrw(" + (int)c.charValue() + ")"; - } - - - - /** - * Returns the decoded version of the character starting at index, or - * null if no decoding is possible. - * - * Formats all are legal both upper/lower case: - * "x - all special characters - * " + chr(x) + " - not supported yet - */ - public Character decodeCharacter( PushbackString input ) { - input.mark(); - Character first = input.next(); - if ( first == null ) { - input.reset(); - return null; - } - - // if this is not an encoded character, return null - if ( first.charValue() != '\"' ) { - input.reset(); - return null; - } - - Character second = input.next(); - return second; - } - +/** + * OWASP Enterprise Security API (ESAPI) + * + * This file is part of the Open Web Application Security Project (OWASP) + * Enterprise Security API (ESAPI) project. For details, please see + * http://www.owasp.org/index.php/ESAPI. + * + * Copyright (c) 2007 - The OWASP Foundation + * + * The ESAPI is published by OWASP under the BSD license. You should read and accept the + * LICENSE before you use, modify, and/or redistribute this software. + * + * @author Jeff Williams Aspect Security + * @created 2007 + */ +package org.owasp.esapi.codecs; + +import org.owasp.esapi.EncoderConstants; + + +/** + * Implementation of the Codec interface for 'quote' encoding from VBScript. + * + * @author Jeff Williams (jeff.williams .at. aspectsecurity.com) Aspect Security + * @since June 1, 2007 + * @see org.owasp.esapi.Encoder + */ +public class VBScriptCodec extends Codec { + + /** + * Encode a String so that it can be safely used in a specific context. + * + * @param immune + * @param input + * the String to encode + * @return the encoded String + */ + public String encode(char[] immune, String input) { + StringBuilder sb = new StringBuilder(); + boolean encoding = false; + boolean inquotes = false; + for ( int i=0; i 0 ) sb.append( "&" ); + if ( !inquotes && i > 0 ) sb.append( "\"" ); + sb.append( c ); + inquotes = true; + encoding = false; + + // handle characters that need encoding + } else { + if ( inquotes && i < input.length() ) sb.append( "\"" ); + if ( i > 0 ) sb.append( "&" ); + sb.append( encodeCharacter( immune, Character.valueOf( c ) ) ); + inquotes = false; + encoding = true; + } + } + return sb.toString(); + } + + + /** + * Returns quote-encoded character + * + * @param immune + */ + public String encodeCharacter( char[] immune, Character c ) { + char ch = c.charValue(); + + // check for immune characters + if ( containsCharacter( ch, immune ) ) { + return ""+ch; + } + + // check for alphanumeric characters + String hex = Codec.getHexForNonAlphanumeric( ch ); + if ( hex == null ) { + return ""+ch; + } + + return "chrw(" + (int)c.charValue() + ")"; + } + + + + /** + * Returns the decoded version of the character starting at index, or + * null if no decoding is possible. + * + * Formats all are legal both upper/lower case: + * "x - all special characters + * " + chr(x) + " - not supported yet + */ + public Character decodeCharacter( PushbackString input ) { + input.mark(); + Character first = input.next(); + if ( first == null ) { + input.reset(); + return null; + } + + // if this is not an encoded character, return null + if ( first.charValue() != '\"' ) { + input.reset(); + return null; + } + + Character second = input.next(); + return second; + } + } \ No newline at end of file From a2fa7eed4e429600b47444a3702cc700e8cd0233 Mon Sep 17 00:00:00 2001 From: kwwall Date: Mon, 18 Jan 2016 23:33:48 -0500 Subject: [PATCH 0061/1069] Change CRLF to LF for issue 356. --- .../org/owasp/esapi/codecs/WindowsCodec.java | 162 +++++++++--------- 1 file changed, 81 insertions(+), 81 deletions(-) diff --git a/src/main/java/org/owasp/esapi/codecs/WindowsCodec.java b/src/main/java/org/owasp/esapi/codecs/WindowsCodec.java index 719f00907..f7abb3a4b 100644 --- a/src/main/java/org/owasp/esapi/codecs/WindowsCodec.java +++ b/src/main/java/org/owasp/esapi/codecs/WindowsCodec.java @@ -1,82 +1,82 @@ -/** - * OWASP Enterprise Security API (ESAPI) - * - * This file is part of the Open Web Application Security Project (OWASP) - * Enterprise Security API (ESAPI) project. For details, please see - * http://www.owasp.org/index.php/ESAPI. - * - * Copyright (c) 2007 - The OWASP Foundation - * - * The ESAPI is published by OWASP under the BSD license. You should read and accept the - * LICENSE before you use, modify, and/or redistribute this software. - * - * @author Jeff Williams Aspect Security - * @created 2007 - */ -package org.owasp.esapi.codecs; - - -/** - * Implementation of the Codec interface for '^' encoding from Windows command shell. - * - * @author Jeff Williams (jeff.williams .at. aspectsecurity.com) Aspect Security - * @since June 1, 2007 - * @see org.owasp.esapi.Encoder - */ -public class WindowsCodec extends Codec { - - - /** - * {@inheritDoc} - * - * Returns Windows shell encoded character (which is ^) - * - * @param immune - */ - public String encodeCharacter( char[] immune, Character c ) { - char ch = c.charValue(); - - // check for immune characters - if ( containsCharacter( ch, immune ) ) { - return ""+ch; - } - - // check for alphanumeric characters - String hex = Codec.getHexForNonAlphanumeric( ch ); - if ( hex == null ) { - return ""+ch; - } - - return "^" + c; - } - - - /** - * {@inheritDoc} - * - * Returns the decoded version of the character starting at index, or - * null if no decoding is possible. - *

    - * Formats all are legal both upper/lower case: - * ^x - all special characters - */ - public Character decodeCharacter( PushbackString input ) { - input.mark(); - Character first = input.next(); - if ( first == null ) { - input.reset(); - return null; - } - - // if this is not an encoded character, return null - if ( first.charValue() != '^' ) { - input.reset(); - return null; - } - - Character second = input.next(); - return second; - } - +/** + * OWASP Enterprise Security API (ESAPI) + * + * This file is part of the Open Web Application Security Project (OWASP) + * Enterprise Security API (ESAPI) project. For details, please see + * http://www.owasp.org/index.php/ESAPI. + * + * Copyright (c) 2007 - The OWASP Foundation + * + * The ESAPI is published by OWASP under the BSD license. You should read and accept the + * LICENSE before you use, modify, and/or redistribute this software. + * + * @author Jeff Williams Aspect Security + * @created 2007 + */ +package org.owasp.esapi.codecs; + + +/** + * Implementation of the Codec interface for '^' encoding from Windows command shell. + * + * @author Jeff Williams (jeff.williams .at. aspectsecurity.com) Aspect Security + * @since June 1, 2007 + * @see org.owasp.esapi.Encoder + */ +public class WindowsCodec extends Codec { + + + /** + * {@inheritDoc} + * + * Returns Windows shell encoded character (which is ^) + * + * @param immune + */ + public String encodeCharacter( char[] immune, Character c ) { + char ch = c.charValue(); + + // check for immune characters + if ( containsCharacter( ch, immune ) ) { + return ""+ch; + } + + // check for alphanumeric characters + String hex = Codec.getHexForNonAlphanumeric( ch ); + if ( hex == null ) { + return ""+ch; + } + + return "^" + c; + } + + + /** + * {@inheritDoc} + * + * Returns the decoded version of the character starting at index, or + * null if no decoding is possible. + *

    + * Formats all are legal both upper/lower case: + * ^x - all special characters + */ + public Character decodeCharacter( PushbackString input ) { + input.mark(); + Character first = input.next(); + if ( first == null ) { + input.reset(); + return null; + } + + // if this is not an encoded character, return null + if ( first.charValue() != '^' ) { + input.reset(); + return null; + } + + Character second = input.next(); + return second; + } + } \ No newline at end of file From 910c6e83640b122ee7652b97f9fcd4a3ba5061e4 Mon Sep 17 00:00:00 2001 From: kwwall Date: Mon, 18 Jan 2016 23:33:48 -0500 Subject: [PATCH 0062/1069] Change CRLF to LF for issue 356. --- .../org/owasp/esapi/EncoderConstants.java | 234 +++++++++--------- 1 file changed, 117 insertions(+), 117 deletions(-) diff --git a/src/main/java/org/owasp/esapi/EncoderConstants.java b/src/main/java/org/owasp/esapi/EncoderConstants.java index d1480276d..1498d5ea7 100644 --- a/src/main/java/org/owasp/esapi/EncoderConstants.java +++ b/src/main/java/org/owasp/esapi/EncoderConstants.java @@ -1,117 +1,117 @@ -package org.owasp.esapi; - -import java.util.Set; - -import org.owasp.esapi.util.CollectionsUtil; - -/** - * Common character classes used for input validation, output encoding, verifying password strength - * CSRF token generation, generating salts, etc - * @author Neil Matatall (neil.matatall .at. gmail.com) - * @see User - */ -public class EncoderConstants { - /** - * !$*-.=?@_ - */ - public final static char[] CHAR_PASSWORD_SPECIALS = { '!', '$', '*', '-', '.', '=', '?', '@', '_' }; - public final static Set PASSWORD_SPECIALS; - static { - PASSWORD_SPECIALS = CollectionsUtil.arrayToSet(CHAR_PASSWORD_SPECIALS); - } - - /** - * a-b - */ - public final static char[] CHAR_LOWERS = { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z' }; - public final static Set LOWERS; - static { - LOWERS = CollectionsUtil.arrayToSet(CHAR_PASSWORD_SPECIALS); - } - - /** - * A-Z - */ - public final static char[] CHAR_UPPERS = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z' }; - public final static Set UPPERS; - static { - UPPERS = CollectionsUtil.arrayToSet(CHAR_UPPERS); - } - /** - * 0-9 - */ - public final static char[] CHAR_DIGITS = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' }; - public final static Set DIGITS; - static { - DIGITS = CollectionsUtil.arrayToSet(CHAR_DIGITS); - } - - /** - * !$*+-.=?@^_|~ - */ - public final static char[] CHAR_SPECIALS = { '!', '$', '*', '+', '-', '.', '=', '?', '@', '^', '_', '|', '~' }; - public final static Set SPECIALS; - static { - SPECIALS = CollectionsUtil.arrayToSet(CHAR_SPECIALS); - } - - /** - * CHAR_LOWERS union CHAR_UPPERS - */ - public final static char[] CHAR_LETTERS = StringUtilities.union(CHAR_LOWERS, CHAR_UPPERS); - public final static Set LETTERS; - static { - LETTERS = CollectionsUtil.arrayToSet(CHAR_LETTERS); - } - - /** - * CHAR_LETTERS union CHAR_DIGITS - */ - public final static char[] CHAR_ALPHANUMERICS = StringUtilities.union(CHAR_LETTERS, CHAR_DIGITS); - public final static Set ALPHANUMERICS; - static { - ALPHANUMERICS = CollectionsUtil.arrayToSet(CHAR_ALPHANUMERICS); - } - - /** - * Password character set, is alphanumerics (without l, i, I, o, O, and 0) - * selected specials like + (bad for URL encoding, | is like i and 1, - * etc...) - */ - public final static char[] CHAR_PASSWORD_LOWERS = { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'j', 'k', 'm', 'n', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z' }; - public final static Set PASSWORD_LOWERS; - static { - PASSWORD_LOWERS = CollectionsUtil.arrayToSet(CHAR_ALPHANUMERICS); - } - - /** - * - */ - public final static char[] CHAR_PASSWORD_UPPERS = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'J', 'K', 'L', 'M', 'N', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z' }; - public final static Set PASSWORD_UPPERS; - static { - PASSWORD_UPPERS = CollectionsUtil.arrayToSet(CHAR_PASSWORD_UPPERS); - } - - /** - * 2-9 - */ - public final static char[] CHAR_PASSWORD_DIGITS = { '2', '3', '4', '5', '6', '7', '8', '9' }; - public final static Set PASSWORD_DIGITS; - static { - PASSWORD_DIGITS = CollectionsUtil.arrayToSet(CHAR_PASSWORD_DIGITS); - } - - /** - * CHAR_PASSWORD_LOWERS union CHAR_PASSWORD_UPPERS - */ - public final static char[] CHAR_PASSWORD_LETTERS = StringUtilities.union( CHAR_PASSWORD_LOWERS, CHAR_PASSWORD_UPPERS ); - public final static Set PASSWORD_LETTERS; - static { - PASSWORD_LETTERS = CollectionsUtil.arrayToSet(CHAR_PASSWORD_LETTERS); - } - - private EncoderConstants() { - // prevent instantiation - } -} +package org.owasp.esapi; + +import java.util.Set; + +import org.owasp.esapi.util.CollectionsUtil; + +/** + * Common character classes used for input validation, output encoding, verifying password strength + * CSRF token generation, generating salts, etc + * @author Neil Matatall (neil.matatall .at. gmail.com) + * @see User + */ +public class EncoderConstants { + /** + * !$*-.=?@_ + */ + public final static char[] CHAR_PASSWORD_SPECIALS = { '!', '$', '*', '-', '.', '=', '?', '@', '_' }; + public final static Set PASSWORD_SPECIALS; + static { + PASSWORD_SPECIALS = CollectionsUtil.arrayToSet(CHAR_PASSWORD_SPECIALS); + } + + /** + * a-b + */ + public final static char[] CHAR_LOWERS = { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z' }; + public final static Set LOWERS; + static { + LOWERS = CollectionsUtil.arrayToSet(CHAR_PASSWORD_SPECIALS); + } + + /** + * A-Z + */ + public final static char[] CHAR_UPPERS = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z' }; + public final static Set UPPERS; + static { + UPPERS = CollectionsUtil.arrayToSet(CHAR_UPPERS); + } + /** + * 0-9 + */ + public final static char[] CHAR_DIGITS = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' }; + public final static Set DIGITS; + static { + DIGITS = CollectionsUtil.arrayToSet(CHAR_DIGITS); + } + + /** + * !$*+-.=?@^_|~ + */ + public final static char[] CHAR_SPECIALS = { '!', '$', '*', '+', '-', '.', '=', '?', '@', '^', '_', '|', '~' }; + public final static Set SPECIALS; + static { + SPECIALS = CollectionsUtil.arrayToSet(CHAR_SPECIALS); + } + + /** + * CHAR_LOWERS union CHAR_UPPERS + */ + public final static char[] CHAR_LETTERS = StringUtilities.union(CHAR_LOWERS, CHAR_UPPERS); + public final static Set LETTERS; + static { + LETTERS = CollectionsUtil.arrayToSet(CHAR_LETTERS); + } + + /** + * CHAR_LETTERS union CHAR_DIGITS + */ + public final static char[] CHAR_ALPHANUMERICS = StringUtilities.union(CHAR_LETTERS, CHAR_DIGITS); + public final static Set ALPHANUMERICS; + static { + ALPHANUMERICS = CollectionsUtil.arrayToSet(CHAR_ALPHANUMERICS); + } + + /** + * Password character set, is alphanumerics (without l, i, I, o, O, and 0) + * selected specials like + (bad for URL encoding, | is like i and 1, + * etc...) + */ + public final static char[] CHAR_PASSWORD_LOWERS = { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'j', 'k', 'm', 'n', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z' }; + public final static Set PASSWORD_LOWERS; + static { + PASSWORD_LOWERS = CollectionsUtil.arrayToSet(CHAR_ALPHANUMERICS); + } + + /** + * + */ + public final static char[] CHAR_PASSWORD_UPPERS = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'J', 'K', 'L', 'M', 'N', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z' }; + public final static Set PASSWORD_UPPERS; + static { + PASSWORD_UPPERS = CollectionsUtil.arrayToSet(CHAR_PASSWORD_UPPERS); + } + + /** + * 2-9 + */ + public final static char[] CHAR_PASSWORD_DIGITS = { '2', '3', '4', '5', '6', '7', '8', '9' }; + public final static Set PASSWORD_DIGITS; + static { + PASSWORD_DIGITS = CollectionsUtil.arrayToSet(CHAR_PASSWORD_DIGITS); + } + + /** + * CHAR_PASSWORD_LOWERS union CHAR_PASSWORD_UPPERS + */ + public final static char[] CHAR_PASSWORD_LETTERS = StringUtilities.union( CHAR_PASSWORD_LOWERS, CHAR_PASSWORD_UPPERS ); + public final static Set PASSWORD_LETTERS; + static { + PASSWORD_LETTERS = CollectionsUtil.arrayToSet(CHAR_PASSWORD_LETTERS); + } + + private EncoderConstants() { + // prevent instantiation + } +} From 464d4bad42378ea0808677ce591ccb6345f40af4 Mon Sep 17 00:00:00 2001 From: kwwall Date: Mon, 18 Jan 2016 23:33:48 -0500 Subject: [PATCH 0063/1069] Change CRLF to LF for issue 356. --- src/main/java/org/owasp/esapi/Encoder.java | 1032 ++++++++++---------- 1 file changed, 516 insertions(+), 516 deletions(-) diff --git a/src/main/java/org/owasp/esapi/Encoder.java b/src/main/java/org/owasp/esapi/Encoder.java index 3bce42fc4..2d2bfb7b9 100644 --- a/src/main/java/org/owasp/esapi/Encoder.java +++ b/src/main/java/org/owasp/esapi/Encoder.java @@ -1,516 +1,516 @@ -/** - * OWASP Enterprise Security API (ESAPI) - * - * This file is part of the Open Web Application Security Project (OWASP) - * Enterprise Security API (ESAPI) project. For details, please see - * http://www.owasp.org/index.php/ESAPI. - * - * Copyright (c) 2007 - The OWASP Foundation - * - * The ESAPI is published by OWASP under the BSD license. You should read and accept the - * LICENSE before you use, modify, and/or redistribute this software. - * - * @author Jeff Williams Aspect Security - * @created 2007 - */ -package org.owasp.esapi; - -import java.io.IOException; - -import org.owasp.esapi.codecs.Codec; -import org.owasp.esapi.errors.EncodingException; - - -/** - * The Encoder interface contains a number of methods for decoding input and encoding output - * so that it will be safe for a variety of interpreters. To prevent - * double-encoding, callers should make sure input does not already contain encoded characters - * by calling canonicalize. Validator implementations should call canonicalize on user input - * before validating to prevent encoded attacks. - *

    - * All of the methods must use a "whitelist" or "positive" security model. - * For the encoding methods, this means that all characters should be encoded, except for a specific list of - * "immune" characters that are known to be safe. - *

    - * The Encoder performs two key functions, encoding and decoding. These functions rely - * on a set of codecs that can be found in the org.owasp.esapi.codecs package. These include: - *

    • CSS Escaping
    • - *
    • HTMLEntity Encoding
    • - *
    • JavaScript Escaping
    • - *
    • MySQL Escaping
    • - *
    • Oracle Escaping
    • - *
    • Percent Encoding (aka URL Encoding)
    • - *
    • Unix Escaping
    • - *
    • VBScript Escaping
    • - *
    • Windows Encoding
    - *

    - * - * @author Jeff Williams (jeff.williams .at. aspectsecurity.com) Aspect Security - * @since June 1, 2007 - */ -public interface Encoder { - - /** - * Standard character sets - */ - - /** - * @deprecated Use {@link EncoderConstants#CHAR_LOWERS} instead - */ - @Deprecated - public final static char[] CHAR_LOWERS = EncoderConstants.CHAR_LOWERS; - /** - * @deprecated Use {@link EncoderConstants#CHAR_UPPERS} instead - * - */ - @Deprecated - public final static char[] CHAR_UPPERS = EncoderConstants.CHAR_UPPERS; - /** - * @deprecated Use {@link EncoderConstants#CHAR_DIGITS} instead - * - */ - @Deprecated - public final static char[] CHAR_DIGITS = EncoderConstants.CHAR_DIGITS; - /** - * @deprecated Use {@link EncoderConstants#CHAR_SPECIALS} instead - * - */ - @Deprecated - public final static char[] CHAR_SPECIALS = EncoderConstants.CHAR_SPECIALS; - /** - * @deprecated Use {@link EncoderConstants#CHAR_LETTERS} instead - * - */ - @Deprecated - public final static char[] CHAR_LETTERS = EncoderConstants.CHAR_LETTERS; - /** - * @deprecated Use {@link EncoderConstants#CHAR_ALPHANUMERICS} instead - * - */ - @Deprecated - public final static char[] CHAR_ALPHANUMERICS = EncoderConstants.CHAR_ALPHANUMERICS; - - - /** - * Password character set, is alphanumerics (without l, i, I, o, O, and 0) - * selected specials like + (bad for URL encoding, | is like i and 1, - * etc...) - * @deprecated Use {@link EncoderConstants#CHAR_PASSWORD_LOWERS} instead - */ - @Deprecated - public final static char[] CHAR_PASSWORD_LOWERS = EncoderConstants.CHAR_PASSWORD_LOWERS; - /** - * @deprecated Use {@link EncoderConstants#CHAR_PASSWORD_UPPERS} instead - * - */ - @Deprecated - public final static char[] CHAR_PASSWORD_UPPERS = EncoderConstants.CHAR_PASSWORD_UPPERS; - /** - * @deprecated Use {@link EncoderConstants#CHAR_PASSWORD_DIGITS} instead - * - */ - @Deprecated - public final static char[] CHAR_PASSWORD_DIGITS = EncoderConstants.CHAR_PASSWORD_DIGITS; - /** - * @deprecated Use {@link EncoderConstants#CHAR_PASSWORD_SPECIALS} instead - * - */ - @Deprecated - public final static char[] CHAR_PASSWORD_SPECIALS = EncoderConstants.CHAR_PASSWORD_SPECIALS; - /** - * @deprecated Use {@link EncoderConstants#CHAR_PASSWORD_LETTERS} instead - * - */ - @Deprecated - public final static char[] CHAR_PASSWORD_LETTERS = EncoderConstants.CHAR_PASSWORD_LETTERS; - - - - /** - * This method is equivalent to calling

    Encoder.canonicalize(input, restrictMultiple, restrictMixed);
    - * - * The default values for restrictMultiple and restrictMixed come from ESAPI.properties - *
    -	 * Encoder.AllowMultipleEncoding=false
    -	 * Encoder.AllowMixedEncoding=false
    -	 * 
    - * - * @see Encoder#canonicalize(String, boolean, boolean) canonicalize - * @see W3C specifications - * - * @param input the text to canonicalize - * @return a String containing the canonicalized text - */ - String canonicalize(String input); - - /** - * This method is the equivalent to calling
    Encoder.canonicalize(input, strict, strict);
    - * - * @see Encoder#canonicalize(String, boolean, boolean) canonicalize - * @see W3C specifications - * - * @param input - * the text to canonicalize - * @param strict - * true if checking for multiple and mixed encoding is desired, false otherwise - * - * @return a String containing the canonicalized text - */ - String canonicalize(String input, boolean strict); - - /** - * Canonicalization is simply the operation of reducing a possibly encoded - * string down to its simplest form. This is important, because attackers - * frequently use encoding to change their input in a way that will bypass - * validation filters, but still be interpreted properly by the target of - * the attack. Note that data encoded more than once is not something that a - * normal user would generate and should be regarded as an attack. - *

    - * Everyone says you shouldn't do validation - * without canonicalizing the data first. This is easier said than done. The canonicalize method can - * be used to simplify just about any input down to its most basic form. Note that canonicalize doesn't - * handle Unicode issues, it focuses on higher level encoding and escaping schemes. In addition to simple - * decoding, canonicalize also handles: - *

    • Perverse but legal variants of escaping schemes
    • - *
    • Multiple escaping (%2526 or &lt;)
    • - *
    • Mixed escaping (%26lt;)
    • - *
    • Nested escaping (%%316 or &%6ct;)
    • - *
    • All combinations of multiple, mixed, and nested encoding/escaping (%253c or ┦gt;)
    - *

    - * Using canonicalize is simple. The default is just... - *

    -     *     String clean = ESAPI.encoder().canonicalize( request.getParameter("input"));
    -     * 
    - * You need to decode untrusted data so that it's safe for ANY downstream interpreter or decoder. For - * example, if your data goes into a Windows command shell, then into a database, and then to a browser, - * you're going to need to decode for all of those systems. You can build a custom encoder to canonicalize - * for your application like this... - *
    -     *     ArrayList list = new ArrayList();
    -     *     list.add( new WindowsCodec() );
    -     *     list.add( new MySQLCodec() );
    -     *     list.add( new PercentCodec() );
    -     *     Encoder encoder = new DefaultEncoder( list );
    -     *     String clean = encoder.canonicalize( request.getParameter( "input" ));
    -     * 
    - * In ESAPI, the Validator uses the canonicalize method before it does validation. So all you need to - * do is to validate as normal and you'll be protected against a host of encoded attacks. - *
    -     *     String input = request.getParameter( "name" );
    -     *     String name = ESAPI.validator().isValidInput( "test", input, "FirstName", 20, false);
    -     * 
    - * However, the default canonicalize() method only decodes HTMLEntity, percent (URL) encoding, and JavaScript - * encoding. If you'd like to use a custom canonicalizer with your validator, that's pretty easy too. - *
    -     *     ... setup custom encoder as above
    -     *     Validator validator = new DefaultValidator( encoder );
    -     *     String input = request.getParameter( "name" );
    -     *     String name = validator.isValidInput( "test", input, "name", 20, false);
    -     * 
    - * Although ESAPI is able to canonicalize multiple, mixed, or nested encoding, it's safer to not accept - * this stuff in the first place. In ESAPI, the default is "strict" mode that throws an IntrusionException - * if it receives anything not single-encoded with a single scheme. This is configurable - * in ESAPI.properties using the properties: - *
    -	 * Encoder.AllowMultipleEncoding=false
    -	 * Encoder.AllowMixedEncoding=false
    -	 * 
    - * This method allows you to override the default behavior by directly specifying whether to restrict - * multiple or mixed encoding. Even if you disable restrictions, you'll still get - * warning messages in the log about each multiple encoding and mixed encoding received. - *
    -     *     // disabling strict mode to allow mixed encoding
    -     *     String url = ESAPI.encoder().canonicalize( request.getParameter("url"), false, false);
    -     * 
    - * - * @see W3C specifications - * - * @param input - * the text to canonicalize - * @param restrictMultiple - * true if checking for multiple encoding is desired, false otherwise - * @param restrictMixed - * true if checking for mixed encoding is desired, false otherwise - * - * @return a String containing the canonicalized text - */ - String canonicalize(String input, boolean restrictMultiple, boolean restrictMixed); - - /** - * Encode data for use in Cascading Style Sheets (CSS) content. - * - * @see CSS Syntax [w3.org] - * - * @param input - * the text to encode for CSS - * - * @return input encoded for CSS - */ - String encodeForCSS(String input); - - /** - * Encode data for use in HTML using HTML entity encoding - *

    - * Note that the following characters: - * 00-08, 0B-0C, 0E-1F, and 7F-9F - *

    cannot be used in HTML. - * - * @see HTML Encodings [wikipedia.org] - * @see SGML Specification [w3.org] - * @see XML Specification [w3.org] - * - * @param input - * the text to encode for HTML - * - * @return input encoded for HTML - */ - String encodeForHTML(String input); - - /** - * Decodes HTML entities. - * @param input the String to decode - * @return the newly decoded String - */ - String decodeForHTML(String input); - - /** - * Encode data for use in HTML attributes. - * - * @param input - * the text to encode for an HTML attribute - * - * @return input encoded for use as an HTML attribute - */ - String encodeForHTMLAttribute(String input); - - - /** - * Encode data for insertion inside a data value or function argument in JavaScript. Including user data - * directly inside a script is quite dangerous. Great care must be taken to prevent including user data - * directly into script code itself, as no amount of encoding will prevent attacks there. - * - * Please note there are some JavaScript functions that can never safely receive untrusted data - * as input – even if the user input is encoded. - * - * For example: - *

    -     *  <script>
    -     *      window.setInterval('<%= EVEN IF YOU ENCODE UNTRUSTED DATA YOU ARE XSSED HERE %>');
    -     *  </script>
    -     * 
    - * @param input - * the text to encode for JavaScript - * - * @return input encoded for use in JavaScript - */ - String encodeForJavaScript(String input); - - /** - * Encode data for insertion inside a data value in a Visual Basic script. Putting user data directly - * inside a script is quite dangerous. Great care must be taken to prevent putting user data - * directly into script code itself, as no amount of encoding will prevent attacks there. - * - * This method is not recommended as VBScript is only supported by Internet Explorer - * - * @param input - * the text to encode for VBScript - * - * @return input encoded for use in VBScript - */ - String encodeForVBScript(String input); - - - /** - * Encode input for use in a SQL query, according to the selected codec - * (appropriate codecs include the MySQLCodec and OracleCodec). - * - * This method is not recommended. The use of the PreparedStatement - * interface is the preferred approach. However, if for some reason - * this is impossible, then this method is provided as a weaker - * alternative. - * - * The best approach is to make sure any single-quotes are double-quoted. - * Another possible approach is to use the {escape} syntax described in the - * JDBC specification in section 1.5.6. - * - * However, this syntax does not work with all drivers, and requires - * modification of all queries. - * - * @see JDBC Specification - * - * @param codec - * a Codec that declares which database 'input' is being encoded for (ie. MySQL, Oracle, etc.) - * @param input - * the text to encode for SQL - * - * @return input encoded for use in SQL - */ - String encodeForSQL(Codec codec, String input); - - /** - * Encode for an operating system command shell according to the selected codec (appropriate codecs include the WindowsCodec and UnixCodec). - * - * Please note the following recommendations before choosing to use this method: - * - * 1) It is strongly recommended that applications avoid making direct OS system calls if possible as such calls are not portable, and they are potentially unsafe. Please use language provided features if at all possible, rather than native OS calls to implement the desired feature. - * 2) If an OS call cannot be avoided, then it is recommended that the program to be invoked be invoked directly (e.g., System.exec("nameofcommand" + "parameterstocommand");) as this avoids the use of the command shell. The "parameterstocommand" should of course be validated before passing them to the OS command. - * 3) If you must use this method, then we recommend validating all user supplied input passed to the command shell as well, in addition to using this method in order to make the command shell invocation safe. - * - * An example use of this method would be: System.exec("dir " + ESAPI.encodeForOS(WindowsCodec, "parameter(s)tocommandwithuserinput"); - * - * @param codec - * a Codec that declares which operating system 'input' is being encoded for (ie. Windows, Unix, etc.) - * @param input - * the text to encode for the command shell - * - * @return input encoded for use in command shell - */ - String encodeForOS(Codec codec, String input); - - /** - * Encode data for use in LDAP queries. - * - * @param input - * the text to encode for LDAP - * - * @return input encoded for use in LDAP - */ - String encodeForLDAP(String input); - - /** - * Encode data for use in an LDAP distinguished name. - * - * @param input - * the text to encode for an LDAP distinguished name - * - * @return input encoded for use in an LDAP distinguished name - */ - String encodeForDN(String input); - - /** - * Encode data for use in an XPath query. - * - * NB: The reference implementation encodes almost everything and may over-encode. - * - * The difficulty with XPath encoding is that XPath has no built in mechanism for escaping - * characters. It is possible to use XQuery in a parameterized way to - * prevent injection. - * - * For more information, refer to this - * article which specifies the following list of characters as the most - * dangerous: ^&"*';<>(). This - * paper suggests disallowing ' and " in queries. - * - * @see XPath Injection [ibm.com] - * @see Blind XPath Injection [packetstormsecurity.org] - * - * @param input - * the text to encode for XPath - * @return - * input encoded for use in XPath - */ - String encodeForXPath(String input); - - /** - * Encode data for use in an XML element. The implementation should follow the XML Encoding - * Standard from the W3C. - *

    - * The use of a real XML parser is strongly encouraged. However, in the - * hopefully rare case that you need to make sure that data is safe for - * inclusion in an XML document and cannot use a parse, this method provides - * a safe mechanism to do so. - * - * @see XML Encoding Standard - * - * @param input - * the text to encode for XML - * - * @return - * input encoded for use in XML - */ - String encodeForXML(String input); - - /** - * Encode data for use in an XML attribute. The implementation should follow - * the XML Encoding - * Standard from the W3C. - *

    - * The use of a real XML parser is highly encouraged. However, in the - * hopefully rare case that you need to make sure that data is safe for - * inclusion in an XML document and cannot use a parse, this method provides - * a safe mechanism to do so. - * - * @see XML Encoding Standard - * - * @param input - * the text to encode for use as an XML attribute - * - * @return - * input encoded for use in an XML attribute - */ - String encodeForXMLAttribute(String input); - - /** - * Encode for use in a URL. This method performs URL encoding - * on the entire string. - * - * @see URL encoding - * - * @param input - * the text to encode for use in a URL - * - * @return input - * encoded for use in a URL - * - * @throws EncodingException - * if encoding fails - */ - String encodeForURL(String input) throws EncodingException; - - /** - * Decode from URL. Implementations should first canonicalize and - * detect any double-encoding. If this check passes, then the data is decoded using URL - * decoding. - * - * @param input - * the text to decode from an encoded URL - * - * @return - * the decoded URL value - * - * @throws EncodingException - * if decoding fails - */ - String decodeFromURL(String input) throws EncodingException; - - /** - * Encode for Base64. - * - * @param input - * the text to encode for Base64 - * @param wrap - * the encoder will wrap lines every 64 characters of output - * - * @return input encoded for Base64 - */ - String encodeForBase64(byte[] input, boolean wrap); - - /** - * Decode data encoded with BASE-64 encoding. - * - * @param input - * the Base64 text to decode - * - * @return input - * decoded from Base64 - * - * @throws IOException - */ - byte[] decodeFromBase64(String input) throws IOException; - -} +/** + * OWASP Enterprise Security API (ESAPI) + * + * This file is part of the Open Web Application Security Project (OWASP) + * Enterprise Security API (ESAPI) project. For details, please see + * http://www.owasp.org/index.php/ESAPI. + * + * Copyright (c) 2007 - The OWASP Foundation + * + * The ESAPI is published by OWASP under the BSD license. You should read and accept the + * LICENSE before you use, modify, and/or redistribute this software. + * + * @author Jeff Williams Aspect Security + * @created 2007 + */ +package org.owasp.esapi; + +import java.io.IOException; + +import org.owasp.esapi.codecs.Codec; +import org.owasp.esapi.errors.EncodingException; + + +/** + * The Encoder interface contains a number of methods for decoding input and encoding output + * so that it will be safe for a variety of interpreters. To prevent + * double-encoding, callers should make sure input does not already contain encoded characters + * by calling canonicalize. Validator implementations should call canonicalize on user input + * before validating to prevent encoded attacks. + *

    + * All of the methods must use a "whitelist" or "positive" security model. + * For the encoding methods, this means that all characters should be encoded, except for a specific list of + * "immune" characters that are known to be safe. + *

    + * The Encoder performs two key functions, encoding and decoding. These functions rely + * on a set of codecs that can be found in the org.owasp.esapi.codecs package. These include: + *

    • CSS Escaping
    • + *
    • HTMLEntity Encoding
    • + *
    • JavaScript Escaping
    • + *
    • MySQL Escaping
    • + *
    • Oracle Escaping
    • + *
    • Percent Encoding (aka URL Encoding)
    • + *
    • Unix Escaping
    • + *
    • VBScript Escaping
    • + *
    • Windows Encoding
    + *

    + * + * @author Jeff Williams (jeff.williams .at. aspectsecurity.com) Aspect Security + * @since June 1, 2007 + */ +public interface Encoder { + + /** + * Standard character sets + */ + + /** + * @deprecated Use {@link EncoderConstants#CHAR_LOWERS} instead + */ + @Deprecated + public final static char[] CHAR_LOWERS = EncoderConstants.CHAR_LOWERS; + /** + * @deprecated Use {@link EncoderConstants#CHAR_UPPERS} instead + * + */ + @Deprecated + public final static char[] CHAR_UPPERS = EncoderConstants.CHAR_UPPERS; + /** + * @deprecated Use {@link EncoderConstants#CHAR_DIGITS} instead + * + */ + @Deprecated + public final static char[] CHAR_DIGITS = EncoderConstants.CHAR_DIGITS; + /** + * @deprecated Use {@link EncoderConstants#CHAR_SPECIALS} instead + * + */ + @Deprecated + public final static char[] CHAR_SPECIALS = EncoderConstants.CHAR_SPECIALS; + /** + * @deprecated Use {@link EncoderConstants#CHAR_LETTERS} instead + * + */ + @Deprecated + public final static char[] CHAR_LETTERS = EncoderConstants.CHAR_LETTERS; + /** + * @deprecated Use {@link EncoderConstants#CHAR_ALPHANUMERICS} instead + * + */ + @Deprecated + public final static char[] CHAR_ALPHANUMERICS = EncoderConstants.CHAR_ALPHANUMERICS; + + + /** + * Password character set, is alphanumerics (without l, i, I, o, O, and 0) + * selected specials like + (bad for URL encoding, | is like i and 1, + * etc...) + * @deprecated Use {@link EncoderConstants#CHAR_PASSWORD_LOWERS} instead + */ + @Deprecated + public final static char[] CHAR_PASSWORD_LOWERS = EncoderConstants.CHAR_PASSWORD_LOWERS; + /** + * @deprecated Use {@link EncoderConstants#CHAR_PASSWORD_UPPERS} instead + * + */ + @Deprecated + public final static char[] CHAR_PASSWORD_UPPERS = EncoderConstants.CHAR_PASSWORD_UPPERS; + /** + * @deprecated Use {@link EncoderConstants#CHAR_PASSWORD_DIGITS} instead + * + */ + @Deprecated + public final static char[] CHAR_PASSWORD_DIGITS = EncoderConstants.CHAR_PASSWORD_DIGITS; + /** + * @deprecated Use {@link EncoderConstants#CHAR_PASSWORD_SPECIALS} instead + * + */ + @Deprecated + public final static char[] CHAR_PASSWORD_SPECIALS = EncoderConstants.CHAR_PASSWORD_SPECIALS; + /** + * @deprecated Use {@link EncoderConstants#CHAR_PASSWORD_LETTERS} instead + * + */ + @Deprecated + public final static char[] CHAR_PASSWORD_LETTERS = EncoderConstants.CHAR_PASSWORD_LETTERS; + + + + /** + * This method is equivalent to calling

    Encoder.canonicalize(input, restrictMultiple, restrictMixed);
    + * + * The default values for restrictMultiple and restrictMixed come from ESAPI.properties + *
    +	 * Encoder.AllowMultipleEncoding=false
    +	 * Encoder.AllowMixedEncoding=false
    +	 * 
    + * + * @see Encoder#canonicalize(String, boolean, boolean) canonicalize + * @see W3C specifications + * + * @param input the text to canonicalize + * @return a String containing the canonicalized text + */ + String canonicalize(String input); + + /** + * This method is the equivalent to calling
    Encoder.canonicalize(input, strict, strict);
    + * + * @see Encoder#canonicalize(String, boolean, boolean) canonicalize + * @see W3C specifications + * + * @param input + * the text to canonicalize + * @param strict + * true if checking for multiple and mixed encoding is desired, false otherwise + * + * @return a String containing the canonicalized text + */ + String canonicalize(String input, boolean strict); + + /** + * Canonicalization is simply the operation of reducing a possibly encoded + * string down to its simplest form. This is important, because attackers + * frequently use encoding to change their input in a way that will bypass + * validation filters, but still be interpreted properly by the target of + * the attack. Note that data encoded more than once is not something that a + * normal user would generate and should be regarded as an attack. + *

    + * Everyone says you shouldn't do validation + * without canonicalizing the data first. This is easier said than done. The canonicalize method can + * be used to simplify just about any input down to its most basic form. Note that canonicalize doesn't + * handle Unicode issues, it focuses on higher level encoding and escaping schemes. In addition to simple + * decoding, canonicalize also handles: + *

    • Perverse but legal variants of escaping schemes
    • + *
    • Multiple escaping (%2526 or &lt;)
    • + *
    • Mixed escaping (%26lt;)
    • + *
    • Nested escaping (%%316 or &%6ct;)
    • + *
    • All combinations of multiple, mixed, and nested encoding/escaping (%253c or ┦gt;)
    + *

    + * Using canonicalize is simple. The default is just... + *

    +     *     String clean = ESAPI.encoder().canonicalize( request.getParameter("input"));
    +     * 
    + * You need to decode untrusted data so that it's safe for ANY downstream interpreter or decoder. For + * example, if your data goes into a Windows command shell, then into a database, and then to a browser, + * you're going to need to decode for all of those systems. You can build a custom encoder to canonicalize + * for your application like this... + *
    +     *     ArrayList list = new ArrayList();
    +     *     list.add( new WindowsCodec() );
    +     *     list.add( new MySQLCodec() );
    +     *     list.add( new PercentCodec() );
    +     *     Encoder encoder = new DefaultEncoder( list );
    +     *     String clean = encoder.canonicalize( request.getParameter( "input" ));
    +     * 
    + * In ESAPI, the Validator uses the canonicalize method before it does validation. So all you need to + * do is to validate as normal and you'll be protected against a host of encoded attacks. + *
    +     *     String input = request.getParameter( "name" );
    +     *     String name = ESAPI.validator().isValidInput( "test", input, "FirstName", 20, false);
    +     * 
    + * However, the default canonicalize() method only decodes HTMLEntity, percent (URL) encoding, and JavaScript + * encoding. If you'd like to use a custom canonicalizer with your validator, that's pretty easy too. + *
    +     *     ... setup custom encoder as above
    +     *     Validator validator = new DefaultValidator( encoder );
    +     *     String input = request.getParameter( "name" );
    +     *     String name = validator.isValidInput( "test", input, "name", 20, false);
    +     * 
    + * Although ESAPI is able to canonicalize multiple, mixed, or nested encoding, it's safer to not accept + * this stuff in the first place. In ESAPI, the default is "strict" mode that throws an IntrusionException + * if it receives anything not single-encoded with a single scheme. This is configurable + * in ESAPI.properties using the properties: + *
    +	 * Encoder.AllowMultipleEncoding=false
    +	 * Encoder.AllowMixedEncoding=false
    +	 * 
    + * This method allows you to override the default behavior by directly specifying whether to restrict + * multiple or mixed encoding. Even if you disable restrictions, you'll still get + * warning messages in the log about each multiple encoding and mixed encoding received. + *
    +     *     // disabling strict mode to allow mixed encoding
    +     *     String url = ESAPI.encoder().canonicalize( request.getParameter("url"), false, false);
    +     * 
    + * + * @see W3C specifications + * + * @param input + * the text to canonicalize + * @param restrictMultiple + * true if checking for multiple encoding is desired, false otherwise + * @param restrictMixed + * true if checking for mixed encoding is desired, false otherwise + * + * @return a String containing the canonicalized text + */ + String canonicalize(String input, boolean restrictMultiple, boolean restrictMixed); + + /** + * Encode data for use in Cascading Style Sheets (CSS) content. + * + * @see CSS Syntax [w3.org] + * + * @param input + * the text to encode for CSS + * + * @return input encoded for CSS + */ + String encodeForCSS(String input); + + /** + * Encode data for use in HTML using HTML entity encoding + *

    + * Note that the following characters: + * 00-08, 0B-0C, 0E-1F, and 7F-9F + *

    cannot be used in HTML. + * + * @see HTML Encodings [wikipedia.org] + * @see SGML Specification [w3.org] + * @see XML Specification [w3.org] + * + * @param input + * the text to encode for HTML + * + * @return input encoded for HTML + */ + String encodeForHTML(String input); + + /** + * Decodes HTML entities. + * @param input the String to decode + * @return the newly decoded String + */ + String decodeForHTML(String input); + + /** + * Encode data for use in HTML attributes. + * + * @param input + * the text to encode for an HTML attribute + * + * @return input encoded for use as an HTML attribute + */ + String encodeForHTMLAttribute(String input); + + + /** + * Encode data for insertion inside a data value or function argument in JavaScript. Including user data + * directly inside a script is quite dangerous. Great care must be taken to prevent including user data + * directly into script code itself, as no amount of encoding will prevent attacks there. + * + * Please note there are some JavaScript functions that can never safely receive untrusted data + * as input – even if the user input is encoded. + * + * For example: + *

    +     *  <script>
    +     *      window.setInterval('<%= EVEN IF YOU ENCODE UNTRUSTED DATA YOU ARE XSSED HERE %>');
    +     *  </script>
    +     * 
    + * @param input + * the text to encode for JavaScript + * + * @return input encoded for use in JavaScript + */ + String encodeForJavaScript(String input); + + /** + * Encode data for insertion inside a data value in a Visual Basic script. Putting user data directly + * inside a script is quite dangerous. Great care must be taken to prevent putting user data + * directly into script code itself, as no amount of encoding will prevent attacks there. + * + * This method is not recommended as VBScript is only supported by Internet Explorer + * + * @param input + * the text to encode for VBScript + * + * @return input encoded for use in VBScript + */ + String encodeForVBScript(String input); + + + /** + * Encode input for use in a SQL query, according to the selected codec + * (appropriate codecs include the MySQLCodec and OracleCodec). + * + * This method is not recommended. The use of the PreparedStatement + * interface is the preferred approach. However, if for some reason + * this is impossible, then this method is provided as a weaker + * alternative. + * + * The best approach is to make sure any single-quotes are double-quoted. + * Another possible approach is to use the {escape} syntax described in the + * JDBC specification in section 1.5.6. + * + * However, this syntax does not work with all drivers, and requires + * modification of all queries. + * + * @see JDBC Specification + * + * @param codec + * a Codec that declares which database 'input' is being encoded for (ie. MySQL, Oracle, etc.) + * @param input + * the text to encode for SQL + * + * @return input encoded for use in SQL + */ + String encodeForSQL(Codec codec, String input); + + /** + * Encode for an operating system command shell according to the selected codec (appropriate codecs include the WindowsCodec and UnixCodec). + * + * Please note the following recommendations before choosing to use this method: + * + * 1) It is strongly recommended that applications avoid making direct OS system calls if possible as such calls are not portable, and they are potentially unsafe. Please use language provided features if at all possible, rather than native OS calls to implement the desired feature. + * 2) If an OS call cannot be avoided, then it is recommended that the program to be invoked be invoked directly (e.g., System.exec("nameofcommand" + "parameterstocommand");) as this avoids the use of the command shell. The "parameterstocommand" should of course be validated before passing them to the OS command. + * 3) If you must use this method, then we recommend validating all user supplied input passed to the command shell as well, in addition to using this method in order to make the command shell invocation safe. + * + * An example use of this method would be: System.exec("dir " + ESAPI.encodeForOS(WindowsCodec, "parameter(s)tocommandwithuserinput"); + * + * @param codec + * a Codec that declares which operating system 'input' is being encoded for (ie. Windows, Unix, etc.) + * @param input + * the text to encode for the command shell + * + * @return input encoded for use in command shell + */ + String encodeForOS(Codec codec, String input); + + /** + * Encode data for use in LDAP queries. + * + * @param input + * the text to encode for LDAP + * + * @return input encoded for use in LDAP + */ + String encodeForLDAP(String input); + + /** + * Encode data for use in an LDAP distinguished name. + * + * @param input + * the text to encode for an LDAP distinguished name + * + * @return input encoded for use in an LDAP distinguished name + */ + String encodeForDN(String input); + + /** + * Encode data for use in an XPath query. + * + * NB: The reference implementation encodes almost everything and may over-encode. + * + * The difficulty with XPath encoding is that XPath has no built in mechanism for escaping + * characters. It is possible to use XQuery in a parameterized way to + * prevent injection. + * + * For more information, refer to this + * article which specifies the following list of characters as the most + * dangerous: ^&"*';<>(). This + * paper suggests disallowing ' and " in queries. + * + * @see XPath Injection [ibm.com] + * @see Blind XPath Injection [packetstormsecurity.org] + * + * @param input + * the text to encode for XPath + * @return + * input encoded for use in XPath + */ + String encodeForXPath(String input); + + /** + * Encode data for use in an XML element. The implementation should follow the XML Encoding + * Standard from the W3C. + *

    + * The use of a real XML parser is strongly encouraged. However, in the + * hopefully rare case that you need to make sure that data is safe for + * inclusion in an XML document and cannot use a parse, this method provides + * a safe mechanism to do so. + * + * @see XML Encoding Standard + * + * @param input + * the text to encode for XML + * + * @return + * input encoded for use in XML + */ + String encodeForXML(String input); + + /** + * Encode data for use in an XML attribute. The implementation should follow + * the XML Encoding + * Standard from the W3C. + *

    + * The use of a real XML parser is highly encouraged. However, in the + * hopefully rare case that you need to make sure that data is safe for + * inclusion in an XML document and cannot use a parse, this method provides + * a safe mechanism to do so. + * + * @see XML Encoding Standard + * + * @param input + * the text to encode for use as an XML attribute + * + * @return + * input encoded for use in an XML attribute + */ + String encodeForXMLAttribute(String input); + + /** + * Encode for use in a URL. This method performs URL encoding + * on the entire string. + * + * @see URL encoding + * + * @param input + * the text to encode for use in a URL + * + * @return input + * encoded for use in a URL + * + * @throws EncodingException + * if encoding fails + */ + String encodeForURL(String input) throws EncodingException; + + /** + * Decode from URL. Implementations should first canonicalize and + * detect any double-encoding. If this check passes, then the data is decoded using URL + * decoding. + * + * @param input + * the text to decode from an encoded URL + * + * @return + * the decoded URL value + * + * @throws EncodingException + * if decoding fails + */ + String decodeFromURL(String input) throws EncodingException; + + /** + * Encode for Base64. + * + * @param input + * the text to encode for Base64 + * @param wrap + * the encoder will wrap lines every 64 characters of output + * + * @return input encoded for Base64 + */ + String encodeForBase64(byte[] input, boolean wrap); + + /** + * Decode data encoded with BASE-64 encoding. + * + * @param input + * the Base64 text to decode + * + * @return input + * decoded from Base64 + * + * @throws IOException + */ + byte[] decodeFromBase64(String input) throws IOException; + +} From 9b8c23eedc363039e10df109697fc2dbf44b10b8 Mon Sep 17 00:00:00 2001 From: kwwall Date: Mon, 18 Jan 2016 23:33:48 -0500 Subject: [PATCH 0064/1069] Change CRLF to LF for issue 356. --- .../org/owasp/esapi/EncryptedProperties.java | 224 +++++++++--------- 1 file changed, 112 insertions(+), 112 deletions(-) diff --git a/src/main/java/org/owasp/esapi/EncryptedProperties.java b/src/main/java/org/owasp/esapi/EncryptedProperties.java index e95b1b858..b3a7cd006 100644 --- a/src/main/java/org/owasp/esapi/EncryptedProperties.java +++ b/src/main/java/org/owasp/esapi/EncryptedProperties.java @@ -1,112 +1,112 @@ -/** - * OWASP Enterprise Security API (ESAPI) - * - * This file is part of the Open Web Application Security Project (OWASP) - * Enterprise Security API (ESAPI) project. For details, please see - * http://www.owasp.org/index.php/ESAPI. - * - * Copyright (c) 2007 - The OWASP Foundation - * - * The ESAPI is published by OWASP under the BSD license. You should read and accept the - * LICENSE before you use, modify, and/or redistribute this software. - * - * @author Jeff Williams Aspect Security - * @created 2007 - */ -package org.owasp.esapi; - -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.util.Set; - -import org.owasp.esapi.errors.EncryptionException; - - -/** - * The {@code EncryptedProperties} interface represents a properties file - * where all the data is encrypted before it is added, and decrypted when it - * retrieved. This interface can be implemented in a number of ways, the - * simplest being extending {@link java.util.Properties} and overloading - * the {@code getProperty} and {@code setProperty} methods. In all cases, - * the master encryption key, as given by the {@code Encryptor.MasterKey} - * property in ESAPI.properties file. - * - * @author Jeff Williams (jeff.williams .at. aspectsecurity.com) Aspect Security - * @since June 1, 2007 - */ -public interface EncryptedProperties { - - /** - * Gets the property value from the encrypted store, decrypts it, and - * returns the plaintext value to the caller. - * - * @param key - * the name of the property to get - * - * @return - * The decrypted property value. null if the key is not set. - * - * @throws EncryptionException - * if the property could not be decrypted - */ - String getProperty(String key) throws EncryptionException; - - /** - * Encrypts the plaintext property value and stores the ciphertext value - * in the encrypted store. - * - * @param key - * the name of the property to set - * @param value - * the value of the property to set - * - * @return - * the previously encrypted property value for the specified key, or - * {@code null} if it did not have one. - * - * @throws EncryptionException - * if the property could not be encrypted - */ - String setProperty(String key, String value) throws EncryptionException; - - /** - * Returns a {@code Set} view of properties. The {@code Set} is backed by a - * {@code java.util.Hashtable}, so changes to the {@code Hashtable} are - * reflected in the {@code Set}, and vice-versa. The {@code Set} supports element - * removal (which removes the corresponding entry from the {@code Hashtable), - * but not element addition. - * - * @return - * a set view of the properties contained in this map. - */ - public Set keySet(); - - /** - * Reads a property list (key and element pairs) from the input stream. - * - * @param in - * the input stream that contains the properties file - * - * @throws IOException - * Signals that an I/O exception has occurred. - */ - public void load(InputStream in) throws IOException; - - /** - * Writes this property list (key and element pairs) in this Properties table to - * the output stream in a format suitable for loading into a Properties table using the load method. - * - * @param out - * the output stream that contains the properties file - * @param comments - * a description of the property list (ex. "Encrypted Properties File"). - * - * @throws IOException - * Signals that an I/O exception has occurred. - */ - public void store(OutputStream out, String comments) throws IOException; - - -} +/** + * OWASP Enterprise Security API (ESAPI) + * + * This file is part of the Open Web Application Security Project (OWASP) + * Enterprise Security API (ESAPI) project. For details, please see + * http://www.owasp.org/index.php/ESAPI. + * + * Copyright (c) 2007 - The OWASP Foundation + * + * The ESAPI is published by OWASP under the BSD license. You should read and accept the + * LICENSE before you use, modify, and/or redistribute this software. + * + * @author Jeff Williams Aspect Security + * @created 2007 + */ +package org.owasp.esapi; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.Set; + +import org.owasp.esapi.errors.EncryptionException; + + +/** + * The {@code EncryptedProperties} interface represents a properties file + * where all the data is encrypted before it is added, and decrypted when it + * retrieved. This interface can be implemented in a number of ways, the + * simplest being extending {@link java.util.Properties} and overloading + * the {@code getProperty} and {@code setProperty} methods. In all cases, + * the master encryption key, as given by the {@code Encryptor.MasterKey} + * property in ESAPI.properties file. + * + * @author Jeff Williams (jeff.williams .at. aspectsecurity.com) Aspect Security + * @since June 1, 2007 + */ +public interface EncryptedProperties { + + /** + * Gets the property value from the encrypted store, decrypts it, and + * returns the plaintext value to the caller. + * + * @param key + * the name of the property to get + * + * @return + * The decrypted property value. null if the key is not set. + * + * @throws EncryptionException + * if the property could not be decrypted + */ + String getProperty(String key) throws EncryptionException; + + /** + * Encrypts the plaintext property value and stores the ciphertext value + * in the encrypted store. + * + * @param key + * the name of the property to set + * @param value + * the value of the property to set + * + * @return + * the previously encrypted property value for the specified key, or + * {@code null} if it did not have one. + * + * @throws EncryptionException + * if the property could not be encrypted + */ + String setProperty(String key, String value) throws EncryptionException; + + /** + * Returns a {@code Set} view of properties. The {@code Set} is backed by a + * {@code java.util.Hashtable}, so changes to the {@code Hashtable} are + * reflected in the {@code Set}, and vice-versa. The {@code Set} supports element + * removal (which removes the corresponding entry from the {@code Hashtable), + * but not element addition. + * + * @return + * a set view of the properties contained in this map. + */ + public Set keySet(); + + /** + * Reads a property list (key and element pairs) from the input stream. + * + * @param in + * the input stream that contains the properties file + * + * @throws IOException + * Signals that an I/O exception has occurred. + */ + public void load(InputStream in) throws IOException; + + /** + * Writes this property list (key and element pairs) in this Properties table to + * the output stream in a format suitable for loading into a Properties table using the load method. + * + * @param out + * the output stream that contains the properties file + * @param comments + * a description of the property list (ex. "Encrypted Properties File"). + * + * @throws IOException + * Signals that an I/O exception has occurred. + */ + public void store(OutputStream out, String comments) throws IOException; + + +} From cf3039aef11fac00c3aeabb776337db915e96c88 Mon Sep 17 00:00:00 2001 From: kwwall Date: Mon, 18 Jan 2016 23:33:48 -0500 Subject: [PATCH 0065/1069] Change CRLF to LF for issue 356. --- src/main/java/org/owasp/esapi/Encryptor.java | 634 +++++++++---------- 1 file changed, 317 insertions(+), 317 deletions(-) diff --git a/src/main/java/org/owasp/esapi/Encryptor.java b/src/main/java/org/owasp/esapi/Encryptor.java index 35bf094dd..979140c86 100644 --- a/src/main/java/org/owasp/esapi/Encryptor.java +++ b/src/main/java/org/owasp/esapi/Encryptor.java @@ -1,318 +1,318 @@ -/** - * OWASP Enterprise Security API (ESAPI) - * - * This file is part of the Open Web Application Security Project (OWASP) - * Enterprise Security API (ESAPI) project. For details, please see - * http://www.owasp.org/index.php/ESAPI. - * - * Copyright © 2007,2009 - The OWASP Foundation - * - * The ESAPI is published by OWASP under the BSD license. You should read and accept the - * LICENSE before you use, modify, and/or redistribute this software. - * - * @author Jeff Williams Aspect Security - * @author kevin.w.wall@gmail.com - * @created 2007 - */ -package org.owasp.esapi; - -import javax.crypto.SecretKey; - -import org.owasp.esapi.crypto.CipherText; -import org.owasp.esapi.crypto.PlainText; -import org.owasp.esapi.errors.EncryptionException; -import org.owasp.esapi.errors.IntegrityException; - - -/** - * The Encryptor interface provides a set of methods for performing common - * encryption, random number, and hashing operations. Implementations should - * rely on a strong cryptographic implementation, such as JCE or BouncyCastle. - * Implementors should take care to ensure that they initialize their - * implementation with a strong "master key", and that they protect this secret - * as much as possible. - *

    - * The main property controlling the selection of the implementation class is the - * property {@code ESAPI.Encryptor} in {@code ESAPI.properties}. Most of the - * the other encryption related properties have property names that start with - * the string "Encryptor.". These properties all you to do things such as - * select the encryption algorithms, the preferred JCE provider, etc. - *

    - * In addition, there are two important properties (initially delivered as unset - * from the ESAPI download) named {@code Encryptor.MasterKey} and - * {@code Encryptor.MasterSalt} that must be set before using ESAPI encryption. - * There is a bash(1) shell script provided with the standard ESAPI distribution - * called 'setMasterKey.sh' that will assist you in setting these two properties. The - * script is in 'src/examples/scripts/setMasterKey.sh'. - *

    - * Possible future enhancements (depending on feedback) are discussed in - * section 4 of - * - * Design Goals in OWASP ESAPI Cryptography. - * - * @author Jeff Williams (jeff.williams .at. aspectsecurity.com) Aspect Security - * @since June 1, 2007 - * @see User Guide for Symmetric Encryption in ESAPI 2.0 - */ -public interface Encryptor { - - /** - * Returns a string representation of the hash of the provided plaintext and - * salt. The salt helps to protect against a rainbow table attack by mixing - * in some extra data with the plaintext. Some good choices for a salt might - * be an account name or some other string that is known to the application - * but not to an attacker. - * See - * this article for more information about hashing as it pertains to password schemes. - * - * @param plaintext - * the plaintext String to encrypt - * @param salt - * the salt to add to the plaintext String before hashing - * - * @return - * the encrypted hash of 'plaintext' stored as a String - * - * @throws EncryptionException - * if the specified hash algorithm could not be found or another problem exists with - * the hashing of 'plaintext' - */ - String hash(String plaintext, String salt) throws EncryptionException; - - /** - * Returns a string representation of the hash of the provided plaintext and - * salt. The salt helps to protect against a rainbow table attack by mixing - * in some extra data with the plaintext. Some good choices for a salt might - * be an account name or some other string that is known to the application - * but not to an attacker. - * See - * this article for more information about hashing as it pertains to password schemes. - * - * @param plaintext - * the plaintext String to encrypt - * @param salt - * the salt to add to the plaintext String before hashing - * @param iterations - * the number of times to iterate the hash - * - * @return - * the encrypted hash of 'plaintext' stored as a String - * - * @throws EncryptionException - * if the specified hash algorithm could not be found or another problem exists with - * the hashing of 'plaintext' - */ - String hash(String plaintext, String salt, int iterations) throws EncryptionException; - - /** - * Encrypts the provided plaintext bytes using the cipher transformation - * specified by the property Encryptor.CipherTransformation - * and the master encryption key as specified by the property - * {@code Encryptor.MasterKey} as defined in the ESAPI.properties file. - *

    - * - * @param plaintext The {@code PlainText} to be encrypted. - * @return the {@code CipherText} object from which the raw ciphertext, the - * IV, the cipher transformation, and many other aspects about - * the encryption detail may be extracted. - * @throws EncryptionException Thrown if something should go wrong such as - * the JCE provider cannot be found, the cipher algorithm, - * cipher mode, or padding scheme not being supported, specifying - * an unsupported key size, specifying an IV of incorrect length, - * etc. - * @see #encrypt(SecretKey, PlainText) - * @since 2.0 - */ - CipherText encrypt(PlainText plaintext) throws EncryptionException; - - - /** - * Encrypts the provided plaintext bytes using the cipher transformation - * specified by the property Encryptor.CipherTransformation - * as defined in the ESAPI.properties file and the - * specified secret key. - *

    - * This method is similar to {@link #encrypt(PlainText)} except that it - * permits a specific {@code SecretKey} to be used for encryption. - * - * @param key The {@code SecretKey} to use for encrypting the plaintext. - * @param plaintext The byte stream to be encrypted. Note if a Java - * {@code String} is to be encrypted, it should be converted - * using {@code "some string".getBytes("UTF-8")}. - * @return the {@code CipherText} object from which the raw ciphertext, the - * IV, the cipher transformation, and many other aspects about - * the encryption detail may be extracted. - * @throws EncryptionException Thrown if something should go wrong such as - * the JCE provider cannot be found, the cipher algorithm, - * cipher mode, or padding scheme not being supported, specifying - * an unsupported key size, specifying an IV of incorrect length, - * etc. - * @see #encrypt(PlainText) - * @since 2.0 - */ - CipherText encrypt(SecretKey key, PlainText plaintext) - throws EncryptionException; - - /** - * Decrypts the provided {@link CipherText} using the information from it - * and the master encryption key as specified by the property - * {@code Encryptor.MasterKey} as defined in the {@code ESAPI.properties} - * file. - *

    - * @param ciphertext The {@code CipherText} object to be decrypted. - * @return The {@code PlainText} object resulting from decrypting the specified - * ciphertext. Note that it it is desired to convert the returned - * plaintext byte array to a Java String is should be done using - * {@code new String(byte[], "UTF-8");} rather than simply using - * {@code new String(byte[]);} which uses native encoding and may - * not be portable across hardware and/or OS platforms. - * @throws EncryptionException Thrown if something should go wrong such as - * the JCE provider cannot be found, the cipher algorithm, - * cipher mode, or padding scheme not being supported, specifying - * an unsupported key size, or incorrect encryption key was - * specified or a {@code PaddingException} occurs. - * @see #decrypt(SecretKey, CipherText) - */ - PlainText decrypt(CipherText ciphertext) throws EncryptionException; - - /** - * Decrypts the provided {@link CipherText} using the information from it - * and the specified secret key. - *

    - * This decrypt method is similar to {@link #decrypt(CipherText)} except that - * it allows decrypting with a secret key other than the master secret key. - *

    - * @param key The {@code SecretKey} to use for encrypting the plaintext. - * @param ciphertext The {@code CipherText} object to be decrypted. - * @return The {@code PlainText} object resulting from decrypting the specified - * ciphertext. Note that it it is desired to convert the returned - * plaintext byte array to a Java String is should be done using - * {@code new String(byte[], "UTF-8");} rather than simply using - * {@code new String(byte[]);} which uses native encoding and may - * not be portable across hardware and/or OS platforms. - * @throws EncryptionException Thrown if something should go wrong such as - * the JCE provider cannot be found, the cipher algorithm, - * cipher mode, or padding scheme not being supported, specifying - * an unsupported key size, or incorrect encryption key was - * specified or a {@code PaddingException} occurs. - * @see #decrypt(CipherText) - */ - PlainText decrypt(SecretKey key, CipherText ciphertext) throws EncryptionException; - - /** - * Create a digital signature for the provided data and return it in a - * string. - *

    - * Limitations: A new public/private key pair used for ESAPI 2.0 digital - * signatures with this method and {@link #verifySignature(String, String)} - * are dynamically created when the default reference implementation class, - * {@link org.owasp.esapi.reference.crypto.JavaEncryptor} is first created. - * Because this key pair is not persisted nor is the public key shared, - * this method and the corresponding {@link #verifySignature(String, String)} - * can not be used with expected results across JVM instances. This limitation - * will be addressed in ESAPI 2.1. - *

    - * - * @param data - * the data to sign - * - * @return - * the digital signature stored as a String - * - * @throws EncryptionException - * if the specified signature algorithm cannot be found - */ - String sign(String data) throws EncryptionException; - - /** - * Verifies a digital signature (created with the sign method) and returns - * the boolean result. - *

    - * Limitations: A new public/private key pair used for ESAPI 2.0 digital - * signatures with this method and {@link #sign(String)} - * are dynamically created when the default reference implementation class, - * {@link org.owasp.esapi.reference.crypto.JavaEncryptor} is first created. - * Because this key pair is not persisted nor is the public key shared, - * this method and the corresponding {@link #sign(String)} - * can not be used with expected results across JVM instances. This limitation - * will be addressed in ESAPI 2.1. - *

    - * @param signature - * the signature to verify against 'data' - * @param data - * the data to verify against 'signature' - * - * @return - * true, if the signature is verified, false otherwise - * - */ - boolean verifySignature(String signature, String data); - - /** - * Creates a seal that binds a set of data and includes an expiration timestamp. - * - * @param data - * the data to seal - * @param timestamp - * the absolute expiration date of the data, expressed as seconds since the epoch - * - * @return - * the seal - * @throws IntegrityException - * - */ - String seal(String data, long timestamp) throws IntegrityException; - - /** - * Unseals data (created with the seal method) and throws an exception - * describing any of the various problems that could exist with a seal, such - * as an invalid seal format, expired timestamp, or decryption error. - * - * @param seal - * the sealed data - * - * @return - * the original (unsealed) data - * - * @throws EncryptionException - * if the unsealed data cannot be retrieved for any reason - */ - String unseal( String seal ) throws EncryptionException; - - /** - * Verifies a seal (created with the seal method) and throws an exception - * describing any of the various problems that could exist with a seal, such - * as an invalid seal format, expired timestamp, or data mismatch. - * - * @param seal - * the seal to verify - * - * @return - * true, if the seal is valid. False otherwise - */ - boolean verifySeal(String seal); - - /** - * Gets an absolute timestamp representing an offset from the current time to be used by - * other functions in the library. - * - * @param offset - * the offset to add to the current time - * - * @return - * the absolute timestamp - */ - public long getRelativeTimeStamp( long offset ); - - - /** - * Gets a timestamp representing the current date and time to be used by - * other functions in the library. - * - * @return - * a timestamp representing the current time - */ - long getTimeStamp(); - +/** + * OWASP Enterprise Security API (ESAPI) + * + * This file is part of the Open Web Application Security Project (OWASP) + * Enterprise Security API (ESAPI) project. For details, please see + * http://www.owasp.org/index.php/ESAPI. + * + * Copyright © 2007,2009 - The OWASP Foundation + * + * The ESAPI is published by OWASP under the BSD license. You should read and accept the + * LICENSE before you use, modify, and/or redistribute this software. + * + * @author Jeff Williams Aspect Security + * @author kevin.w.wall@gmail.com + * @created 2007 + */ +package org.owasp.esapi; + +import javax.crypto.SecretKey; + +import org.owasp.esapi.crypto.CipherText; +import org.owasp.esapi.crypto.PlainText; +import org.owasp.esapi.errors.EncryptionException; +import org.owasp.esapi.errors.IntegrityException; + + +/** + * The Encryptor interface provides a set of methods for performing common + * encryption, random number, and hashing operations. Implementations should + * rely on a strong cryptographic implementation, such as JCE or BouncyCastle. + * Implementors should take care to ensure that they initialize their + * implementation with a strong "master key", and that they protect this secret + * as much as possible. + *

    + * The main property controlling the selection of the implementation class is the + * property {@code ESAPI.Encryptor} in {@code ESAPI.properties}. Most of the + * the other encryption related properties have property names that start with + * the string "Encryptor.". These properties all you to do things such as + * select the encryption algorithms, the preferred JCE provider, etc. + *

    + * In addition, there are two important properties (initially delivered as unset + * from the ESAPI download) named {@code Encryptor.MasterKey} and + * {@code Encryptor.MasterSalt} that must be set before using ESAPI encryption. + * There is a bash(1) shell script provided with the standard ESAPI distribution + * called 'setMasterKey.sh' that will assist you in setting these two properties. The + * script is in 'src/examples/scripts/setMasterKey.sh'. + *

    + * Possible future enhancements (depending on feedback) are discussed in + * section 4 of + * + * Design Goals in OWASP ESAPI Cryptography. + * + * @author Jeff Williams (jeff.williams .at. aspectsecurity.com) Aspect Security + * @since June 1, 2007 + * @see User Guide for Symmetric Encryption in ESAPI 2.0 + */ +public interface Encryptor { + + /** + * Returns a string representation of the hash of the provided plaintext and + * salt. The salt helps to protect against a rainbow table attack by mixing + * in some extra data with the plaintext. Some good choices for a salt might + * be an account name or some other string that is known to the application + * but not to an attacker. + * See + * this article for more information about hashing as it pertains to password schemes. + * + * @param plaintext + * the plaintext String to encrypt + * @param salt + * the salt to add to the plaintext String before hashing + * + * @return + * the encrypted hash of 'plaintext' stored as a String + * + * @throws EncryptionException + * if the specified hash algorithm could not be found or another problem exists with + * the hashing of 'plaintext' + */ + String hash(String plaintext, String salt) throws EncryptionException; + + /** + * Returns a string representation of the hash of the provided plaintext and + * salt. The salt helps to protect against a rainbow table attack by mixing + * in some extra data with the plaintext. Some good choices for a salt might + * be an account name or some other string that is known to the application + * but not to an attacker. + * See + * this article for more information about hashing as it pertains to password schemes. + * + * @param plaintext + * the plaintext String to encrypt + * @param salt + * the salt to add to the plaintext String before hashing + * @param iterations + * the number of times to iterate the hash + * + * @return + * the encrypted hash of 'plaintext' stored as a String + * + * @throws EncryptionException + * if the specified hash algorithm could not be found or another problem exists with + * the hashing of 'plaintext' + */ + String hash(String plaintext, String salt, int iterations) throws EncryptionException; + + /** + * Encrypts the provided plaintext bytes using the cipher transformation + * specified by the property Encryptor.CipherTransformation + * and the master encryption key as specified by the property + * {@code Encryptor.MasterKey} as defined in the ESAPI.properties file. + *

    + * + * @param plaintext The {@code PlainText} to be encrypted. + * @return the {@code CipherText} object from which the raw ciphertext, the + * IV, the cipher transformation, and many other aspects about + * the encryption detail may be extracted. + * @throws EncryptionException Thrown if something should go wrong such as + * the JCE provider cannot be found, the cipher algorithm, + * cipher mode, or padding scheme not being supported, specifying + * an unsupported key size, specifying an IV of incorrect length, + * etc. + * @see #encrypt(SecretKey, PlainText) + * @since 2.0 + */ + CipherText encrypt(PlainText plaintext) throws EncryptionException; + + + /** + * Encrypts the provided plaintext bytes using the cipher transformation + * specified by the property Encryptor.CipherTransformation + * as defined in the ESAPI.properties file and the + * specified secret key. + *

    + * This method is similar to {@link #encrypt(PlainText)} except that it + * permits a specific {@code SecretKey} to be used for encryption. + * + * @param key The {@code SecretKey} to use for encrypting the plaintext. + * @param plaintext The byte stream to be encrypted. Note if a Java + * {@code String} is to be encrypted, it should be converted + * using {@code "some string".getBytes("UTF-8")}. + * @return the {@code CipherText} object from which the raw ciphertext, the + * IV, the cipher transformation, and many other aspects about + * the encryption detail may be extracted. + * @throws EncryptionException Thrown if something should go wrong such as + * the JCE provider cannot be found, the cipher algorithm, + * cipher mode, or padding scheme not being supported, specifying + * an unsupported key size, specifying an IV of incorrect length, + * etc. + * @see #encrypt(PlainText) + * @since 2.0 + */ + CipherText encrypt(SecretKey key, PlainText plaintext) + throws EncryptionException; + + /** + * Decrypts the provided {@link CipherText} using the information from it + * and the master encryption key as specified by the property + * {@code Encryptor.MasterKey} as defined in the {@code ESAPI.properties} + * file. + *

    + * @param ciphertext The {@code CipherText} object to be decrypted. + * @return The {@code PlainText} object resulting from decrypting the specified + * ciphertext. Note that it it is desired to convert the returned + * plaintext byte array to a Java String is should be done using + * {@code new String(byte[], "UTF-8");} rather than simply using + * {@code new String(byte[]);} which uses native encoding and may + * not be portable across hardware and/or OS platforms. + * @throws EncryptionException Thrown if something should go wrong such as + * the JCE provider cannot be found, the cipher algorithm, + * cipher mode, or padding scheme not being supported, specifying + * an unsupported key size, or incorrect encryption key was + * specified or a {@code PaddingException} occurs. + * @see #decrypt(SecretKey, CipherText) + */ + PlainText decrypt(CipherText ciphertext) throws EncryptionException; + + /** + * Decrypts the provided {@link CipherText} using the information from it + * and the specified secret key. + *

    + * This decrypt method is similar to {@link #decrypt(CipherText)} except that + * it allows decrypting with a secret key other than the master secret key. + *

    + * @param key The {@code SecretKey} to use for encrypting the plaintext. + * @param ciphertext The {@code CipherText} object to be decrypted. + * @return The {@code PlainText} object resulting from decrypting the specified + * ciphertext. Note that it it is desired to convert the returned + * plaintext byte array to a Java String is should be done using + * {@code new String(byte[], "UTF-8");} rather than simply using + * {@code new String(byte[]);} which uses native encoding and may + * not be portable across hardware and/or OS platforms. + * @throws EncryptionException Thrown if something should go wrong such as + * the JCE provider cannot be found, the cipher algorithm, + * cipher mode, or padding scheme not being supported, specifying + * an unsupported key size, or incorrect encryption key was + * specified or a {@code PaddingException} occurs. + * @see #decrypt(CipherText) + */ + PlainText decrypt(SecretKey key, CipherText ciphertext) throws EncryptionException; + + /** + * Create a digital signature for the provided data and return it in a + * string. + *

    + * Limitations: A new public/private key pair used for ESAPI 2.0 digital + * signatures with this method and {@link #verifySignature(String, String)} + * are dynamically created when the default reference implementation class, + * {@link org.owasp.esapi.reference.crypto.JavaEncryptor} is first created. + * Because this key pair is not persisted nor is the public key shared, + * this method and the corresponding {@link #verifySignature(String, String)} + * can not be used with expected results across JVM instances. This limitation + * will be addressed in ESAPI 2.1. + *

    + * + * @param data + * the data to sign + * + * @return + * the digital signature stored as a String + * + * @throws EncryptionException + * if the specified signature algorithm cannot be found + */ + String sign(String data) throws EncryptionException; + + /** + * Verifies a digital signature (created with the sign method) and returns + * the boolean result. + *

    + * Limitations: A new public/private key pair used for ESAPI 2.0 digital + * signatures with this method and {@link #sign(String)} + * are dynamically created when the default reference implementation class, + * {@link org.owasp.esapi.reference.crypto.JavaEncryptor} is first created. + * Because this key pair is not persisted nor is the public key shared, + * this method and the corresponding {@link #sign(String)} + * can not be used with expected results across JVM instances. This limitation + * will be addressed in ESAPI 2.1. + *

    + * @param signature + * the signature to verify against 'data' + * @param data + * the data to verify against 'signature' + * + * @return + * true, if the signature is verified, false otherwise + * + */ + boolean verifySignature(String signature, String data); + + /** + * Creates a seal that binds a set of data and includes an expiration timestamp. + * + * @param data + * the data to seal + * @param timestamp + * the absolute expiration date of the data, expressed as seconds since the epoch + * + * @return + * the seal + * @throws IntegrityException + * + */ + String seal(String data, long timestamp) throws IntegrityException; + + /** + * Unseals data (created with the seal method) and throws an exception + * describing any of the various problems that could exist with a seal, such + * as an invalid seal format, expired timestamp, or decryption error. + * + * @param seal + * the sealed data + * + * @return + * the original (unsealed) data + * + * @throws EncryptionException + * if the unsealed data cannot be retrieved for any reason + */ + String unseal( String seal ) throws EncryptionException; + + /** + * Verifies a seal (created with the seal method) and throws an exception + * describing any of the various problems that could exist with a seal, such + * as an invalid seal format, expired timestamp, or data mismatch. + * + * @param seal + * the seal to verify + * + * @return + * true, if the seal is valid. False otherwise + */ + boolean verifySeal(String seal); + + /** + * Gets an absolute timestamp representing an offset from the current time to be used by + * other functions in the library. + * + * @param offset + * the offset to add to the current time + * + * @return + * the absolute timestamp + */ + public long getRelativeTimeStamp( long offset ); + + + /** + * Gets a timestamp representing the current date and time to be used by + * other functions in the library. + * + * @return + * a timestamp representing the current time + */ + long getTimeStamp(); + } \ No newline at end of file From 2fea8174b8820d62decbfefc5dc51ecbb5ddcd96 Mon Sep 17 00:00:00 2001 From: kwwall Date: Mon, 18 Jan 2016 23:33:48 -0500 Subject: [PATCH 0066/1069] Change CRLF to LF for issue 356. --- .../AuthenticationAccountsException.java | 118 +++++++++--------- 1 file changed, 59 insertions(+), 59 deletions(-) diff --git a/src/main/java/org/owasp/esapi/errors/AuthenticationAccountsException.java b/src/main/java/org/owasp/esapi/errors/AuthenticationAccountsException.java index 98954db6f..c81ff844b 100644 --- a/src/main/java/org/owasp/esapi/errors/AuthenticationAccountsException.java +++ b/src/main/java/org/owasp/esapi/errors/AuthenticationAccountsException.java @@ -1,59 +1,59 @@ -/** - * OWASP Enterprise Security API (ESAPI) - * - * This file is part of the Open Web Application Security Project (OWASP) - * Enterprise Security API (ESAPI) project. For details, please see - * http://www.owasp.org/index.php/ESAPI. - * - * Copyright (c) 2007 - The OWASP Foundation - * - * The ESAPI is published by OWASP under the BSD license. You should read and accept the - * LICENSE before you use, modify, and/or redistribute this software. - * - * @author Jeff Williams Aspect Security - * @created 2007 - */ -package org.owasp.esapi.errors; - -/** - * An AuthenticationException should be thrown when anything goes wrong during - * login or logout. They are also appropriate for any problems related to - * identity management. - * - * @author Jeff Williams (jeff.williams@aspectsecurity.com) - */ -public class AuthenticationAccountsException extends AuthenticationException { - - /** The Constant serialVersionUID. */ - private static final long serialVersionUID = 1L; - - /** - * Instantiates a new authentication exception. - */ - protected AuthenticationAccountsException() { - // hidden - } - - /** - * Creates a new instance of {@code AuthenticationAccountsException}. - * - * @param userMessage the message displayed to the user - * @param logMessage the message logged - * - */ - public AuthenticationAccountsException(String userMessage, String logMessage) { - super(userMessage, logMessage); - } - - /** - * Instantiates a new authentication exception. - * - * @param userMessage the message displayed to the user - * @param logMessage the message logged - * @param cause the root cause - */ - public AuthenticationAccountsException(String userMessage, String logMessage, Throwable cause) { - super(userMessage, logMessage, cause); - } - -} +/** + * OWASP Enterprise Security API (ESAPI) + * + * This file is part of the Open Web Application Security Project (OWASP) + * Enterprise Security API (ESAPI) project. For details, please see + * http://www.owasp.org/index.php/ESAPI. + * + * Copyright (c) 2007 - The OWASP Foundation + * + * The ESAPI is published by OWASP under the BSD license. You should read and accept the + * LICENSE before you use, modify, and/or redistribute this software. + * + * @author Jeff Williams Aspect Security + * @created 2007 + */ +package org.owasp.esapi.errors; + +/** + * An AuthenticationException should be thrown when anything goes wrong during + * login or logout. They are also appropriate for any problems related to + * identity management. + * + * @author Jeff Williams (jeff.williams@aspectsecurity.com) + */ +public class AuthenticationAccountsException extends AuthenticationException { + + /** The Constant serialVersionUID. */ + private static final long serialVersionUID = 1L; + + /** + * Instantiates a new authentication exception. + */ + protected AuthenticationAccountsException() { + // hidden + } + + /** + * Creates a new instance of {@code AuthenticationAccountsException}. + * + * @param userMessage the message displayed to the user + * @param logMessage the message logged + * + */ + public AuthenticationAccountsException(String userMessage, String logMessage) { + super(userMessage, logMessage); + } + + /** + * Instantiates a new authentication exception. + * + * @param userMessage the message displayed to the user + * @param logMessage the message logged + * @param cause the root cause + */ + public AuthenticationAccountsException(String userMessage, String logMessage, Throwable cause) { + super(userMessage, logMessage, cause); + } + +} From 4cd63092d4f8f98f9bbc3e5a139a735d81d8a3a6 Mon Sep 17 00:00:00 2001 From: kwwall Date: Mon, 18 Jan 2016 23:33:48 -0500 Subject: [PATCH 0067/1069] Change CRLF to LF for issue 356. --- .../AuthenticationCredentialsException.java | 116 +++++++++--------- 1 file changed, 58 insertions(+), 58 deletions(-) diff --git a/src/main/java/org/owasp/esapi/errors/AuthenticationCredentialsException.java b/src/main/java/org/owasp/esapi/errors/AuthenticationCredentialsException.java index 0bcd0a70f..e48187739 100644 --- a/src/main/java/org/owasp/esapi/errors/AuthenticationCredentialsException.java +++ b/src/main/java/org/owasp/esapi/errors/AuthenticationCredentialsException.java @@ -1,58 +1,58 @@ -/** - * OWASP Enterprise Security API (ESAPI) - * - * This file is part of the Open Web Application Security Project (OWASP) - * Enterprise Security API (ESAPI) project. For details, please see - * http://www.owasp.org/index.php/ESAPI. - * - * Copyright (c) 2007 - The OWASP Foundation - * - * The ESAPI is published by OWASP under the BSD license. You should read and accept the - * LICENSE before you use, modify, and/or redistribute this software. - * - * @author Jeff Williams Aspect Security - * @created 2007 - */ -package org.owasp.esapi.errors; - -/** - * An AuthenticationException should be thrown when anything goes wrong during - * login or logout. They are also appropriate for any problems related to - * identity management. - * - * @author Jeff Williams (jeff.williams@aspectsecurity.com) - */ -public class AuthenticationCredentialsException extends AuthenticationException { - - /** The Constant serialVersionUID. */ - private static final long serialVersionUID = 1L; - - /** - * Instantiates a new authentication exception. - */ - protected AuthenticationCredentialsException() { - // hidden - } - - /** - * Creates a new instance of {@code AuthenticationCredentialsException}. - * - * @param userMessage the message displayed to the user - * @param logMessage the message logged - */ - public AuthenticationCredentialsException(String userMessage, String logMessage) { - super(userMessage, logMessage); - } - - /** - * Instantiates a new authentication exception. - * - * @param userMessage the message displayed to the user - * @param logMessage the message logged - * @param cause the root cause - */ - public AuthenticationCredentialsException(String userMessage, String logMessage, Throwable cause) { - super(userMessage, logMessage, cause); - } - -} +/** + * OWASP Enterprise Security API (ESAPI) + * + * This file is part of the Open Web Application Security Project (OWASP) + * Enterprise Security API (ESAPI) project. For details, please see + * http://www.owasp.org/index.php/ESAPI. + * + * Copyright (c) 2007 - The OWASP Foundation + * + * The ESAPI is published by OWASP under the BSD license. You should read and accept the + * LICENSE before you use, modify, and/or redistribute this software. + * + * @author Jeff Williams Aspect Security + * @created 2007 + */ +package org.owasp.esapi.errors; + +/** + * An AuthenticationException should be thrown when anything goes wrong during + * login or logout. They are also appropriate for any problems related to + * identity management. + * + * @author Jeff Williams (jeff.williams@aspectsecurity.com) + */ +public class AuthenticationCredentialsException extends AuthenticationException { + + /** The Constant serialVersionUID. */ + private static final long serialVersionUID = 1L; + + /** + * Instantiates a new authentication exception. + */ + protected AuthenticationCredentialsException() { + // hidden + } + + /** + * Creates a new instance of {@code AuthenticationCredentialsException}. + * + * @param userMessage the message displayed to the user + * @param logMessage the message logged + */ + public AuthenticationCredentialsException(String userMessage, String logMessage) { + super(userMessage, logMessage); + } + + /** + * Instantiates a new authentication exception. + * + * @param userMessage the message displayed to the user + * @param logMessage the message logged + * @param cause the root cause + */ + public AuthenticationCredentialsException(String userMessage, String logMessage, Throwable cause) { + super(userMessage, logMessage, cause); + } + +} From 085618183e6e37ffeee359f974c2e61721bce7e7 Mon Sep 17 00:00:00 2001 From: kwwall Date: Mon, 18 Jan 2016 23:33:48 -0500 Subject: [PATCH 0068/1069] Change CRLF to LF for issue 356. --- .../esapi/errors/AuthenticationException.java | 116 +++++++++--------- 1 file changed, 58 insertions(+), 58 deletions(-) diff --git a/src/main/java/org/owasp/esapi/errors/AuthenticationException.java b/src/main/java/org/owasp/esapi/errors/AuthenticationException.java index 12ef63e9e..c8198f57f 100644 --- a/src/main/java/org/owasp/esapi/errors/AuthenticationException.java +++ b/src/main/java/org/owasp/esapi/errors/AuthenticationException.java @@ -1,58 +1,58 @@ -/** - * OWASP Enterprise Security API (ESAPI) - * - * This file is part of the Open Web Application Security Project (OWASP) - * Enterprise Security API (ESAPI) project. For details, please see - * http://www.owasp.org/index.php/ESAPI. - * - * Copyright (c) 2007 - The OWASP Foundation - * - * The ESAPI is published by OWASP under the BSD license. You should read and accept the - * LICENSE before you use, modify, and/or redistribute this software. - * - * @author Jeff Williams Aspect Security - * @created 2007 - */ -package org.owasp.esapi.errors; - -/** - * An AuthenticationException should be thrown when anything goes wrong during - * login or logout. They are also appropriate for any problems related to - * identity management. - * - * @author Jeff Williams (jeff.williams@aspectsecurity.com) - */ -public class AuthenticationException extends EnterpriseSecurityException { - - /** The Constant serialVersionUID. */ - private static final long serialVersionUID = 1L; - - /** - * Instantiates a new authentication exception. - */ - protected AuthenticationException() { - // hidden - } - - /** - * Creates a new instance of {@code AuthenticationException}. - * - * @param userMessage the message displayed to the user - * @param logMessage the message logged - */ - public AuthenticationException(String userMessage, String logMessage) { - super(userMessage, logMessage); - } - - /** - * Instantiates a new authentication exception. - * - * @param userMessage the message displayed to the user - * @param logMessage the message logged - * @param cause the root cause - */ - public AuthenticationException(String userMessage, String logMessage, Throwable cause) { - super(userMessage, logMessage, cause); - } - -} +/** + * OWASP Enterprise Security API (ESAPI) + * + * This file is part of the Open Web Application Security Project (OWASP) + * Enterprise Security API (ESAPI) project. For details, please see + * http://www.owasp.org/index.php/ESAPI. + * + * Copyright (c) 2007 - The OWASP Foundation + * + * The ESAPI is published by OWASP under the BSD license. You should read and accept the + * LICENSE before you use, modify, and/or redistribute this software. + * + * @author Jeff Williams Aspect Security + * @created 2007 + */ +package org.owasp.esapi.errors; + +/** + * An AuthenticationException should be thrown when anything goes wrong during + * login or logout. They are also appropriate for any problems related to + * identity management. + * + * @author Jeff Williams (jeff.williams@aspectsecurity.com) + */ +public class AuthenticationException extends EnterpriseSecurityException { + + /** The Constant serialVersionUID. */ + private static final long serialVersionUID = 1L; + + /** + * Instantiates a new authentication exception. + */ + protected AuthenticationException() { + // hidden + } + + /** + * Creates a new instance of {@code AuthenticationException}. + * + * @param userMessage the message displayed to the user + * @param logMessage the message logged + */ + public AuthenticationException(String userMessage, String logMessage) { + super(userMessage, logMessage); + } + + /** + * Instantiates a new authentication exception. + * + * @param userMessage the message displayed to the user + * @param logMessage the message logged + * @param cause the root cause + */ + public AuthenticationException(String userMessage, String logMessage, Throwable cause) { + super(userMessage, logMessage, cause); + } + +} From ae57d5a3a2a81eb05b5617bfc3503e9041088144 Mon Sep 17 00:00:00 2001 From: kwwall Date: Mon, 18 Jan 2016 23:33:48 -0500 Subject: [PATCH 0069/1069] Change CRLF to LF for issue 356. --- .../errors/AuthenticationHostException.java | 114 +++++++++--------- 1 file changed, 57 insertions(+), 57 deletions(-) diff --git a/src/main/java/org/owasp/esapi/errors/AuthenticationHostException.java b/src/main/java/org/owasp/esapi/errors/AuthenticationHostException.java index 159a60b67..1deac4b72 100644 --- a/src/main/java/org/owasp/esapi/errors/AuthenticationHostException.java +++ b/src/main/java/org/owasp/esapi/errors/AuthenticationHostException.java @@ -1,57 +1,57 @@ -/** - * OWASP Enterprise Security API (ESAPI) - * - * This file is part of the Open Web Application Security Project (OWASP) - * Enterprise Security API (ESAPI) project. For details, please see - * http://www.owasp.org/index.php/ESAPI. - * - * Copyright (c) 2007 - The OWASP Foundation - * - * The ESAPI is published by OWASP under the BSD license. You should read and accept the - * LICENSE before you use, modify, and/or redistribute this software. - * - * @author Jeff Williams Aspect Security - * @created 2007 - */ -package org.owasp.esapi.errors; - -/** - * An AuthenticationHostException should be thrown when there is a problem with - * the host involved with authentication, particularly if the host changes unexpectedly. - * - * @author Jeff Williams (jeff.williams@aspectsecurity.com) - */ -public class AuthenticationHostException extends AuthenticationException { - - /** The Constant serialVersionUID. */ - private static final long serialVersionUID = 1L; - - /** - * Instantiates a new authentication exception. - */ - protected AuthenticationHostException() { - // hidden - } - - /** - * Creates a new instance of AuthenticationHostException. - * - * @param userMessage the message displayed to the user - * @param logMessage the message logged - */ - public AuthenticationHostException(String userMessage, String logMessage) { - super(userMessage, logMessage); - } - - /** - * Instantiates a new authentication exception. - * - * @param userMessage the message displayed to the user - * @param logMessage the message logged - * @param cause the cause - */ - public AuthenticationHostException(String userMessage, String logMessage, Throwable cause) { - super(userMessage, logMessage, cause); - } - -} +/** + * OWASP Enterprise Security API (ESAPI) + * + * This file is part of the Open Web Application Security Project (OWASP) + * Enterprise Security API (ESAPI) project. For details, please see + * http://www.owasp.org/index.php/ESAPI. + * + * Copyright (c) 2007 - The OWASP Foundation + * + * The ESAPI is published by OWASP under the BSD license. You should read and accept the + * LICENSE before you use, modify, and/or redistribute this software. + * + * @author Jeff Williams Aspect Security + * @created 2007 + */ +package org.owasp.esapi.errors; + +/** + * An AuthenticationHostException should be thrown when there is a problem with + * the host involved with authentication, particularly if the host changes unexpectedly. + * + * @author Jeff Williams (jeff.williams@aspectsecurity.com) + */ +public class AuthenticationHostException extends AuthenticationException { + + /** The Constant serialVersionUID. */ + private static final long serialVersionUID = 1L; + + /** + * Instantiates a new authentication exception. + */ + protected AuthenticationHostException() { + // hidden + } + + /** + * Creates a new instance of AuthenticationHostException. + * + * @param userMessage the message displayed to the user + * @param logMessage the message logged + */ + public AuthenticationHostException(String userMessage, String logMessage) { + super(userMessage, logMessage); + } + + /** + * Instantiates a new authentication exception. + * + * @param userMessage the message displayed to the user + * @param logMessage the message logged + * @param cause the cause + */ + public AuthenticationHostException(String userMessage, String logMessage, Throwable cause) { + super(userMessage, logMessage, cause); + } + +} From 8fa2fec8cc2a392da648388cdca3a42b13a7213d Mon Sep 17 00:00:00 2001 From: kwwall Date: Mon, 18 Jan 2016 23:33:48 -0500 Subject: [PATCH 0070/1069] Change CRLF to LF for issue 356. --- .../errors/AuthenticationLoginException.java | 116 +++++++++--------- 1 file changed, 58 insertions(+), 58 deletions(-) diff --git a/src/main/java/org/owasp/esapi/errors/AuthenticationLoginException.java b/src/main/java/org/owasp/esapi/errors/AuthenticationLoginException.java index 6effd2af9..d389591bc 100644 --- a/src/main/java/org/owasp/esapi/errors/AuthenticationLoginException.java +++ b/src/main/java/org/owasp/esapi/errors/AuthenticationLoginException.java @@ -1,58 +1,58 @@ -/** - * OWASP Enterprise Security API (ESAPI) - * - * This file is part of the Open Web Application Security Project (OWASP) - * Enterprise Security API (ESAPI) project. For details, please see - * http://www.owasp.org/index.php/ESAPI. - * - * Copyright (c) 2007 - The OWASP Foundation - * - * The ESAPI is published by OWASP under the BSD license. You should read and accept the - * LICENSE before you use, modify, and/or redistribute this software. - * - * @author Jeff Williams Aspect Security - * @created 2007 - */ -package org.owasp.esapi.errors; - -/** - * An AuthenticationException should be thrown when anything goes wrong during - * login or logout. They are also appropriate for any problems related to - * identity management. - * - * @author Jeff Williams (jeff.williams@aspectsecurity.com) - */ -public class AuthenticationLoginException extends AuthenticationException { - - /** The Constant serialVersionUID. */ - private static final long serialVersionUID = 1L; - - /** - * Instantiates a new authentication exception. - */ - protected AuthenticationLoginException() { - // hidden - } - - /** - * Creates a new instance of EnterpriseSecurityException. - * - * @param userMessage the message displayed to the user - * @param logMessage the message logged - */ - public AuthenticationLoginException(String userMessage, String logMessage) { - super(userMessage, logMessage); - } - - /** - * Instantiates a new authentication exception. - * - * @param userMessage the message displayed to the user - * @param logMessage the message logged - * @param cause the cause - */ - public AuthenticationLoginException(String userMessage, String logMessage, Throwable cause) { - super(userMessage, logMessage, cause); - } - -} +/** + * OWASP Enterprise Security API (ESAPI) + * + * This file is part of the Open Web Application Security Project (OWASP) + * Enterprise Security API (ESAPI) project. For details, please see + * http://www.owasp.org/index.php/ESAPI. + * + * Copyright (c) 2007 - The OWASP Foundation + * + * The ESAPI is published by OWASP under the BSD license. You should read and accept the + * LICENSE before you use, modify, and/or redistribute this software. + * + * @author Jeff Williams Aspect Security + * @created 2007 + */ +package org.owasp.esapi.errors; + +/** + * An AuthenticationException should be thrown when anything goes wrong during + * login or logout. They are also appropriate for any problems related to + * identity management. + * + * @author Jeff Williams (jeff.williams@aspectsecurity.com) + */ +public class AuthenticationLoginException extends AuthenticationException { + + /** The Constant serialVersionUID. */ + private static final long serialVersionUID = 1L; + + /** + * Instantiates a new authentication exception. + */ + protected AuthenticationLoginException() { + // hidden + } + + /** + * Creates a new instance of EnterpriseSecurityException. + * + * @param userMessage the message displayed to the user + * @param logMessage the message logged + */ + public AuthenticationLoginException(String userMessage, String logMessage) { + super(userMessage, logMessage); + } + + /** + * Instantiates a new authentication exception. + * + * @param userMessage the message displayed to the user + * @param logMessage the message logged + * @param cause the cause + */ + public AuthenticationLoginException(String userMessage, String logMessage, Throwable cause) { + super(userMessage, logMessage, cause); + } + +} From eaa5d7493f6eb0ffd936730e6f5314de53d09fa0 Mon Sep 17 00:00:00 2001 From: kwwall Date: Mon, 18 Jan 2016 23:33:48 -0500 Subject: [PATCH 0071/1069] Change CRLF to LF for issue 356. --- .../esapi/errors/AvailabilityException.java | 114 +++++++++--------- 1 file changed, 57 insertions(+), 57 deletions(-) diff --git a/src/main/java/org/owasp/esapi/errors/AvailabilityException.java b/src/main/java/org/owasp/esapi/errors/AvailabilityException.java index 3530cc3a2..3ec04b677 100644 --- a/src/main/java/org/owasp/esapi/errors/AvailabilityException.java +++ b/src/main/java/org/owasp/esapi/errors/AvailabilityException.java @@ -1,57 +1,57 @@ -/** - * OWASP Enterprise Security API (ESAPI) - * - * This file is part of the Open Web Application Security Project (OWASP) - * Enterprise Security API (ESAPI) project. For details, please see - * http://www.owasp.org/index.php/ESAPI. - * - * Copyright (c) 2007 - The OWASP Foundation - * - * The ESAPI is published by OWASP under the BSD license. You should read and accept the - * LICENSE before you use, modify, and/or redistribute this software. - * - * @author Jeff Williams Aspect Security - * @created 2007 - */ -package org.owasp.esapi.errors; - -/** - * An AvailabilityException should be thrown when the availability of a limited - * resource is in jeopardy. For example, if a database connection pool runs out - * of connections, an availability exception should be thrown. - * - * @author Jeff Williams (jeff.williams@aspectsecurity.com) - */ -public class AvailabilityException extends EnterpriseSecurityException { - - /** The Constant serialVersionUID. */ - private static final long serialVersionUID = 1L; - - /** - * Instantiates a new availability exception. - */ - protected AvailabilityException() { - // hidden - } - - /** - * Creates a new instance of AvailabilityException. - * - * @param userMessage the message displayed to the user - * @param logMessage the message logged - */ - public AvailabilityException(String userMessage, String logMessage) { - super(userMessage, logMessage); - } - - /** - * Instantiates a new AvailabilityException. - * - * @param userMessage the message displayed to the user - * @param logMessage the message logged - * @param cause the cause - */ - public AvailabilityException(String userMessage, String logMessage, Throwable cause) { - super(userMessage, logMessage, cause); - } -} +/** + * OWASP Enterprise Security API (ESAPI) + * + * This file is part of the Open Web Application Security Project (OWASP) + * Enterprise Security API (ESAPI) project. For details, please see + * http://www.owasp.org/index.php/ESAPI. + * + * Copyright (c) 2007 - The OWASP Foundation + * + * The ESAPI is published by OWASP under the BSD license. You should read and accept the + * LICENSE before you use, modify, and/or redistribute this software. + * + * @author Jeff Williams Aspect Security + * @created 2007 + */ +package org.owasp.esapi.errors; + +/** + * An AvailabilityException should be thrown when the availability of a limited + * resource is in jeopardy. For example, if a database connection pool runs out + * of connections, an availability exception should be thrown. + * + * @author Jeff Williams (jeff.williams@aspectsecurity.com) + */ +public class AvailabilityException extends EnterpriseSecurityException { + + /** The Constant serialVersionUID. */ + private static final long serialVersionUID = 1L; + + /** + * Instantiates a new availability exception. + */ + protected AvailabilityException() { + // hidden + } + + /** + * Creates a new instance of AvailabilityException. + * + * @param userMessage the message displayed to the user + * @param logMessage the message logged + */ + public AvailabilityException(String userMessage, String logMessage) { + super(userMessage, logMessage); + } + + /** + * Instantiates a new AvailabilityException. + * + * @param userMessage the message displayed to the user + * @param logMessage the message logged + * @param cause the cause + */ + public AvailabilityException(String userMessage, String logMessage, Throwable cause) { + super(userMessage, logMessage, cause); + } +} From 4eccc3c2c999ee98165a89f4672537579a25835a Mon Sep 17 00:00:00 2001 From: kwwall Date: Mon, 18 Jan 2016 23:33:48 -0500 Subject: [PATCH 0072/1069] Change CRLF to LF for issue 356. --- .../esapi/errors/CertificateException.java | 112 +++++++++--------- 1 file changed, 56 insertions(+), 56 deletions(-) diff --git a/src/main/java/org/owasp/esapi/errors/CertificateException.java b/src/main/java/org/owasp/esapi/errors/CertificateException.java index 957d32aac..2f84b2c01 100644 --- a/src/main/java/org/owasp/esapi/errors/CertificateException.java +++ b/src/main/java/org/owasp/esapi/errors/CertificateException.java @@ -1,56 +1,56 @@ -/** - * OWASP Enterprise Security API (ESAPI) - * - * This file is part of the Open Web Application Security Project (OWASP) - * Enterprise Security API (ESAPI) project. For details, please see - * http://www.owasp.org/index.php/ESAPI. - * - * Copyright (c) 2007 - The OWASP Foundation - * - * The ESAPI is published by OWASP under the BSD license. You should read and accept the - * LICENSE before you use, modify, and/or redistribute this software. - * - * @author Jeff Williams Aspect Security - * @created 2007 - */ -package org.owasp.esapi.errors; - -/** - * A CertificateException should be thrown for any problems that arise during - * processing of digital certificates. - * - * @author Jeff Williams (jeff.williams@aspectsecurity.com) - */ -public class CertificateException extends EnterpriseSecurityException { - - /** The Constant serialVersionUID. */ - private static final long serialVersionUID = 1L; - - /** - * Instantiates a new certificate exception. - */ - protected CertificateException() { - // hidden - } - - /** - * Creates a new instance of CertificateException. - * - * @param userMessage the message displayed to the user - * @param logMessage the message logged - */ - public CertificateException(String userMessage, String logMessage) { - super(userMessage, logMessage); - } - - /** - * Instantiates a new CertificateException. - * - * @param userMessage the message displayed to the user - * @param logMessage the message logged - * @param cause the cause - */ - public CertificateException(String userMessage, String logMessage, Throwable cause) { - super(userMessage, logMessage, cause); - } -} +/** + * OWASP Enterprise Security API (ESAPI) + * + * This file is part of the Open Web Application Security Project (OWASP) + * Enterprise Security API (ESAPI) project. For details, please see + * http://www.owasp.org/index.php/ESAPI. + * + * Copyright (c) 2007 - The OWASP Foundation + * + * The ESAPI is published by OWASP under the BSD license. You should read and accept the + * LICENSE before you use, modify, and/or redistribute this software. + * + * @author Jeff Williams Aspect Security + * @created 2007 + */ +package org.owasp.esapi.errors; + +/** + * A CertificateException should be thrown for any problems that arise during + * processing of digital certificates. + * + * @author Jeff Williams (jeff.williams@aspectsecurity.com) + */ +public class CertificateException extends EnterpriseSecurityException { + + /** The Constant serialVersionUID. */ + private static final long serialVersionUID = 1L; + + /** + * Instantiates a new certificate exception. + */ + protected CertificateException() { + // hidden + } + + /** + * Creates a new instance of CertificateException. + * + * @param userMessage the message displayed to the user + * @param logMessage the message logged + */ + public CertificateException(String userMessage, String logMessage) { + super(userMessage, logMessage); + } + + /** + * Instantiates a new CertificateException. + * + * @param userMessage the message displayed to the user + * @param logMessage the message logged + * @param cause the cause + */ + public CertificateException(String userMessage, String logMessage, Throwable cause) { + super(userMessage, logMessage, cause); + } +} From 3e0fccfb1040715dbcb7d61314b86906892eff04 Mon Sep 17 00:00:00 2001 From: kwwall Date: Mon, 18 Jan 2016 23:33:48 -0500 Subject: [PATCH 0073/1069] Change CRLF to LF for issue 356. --- .../esapi/errors/ConfigurationException.java | 68 +++++++++---------- 1 file changed, 34 insertions(+), 34 deletions(-) diff --git a/src/main/java/org/owasp/esapi/errors/ConfigurationException.java b/src/main/java/org/owasp/esapi/errors/ConfigurationException.java index f34c25a36..4e6a38130 100644 --- a/src/main/java/org/owasp/esapi/errors/ConfigurationException.java +++ b/src/main/java/org/owasp/esapi/errors/ConfigurationException.java @@ -1,34 +1,34 @@ -package org.owasp.esapi.errors; - -/** - * A {@code ConfigurationException} should be thrown when a problem arises because of - * a problem in one of ESAPI's configuration files, such as a missing required - * property or invalid setting of a property, or missing or unreadable - * configuration file, etc. - *

    - * A {@code ConfigurationException} is a {@code RuntimeException} - * because 1) configuration properties can, for the most part, only be checked - * at run-time, and 2) we want this to be an unchecked exception to make ESAPI - * easy to use and not cluttered with catching a bunch of try/catch blocks. - *

    - */ -public class ConfigurationException extends RuntimeException { - - protected static final long serialVersionUID = 1L; - - public ConfigurationException(Exception e) { - super(e); - } - - public ConfigurationException(String s) { - super(s); - } - - public ConfigurationException(String s, Throwable cause) { - super(s, cause); - } - - public ConfigurationException(Throwable cause) { - super(cause); - } -} +package org.owasp.esapi.errors; + +/** + * A {@code ConfigurationException} should be thrown when a problem arises because of + * a problem in one of ESAPI's configuration files, such as a missing required + * property or invalid setting of a property, or missing or unreadable + * configuration file, etc. + *

    + * A {@code ConfigurationException} is a {@code RuntimeException} + * because 1) configuration properties can, for the most part, only be checked + * at run-time, and 2) we want this to be an unchecked exception to make ESAPI + * easy to use and not cluttered with catching a bunch of try/catch blocks. + *

    + */ +public class ConfigurationException extends RuntimeException { + + protected static final long serialVersionUID = 1L; + + public ConfigurationException(Exception e) { + super(e); + } + + public ConfigurationException(String s) { + super(s); + } + + public ConfigurationException(String s, Throwable cause) { + super(s, cause); + } + + public ConfigurationException(Throwable cause) { + super(cause); + } +} From 43643d7e1255baa403e81ede0c0d2aeadcd00a61 Mon Sep 17 00:00:00 2001 From: kwwall Date: Mon, 18 Jan 2016 23:33:48 -0500 Subject: [PATCH 0074/1069] Change CRLF to LF for issue 356. --- .../owasp/esapi/errors/EncodingException.java | 124 +++++++++--------- 1 file changed, 62 insertions(+), 62 deletions(-) diff --git a/src/main/java/org/owasp/esapi/errors/EncodingException.java b/src/main/java/org/owasp/esapi/errors/EncodingException.java index 9335004a1..f2b269445 100644 --- a/src/main/java/org/owasp/esapi/errors/EncodingException.java +++ b/src/main/java/org/owasp/esapi/errors/EncodingException.java @@ -1,62 +1,62 @@ -/** - * OWASP Enterprise Security API (ESAPI) - * - * This file is part of the Open Web Application Security Project (OWASP) - * Enterprise Security API (ESAPI) project. For details, please see - * http://www.owasp.org/index.php/ESAPI. - * - * Copyright (c) 2007 - The OWASP Foundation - * - * The ESAPI is published by OWASP under the BSD license. You should read and accept the - * LICENSE before you use, modify, and/or redistribute this software. - * - * @author Jeff Williams Aspect Security - * @created 2007 - */ -package org.owasp.esapi.errors; - -/** - * An EncodingException should be thrown for any problems that occur when - * encoding or decoding data. - * - * @author Jeff Williams (jeff.williams@aspectsecurity.com) - */ -public class EncodingException extends EnterpriseSecurityException { - - /** The Constant serialVersionUID. */ - private static final long serialVersionUID = 1L; - - /** - * Instantiates a new service exception. - */ - protected EncodingException() { - // hidden - } - - /** - * Creates a new instance of EncodingException. - * - * @param userMessage - * the message displayed to the user - * @param logMessage - * the message logged - */ - public EncodingException(String userMessage, String logMessage) { - super(userMessage, logMessage); - } - - /** - * Instantiates a new EncodingException. - * - * @param userMessage - * the message displayed to the user - * @param logMessage - * the message logged - * @param cause - * the cause - */ - public EncodingException(String userMessage, String logMessage, Throwable cause) { - super(userMessage, logMessage, cause); - } - -} +/** + * OWASP Enterprise Security API (ESAPI) + * + * This file is part of the Open Web Application Security Project (OWASP) + * Enterprise Security API (ESAPI) project. For details, please see + * http://www.owasp.org/index.php/ESAPI. + * + * Copyright (c) 2007 - The OWASP Foundation + * + * The ESAPI is published by OWASP under the BSD license. You should read and accept the + * LICENSE before you use, modify, and/or redistribute this software. + * + * @author Jeff Williams Aspect Security + * @created 2007 + */ +package org.owasp.esapi.errors; + +/** + * An EncodingException should be thrown for any problems that occur when + * encoding or decoding data. + * + * @author Jeff Williams (jeff.williams@aspectsecurity.com) + */ +public class EncodingException extends EnterpriseSecurityException { + + /** The Constant serialVersionUID. */ + private static final long serialVersionUID = 1L; + + /** + * Instantiates a new service exception. + */ + protected EncodingException() { + // hidden + } + + /** + * Creates a new instance of EncodingException. + * + * @param userMessage + * the message displayed to the user + * @param logMessage + * the message logged + */ + public EncodingException(String userMessage, String logMessage) { + super(userMessage, logMessage); + } + + /** + * Instantiates a new EncodingException. + * + * @param userMessage + * the message displayed to the user + * @param logMessage + * the message logged + * @param cause + * the cause + */ + public EncodingException(String userMessage, String logMessage, Throwable cause) { + super(userMessage, logMessage, cause); + } + +} From 0b856339cfcf20a3c0b72f99ebfa349ed6b945b1 Mon Sep 17 00:00:00 2001 From: kwwall Date: Mon, 18 Jan 2016 23:33:48 -0500 Subject: [PATCH 0075/1069] Change CRLF to LF for issue 356. --- .../esapi/errors/EncryptionException.java | 122 +++++++++--------- 1 file changed, 61 insertions(+), 61 deletions(-) diff --git a/src/main/java/org/owasp/esapi/errors/EncryptionException.java b/src/main/java/org/owasp/esapi/errors/EncryptionException.java index 965a52e24..163a1d228 100644 --- a/src/main/java/org/owasp/esapi/errors/EncryptionException.java +++ b/src/main/java/org/owasp/esapi/errors/EncryptionException.java @@ -1,61 +1,61 @@ -/** - * OWASP Enterprise Security API (ESAPI) - * - * This file is part of the Open Web Application Security Project (OWASP) - * Enterprise Security API (ESAPI) project. For details, please see - * http://www.owasp.org/index.php/ESAPI. - * - * Copyright (c) 2007 - The OWASP Foundation - * - * The ESAPI is published by OWASP under the BSD license. You should read and accept the - * LICENSE before you use, modify, and/or redistribute this software. - * - * @author Jeff Williams Aspect Security - * @created 2007 - */ -package org.owasp.esapi.errors; - -/** - * An EncryptionException should be thrown for any problems related to - * encryption, hashing, or digital signatures. - * - * @author Jeff Williams (jeff.williams@aspectsecurity.com) - */ -public class EncryptionException extends EnterpriseSecurityException { - - /** The Constant serialVersionUID. */ - private static final long serialVersionUID = 1L; - - /** - * Instantiates a new EncryptionException. - */ - protected EncryptionException() { - // hidden - } - - /** - * Creates a new instance of EncryptionException. - * - * @param userMessage - * the message displayed to the user - * @param logMessage - * the message logged - */ - public EncryptionException(String userMessage, String logMessage) { - super(userMessage, logMessage); - } - - /** - * Instantiates a new EncryptionException. - * - * @param userMessage - * the message displayed to the user - * @param logMessage - * the message logged - * @param cause - * the cause - */ - public EncryptionException(String userMessage, String logMessage, Throwable cause) { - super(userMessage, logMessage, cause); - } -} +/** + * OWASP Enterprise Security API (ESAPI) + * + * This file is part of the Open Web Application Security Project (OWASP) + * Enterprise Security API (ESAPI) project. For details, please see + * http://www.owasp.org/index.php/ESAPI. + * + * Copyright (c) 2007 - The OWASP Foundation + * + * The ESAPI is published by OWASP under the BSD license. You should read and accept the + * LICENSE before you use, modify, and/or redistribute this software. + * + * @author Jeff Williams Aspect Security + * @created 2007 + */ +package org.owasp.esapi.errors; + +/** + * An EncryptionException should be thrown for any problems related to + * encryption, hashing, or digital signatures. + * + * @author Jeff Williams (jeff.williams@aspectsecurity.com) + */ +public class EncryptionException extends EnterpriseSecurityException { + + /** The Constant serialVersionUID. */ + private static final long serialVersionUID = 1L; + + /** + * Instantiates a new EncryptionException. + */ + protected EncryptionException() { + // hidden + } + + /** + * Creates a new instance of EncryptionException. + * + * @param userMessage + * the message displayed to the user + * @param logMessage + * the message logged + */ + public EncryptionException(String userMessage, String logMessage) { + super(userMessage, logMessage); + } + + /** + * Instantiates a new EncryptionException. + * + * @param userMessage + * the message displayed to the user + * @param logMessage + * the message logged + * @param cause + * the cause + */ + public EncryptionException(String userMessage, String logMessage, Throwable cause) { + super(userMessage, logMessage, cause); + } +} From 778d48f75f5130e03173ad613c47ab673da0c01a Mon Sep 17 00:00:00 2001 From: kwwall Date: Mon, 18 Jan 2016 23:33:48 -0500 Subject: [PATCH 0076/1069] Change CRLF to LF for issue 356. --- .../errors/EnterpriseSecurityException.java | 300 +++++++++--------- 1 file changed, 150 insertions(+), 150 deletions(-) diff --git a/src/main/java/org/owasp/esapi/errors/EnterpriseSecurityException.java b/src/main/java/org/owasp/esapi/errors/EnterpriseSecurityException.java index 8c6d46114..93ee30191 100644 --- a/src/main/java/org/owasp/esapi/errors/EnterpriseSecurityException.java +++ b/src/main/java/org/owasp/esapi/errors/EnterpriseSecurityException.java @@ -1,150 +1,150 @@ -/** - * OWASP Enterprise Security API (ESAPI) - * - * This file is part of the Open Web Application Security Project (OWASP) - * Enterprise Security API (ESAPI) project. For details, please see - * http://www.owasp.org/index.php/ESAPI. - * - * Copyright (c) 2007 - The OWASP Foundation - * - * The ESAPI is published by OWASP under the BSD license. You should read and accept the - * LICENSE before you use, modify, and/or redistribute this software. - * - * @author Jeff Williams Aspect Security - * @created 2007 - */ -package org.owasp.esapi.errors; - -import org.owasp.esapi.ESAPI; -import org.owasp.esapi.Logger; - - -/** - * EnterpriseSecurityException is the base class for all security related exceptions. You should pass in the root cause - * exception where possible. Constructors for classes extending EnterpriseSecurityException should be sure to call the - * appropriate super() method in order to ensure that logging and intrusion detection occur properly. - *

    - * All EnterpriseSecurityExceptions have two messages, one for the user and one for the log file. This way, a message - * can be shown to the user that doesn't contain sensitive information or unnecessary implementation details. Meanwhile, - * all the critical information can be included in the exception so that it gets logged. - *

    - * Note that the "logMessage" for ALL EnterpriseSecurityExceptions is logged in the log file. This feature should be - * used extensively throughout ESAPI implementations and the result is a fairly complete set of security log records. - * ALL EnterpriseSecurityExceptions are also sent to the IntrusionDetector for use in detecting anomalous patterns of - * application usage. - *

    - * @author Jeff Williams (jeff.williams@aspectsecurity.com) - */ -public class EnterpriseSecurityException extends Exception { - - protected static final long serialVersionUID = 1L; - - /** The logger. */ - protected final transient Logger logger = ESAPI.getLogger("EnterpriseSecurityException"); - - /** - * - */ - protected String logMessage = null; - - /** - * Instantiates a new enterprise security exception. - */ - protected EnterpriseSecurityException() { - // hidden - } - - /** - * Instantiates a new enterprise security exception with a user message. - * (Needed by anything which subclasses this.) - * - * @param userMessage Message displayed to user. - */ - protected EnterpriseSecurityException(String userMessage) { - // hidden - super(userMessage); - } - - /** - * Instantiates a new enterprise security exception with a user message - * and cause. (Needed by anything which subclasses this.) - * - * @param userMessage Message displayed to user. - * @param cause The cause (which is saved for later retrieval by the - * getCause() method). (A null value is permitted, and indicates that the - * cause is nonexistent or unknown.) - */ - protected EnterpriseSecurityException(String userMessage, Throwable cause) - { - // hidden - super(userMessage, cause); - } - - /** - * Creates a new instance of EnterpriseSecurityException. This exception is automatically logged, so that simply by - * using this API, applications will generate an extensive security log. In addition, this exception is - * automatically registered with the IntrusionDetector, so that quotas can be checked. - * - * It should be noted that messages that are intended to be displayed to the user should be safe for display. In - * other words, don't pass in unsanitized data here. Also could hold true for the logging message depending on the - * context of the exception. - * - * @param userMessage - * the message displayed to the user - * @param logMessage - * the message logged - */ - public EnterpriseSecurityException(String userMessage, String logMessage) { - super(userMessage); - this.logMessage = logMessage; - if (!ESAPI.securityConfiguration().getDisableIntrusionDetection()) { - ESAPI.intrusionDetector().addException(this); - } - } - - /** - * Creates a new instance of EnterpriseSecurityException that includes a root cause Throwable. - * - * It should be noted that messages that are intended to be displayed to the user should be safe for display. In - * other words, don't pass in unsanitized data here. Also could hold true for the logging message depending on the - * context of the exception. - * - * @param userMessage - * the message displayed to the user - * @param logMessage - * the message logged - * @param cause the cause - */ - public EnterpriseSecurityException(String userMessage, String logMessage, Throwable cause) { - super(userMessage, cause); - this.logMessage = logMessage; - if (!ESAPI.securityConfiguration().getDisableIntrusionDetection()) { - ESAPI.intrusionDetector().addException(this); - } - } - - /** - * Returns message meant for display to users - * - * Note that if you are unsure of what set this message, it would probably - * be a good idea to encode this message before displaying it to the end user. - * - * @return a String containing a message that is safe to display to users - */ - public String getUserMessage() { - return getMessage(); - } - - /** - * Returns a message that is safe to display in logs, but may contain - * sensitive information and therefore probably should not be displayed to - * users. - * - * @return a String containing a message that is safe to display in logs, - * but probably not to users as it may contain sensitive information. - */ - public String getLogMessage() { - return logMessage; - } - -} +/** + * OWASP Enterprise Security API (ESAPI) + * + * This file is part of the Open Web Application Security Project (OWASP) + * Enterprise Security API (ESAPI) project. For details, please see + * http://www.owasp.org/index.php/ESAPI. + * + * Copyright (c) 2007 - The OWASP Foundation + * + * The ESAPI is published by OWASP under the BSD license. You should read and accept the + * LICENSE before you use, modify, and/or redistribute this software. + * + * @author Jeff Williams Aspect Security + * @created 2007 + */ +package org.owasp.esapi.errors; + +import org.owasp.esapi.ESAPI; +import org.owasp.esapi.Logger; + + +/** + * EnterpriseSecurityException is the base class for all security related exceptions. You should pass in the root cause + * exception where possible. Constructors for classes extending EnterpriseSecurityException should be sure to call the + * appropriate super() method in order to ensure that logging and intrusion detection occur properly. + *

    + * All EnterpriseSecurityExceptions have two messages, one for the user and one for the log file. This way, a message + * can be shown to the user that doesn't contain sensitive information or unnecessary implementation details. Meanwhile, + * all the critical information can be included in the exception so that it gets logged. + *

    + * Note that the "logMessage" for ALL EnterpriseSecurityExceptions is logged in the log file. This feature should be + * used extensively throughout ESAPI implementations and the result is a fairly complete set of security log records. + * ALL EnterpriseSecurityExceptions are also sent to the IntrusionDetector for use in detecting anomalous patterns of + * application usage. + *

    + * @author Jeff Williams (jeff.williams@aspectsecurity.com) + */ +public class EnterpriseSecurityException extends Exception { + + protected static final long serialVersionUID = 1L; + + /** The logger. */ + protected final transient Logger logger = ESAPI.getLogger("EnterpriseSecurityException"); + + /** + * + */ + protected String logMessage = null; + + /** + * Instantiates a new enterprise security exception. + */ + protected EnterpriseSecurityException() { + // hidden + } + + /** + * Instantiates a new enterprise security exception with a user message. + * (Needed by anything which subclasses this.) + * + * @param userMessage Message displayed to user. + */ + protected EnterpriseSecurityException(String userMessage) { + // hidden + super(userMessage); + } + + /** + * Instantiates a new enterprise security exception with a user message + * and cause. (Needed by anything which subclasses this.) + * + * @param userMessage Message displayed to user. + * @param cause The cause (which is saved for later retrieval by the + * getCause() method). (A null value is permitted, and indicates that the + * cause is nonexistent or unknown.) + */ + protected EnterpriseSecurityException(String userMessage, Throwable cause) + { + // hidden + super(userMessage, cause); + } + + /** + * Creates a new instance of EnterpriseSecurityException. This exception is automatically logged, so that simply by + * using this API, applications will generate an extensive security log. In addition, this exception is + * automatically registered with the IntrusionDetector, so that quotas can be checked. + * + * It should be noted that messages that are intended to be displayed to the user should be safe for display. In + * other words, don't pass in unsanitized data here. Also could hold true for the logging message depending on the + * context of the exception. + * + * @param userMessage + * the message displayed to the user + * @param logMessage + * the message logged + */ + public EnterpriseSecurityException(String userMessage, String logMessage) { + super(userMessage); + this.logMessage = logMessage; + if (!ESAPI.securityConfiguration().getDisableIntrusionDetection()) { + ESAPI.intrusionDetector().addException(this); + } + } + + /** + * Creates a new instance of EnterpriseSecurityException that includes a root cause Throwable. + * + * It should be noted that messages that are intended to be displayed to the user should be safe for display. In + * other words, don't pass in unsanitized data here. Also could hold true for the logging message depending on the + * context of the exception. + * + * @param userMessage + * the message displayed to the user + * @param logMessage + * the message logged + * @param cause the cause + */ + public EnterpriseSecurityException(String userMessage, String logMessage, Throwable cause) { + super(userMessage, cause); + this.logMessage = logMessage; + if (!ESAPI.securityConfiguration().getDisableIntrusionDetection()) { + ESAPI.intrusionDetector().addException(this); + } + } + + /** + * Returns message meant for display to users + * + * Note that if you are unsure of what set this message, it would probably + * be a good idea to encode this message before displaying it to the end user. + * + * @return a String containing a message that is safe to display to users + */ + public String getUserMessage() { + return getMessage(); + } + + /** + * Returns a message that is safe to display in logs, but may contain + * sensitive information and therefore probably should not be displayed to + * users. + * + * @return a String containing a message that is safe to display in logs, + * but probably not to users as it may contain sensitive information. + */ + public String getLogMessage() { + return logMessage; + } + +} From 9d864eaf3240701272d166888a889962bdb8620c Mon Sep 17 00:00:00 2001 From: kwwall Date: Mon, 18 Jan 2016 23:33:48 -0500 Subject: [PATCH 0077/1069] Change CRLF to LF for issue 356. --- .../owasp/esapi/errors/ExecutorException.java | 124 +++++++++--------- 1 file changed, 62 insertions(+), 62 deletions(-) diff --git a/src/main/java/org/owasp/esapi/errors/ExecutorException.java b/src/main/java/org/owasp/esapi/errors/ExecutorException.java index d453129cd..a27c74fa3 100644 --- a/src/main/java/org/owasp/esapi/errors/ExecutorException.java +++ b/src/main/java/org/owasp/esapi/errors/ExecutorException.java @@ -1,62 +1,62 @@ -/** - * OWASP Enterprise Security API (ESAPI) - * - * This file is part of the Open Web Application Security Project (OWASP) - * Enterprise Security API (ESAPI) project. For details, please see - * http://www.owasp.org/index.php/ESAPI. - * - * Copyright (c) 2007 - The OWASP Foundation - * - * The ESAPI is published by OWASP under the BSD license. You should read and accept the - * LICENSE before you use, modify, and/or redistribute this software. - * - * @author Jeff Williams Aspect Security - * @created 2007 - */ -package org.owasp.esapi.errors; - -/** - * An ExecutorException should be thrown for any problems that arise during the - * execution of a system executable. - * - * @author Jeff Williams (jeff.williams@aspectsecurity.com) - */ -public class ExecutorException extends EnterpriseSecurityException { - - /** The Constant serialVersionUID. */ - private static final long serialVersionUID = 1L; - - /** - * Instantiates a new ExecutorException. - */ - protected ExecutorException() { - // hidden - } - - /** - * Creates a new instance of ExecutorException. - * - * @param userMessage - * the message to display to users - * @param logMessage - * the message logged - */ - public ExecutorException(String userMessage, String logMessage) { - super(userMessage, logMessage); - } - - /** - * Instantiates a new ExecutorException. - * - * @param userMessage - * the message to display to users - * @param logMessage - * the message logged - * @param cause - * the cause - */ - public ExecutorException(String userMessage, String logMessage, Throwable cause) { - super(userMessage, logMessage, cause); - } - -} +/** + * OWASP Enterprise Security API (ESAPI) + * + * This file is part of the Open Web Application Security Project (OWASP) + * Enterprise Security API (ESAPI) project. For details, please see + * http://www.owasp.org/index.php/ESAPI. + * + * Copyright (c) 2007 - The OWASP Foundation + * + * The ESAPI is published by OWASP under the BSD license. You should read and accept the + * LICENSE before you use, modify, and/or redistribute this software. + * + * @author Jeff Williams Aspect Security + * @created 2007 + */ +package org.owasp.esapi.errors; + +/** + * An ExecutorException should be thrown for any problems that arise during the + * execution of a system executable. + * + * @author Jeff Williams (jeff.williams@aspectsecurity.com) + */ +public class ExecutorException extends EnterpriseSecurityException { + + /** The Constant serialVersionUID. */ + private static final long serialVersionUID = 1L; + + /** + * Instantiates a new ExecutorException. + */ + protected ExecutorException() { + // hidden + } + + /** + * Creates a new instance of ExecutorException. + * + * @param userMessage + * the message to display to users + * @param logMessage + * the message logged + */ + public ExecutorException(String userMessage, String logMessage) { + super(userMessage, logMessage); + } + + /** + * Instantiates a new ExecutorException. + * + * @param userMessage + * the message to display to users + * @param logMessage + * the message logged + * @param cause + * the cause + */ + public ExecutorException(String userMessage, String logMessage, Throwable cause) { + super(userMessage, logMessage, cause); + } + +} From e3066d0fb2fa08b5abbe6e37023837b59f866324 Mon Sep 17 00:00:00 2001 From: kwwall Date: Mon, 18 Jan 2016 23:33:48 -0500 Subject: [PATCH 0078/1069] Change CRLF to LF for issue 356. --- .../esapi/errors/IntegrityException.java | 124 +++++++++--------- 1 file changed, 62 insertions(+), 62 deletions(-) diff --git a/src/main/java/org/owasp/esapi/errors/IntegrityException.java b/src/main/java/org/owasp/esapi/errors/IntegrityException.java index 02e37979a..59d97a189 100644 --- a/src/main/java/org/owasp/esapi/errors/IntegrityException.java +++ b/src/main/java/org/owasp/esapi/errors/IntegrityException.java @@ -1,62 +1,62 @@ -/** - * OWASP Enterprise Security API (ESAPI) - * - * This file is part of the Open Web Application Security Project (OWASP) - * Enterprise Security API (ESAPI) project. For details, please see - * http://www.owasp.org/index.php/ESAPI. - * - * Copyright (c) 2007 - The OWASP Foundation - * - * The ESAPI is published by OWASP under the BSD license. You should read and accept the - * LICENSE before you use, modify, and/or redistribute this software. - * - * @author Jeff Williams Aspect Security - * @created 2007 - */ -package org.owasp.esapi.errors; - -/** - * An IntegrityException should be thrown when a problem with the integrity of data - * has been detected. For example, if a financial account cannot be reconciled after - * a transaction has been performed, an integrity exception should be thrown. - * - * @author Jeff Williams (jeff.williams@aspectsecurity.com) - */ -public class IntegrityException extends EnterpriseSecurityException { - - /** The Constant serialVersionUID. */ - private static final long serialVersionUID = 1L; - - /** - * Instantiates a new availability exception. - */ - protected IntegrityException() { - // hidden - } - - /** - * Creates a new instance of IntegrityException. - * - * @param userMessage - * the message to display to users - * @param logMessage - * the message logged - */ - public IntegrityException(String userMessage, String logMessage) { - super(userMessage, logMessage); - } - - /** - * Instantiates a new IntegrityException. - * - * @param userMessage - * the message to display to users - * @param logMessage - * the message logged - * @param cause - * the cause - */ - public IntegrityException(String userMessage, String logMessage, Throwable cause) { - super(userMessage, logMessage, cause); - } -} +/** + * OWASP Enterprise Security API (ESAPI) + * + * This file is part of the Open Web Application Security Project (OWASP) + * Enterprise Security API (ESAPI) project. For details, please see + * http://www.owasp.org/index.php/ESAPI. + * + * Copyright (c) 2007 - The OWASP Foundation + * + * The ESAPI is published by OWASP under the BSD license. You should read and accept the + * LICENSE before you use, modify, and/or redistribute this software. + * + * @author Jeff Williams Aspect Security + * @created 2007 + */ +package org.owasp.esapi.errors; + +/** + * An IntegrityException should be thrown when a problem with the integrity of data + * has been detected. For example, if a financial account cannot be reconciled after + * a transaction has been performed, an integrity exception should be thrown. + * + * @author Jeff Williams (jeff.williams@aspectsecurity.com) + */ +public class IntegrityException extends EnterpriseSecurityException { + + /** The Constant serialVersionUID. */ + private static final long serialVersionUID = 1L; + + /** + * Instantiates a new availability exception. + */ + protected IntegrityException() { + // hidden + } + + /** + * Creates a new instance of IntegrityException. + * + * @param userMessage + * the message to display to users + * @param logMessage + * the message logged + */ + public IntegrityException(String userMessage, String logMessage) { + super(userMessage, logMessage); + } + + /** + * Instantiates a new IntegrityException. + * + * @param userMessage + * the message to display to users + * @param logMessage + * the message logged + * @param cause + * the cause + */ + public IntegrityException(String userMessage, String logMessage, Throwable cause) { + super(userMessage, logMessage, cause); + } +} From a687babed82b92ce30eefe33314d7907d3a0c549 Mon Sep 17 00:00:00 2001 From: kwwall Date: Mon, 18 Jan 2016 23:33:48 -0500 Subject: [PATCH 0079/1069] Change CRLF to LF for issue 356. --- .../esapi/errors/IntrusionException.java | 184 +++++++++--------- 1 file changed, 92 insertions(+), 92 deletions(-) diff --git a/src/main/java/org/owasp/esapi/errors/IntrusionException.java b/src/main/java/org/owasp/esapi/errors/IntrusionException.java index 718f8a7c3..b5253ea28 100644 --- a/src/main/java/org/owasp/esapi/errors/IntrusionException.java +++ b/src/main/java/org/owasp/esapi/errors/IntrusionException.java @@ -1,92 +1,92 @@ -/** - * OWASP Enterprise Security API (ESAPI) - * - * This file is part of the Open Web Application Security Project (OWASP) - * Enterprise Security API (ESAPI) project. For details, please see - * http://www.owasp.org/index.php/ESAPI. - * - * Copyright (c) 2007 - The OWASP Foundation - * - * The ESAPI is published by OWASP under the BSD license. You should read and accept the - * LICENSE before you use, modify, and/or redistribute this software. - * - * @author Jeff Williams Aspect Security - * @created 2007 - */ -package org.owasp.esapi.errors; - -import org.owasp.esapi.ESAPI; -import org.owasp.esapi.Logger; - -/** - * An IntrusionException should be thrown anytime an error condition arises that is likely to be the result of an attack - * in progress. IntrusionExceptions are handled specially by the IntrusionDetector, which is equipped to respond by - * either specially logging the event, logging out the current user, or invalidating the current user's account. - *

    - * Unlike other exceptions in the ESAPI, the IntrusionException is a RuntimeException so that it can be thrown from - * anywhere and will not require a lot of special exception handling. - * - * @author Jeff Williams (jeff.williams@aspectsecurity.com) - */ -public class IntrusionException extends EnterpriseSecurityRuntimeException { - - /** The Constant serialVersionUID. */ - private static final long serialVersionUID = 1L; - - /** The logger. */ - protected final transient Logger logger = ESAPI.getLogger("IntrusionException"); - - /** - * - */ - protected String logMessage = null; - - /** - * Creates a new instance of IntrusionException. - * - * @param userMessage - * the message to display to users - * @param logMessage - * the message logged - */ - public IntrusionException(String userMessage, String logMessage) { - super(userMessage); - this.logMessage = logMessage; - logger.error(Logger.SECURITY_FAILURE, "INTRUSION - " + logMessage); - } - - /** - * Instantiates a new intrusion exception. - * - * @param userMessage - * the message to display to users - * @param logMessage - * the message logged - * @param cause - * the cause - */ - public IntrusionException(String userMessage, String logMessage, Throwable cause) { - super(userMessage, cause); - this.logMessage = logMessage; - logger.error(Logger.SECURITY_FAILURE, "INTRUSION - " + logMessage, cause); - } - - /** - * Returns a String containing a message that is safe to display to users - * - * @return a String containing a message that is safe to display to users - */ - public String getUserMessage() { - return getMessage(); - } - - /** - * Returns a String that is safe to display in logs, but probably not to users - * - * @return a String containing a message that is safe to display in logs, but probably not to users - */ - public String getLogMessage() { - return logMessage; - } - -} +/** + * OWASP Enterprise Security API (ESAPI) + * + * This file is part of the Open Web Application Security Project (OWASP) + * Enterprise Security API (ESAPI) project. For details, please see + * http://www.owasp.org/index.php/ESAPI. + * + * Copyright (c) 2007 - The OWASP Foundation + * + * The ESAPI is published by OWASP under the BSD license. You should read and accept the + * LICENSE before you use, modify, and/or redistribute this software. + * + * @author Jeff Williams Aspect Security + * @created 2007 + */ +package org.owasp.esapi.errors; + +import org.owasp.esapi.ESAPI; +import org.owasp.esapi.Logger; + +/** + * An IntrusionException should be thrown anytime an error condition arises that is likely to be the result of an attack + * in progress. IntrusionExceptions are handled specially by the IntrusionDetector, which is equipped to respond by + * either specially logging the event, logging out the current user, or invalidating the current user's account. + *

    + * Unlike other exceptions in the ESAPI, the IntrusionException is a RuntimeException so that it can be thrown from + * anywhere and will not require a lot of special exception handling. + * + * @author Jeff Williams (jeff.williams@aspectsecurity.com) + */ +public class IntrusionException extends EnterpriseSecurityRuntimeException { + + /** The Constant serialVersionUID. */ + private static final long serialVersionUID = 1L; + + /** The logger. */ + protected final transient Logger logger = ESAPI.getLogger("IntrusionException"); + + /** + * + */ + protected String logMessage = null; + + /** + * Creates a new instance of IntrusionException. + * + * @param userMessage + * the message to display to users + * @param logMessage + * the message logged + */ + public IntrusionException(String userMessage, String logMessage) { + super(userMessage); + this.logMessage = logMessage; + logger.error(Logger.SECURITY_FAILURE, "INTRUSION - " + logMessage); + } + + /** + * Instantiates a new intrusion exception. + * + * @param userMessage + * the message to display to users + * @param logMessage + * the message logged + * @param cause + * the cause + */ + public IntrusionException(String userMessage, String logMessage, Throwable cause) { + super(userMessage, cause); + this.logMessage = logMessage; + logger.error(Logger.SECURITY_FAILURE, "INTRUSION - " + logMessage, cause); + } + + /** + * Returns a String containing a message that is safe to display to users + * + * @return a String containing a message that is safe to display to users + */ + public String getUserMessage() { + return getMessage(); + } + + /** + * Returns a String that is safe to display in logs, but probably not to users + * + * @return a String containing a message that is safe to display in logs, but probably not to users + */ + public String getLogMessage() { + return logMessage; + } + +} From dd7e5732ee6ba7fafe349284509f5f2c8612e829 Mon Sep 17 00:00:00 2001 From: kwwall Date: Mon, 18 Jan 2016 23:33:48 -0500 Subject: [PATCH 0080/1069] Change CRLF to LF for issue 356. --- .../ValidationAvailabilityException.java | 114 +++++++++--------- 1 file changed, 57 insertions(+), 57 deletions(-) diff --git a/src/main/java/org/owasp/esapi/errors/ValidationAvailabilityException.java b/src/main/java/org/owasp/esapi/errors/ValidationAvailabilityException.java index d749e4f04..4e4a1d48b 100644 --- a/src/main/java/org/owasp/esapi/errors/ValidationAvailabilityException.java +++ b/src/main/java/org/owasp/esapi/errors/ValidationAvailabilityException.java @@ -1,57 +1,57 @@ -/** - * OWASP Enterprise Security API (ESAPI) - * - * This file is part of the Open Web Application Security Project (OWASP) - * Enterprise Security API (ESAPI) project. For details, please see - * http://www.owasp.org/index.php/ESAPI. - * - * Copyright (c) 2007 - The OWASP Foundation - * - * The ESAPI is published by OWASP under the BSD license. You should read and accept the - * LICENSE before you use, modify, and/or redistribute this software. - * - * @author Jeff Williams Aspect Security - * @created 2007 - */ -package org.owasp.esapi.errors; - -/** - * @author Jeff Williams (jeff.williams@aspectsecurity.com) - */ -public class ValidationAvailabilityException extends ValidationException { - - /** The Constant serialVersionUID. */ - private static final long serialVersionUID = 1L; - - /** - * Instantiates a new validation exception. - */ - protected ValidationAvailabilityException() { - // hidden - } - - /** - * Create a new ValidationException - * @param userMessage - * the message to display to users - * @param logMessage - * the message logged - */ - public ValidationAvailabilityException(String userMessage, String logMessage) { - super(userMessage, logMessage); - } - - /** - * Create a new ValidationException - * @param userMessage - * the message to display to users - * @param logMessage - * the message logged - * @param cause - * the cause - */ - public ValidationAvailabilityException(String userMessage, String logMessage, Throwable cause) { - super(userMessage, logMessage, cause); - } - -} +/** + * OWASP Enterprise Security API (ESAPI) + * + * This file is part of the Open Web Application Security Project (OWASP) + * Enterprise Security API (ESAPI) project. For details, please see + * http://www.owasp.org/index.php/ESAPI. + * + * Copyright (c) 2007 - The OWASP Foundation + * + * The ESAPI is published by OWASP under the BSD license. You should read and accept the + * LICENSE before you use, modify, and/or redistribute this software. + * + * @author Jeff Williams Aspect Security + * @created 2007 + */ +package org.owasp.esapi.errors; + +/** + * @author Jeff Williams (jeff.williams@aspectsecurity.com) + */ +public class ValidationAvailabilityException extends ValidationException { + + /** The Constant serialVersionUID. */ + private static final long serialVersionUID = 1L; + + /** + * Instantiates a new validation exception. + */ + protected ValidationAvailabilityException() { + // hidden + } + + /** + * Create a new ValidationException + * @param userMessage + * the message to display to users + * @param logMessage + * the message logged + */ + public ValidationAvailabilityException(String userMessage, String logMessage) { + super(userMessage, logMessage); + } + + /** + * Create a new ValidationException + * @param userMessage + * the message to display to users + * @param logMessage + * the message logged + * @param cause + * the cause + */ + public ValidationAvailabilityException(String userMessage, String logMessage, Throwable cause) { + super(userMessage, logMessage, cause); + } + +} From 0041a07432ec4d1133be6e6bdf2671944d50f19a Mon Sep 17 00:00:00 2001 From: kwwall Date: Mon, 18 Jan 2016 23:33:48 -0500 Subject: [PATCH 0081/1069] Change CRLF to LF for issue 356. --- .../esapi/errors/ValidationException.java | 230 +++++++++--------- 1 file changed, 115 insertions(+), 115 deletions(-) diff --git a/src/main/java/org/owasp/esapi/errors/ValidationException.java b/src/main/java/org/owasp/esapi/errors/ValidationException.java index b6ebcb9c3..a33562f96 100644 --- a/src/main/java/org/owasp/esapi/errors/ValidationException.java +++ b/src/main/java/org/owasp/esapi/errors/ValidationException.java @@ -1,115 +1,115 @@ -/** - * OWASP Enterprise Security API (ESAPI) - * - * This file is part of the Open Web Application Security Project (OWASP) - * Enterprise Security API (ESAPI) project. For details, please see - * http://www.owasp.org/index.php/ESAPI. - * - * Copyright (c) 2007 - The OWASP Foundation - * - * The ESAPI is published by OWASP under the BSD license. You should read and accept the - * LICENSE before you use, modify, and/or redistribute this software. - * - * @author Jeff Williams Aspect Security - * @created 2007 - */ -package org.owasp.esapi.errors; - -/** - * A ValidationException should be thrown to indicate that the data provided by - * the user or from some other external source does not match the validation - * rules that have been specified for that data. - * - * @author Jeff Williams (jeff.williams@aspectsecurity.com) - */ -public class ValidationException extends EnterpriseSecurityException { - - protected static final long serialVersionUID = 1L; - - /** The UI reference that caused this ValidationException */ - private String context; - - /** - * Instantiates a new validation exception. - */ - protected ValidationException() { - // hidden - } - - /** - * Creates a new instance of ValidationException. - * - * @param userMessage - * the message to display to users - * @param logMessage - * the message logged - */ - public ValidationException(String userMessage, String logMessage) { - super(userMessage, logMessage); - } - - /** - * Instantiates a new ValidationException. - * - * @param userMessage - * the message to display to users - * @param logMessage - * the message logged - * @param cause - * the cause - */ - public ValidationException(String userMessage, String logMessage, Throwable cause) { - super(userMessage, logMessage, cause); - } - - /** - * Creates a new instance of ValidationException. - * - * @param userMessage - * the message to display to users - * @param logMessage - * the message logged - * @param context - * the source that caused this exception - */ - public ValidationException(String userMessage, String logMessage, String context) { - super(userMessage, logMessage); - setContext(context); - } - - /** - * Instantiates a new ValidationException. - * - * @param userMessage - * the message to display to users - * @param logMessage - * the message logged - * @param cause - * the cause - * @param context - * the source that caused this exception - */ - public ValidationException(String userMessage, String logMessage, Throwable cause, String context) { - super(userMessage, logMessage, cause); - setContext(context); - } - - /** - * Returns the UI reference that caused this ValidationException - * - * @return context, the source that caused the exception, stored as a string - */ - public String getContext() { - return context; - } - - /** - * Set's the UI reference that caused this ValidationException - * - * @param context - * the context to set, passed as a String - */ - public void setContext(String context) { - this.context = context; - } -} +/** + * OWASP Enterprise Security API (ESAPI) + * + * This file is part of the Open Web Application Security Project (OWASP) + * Enterprise Security API (ESAPI) project. For details, please see + * http://www.owasp.org/index.php/ESAPI. + * + * Copyright (c) 2007 - The OWASP Foundation + * + * The ESAPI is published by OWASP under the BSD license. You should read and accept the + * LICENSE before you use, modify, and/or redistribute this software. + * + * @author Jeff Williams Aspect Security + * @created 2007 + */ +package org.owasp.esapi.errors; + +/** + * A ValidationException should be thrown to indicate that the data provided by + * the user or from some other external source does not match the validation + * rules that have been specified for that data. + * + * @author Jeff Williams (jeff.williams@aspectsecurity.com) + */ +public class ValidationException extends EnterpriseSecurityException { + + protected static final long serialVersionUID = 1L; + + /** The UI reference that caused this ValidationException */ + private String context; + + /** + * Instantiates a new validation exception. + */ + protected ValidationException() { + // hidden + } + + /** + * Creates a new instance of ValidationException. + * + * @param userMessage + * the message to display to users + * @param logMessage + * the message logged + */ + public ValidationException(String userMessage, String logMessage) { + super(userMessage, logMessage); + } + + /** + * Instantiates a new ValidationException. + * + * @param userMessage + * the message to display to users + * @param logMessage + * the message logged + * @param cause + * the cause + */ + public ValidationException(String userMessage, String logMessage, Throwable cause) { + super(userMessage, logMessage, cause); + } + + /** + * Creates a new instance of ValidationException. + * + * @param userMessage + * the message to display to users + * @param logMessage + * the message logged + * @param context + * the source that caused this exception + */ + public ValidationException(String userMessage, String logMessage, String context) { + super(userMessage, logMessage); + setContext(context); + } + + /** + * Instantiates a new ValidationException. + * + * @param userMessage + * the message to display to users + * @param logMessage + * the message logged + * @param cause + * the cause + * @param context + * the source that caused this exception + */ + public ValidationException(String userMessage, String logMessage, Throwable cause, String context) { + super(userMessage, logMessage, cause); + setContext(context); + } + + /** + * Returns the UI reference that caused this ValidationException + * + * @return context, the source that caused the exception, stored as a string + */ + public String getContext() { + return context; + } + + /** + * Set's the UI reference that caused this ValidationException + * + * @param context + * the context to set, passed as a String + */ + public void setContext(String context) { + this.context = context; + } +} From 855eefcf40ec294bfb0b086449ce8c6a34365faf Mon Sep 17 00:00:00 2001 From: kwwall Date: Mon, 18 Jan 2016 23:33:48 -0500 Subject: [PATCH 0082/1069] Change CRLF to LF for issue 356. --- .../errors/ValidationUploadException.java | 118 +++++++++--------- 1 file changed, 59 insertions(+), 59 deletions(-) diff --git a/src/main/java/org/owasp/esapi/errors/ValidationUploadException.java b/src/main/java/org/owasp/esapi/errors/ValidationUploadException.java index 0541e42cf..4a04de2eb 100644 --- a/src/main/java/org/owasp/esapi/errors/ValidationUploadException.java +++ b/src/main/java/org/owasp/esapi/errors/ValidationUploadException.java @@ -1,59 +1,59 @@ -/** - * OWASP Enterprise Security API (ESAPI) - * - * This file is part of the Open Web Application Security Project (OWASP) - * Enterprise Security API (ESAPI) project. For details, please see - * http://www.owasp.org/index.php/ESAPI. - * - * Copyright (c) 2007 - The OWASP Foundation - * - * The ESAPI is published by OWASP under the BSD license. You should read and accept the - * LICENSE before you use, modify, and/or redistribute this software. - * - * @author Jeff Williams Aspect Security - * @created 2007 - */ -package org.owasp.esapi.errors; - -/** - * @author Jeff Williams (jeff.williams@aspectsecurity.com) - */ -public class ValidationUploadException extends ValidationException { - - /** The Constant serialVersionUID. */ - private static final long serialVersionUID = 1L; - - /** - * Instantiates a new validation exception. - */ - protected ValidationUploadException() { - // hidden - } - - /** - * Create a new ValidationException - * - * @param userMessage - * the message to display to users - * @param logMessage - * the message logged - */ - public ValidationUploadException(String userMessage, String logMessage) { - super(userMessage, logMessage); - } - - /** - * Create a new ValidationException - * - * @param userMessage - * the message to display to users - * @param logMessage - * the message logged - * @param cause - * the cause - */ - public ValidationUploadException(String userMessage, String logMessage, Throwable cause) { - super(userMessage, logMessage, cause); - } - -} +/** + * OWASP Enterprise Security API (ESAPI) + * + * This file is part of the Open Web Application Security Project (OWASP) + * Enterprise Security API (ESAPI) project. For details, please see + * http://www.owasp.org/index.php/ESAPI. + * + * Copyright (c) 2007 - The OWASP Foundation + * + * The ESAPI is published by OWASP under the BSD license. You should read and accept the + * LICENSE before you use, modify, and/or redistribute this software. + * + * @author Jeff Williams Aspect Security + * @created 2007 + */ +package org.owasp.esapi.errors; + +/** + * @author Jeff Williams (jeff.williams@aspectsecurity.com) + */ +public class ValidationUploadException extends ValidationException { + + /** The Constant serialVersionUID. */ + private static final long serialVersionUID = 1L; + + /** + * Instantiates a new validation exception. + */ + protected ValidationUploadException() { + // hidden + } + + /** + * Create a new ValidationException + * + * @param userMessage + * the message to display to users + * @param logMessage + * the message logged + */ + public ValidationUploadException(String userMessage, String logMessage) { + super(userMessage, logMessage); + } + + /** + * Create a new ValidationException + * + * @param userMessage + * the message to display to users + * @param logMessage + * the message logged + * @param cause + * the cause + */ + public ValidationUploadException(String userMessage, String logMessage, Throwable cause) { + super(userMessage, logMessage, cause); + } + +} From fc745071b3c034e9993e49a75147b5d344677d8d Mon Sep 17 00:00:00 2001 From: kwwall Date: Mon, 18 Jan 2016 23:33:48 -0500 Subject: [PATCH 0083/1069] Change CRLF to LF for issue 356. --- src/main/java/org/owasp/esapi/Executor.java | 156 ++++++++++---------- 1 file changed, 78 insertions(+), 78 deletions(-) diff --git a/src/main/java/org/owasp/esapi/Executor.java b/src/main/java/org/owasp/esapi/Executor.java index 6286741ed..b475da90d 100644 --- a/src/main/java/org/owasp/esapi/Executor.java +++ b/src/main/java/org/owasp/esapi/Executor.java @@ -1,78 +1,78 @@ -/** - * OWASP Enterprise Security API (ESAPI) - * - * This file is part of the Open Web Application Security Project (OWASP) - * Enterprise Security API (ESAPI) project. For details, please see - * http://www.owasp.org/index.php/ESAPI. - * - * Copyright (c) 2007 - The OWASP Foundation - * - * The ESAPI is published by OWASP under the BSD license. You should read and accept the - * LICENSE before you use, modify, and/or redistribute this software. - * - * @author Jeff Williams Aspect Security - * @created 2007 - */ -package org.owasp.esapi; - -import java.io.File; -import java.util.List; - -import org.owasp.esapi.codecs.Codec; -import org.owasp.esapi.errors.ExecutorException; - -/** - * The Executor interface is used to run an OS command with reduced security risk. - * - *

    Implementations should do as much as possible to minimize the risk of - * injection into either the command or parameters. In addition, implementations - * should timeout after a specified time period in order to help prevent denial - * of service attacks.

    - * - *

    The class should perform logging and error handling as - * well. Finally, implementation should handle errors and generate an - * ExecutorException with all the necessary information.

    - * - *

    The reference implementation does all of the above.

    - * - * @author Jeff Williams (jeff.williams .at. aspectsecurity.com) Aspect Security - * @since June 1, 2007 - */ -public interface Executor { - - /** - * Invokes the specified executable with default workdir and codec and not logging parameters. - * - * @param executable - * the command to execute - * @param params - * the parameters of the command being executed - */ - ExecuteResult executeSystemCommand(File executable, List params) throws ExecutorException; - - /** - * Executes a system command after checking that the executable exists and - * escaping all the parameters to ensure that injection is impossible. - * Implementations must change to the specified working - * directory before invoking the command. - * - * @param executable - * the command to execute - * @param params - * the parameters of the command being executed - * @param workdir - * the working directory - * @param codec - * the codec to use to encode for the particular OS in use - * @param logParams - * use false if any parameters contains sensitive or confidential information - * - * @return the output of the command being run - * - * @throws ExecutorException - * the service exception - */ - ExecuteResult executeSystemCommand(File executable, List params, File workdir, Codec codec, boolean logParams, boolean redirectErrorStream) throws ExecutorException; - -} +/** + * OWASP Enterprise Security API (ESAPI) + * + * This file is part of the Open Web Application Security Project (OWASP) + * Enterprise Security API (ESAPI) project. For details, please see + * http://www.owasp.org/index.php/ESAPI. + * + * Copyright (c) 2007 - The OWASP Foundation + * + * The ESAPI is published by OWASP under the BSD license. You should read and accept the + * LICENSE before you use, modify, and/or redistribute this software. + * + * @author Jeff Williams Aspect Security + * @created 2007 + */ +package org.owasp.esapi; + +import java.io.File; +import java.util.List; + +import org.owasp.esapi.codecs.Codec; +import org.owasp.esapi.errors.ExecutorException; + +/** + * The Executor interface is used to run an OS command with reduced security risk. + * + *

    Implementations should do as much as possible to minimize the risk of + * injection into either the command or parameters. In addition, implementations + * should timeout after a specified time period in order to help prevent denial + * of service attacks.

    + * + *

    The class should perform logging and error handling as + * well. Finally, implementation should handle errors and generate an + * ExecutorException with all the necessary information.

    + * + *

    The reference implementation does all of the above.

    + * + * @author Jeff Williams (jeff.williams .at. aspectsecurity.com) Aspect Security + * @since June 1, 2007 + */ +public interface Executor { + + /** + * Invokes the specified executable with default workdir and codec and not logging parameters. + * + * @param executable + * the command to execute + * @param params + * the parameters of the command being executed + */ + ExecuteResult executeSystemCommand(File executable, List params) throws ExecutorException; + + /** + * Executes a system command after checking that the executable exists and + * escaping all the parameters to ensure that injection is impossible. + * Implementations must change to the specified working + * directory before invoking the command. + * + * @param executable + * the command to execute + * @param params + * the parameters of the command being executed + * @param workdir + * the working directory + * @param codec + * the codec to use to encode for the particular OS in use + * @param logParams + * use false if any parameters contains sensitive or confidential information + * + * @return the output of the command being run + * + * @throws ExecutorException + * the service exception + */ + ExecuteResult executeSystemCommand(File executable, List params, File workdir, Codec codec, boolean logParams, boolean redirectErrorStream) throws ExecutorException; + +} From a5c5d3c56373254da11b9e64cca60f0aaf268129 Mon Sep 17 00:00:00 2001 From: kwwall Date: Mon, 18 Jan 2016 23:33:48 -0500 Subject: [PATCH 0084/1069] Change CRLF to LF for issue 356. --- .../owasp/esapi/filters/ClickjackFilter.java | 216 +++++++++--------- 1 file changed, 108 insertions(+), 108 deletions(-) diff --git a/src/main/java/org/owasp/esapi/filters/ClickjackFilter.java b/src/main/java/org/owasp/esapi/filters/ClickjackFilter.java index 86025b1dd..8f1038691 100644 --- a/src/main/java/org/owasp/esapi/filters/ClickjackFilter.java +++ b/src/main/java/org/owasp/esapi/filters/ClickjackFilter.java @@ -1,108 +1,108 @@ -/** - * OWASP Enterprise Security API (ESAPI) - * - * This file is part of the Open Web Application Security Project (OWASP) - * Enterprise Security API (ESAPI) project. For details, please see - * http://www.owasp.org/index.php/ESAPI. - * - * Copyright (c) 2007 - The OWASP Foundation - * - * The ESAPI is published by OWASP under the BSD license. You should read and accept the - * LICENSE before you use, modify, and/or redistribute this software. - * - * @author Jeff Williams Aspect Security - * @created February 6, 2009 - */ - -package org.owasp.esapi.filters; -import java.io.IOException; - -import javax.servlet.Filter; -import javax.servlet.FilterChain; -import javax.servlet.FilterConfig; -import javax.servlet.ServletException; -import javax.servlet.ServletRequest; -import javax.servlet.ServletResponse; -import javax.servlet.http.HttpServletResponse; - -/** - * The {@code ClickjackFilter} is discussed at - * {@link http://www.owasp.org/index.php/ClickjackFilter_for_Java_EE}. - *
    - *     
    - *            ClickjackFilterDeny
    - *            org.owasp.filters.ClickjackFilter
    - *            
    - *                mode
    - *                 DENY
    - *             
    - *         
    - *         
    - *         
    - *             ClickjackFilterSameOrigin
    - *             org.owasp.filters.ClickjackFilter
    - *             
    - *                 mode
    - *                 SAMEORIGIN
    - *             
    - *         
    - *        
    - *        
    - *         
    - *            ClickjackFilterDeny
    - *            /*
    - *        
    - *         
    - *         
    + *         
    + *            ClickjackFilterDeny
    + *            /*
    + *        
    + *         
    + *         
    -	
    -
    -
    -
    -	
    -	
    -		
    -		
    -		
    -		
    -	
    -	
    -	
    -	
    -		
    -		
    -		
    -		 
    -		
    -		
    -	
    -	
    -	
    -	
    -
    -	
    -
    -	
    -		
    -
    -		
    -		 	
    -		 		
    -		 	
    -		 
    -		 
    -		 
    -		 	
    -		 		
    -		 	
    -		 
    -
    -		
    -			
    -				
    -				
    -			
    -		
    -	
    -		
    -			
    -				
    -				
    -				
    -				
    -				
    -			
    -		
    -
    -	
    -
    -
    -	
    -	
    -	
    -		
    -		
    -	
    -
    -
    -	
    -
    -		
    -
    -		
    -		
    -		
    -		
    -		
    -		
    -		
    -		
    -		
    -		
    -
    -		
    -		
    -		
    -			
    -		
    -
    -				
    -		
    -		
    -		
    -		
    -		
    -		
    -		
    -
    -		
    -		
    -		
    -		 
    -		
    -						
    -		
    -		
    -		
    -
    -			
    -			
    -				
    -					
    -					
    -				
    -			
    -			
    -				
    -					
    -				
    -			
    -		
    -
    -		
    -
    -		
    -		
    -		
    -		
    -	
    -
    -
    -
    -	
    -
    -	
    -	
    -
    -
    -	
    -		
    -		
    -		
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		                                  
    -		 
    -		
    -		
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		
    -		
    -		 
    -		
    -		 
    -		 
    -		 
    -		 
    -		 
    -		
    -		
    -		 
    -		 
    -		 
    -		 
    -		 
    -		
    -		
    -		
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		
    -		
    -		    
    -		 
    -		 
    -		
    -		
    -		     
    -		 
    -		 
    -		
    -		
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -	
    -		
    -		 
    -		
    -		
    -		     
    -		 
    -		
    -		
    -		
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		
    -		
    -		     
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		
    -		 
    -		
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		
    -		
    -		
    -		 
    -		 
    -		 
    -		 
    -		 
    -		
    -		 
    -		
    -		
    -		
    -		 
    -		
    -		
    -		 
    -		
    -		 
    -		 
    -		 
    -		
    -		 
    -		
    -		 
    -		 
    -		
    -		 
    -		 
    -		 
    -		
    -		
    -		 
    -		 
    -		
    -		
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		
    -		 
    -		
    -		 
    -	
    -
    -
    +
    +	
    +
    +	
    +
    +
    +
    +	
    +	
    +		
    +		
    +		
    +		
    +	
    +	
    +	
    +	
    +		
    +		
    +		
    +		 
    +		
    +		
    +	
    +	
    +	
    +	
    +
    +	
    +
    +	
    +		
    +
    +		
    +		 	
    +		 		
    +		 	
    +		 
    +		 
    +		 
    +		 	
    +		 		
    +		 	
    +		 
    +
    +		
    +			
    +				
    +				
    +			
    +		
    +	
    +		
    +			
    +				
    +				
    +				
    +				
    +				
    +			
    +		
    +
    +	
    +
    +
    +	
    +	
    +	
    +		
    +		
    +	
    +
    +
    +	
    +
    +		
    +
    +		
    +		
    +		
    +		
    +		
    +		
    +		
    +		
    +		
    +		
    +
    +		
    +		
    +		
    +			
    +		
    +
    +				
    +		
    +		
    +		
    +		
    +		
    +		
    +		
    +
    +		
    +		
    +		
    +		 
    +		
    +						
    +		
    +		
    +		
    +
    +			
    +			
    +				
    +					
    +					
    +				
    +			
    +			
    +				
    +					
    +				
    +			
    +		
    +
    +		
    +
    +		
    +		
    +		
    +		
    +	
    +
    +
    +
    +	
    +
    +	
    +	
    +
    +
    +	
    +		
    +		
    +		
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		                                  
    +		 
    +		
    +		
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		
    +		
    +		 
    +		
    +		 
    +		 
    +		 
    +		 
    +		 
    +		
    +		
    +		 
    +		 
    +		 
    +		 
    +		 
    +		
    +		
    +		
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		
    +		
    +		    
    +		 
    +		 
    +		
    +		
    +		     
    +		 
    +		 
    +		
    +		
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +	
    +		
    +		 
    +		
    +		
    +		     
    +		 
    +		
    +		
    +		
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		
    +		
    +		     
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		
    +		 
    +		
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		
    +		
    +		
    +		 
    +		 
    +		 
    +		 
    +		 
    +		
    +		 
    +		
    +		
    +		
    +		 
    +		
    +		
    +		 
    +		
    +		 
    +		 
    +		 
    +		
    +		 
    +		
    +		 
    +		 
    +		
    +		 
    +		 
    +		 
    +		
    +		
    +		 
    +		 
    +		
    +		
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		
    +		 
    +		
    +		 
    +	
    +
    +
    
    From 4db1a33cff1c017a1627b710977a7bf2d6089140 Mon Sep 17 00:00:00 2001
    From: kwwall 
    Date: Mon, 18 Jan 2016 23:34:45 -0500
    Subject: [PATCH 0231/1069] Change CRLF to LF for issue 356.
    
    ---
     .../esapi/ESAPI-AccessControlPolicy.xml       | 254 +++++++++---------
     1 file changed, 127 insertions(+), 127 deletions(-)
    
    diff --git a/configuration/esapi/ESAPI-AccessControlPolicy.xml b/configuration/esapi/ESAPI-AccessControlPolicy.xml
    index 2ed0732bc..4d2ca74b5 100644
    --- a/configuration/esapi/ESAPI-AccessControlPolicy.xml
    +++ b/configuration/esapi/ESAPI-AccessControlPolicy.xml
    @@ -1,127 +1,127 @@
    -
    -
    -
    -	
    -		
    -		
    -		
    -		
    -		
    -		
    -		
    -			
    -				
    -			
    -		
    -		
    -		
    -		
    -			
    -				 
    -				
    -				
    -			
    -		
    -		
    -			
    -				
    -				
    -				
    -			
    -		
    -		
    -			
    -				
    -				
    -				
    -			
    -		
    -		
    -			
    -				
    -				
    -				
    -			
    -		
    -		
    -			
    -				
    -				
    -				
    -			
    -		
    -		
    -		
    -	
    -
    -
    -
    -
    -
    -
    -
    +
    +
    +
    +	
    +		
    +		
    +		
    +		
    +		
    +		
    +		
    +			
    +				
    +			
    +		
    +		
    +		
    +		
    +			
    +				 
    +				
    +				
    +			
    +		
    +		
    +			
    +				
    +				
    +				
    +			
    +		
    +		
    +			
    +				
    +				
    +				
    +			
    +		
    +		
    +			
    +				
    +				
    +				
    +			
    +		
    +		
    +			
    +				
    +				
    +				
    +			
    +		
    +		
    +		
    +	
    +
    +
    +
    +
    +
    +
    +
    
    From 19ce3948cf19020046a4186353a5b32a37fbf66d Mon Sep 17 00:00:00 2001
    From: kwwall 
    Date: Mon, 18 Jan 2016 23:34:45 -0500
    Subject: [PATCH 0232/1069] Change CRLF to LF for issue 356.
    
    ---
     .../esapi/waf-policies/add-header-policy.xml  | 56 +++++++++----------
     1 file changed, 28 insertions(+), 28 deletions(-)
    
    diff --git a/configuration/esapi/waf-policies/add-header-policy.xml b/configuration/esapi/waf-policies/add-header-policy.xml
    index b2d8efc49..0a48ed2d0 100644
    --- a/configuration/esapi/waf-policies/add-header-policy.xml
    +++ b/configuration/esapi/waf-policies/add-header-policy.xml
    @@ -1,29 +1,29 @@
    -
    -
    -	
    -
    -
    -
    -	
    -		redirect
    -		
    -			/security/error.jsp
    -			403
    -		
    -	
    -	
    -	
    -		
    -			/marketing/.*
    -		
    -	
    -	
    +
    +
    +	
    +
    +
    +
    +	
    +		redirect
    +		
    +			/security/error.jsp
    +			403
    +		
    +	
    +	
    +	
    +		
    +			/marketing/.*
    +		
    +	
    +	
     
    \ No newline at end of file
    
    From 43540728991f54e01843af3668e6c524b81e95d5 Mon Sep 17 00:00:00 2001
    From: kwwall 
    Date: Mon, 18 Jan 2016 23:34:45 -0500
    Subject: [PATCH 0233/1069] Change CRLF to LF for issue 356.
    
    ---
     .../waf-policies/add-httponly-policy.xml      | 52 +++++++++----------
     1 file changed, 26 insertions(+), 26 deletions(-)
    
    diff --git a/configuration/esapi/waf-policies/add-httponly-policy.xml b/configuration/esapi/waf-policies/add-httponly-policy.xml
    index 1232f5997..4420e6ce0 100644
    --- a/configuration/esapi/waf-policies/add-httponly-policy.xml
    +++ b/configuration/esapi/waf-policies/add-httponly-policy.xml
    @@ -1,27 +1,27 @@
    -
    -
    -	
    -
    -
    -
    -	
    -		redirect
    -		
    -			/security/error.jsp
    -			403
    -		
    -	
    -
    -	
    -		
    -			
    -		
    -	
    -	
    +
    +
    +	
    +
    +
    +
    +	
    +		redirect
    +		
    +			/security/error.jsp
    +			403
    +		
    +	
    +
    +	
    +		
    +			
    +		
    +	
    +	
     
    \ No newline at end of file
    
    From bff90d3ce384e826e69eea661a98a82caad7172d Mon Sep 17 00:00:00 2001
    From: kwwall 
    Date: Mon, 18 Jan 2016 23:34:45 -0500
    Subject: [PATCH 0234/1069] Change CRLF to LF for issue 356.
    
    ---
     .../esapi/waf-policies/add-secure-policy.xml  | 52 +++++++++----------
     1 file changed, 26 insertions(+), 26 deletions(-)
    
    diff --git a/configuration/esapi/waf-policies/add-secure-policy.xml b/configuration/esapi/waf-policies/add-secure-policy.xml
    index 12298a45a..80067da42 100644
    --- a/configuration/esapi/waf-policies/add-secure-policy.xml
    +++ b/configuration/esapi/waf-policies/add-secure-policy.xml
    @@ -1,27 +1,27 @@
    -
    -
    -	
    -
    -
    -
    -	
    -		redirect
    -		
    -			/security/error.jsp
    -			403
    -		
    -	
    -	
    -	
    -		
    -			
    -		
    -	
    -	
    +
    +
    +	
    +
    +
    +
    +	
    +		redirect
    +		
    +			/security/error.jsp
    +			403
    +		
    +	
    +	
    +	
    +		
    +			
    +		
    +	
    +	
     
    \ No newline at end of file
    
    From cf2e253532b2dad8f084d3620e01aaf3bd4914c9 Mon Sep 17 00:00:00 2001
    From: kwwall 
    Date: Mon, 18 Jan 2016 23:34:45 -0500
    Subject: [PATCH 0235/1069] Change CRLF to LF for issue 356.
    
    ---
     .../waf-policies/authentication-policy.xml    | 58 +++++++++----------
     1 file changed, 29 insertions(+), 29 deletions(-)
    
    diff --git a/configuration/esapi/waf-policies/authentication-policy.xml b/configuration/esapi/waf-policies/authentication-policy.xml
    index 1c1a485be..5a77e6c0b 100644
    --- a/configuration/esapi/waf-policies/authentication-policy.xml
    +++ b/configuration/esapi/waf-policies/authentication-policy.xml
    @@ -1,30 +1,30 @@
    -
    -
    -	
    -
    -
    -
    -	
    -		redirect
    -		
    -			/security/error.jsp
    -			403
    -		
    -	>
    -
    -	
    -	
    -		/index.html
    -		/images/.*
    -	
    -
    +
    +
    +	
    +
    +
    +
    +	
    +		redirect
    +		
    +			/security/error.jsp
    +			403
    +		
    +	>
    +
    +	
    +	
    +		/index.html
    +		/images/.*
    +	
    +
     
    \ No newline at end of file
    
    From ec6c70e6e3a6dc16dc326584a29ee751e834a499 Mon Sep 17 00:00:00 2001
    From: kwwall 
    Date: Mon, 18 Jan 2016 23:34:45 -0500
    Subject: [PATCH 0236/1069] Change CRLF to LF for issue 356.
    
    ---
     .../esapi/waf-policies/bean-shell-policy.xml  | 58 +++++++++----------
     1 file changed, 29 insertions(+), 29 deletions(-)
    
    diff --git a/configuration/esapi/waf-policies/bean-shell-policy.xml b/configuration/esapi/waf-policies/bean-shell-policy.xml
    index 2e3f7bb93..76711c82d 100644
    --- a/configuration/esapi/waf-policies/bean-shell-policy.xml
    +++ b/configuration/esapi/waf-policies/bean-shell-policy.xml
    @@ -1,30 +1,30 @@
    -
    -
    -	
    -
    -
    -
    -	
    -		redirect
    -		
    -			/security/error.jsp
    -			403
    -		
    -	
    -
    -	
    -		
    -		
    -	
    -
    +
    +
    +	
    +
    +
    +
    +	
    +		redirect
    +		
    +			/security/error.jsp
    +			403
    +		
    +	
    +
    +	
    +		
    +		
    +	
    +
     
    \ No newline at end of file
    
    From a849b75e7717eaeca252db52da4247e84125e308 Mon Sep 17 00:00:00 2001
    From: kwwall 
    Date: Mon, 18 Jan 2016 23:34:45 -0500
    Subject: [PATCH 0237/1069] Change CRLF to LF for issue 356.
    
    ---
     .../waf-policies/detect-outbound-policy.xml   | 52 +++++++++----------
     1 file changed, 26 insertions(+), 26 deletions(-)
    
    diff --git a/configuration/esapi/waf-policies/detect-outbound-policy.xml b/configuration/esapi/waf-policies/detect-outbound-policy.xml
    index 8312be95d..ac756aefa 100644
    --- a/configuration/esapi/waf-policies/detect-outbound-policy.xml
    +++ b/configuration/esapi/waf-policies/detect-outbound-policy.xml
    @@ -1,27 +1,27 @@
    -
    -
    -	
    -
    -
    -
    -	
    -		redirect
    -		
    -			/security/error.jsp
    -			403
    -		
    -	
    -
    -	
    -		
    -	
    -	
    +
    +
    +	
    +
    +
    +
    +	
    +		redirect
    +		
    +			/security/error.jsp
    +			403
    +		
    +	
    +
    +	
    +		
    +	
    +	
     
    \ No newline at end of file
    
    From 7994d0e654ebe80915b5395475fd5ab1ecb2f199 Mon Sep 17 00:00:00 2001
    From: kwwall 
    Date: Mon, 18 Jan 2016 23:34:46 -0500
    Subject: [PATCH 0238/1069] Change CRLF to LF for issue 356.
    
    ---
     .../waf-policies/dynamic-insertion-policy.xml | 52 +++++++++----------
     1 file changed, 26 insertions(+), 26 deletions(-)
    
    diff --git a/configuration/esapi/waf-policies/dynamic-insertion-policy.xml b/configuration/esapi/waf-policies/dynamic-insertion-policy.xml
    index 9c1aa4097..5b0926c2b 100644
    --- a/configuration/esapi/waf-policies/dynamic-insertion-policy.xml
    +++ b/configuration/esapi/waf-policies/dynamic-insertion-policy.xml
    @@ -1,27 +1,27 @@
    -
    -
    -	
    -
    -
    -
    -	
    -		redirect
    -		
    -			/security/error.jsp
    -			403
    -		
    -	
    -
    -	
    -		
    -			
    -		
    -	
    -	
    +
    +
    +	
    +
    +
    +
    +	
    +		redirect
    +		
    +			/security/error.jsp
    +			403
    +		
    +	
    +
    +	
    +		
    +			
    +		
    +	
    +	
     
    \ No newline at end of file
    
    From 1fcdc4894aa22ef175928fa3fb80548d1242a380 Mon Sep 17 00:00:00 2001
    From: kwwall 
    Date: Mon, 18 Jan 2016 23:34:46 -0500
    Subject: [PATCH 0239/1069] Change CRLF to LF for issue 356.
    
    ---
     .../waf-policies/enforce-https-policy.xml     | 54 +++++++++----------
     1 file changed, 27 insertions(+), 27 deletions(-)
    
    diff --git a/configuration/esapi/waf-policies/enforce-https-policy.xml b/configuration/esapi/waf-policies/enforce-https-policy.xml
    index ab123b7e6..beb88513a 100644
    --- a/configuration/esapi/waf-policies/enforce-https-policy.xml
    +++ b/configuration/esapi/waf-policies/enforce-https-policy.xml
    @@ -1,28 +1,28 @@
    -
    -
    -	
    -
    -
    -
    -	
    -		redirect
    -		
    -			/security/error.jsp
    -			403
    -		
    -	
    -
    -	
    -		
    -			/index.html
    -			/images/.*
    -		
    -	
    -
    +
    +
    +	
    +
    +
    +
    +	
    +		redirect
    +		
    +			/security/error.jsp
    +			403
    +		
    +	
    +
    +	
    +		
    +			/index.html
    +			/images/.*
    +		
    +	
    +
     
    \ No newline at end of file
    
    From f46fde8aa7ff9921897f365e373126170b1446de Mon Sep 17 00:00:00 2001
    From: kwwall 
    Date: Mon, 18 Jan 2016 23:34:46 -0500
    Subject: [PATCH 0240/1069] Change CRLF to LF for issue 356.
    
    ---
     .../esapi/waf-policies/must-match-policy.xml  | 68 +++++++++----------
     1 file changed, 34 insertions(+), 34 deletions(-)
    
    diff --git a/configuration/esapi/waf-policies/must-match-policy.xml b/configuration/esapi/waf-policies/must-match-policy.xml
    index 721fa4cfa..af26fc4cd 100644
    --- a/configuration/esapi/waf-policies/must-match-policy.xml
    +++ b/configuration/esapi/waf-policies/must-match-policy.xml
    @@ -1,35 +1,35 @@
    -
    -
    -	
    -
    -
    -
    -	
    -		redirect
    -		
    -			/security/error.jsp
    -			403
    -		
    -	
    -
    -	
    -		
    -		
    -	
    -
    +
    +
    +	
    +
    +
    +
    +	
    +		redirect
    +		
    +			/security/error.jsp
    +			403
    +		
    +	
    +
    +	
    +		
    +		
    +	
    +
     
    \ No newline at end of file
    
    From 7a177267b4d67522df100e615009528f3c2f9a63 Mon Sep 17 00:00:00 2001
    From: kwwall 
    Date: Mon, 18 Jan 2016 23:34:46 -0500
    Subject: [PATCH 0241/1069] Change CRLF to LF for issue 356.
    
    ---
     .../waf-policies/replace-outbound-policy.xml  | 52 +++++++++----------
     1 file changed, 26 insertions(+), 26 deletions(-)
    
    diff --git a/configuration/esapi/waf-policies/replace-outbound-policy.xml b/configuration/esapi/waf-policies/replace-outbound-policy.xml
    index 9c1aa4097..5b0926c2b 100644
    --- a/configuration/esapi/waf-policies/replace-outbound-policy.xml
    +++ b/configuration/esapi/waf-policies/replace-outbound-policy.xml
    @@ -1,27 +1,27 @@
    -
    -
    -	
    -
    -
    -
    -	
    -		redirect
    -		
    -			/security/error.jsp
    -			403
    -		
    -	
    -
    -	
    -		
    -			
    -		
    -	
    -	
    +
    +
    +	
    +
    +
    +
    +	
    +		redirect
    +		
    +			/security/error.jsp
    +			403
    +		
    +	
    +
    +	
    +		
    +			
    +		
    +	
    +	
     
    \ No newline at end of file
    
    From e20146fc9bb685ec06e2a655b4c378a00af9c53e Mon Sep 17 00:00:00 2001
    From: kwwall 
    Date: Mon, 18 Jan 2016 23:34:46 -0500
    Subject: [PATCH 0242/1069] Change CRLF to LF for issue 356.
    
    ---
     .../restrict-content-type-policy.xml          | 52 +++++++++----------
     1 file changed, 26 insertions(+), 26 deletions(-)
    
    diff --git a/configuration/esapi/waf-policies/restrict-content-type-policy.xml b/configuration/esapi/waf-policies/restrict-content-type-policy.xml
    index a9b8a4a38..b766f19d3 100644
    --- a/configuration/esapi/waf-policies/restrict-content-type-policy.xml
    +++ b/configuration/esapi/waf-policies/restrict-content-type-policy.xml
    @@ -1,27 +1,27 @@
    -
    -
    -	
    -
    -
    -
    -	
    -		redirect
    -		
    -			/security/error.jsp
    -			403
    -		
    -	
    -
    -	
    -		
    -			/fileupload.jsp
    -		
    -
    -	
    -
    +
    +
    +	
    +
    +
    +
    +	
    +		redirect
    +		
    +			/security/error.jsp
    +			403
    +		
    +	
    +
    +	
    +		
    +			/fileupload.jsp
    +		
    +
    +	
    +
     
    \ No newline at end of file
    
    From bd94f010f471095e4357e6e020d5844c88d93126 Mon Sep 17 00:00:00 2001
    From: kwwall 
    Date: Mon, 18 Jan 2016 23:34:46 -0500
    Subject: [PATCH 0243/1069] Change CRLF to LF for issue 356.
    
    ---
     .../restrict-extension-policy.xml             | 48 +++++++++----------
     1 file changed, 24 insertions(+), 24 deletions(-)
    
    diff --git a/configuration/esapi/waf-policies/restrict-extension-policy.xml b/configuration/esapi/waf-policies/restrict-extension-policy.xml
    index fe1dbe193..a572bd4ea 100644
    --- a/configuration/esapi/waf-policies/restrict-extension-policy.xml
    +++ b/configuration/esapi/waf-policies/restrict-extension-policy.xml
    @@ -1,25 +1,25 @@
    -
    -
    -	
    -
    -
    -
    -	
    -		redirect
    -		
    -			/security/error.jsp
    -			403
    -		
    -	
    -
    -	
    -		
    -		
    -	
    -
    +
    +
    +	
    +
    +
    +
    +	
    +		redirect
    +		
    +			/security/error.jsp
    +			403
    +		
    +	
    +
    +	
    +		
    +		
    +	
    +
     
    \ No newline at end of file
    
    From 30703f6d73e1a488a43d34b197e1b4b02995d2d8 Mon Sep 17 00:00:00 2001
    From: kwwall 
    Date: Mon, 18 Jan 2016 23:34:46 -0500
    Subject: [PATCH 0244/1069] Change CRLF to LF for issue 356.
    
    ---
     .../waf-policies/restrict-method-policy.xml   | 48 +++++++++----------
     1 file changed, 24 insertions(+), 24 deletions(-)
    
    diff --git a/configuration/esapi/waf-policies/restrict-method-policy.xml b/configuration/esapi/waf-policies/restrict-method-policy.xml
    index 5a2337cf7..c1eb5a36e 100644
    --- a/configuration/esapi/waf-policies/restrict-method-policy.xml
    +++ b/configuration/esapi/waf-policies/restrict-method-policy.xml
    @@ -1,25 +1,25 @@
    -
    -
    -	
    -
    -
    -
    -	
    -		redirect
    -		
    -			/security/error.jsp
    -			403
    -		
    -	
    -
    -	
    -		
    -		
    -	
    -
    +
    +
    +	
    +
    +
    +
    +	
    +		redirect
    +		
    +			/security/error.jsp
    +			403
    +		
    +	
    +
    +	
    +		
    +		
    +	
    +
     
    \ No newline at end of file
    
    From b80eee75e4ec46a0794dfdc5c6dc9fc71b4305d0 Mon Sep 17 00:00:00 2001
    From: kwwall 
    Date: Mon, 18 Jan 2016 23:34:46 -0500
    Subject: [PATCH 0245/1069] Change CRLF to LF for issue 356.
    
    ---
     .../restrict-source-ip-policy.xml             | 52 +++++++++----------
     1 file changed, 26 insertions(+), 26 deletions(-)
    
    diff --git a/configuration/esapi/waf-policies/restrict-source-ip-policy.xml b/configuration/esapi/waf-policies/restrict-source-ip-policy.xml
    index d23d87a02..72860a7d3 100644
    --- a/configuration/esapi/waf-policies/restrict-source-ip-policy.xml
    +++ b/configuration/esapi/waf-policies/restrict-source-ip-policy.xml
    @@ -1,27 +1,27 @@
    -
    -
    -	
    -
    -
    -
    -	
    -		redirect
    -		
    -			/security/error.jsp
    -			403
    -		
    -	
    -
    -	
    -		/admin/.*
    -
    -	
    -
    +
    +
    +	
    +
    +
    +
    +	
    +		redirect
    +		
    +			/security/error.jsp
    +			403
    +		
    +	
    +
    +	
    +		/admin/.*
    +
    +	
    +
     
    \ No newline at end of file
    
    From e6704dc1e8d247b97e00fb4272f3f998c72b72e2 Mon Sep 17 00:00:00 2001
    From: kwwall 
    Date: Mon, 18 Jan 2016 23:34:46 -0500
    Subject: [PATCH 0246/1069] Change CRLF to LF for issue 356.
    
    ---
     .../restrict-user-agent-policy.xml            | 50 +++++++++----------
     1 file changed, 25 insertions(+), 25 deletions(-)
    
    diff --git a/configuration/esapi/waf-policies/restrict-user-agent-policy.xml b/configuration/esapi/waf-policies/restrict-user-agent-policy.xml
    index 7cdebd595..c550a2c61 100644
    --- a/configuration/esapi/waf-policies/restrict-user-agent-policy.xml
    +++ b/configuration/esapi/waf-policies/restrict-user-agent-policy.xml
    @@ -1,26 +1,26 @@
    -
    -
    -	
    -
    -
    -
    -	
    -		redirect
    -		
    -			/security/error.jsp
    -			403
    -		
    -	
    -
    -	
    -		
    -			/index.html
    -		
    -	
    -
    +
    +
    +	
    +
    +
    +
    +	
    +		redirect
    +		
    +			/security/error.jsp
    +			403
    +		
    +	
    +
    +	
    +		
    +			/index.html
    +		
    +	
    +
     
    \ No newline at end of file
    
    From 158a6bbcfa98bac6a1d08fc76c2c2e6ec26dc193 Mon Sep 17 00:00:00 2001
    From: kwwall 
    Date: Mon, 18 Jan 2016 23:34:46 -0500
    Subject: [PATCH 0247/1069] Change CRLF to LF for issue 356.
    
    ---
     .../waf-policies/virtual-patch-policy.xml     | 52 +++++++++----------
     1 file changed, 26 insertions(+), 26 deletions(-)
    
    diff --git a/configuration/esapi/waf-policies/virtual-patch-policy.xml b/configuration/esapi/waf-policies/virtual-patch-policy.xml
    index 61c93fa88..8b989dd6e 100644
    --- a/configuration/esapi/waf-policies/virtual-patch-policy.xml
    +++ b/configuration/esapi/waf-policies/virtual-patch-policy.xml
    @@ -1,27 +1,27 @@
    -
    -
    -	
    -
    -
    -
    -	
    -		redirect
    -		
    -			/security/error.jsp
    -			403
    -		
    -	
    -
    -	
    -		
    -	
    -
    +
    +
    +	
    +
    +
    +
    +	
    +		redirect
    +		
    +			/security/error.jsp
    +			403
    +		
    +	
    +
    +	
    +		
    +	
    +
     
    \ No newline at end of file
    
    From c9d3cdb68b4b34bb02b53291b04aa42f23122c30 Mon Sep 17 00:00:00 2001
    From: kwwall 
    Date: Mon, 18 Jan 2016 23:34:46 -0500
    Subject: [PATCH 0248/1069] Change CRLF to LF for issue 356.
    
    ---
     configuration/log4j.xml | 94 ++++++++++++++++++++---------------------
     1 file changed, 47 insertions(+), 47 deletions(-)
    
    diff --git a/configuration/log4j.xml b/configuration/log4j.xml
    index 6f895d580..758514e80 100644
    --- a/configuration/log4j.xml
    +++ b/configuration/log4j.xml
    @@ -1,47 +1,47 @@
    -
    -
    -
    -
    -   
    -     
    -     
    -       
    -     
    -   
    -
    -  
    -    
    -  
    -
    -  
    -    
    -  
    -
    -  
    -    
    - 
    -
    -  
    -    
    -  
    -
    -  
    -    
    -  
    -
    -  
    -    
    -  
    -
    -  
    -    
    -  
    -
    -   
    -     
    -     
    -  
    -
    -  
    -  
    -
    +
    +
    +
    +
    +   
    +     
    +     
    +       
    +     
    +   
    +
    +  
    +    
    +  
    +
    +  
    +    
    +  
    +
    +  
    +    
    + 
    +
    +  
    +    
    +  
    +
    +  
    +    
    +  
    +
    +  
    +    
    +  
    +
    +  
    +    
    +  
    +
    +   
    +     
    +     
    +  
    +
    +  
    +  
    +
    
    From b7334c4327223c77746a7fbbcac2429df78315d3 Mon Sep 17 00:00:00 2001
    From: kwwall 
    Date: Mon, 18 Jan 2016 23:34:46 -0500
    Subject: [PATCH 0249/1069] Change CRLF to LF for issue 356.
    
    ---
     javadoc.xml | 12 ++++++------
     1 file changed, 6 insertions(+), 6 deletions(-)
    
    diff --git a/javadoc.xml b/javadoc.xml
    index daa7ba95f..791f62ae7 100644
    --- a/javadoc.xml
    +++ b/javadoc.xml
    @@ -1,6 +1,6 @@
    -
    -
    -    
    -        
    -    
    -
    +
    +
    +    
    +        
    +    
    +
    
    From ead51f90e60e4612a2e35d380fe9f55359b9a5b9 Mon Sep 17 00:00:00 2001
    From: kwwall 
    Date: Mon, 18 Jan 2016 23:34:46 -0500
    Subject: [PATCH 0250/1069] Change CRLF to LF for issue 356.
    
    ---
     .../org.eclipse.wst.common.project.facet.core.xml      | 10 +++++-----
     1 file changed, 5 insertions(+), 5 deletions(-)
    
    diff --git a/.settings/org.eclipse.wst.common.project.facet.core.xml b/.settings/org.eclipse.wst.common.project.facet.core.xml
    index 613f29cbf..9bbcbd78e 100644
    --- a/.settings/org.eclipse.wst.common.project.facet.core.xml
    +++ b/.settings/org.eclipse.wst.common.project.facet.core.xml
    @@ -1,6 +1,6 @@
    -
    -  
    -  
    -  
    -  
    +
    +  
    +  
    +  
    +  
     
    \ No newline at end of file
    
    From af87670af171d0352350065aa621fbc0a7f906fd Mon Sep 17 00:00:00 2001
    From: kwwall 
    Date: Mon, 18 Jan 2016 23:34:46 -0500
    Subject: [PATCH 0251/1069] Change CRLF to LF for issue 356.
    
    ---
     src/test/resources/esapi/antisamy-esapi.xml | 984 ++++++++++----------
     1 file changed, 492 insertions(+), 492 deletions(-)
    
    diff --git a/src/test/resources/esapi/antisamy-esapi.xml b/src/test/resources/esapi/antisamy-esapi.xml
    index 500ab5943..14880d0b5 100644
    --- a/src/test/resources/esapi/antisamy-esapi.xml
    +++ b/src/test/resources/esapi/antisamy-esapi.xml
    @@ -1,492 +1,492 @@
    -
    -	
    -
    -	
    -
    -
    -
    -	
    -	
    -		
    -		
    -		
    -		
    -	
    -	
    -	
    -	
    -		
    -		
    -		
    -		 
    -		
    -		
    -	
    -	
    -	
    -	
    -
    -	
    -
    -	
    -		
    -
    -		
    -		 	
    -		 		
    -		 	
    -		 
    -		 
    -		 
    -		 	
    -		 		
    -		 	
    -		 
    -
    -		
    -			
    -				
    -				
    -			
    -		
    -	
    -		
    -			
    -				
    -				
    -				
    -				
    -				
    -			
    -		
    -
    -	
    -
    -
    -	
    -	
    -	
    -		
    -		
    -	
    -
    -
    -	
    -
    -		
    -
    -		
    -		
    -		
    -		
    -		
    -		
    -		
    -		
    -		
    -		
    -
    -		
    -		
    -		
    -			
    -		
    -
    -				
    -		
    -		
    -		
    -		
    -		
    -		
    -		
    -
    -		
    -		
    -		
    -		 
    -		
    -						
    -		
    -		
    -		
    -
    -			
    -			
    -				
    -					
    -					
    -				
    -			
    -			
    -				
    -					
    -				
    -			
    -		
    -
    -		
    -
    -		
    -		
    -		
    -		
    -	
    -
    -
    -
    -	
    -
    -	
    -	
    -
    -
    -	
    -		
    -		
    -		
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		                                  
    -		 
    -		
    -		
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		
    -		
    -		 
    -		
    -		 
    -		 
    -		 
    -		 
    -		 
    -		
    -		
    -		 
    -		 
    -		 
    -		 
    -		 
    -		
    -		
    -		
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		
    -		
    -		    
    -		 
    -		 
    -		
    -		
    -		     
    -		 
    -		 
    -		
    -		
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -	
    -		
    -		 
    -		
    -		
    -		     
    -		 
    -		
    -		
    -		
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		
    -		
    -		     
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		
    -		 
    -		
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		
    -		
    -		
    -		 
    -		 
    -		 
    -		 
    -		 
    -		
    -		 
    -		
    -		
    -		
    -		 
    -		
    -		
    -		 
    -		
    -		 
    -		 
    -		 
    -		
    -		 
    -		
    -		 
    -		 
    -		
    -		 
    -		 
    -		 
    -		
    -		
    -		 
    -		 
    -		
    -		
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		 
    -		
    -		 
    -		
    -		 
    -	
    -
    -
    +
    +	
    +
    +	
    +
    +
    +
    +	
    +	
    +		
    +		
    +		
    +		
    +	
    +	
    +	
    +	
    +		
    +		
    +		
    +		 
    +		
    +		
    +	
    +	
    +	
    +	
    +
    +	
    +
    +	
    +		
    +
    +		
    +		 	
    +		 		
    +		 	
    +		 
    +		 
    +		 
    +		 	
    +		 		
    +		 	
    +		 
    +
    +		
    +			
    +				
    +				
    +			
    +		
    +	
    +		
    +			
    +				
    +				
    +				
    +				
    +				
    +			
    +		
    +
    +	
    +
    +
    +	
    +	
    +	
    +		
    +		
    +	
    +
    +
    +	
    +
    +		
    +
    +		
    +		
    +		
    +		
    +		
    +		
    +		
    +		
    +		
    +		
    +
    +		
    +		
    +		
    +			
    +		
    +
    +				
    +		
    +		
    +		
    +		
    +		
    +		
    +		
    +
    +		
    +		
    +		
    +		 
    +		
    +						
    +		
    +		
    +		
    +
    +			
    +			
    +				
    +					
    +					
    +				
    +			
    +			
    +				
    +					
    +				
    +			
    +		
    +
    +		
    +
    +		
    +		
    +		
    +		
    +	
    +
    +
    +
    +	
    +
    +	
    +	
    +
    +
    +	
    +		
    +		
    +		
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		                                  
    +		 
    +		
    +		
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		
    +		
    +		 
    +		
    +		 
    +		 
    +		 
    +		 
    +		 
    +		
    +		
    +		 
    +		 
    +		 
    +		 
    +		 
    +		
    +		
    +		
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		
    +		
    +		    
    +		 
    +		 
    +		
    +		
    +		     
    +		 
    +		 
    +		
    +		
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +	
    +		
    +		 
    +		
    +		
    +		     
    +		 
    +		
    +		
    +		
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		
    +		
    +		     
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		
    +		 
    +		
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		
    +		
    +		
    +		 
    +		 
    +		 
    +		 
    +		 
    +		
    +		 
    +		
    +		
    +		
    +		 
    +		
    +		
    +		 
    +		
    +		 
    +		 
    +		 
    +		
    +		 
    +		
    +		 
    +		 
    +		
    +		 
    +		 
    +		 
    +		
    +		
    +		 
    +		 
    +		
    +		
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		 
    +		
    +		 
    +		
    +		 
    +	
    +
    +
    
    From 41f2c2a18229e6a146e705fdb322aed47b4c9933 Mon Sep 17 00:00:00 2001
    From: kwwall 
    Date: Mon, 18 Jan 2016 23:34:46 -0500
    Subject: [PATCH 0252/1069] Change CRLF to LF for issue 356.
    
    ---
     .../esapi/ESAPI-AccessControlPolicy.xml       | 254 +++++++++---------
     1 file changed, 127 insertions(+), 127 deletions(-)
    
    diff --git a/src/test/resources/esapi/ESAPI-AccessControlPolicy.xml b/src/test/resources/esapi/ESAPI-AccessControlPolicy.xml
    index 2ed0732bc..4d2ca74b5 100644
    --- a/src/test/resources/esapi/ESAPI-AccessControlPolicy.xml
    +++ b/src/test/resources/esapi/ESAPI-AccessControlPolicy.xml
    @@ -1,127 +1,127 @@
    -
    -
    -
    -	
    -		
    -		
    -		
    -		
    -		
    -		
    -		
    -			
    -				
    -			
    -		
    -		
    -		
    -		
    -			
    -				 
    -				
    -				
    -			
    -		
    -		
    -			
    -				
    -				
    -				
    -			
    -		
    -		
    -			
    -				
    -				
    -				
    -			
    -		
    -		
    -			
    -				
    -				
    -				
    -			
    -		
    -		
    -			
    -				
    -				
    -				
    -			
    -		
    -		
    -		
    -	
    -
    -
    -
    -
    -
    -
    -
    +
    +
    +
    +	
    +		
    +		
    +		
    +		
    +		
    +		
    +		
    +			
    +				
    +			
    +		
    +		
    +		
    +		
    +			
    +				 
    +				
    +				
    +			
    +		
    +		
    +			
    +				
    +				
    +				
    +			
    +		
    +		
    +			
    +				
    +				
    +				
    +			
    +		
    +		
    +			
    +				
    +				
    +				
    +			
    +		
    +		
    +			
    +				
    +				
    +				
    +			
    +		
    +		
    +		
    +	
    +
    +
    +
    +
    +
    +
    +
    
    From 41e96c6189d60f565fec213d4700a577ac65de00 Mon Sep 17 00:00:00 2001
    From: kwwall 
    Date: Mon, 18 Jan 2016 23:34:46 -0500
    Subject: [PATCH 0253/1069] Change CRLF to LF for issue 356.
    
    ---
     .../esapi/waf-policies/add-header-policy.xml  | 56 +++++++++----------
     1 file changed, 28 insertions(+), 28 deletions(-)
    
    diff --git a/src/test/resources/esapi/waf-policies/add-header-policy.xml b/src/test/resources/esapi/waf-policies/add-header-policy.xml
    index b2d8efc49..0a48ed2d0 100644
    --- a/src/test/resources/esapi/waf-policies/add-header-policy.xml
    +++ b/src/test/resources/esapi/waf-policies/add-header-policy.xml
    @@ -1,29 +1,29 @@
    -
    -
    -	
    -
    -
    -
    -	
    -		redirect
    -		
    -			/security/error.jsp
    -			403
    -		
    -	
    -	
    -	
    -		
    -			/marketing/.*
    -		
    -	
    -	
    +
    +
    +	
    +
    +
    +
    +	
    +		redirect
    +		
    +			/security/error.jsp
    +			403
    +		
    +	
    +	
    +	
    +		
    +			/marketing/.*
    +		
    +	
    +	
     
    \ No newline at end of file
    
    From 96d91bbe98af8611c695cc9615dd4a19587ea169 Mon Sep 17 00:00:00 2001
    From: kwwall 
    Date: Mon, 18 Jan 2016 23:34:46 -0500
    Subject: [PATCH 0254/1069] Change CRLF to LF for issue 356.
    
    ---
     .../waf-policies/add-httponly-policy.xml      | 52 +++++++++----------
     1 file changed, 26 insertions(+), 26 deletions(-)
    
    diff --git a/src/test/resources/esapi/waf-policies/add-httponly-policy.xml b/src/test/resources/esapi/waf-policies/add-httponly-policy.xml
    index 1232f5997..4420e6ce0 100644
    --- a/src/test/resources/esapi/waf-policies/add-httponly-policy.xml
    +++ b/src/test/resources/esapi/waf-policies/add-httponly-policy.xml
    @@ -1,27 +1,27 @@
    -
    -
    -	
    -
    -
    -
    -	
    -		redirect
    -		
    -			/security/error.jsp
    -			403
    -		
    -	
    -
    -	
    -		
    -			
    -		
    -	
    -	
    +
    +
    +	
    +
    +
    +
    +	
    +		redirect
    +		
    +			/security/error.jsp
    +			403
    +		
    +	
    +
    +	
    +		
    +			
    +		
    +	
    +	
     
    \ No newline at end of file
    
    From 6979a3c7b8ff112dbef1f5559197361e251a79d5 Mon Sep 17 00:00:00 2001
    From: kwwall 
    Date: Mon, 18 Jan 2016 23:34:46 -0500
    Subject: [PATCH 0255/1069] Change CRLF to LF for issue 356.
    
    ---
     .../esapi/waf-policies/add-secure-policy.xml  | 52 +++++++++----------
     1 file changed, 26 insertions(+), 26 deletions(-)
    
    diff --git a/src/test/resources/esapi/waf-policies/add-secure-policy.xml b/src/test/resources/esapi/waf-policies/add-secure-policy.xml
    index 12298a45a..80067da42 100644
    --- a/src/test/resources/esapi/waf-policies/add-secure-policy.xml
    +++ b/src/test/resources/esapi/waf-policies/add-secure-policy.xml
    @@ -1,27 +1,27 @@
    -
    -
    -	
    -
    -
    -
    -	
    -		redirect
    -		
    -			/security/error.jsp
    -			403
    -		
    -	
    -	
    -	
    -		
    -			
    -		
    -	
    -	
    +
    +
    +	
    +
    +
    +
    +	
    +		redirect
    +		
    +			/security/error.jsp
    +			403
    +		
    +	
    +	
    +	
    +		
    +			
    +		
    +	
    +	
     
    \ No newline at end of file
    
    From 73d5af5d969c471a369e91d7ff0916ddbdcf4d2f Mon Sep 17 00:00:00 2001
    From: kwwall 
    Date: Mon, 18 Jan 2016 23:34:46 -0500
    Subject: [PATCH 0256/1069] Change CRLF to LF for issue 356.
    
    ---
     .../waf-policies/authentication-policy.xml    | 58 +++++++++----------
     1 file changed, 29 insertions(+), 29 deletions(-)
    
    diff --git a/src/test/resources/esapi/waf-policies/authentication-policy.xml b/src/test/resources/esapi/waf-policies/authentication-policy.xml
    index 1c1a485be..5a77e6c0b 100644
    --- a/src/test/resources/esapi/waf-policies/authentication-policy.xml
    +++ b/src/test/resources/esapi/waf-policies/authentication-policy.xml
    @@ -1,30 +1,30 @@
    -
    -
    -	
    -
    -
    -
    -	
    -		redirect
    -		
    -			/security/error.jsp
    -			403
    -		
    -	>
    -
    -	
    -	
    -		/index.html
    -		/images/.*
    -	
    -
    +
    +
    +	
    +
    +
    +
    +	
    +		redirect
    +		
    +			/security/error.jsp
    +			403
    +		
    +	>
    +
    +	
    +	
    +		/index.html
    +		/images/.*
    +	
    +
     
    \ No newline at end of file
    
    From 53ee54a5cd23846007580bfe2972e5cd99af7351 Mon Sep 17 00:00:00 2001
    From: kwwall 
    Date: Mon, 18 Jan 2016 23:34:46 -0500
    Subject: [PATCH 0257/1069] Change CRLF to LF for issue 356.
    
    ---
     .../esapi/waf-policies/bean-shell-policy.xml  | 58 +++++++++----------
     1 file changed, 29 insertions(+), 29 deletions(-)
    
    diff --git a/src/test/resources/esapi/waf-policies/bean-shell-policy.xml b/src/test/resources/esapi/waf-policies/bean-shell-policy.xml
    index 5d2f9275f..948d151c0 100644
    --- a/src/test/resources/esapi/waf-policies/bean-shell-policy.xml
    +++ b/src/test/resources/esapi/waf-policies/bean-shell-policy.xml
    @@ -1,30 +1,30 @@
    -
    -
    -	
    -
    -
    -
    -	
    -		redirect
    -		
    -			/security/error.jsp
    -			403
    -		
    -	
    -
    -	
    -		
    -		
    -	
    -
    +
    +
    +	
    +
    +
    +
    +	
    +		redirect
    +		
    +			/security/error.jsp
    +			403
    +		
    +	
    +
    +	
    +		
    +		
    +	
    +
     
    \ No newline at end of file
    
    From 3ac816c7b36cb5b5f789ab48f16ebb65254c93fd Mon Sep 17 00:00:00 2001
    From: kwwall 
    Date: Mon, 18 Jan 2016 23:34:46 -0500
    Subject: [PATCH 0258/1069] Change CRLF to LF for issue 356.
    
    ---
     .../waf-policies/detect-outbound-policy.xml   | 52 +++++++++----------
     1 file changed, 26 insertions(+), 26 deletions(-)
    
    diff --git a/src/test/resources/esapi/waf-policies/detect-outbound-policy.xml b/src/test/resources/esapi/waf-policies/detect-outbound-policy.xml
    index 8312be95d..ac756aefa 100644
    --- a/src/test/resources/esapi/waf-policies/detect-outbound-policy.xml
    +++ b/src/test/resources/esapi/waf-policies/detect-outbound-policy.xml
    @@ -1,27 +1,27 @@
    -
    -
    -	
    -
    -
    -
    -	
    -		redirect
    -		
    -			/security/error.jsp
    -			403
    -		
    -	
    -
    -	
    -		
    -	
    -	
    +
    +
    +	
    +
    +
    +
    +	
    +		redirect
    +		
    +			/security/error.jsp
    +			403
    +		
    +	
    +
    +	
    +		
    +	
    +	
     
    \ No newline at end of file
    
    From 7eb39e3846c2c547dc58008c7adaf719eb622309 Mon Sep 17 00:00:00 2001
    From: kwwall 
    Date: Mon, 18 Jan 2016 23:34:46 -0500
    Subject: [PATCH 0259/1069] Change CRLF to LF for issue 356.
    
    ---
     .../waf-policies/dynamic-insertion-policy.xml | 52 +++++++++----------
     1 file changed, 26 insertions(+), 26 deletions(-)
    
    diff --git a/src/test/resources/esapi/waf-policies/dynamic-insertion-policy.xml b/src/test/resources/esapi/waf-policies/dynamic-insertion-policy.xml
    index 9c1aa4097..5b0926c2b 100644
    --- a/src/test/resources/esapi/waf-policies/dynamic-insertion-policy.xml
    +++ b/src/test/resources/esapi/waf-policies/dynamic-insertion-policy.xml
    @@ -1,27 +1,27 @@
    -
    -
    -	
    -
    -
    -
    -	
    -		redirect
    -		
    -			/security/error.jsp
    -			403
    -		
    -	
    -
    -	
    -		
    -			
    -		
    -	
    -	
    +
    +
    +	
    +
    +
    +
    +	
    +		redirect
    +		
    +			/security/error.jsp
    +			403
    +		
    +	
    +
    +	
    +		
    +			
    +		
    +	
    +	
     
    \ No newline at end of file
    
    From b265e8e449a7da2edc9218b9b54ecd9240a67ebe Mon Sep 17 00:00:00 2001
    From: kwwall 
    Date: Mon, 18 Jan 2016 23:34:46 -0500
    Subject: [PATCH 0260/1069] Change CRLF to LF for issue 356.
    
    ---
     .../waf-policies/enforce-https-policy.xml     | 54 +++++++++----------
     1 file changed, 27 insertions(+), 27 deletions(-)
    
    diff --git a/src/test/resources/esapi/waf-policies/enforce-https-policy.xml b/src/test/resources/esapi/waf-policies/enforce-https-policy.xml
    index ab123b7e6..beb88513a 100644
    --- a/src/test/resources/esapi/waf-policies/enforce-https-policy.xml
    +++ b/src/test/resources/esapi/waf-policies/enforce-https-policy.xml
    @@ -1,28 +1,28 @@
    -
    -
    -	
    -
    -
    -
    -	
    -		redirect
    -		
    -			/security/error.jsp
    -			403
    -		
    -	
    -
    -	
    -		
    -			/index.html
    -			/images/.*
    -		
    -	
    -
    +
    +
    +	
    +
    +
    +
    +	
    +		redirect
    +		
    +			/security/error.jsp
    +			403
    +		
    +	
    +
    +	
    +		
    +			/index.html
    +			/images/.*
    +		
    +	
    +
     
    \ No newline at end of file
    
    From cae9ce34e32df0115e23c6b57ac6f8cb644a977c Mon Sep 17 00:00:00 2001
    From: kwwall 
    Date: Mon, 18 Jan 2016 23:34:46 -0500
    Subject: [PATCH 0261/1069] Change CRLF to LF for issue 356.
    
    ---
     .../esapi/waf-policies/must-match-policy.xml  | 68 +++++++++----------
     1 file changed, 34 insertions(+), 34 deletions(-)
    
    diff --git a/src/test/resources/esapi/waf-policies/must-match-policy.xml b/src/test/resources/esapi/waf-policies/must-match-policy.xml
    index 721fa4cfa..af26fc4cd 100644
    --- a/src/test/resources/esapi/waf-policies/must-match-policy.xml
    +++ b/src/test/resources/esapi/waf-policies/must-match-policy.xml
    @@ -1,35 +1,35 @@
    -
    -
    -	
    -
    -
    -
    -	
    -		redirect
    -		
    -			/security/error.jsp
    -			403
    -		
    -	
    -
    -	
    -		
    -		
    -	
    -
    +
    +
    +	
    +
    +
    +
    +	
    +		redirect
    +		
    +			/security/error.jsp
    +			403
    +		
    +	
    +
    +	
    +		
    +		
    +	
    +
     
    \ No newline at end of file
    
    From 7f4022d27f07645eaecc7f495163b525d3f45665 Mon Sep 17 00:00:00 2001
    From: kwwall 
    Date: Mon, 18 Jan 2016 23:34:46 -0500
    Subject: [PATCH 0262/1069] Change CRLF to LF for issue 356.
    
    ---
     .../waf-policies/replace-outbound-policy.xml  | 52 +++++++++----------
     1 file changed, 26 insertions(+), 26 deletions(-)
    
    diff --git a/src/test/resources/esapi/waf-policies/replace-outbound-policy.xml b/src/test/resources/esapi/waf-policies/replace-outbound-policy.xml
    index 9c1aa4097..5b0926c2b 100644
    --- a/src/test/resources/esapi/waf-policies/replace-outbound-policy.xml
    +++ b/src/test/resources/esapi/waf-policies/replace-outbound-policy.xml
    @@ -1,27 +1,27 @@
    -
    -
    -	
    -
    -
    -
    -	
    -		redirect
    -		
    -			/security/error.jsp
    -			403
    -		
    -	
    -
    -	
    -		
    -			
    -		
    -	
    -	
    +
    +
    +	
    +
    +
    +
    +	
    +		redirect
    +		
    +			/security/error.jsp
    +			403
    +		
    +	
    +
    +	
    +		
    +			
    +		
    +	
    +	
     
    \ No newline at end of file
    
    From 7e519cd4bb7fed416672e4f4304e99aa21f0e0e8 Mon Sep 17 00:00:00 2001
    From: kwwall 
    Date: Mon, 18 Jan 2016 23:34:46 -0500
    Subject: [PATCH 0263/1069] Change CRLF to LF for issue 356.
    
    ---
     .../restrict-content-type-policy.xml          | 52 +++++++++----------
     1 file changed, 26 insertions(+), 26 deletions(-)
    
    diff --git a/src/test/resources/esapi/waf-policies/restrict-content-type-policy.xml b/src/test/resources/esapi/waf-policies/restrict-content-type-policy.xml
    index a9b8a4a38..b766f19d3 100644
    --- a/src/test/resources/esapi/waf-policies/restrict-content-type-policy.xml
    +++ b/src/test/resources/esapi/waf-policies/restrict-content-type-policy.xml
    @@ -1,27 +1,27 @@
    -
    -
    -	
    -
    -
    -
    -	
    -		redirect
    -		
    -			/security/error.jsp
    -			403
    -		
    -	
    -
    -	
    -		
    -			/fileupload.jsp
    -		
    -
    -	
    -
    +
    +
    +	
    +
    +
    +
    +	
    +		redirect
    +		
    +			/security/error.jsp
    +			403
    +		
    +	
    +
    +	
    +		
    +			/fileupload.jsp
    +		
    +
    +	
    +
     
    \ No newline at end of file
    
    From 21d2171cc925201b11da8c88ceba8d3d27d9d17b Mon Sep 17 00:00:00 2001
    From: kwwall 
    Date: Mon, 18 Jan 2016 23:34:46 -0500
    Subject: [PATCH 0264/1069] Change CRLF to LF for issue 356.
    
    ---
     .../restrict-extension-policy.xml             | 48 +++++++++----------
     1 file changed, 24 insertions(+), 24 deletions(-)
    
    diff --git a/src/test/resources/esapi/waf-policies/restrict-extension-policy.xml b/src/test/resources/esapi/waf-policies/restrict-extension-policy.xml
    index fe1dbe193..a572bd4ea 100644
    --- a/src/test/resources/esapi/waf-policies/restrict-extension-policy.xml
    +++ b/src/test/resources/esapi/waf-policies/restrict-extension-policy.xml
    @@ -1,25 +1,25 @@
    -
    -
    -	
    -
    -
    -
    -	
    -		redirect
    -		
    -			/security/error.jsp
    -			403
    -		
    -	
    -
    -	
    -		
    -		
    -	
    -
    +
    +
    +	
    +
    +
    +
    +	
    +		redirect
    +		
    +			/security/error.jsp
    +			403
    +		
    +	
    +
    +	
    +		
    +		
    +	
    +
     
    \ No newline at end of file
    
    From 5aca43aa27eb9670ea808ef4f88e2e68697f7242 Mon Sep 17 00:00:00 2001
    From: kwwall 
    Date: Mon, 18 Jan 2016 23:34:46 -0500
    Subject: [PATCH 0265/1069] Change CRLF to LF for issue 356.
    
    ---
     .../waf-policies/restrict-method-policy.xml   | 48 +++++++++----------
     1 file changed, 24 insertions(+), 24 deletions(-)
    
    diff --git a/src/test/resources/esapi/waf-policies/restrict-method-policy.xml b/src/test/resources/esapi/waf-policies/restrict-method-policy.xml
    index 5a2337cf7..c1eb5a36e 100644
    --- a/src/test/resources/esapi/waf-policies/restrict-method-policy.xml
    +++ b/src/test/resources/esapi/waf-policies/restrict-method-policy.xml
    @@ -1,25 +1,25 @@
    -
    -
    -	
    -
    -
    -
    -	
    -		redirect
    -		
    -			/security/error.jsp
    -			403
    -		
    -	
    -
    -	
    -		
    -		
    -	
    -
    +
    +
    +	
    +
    +
    +
    +	
    +		redirect
    +		
    +			/security/error.jsp
    +			403
    +		
    +	
    +
    +	
    +		
    +		
    +	
    +
     
    \ No newline at end of file
    
    From 4c9dcef430b2e08ef45280ddd1b6f6f98904b706 Mon Sep 17 00:00:00 2001
    From: kwwall 
    Date: Mon, 18 Jan 2016 23:34:46 -0500
    Subject: [PATCH 0266/1069] Change CRLF to LF for issue 356.
    
    ---
     .../restrict-source-ip-policy.xml             | 52 +++++++++----------
     1 file changed, 26 insertions(+), 26 deletions(-)
    
    diff --git a/src/test/resources/esapi/waf-policies/restrict-source-ip-policy.xml b/src/test/resources/esapi/waf-policies/restrict-source-ip-policy.xml
    index d23d87a02..72860a7d3 100644
    --- a/src/test/resources/esapi/waf-policies/restrict-source-ip-policy.xml
    +++ b/src/test/resources/esapi/waf-policies/restrict-source-ip-policy.xml
    @@ -1,27 +1,27 @@
    -
    -
    -	
    -
    -
    -
    -	
    -		redirect
    -		
    -			/security/error.jsp
    -			403
    -		
    -	
    -
    -	
    -		/admin/.*
    -
    -	
    -
    +
    +
    +	
    +
    +
    +
    +	
    +		redirect
    +		
    +			/security/error.jsp
    +			403
    +		
    +	
    +
    +	
    +		/admin/.*
    +
    +	
    +
     
    \ No newline at end of file
    
    From 0038ea8eade5399c5711bbc0123e3f4e6258e368 Mon Sep 17 00:00:00 2001
    From: kwwall 
    Date: Mon, 18 Jan 2016 23:34:46 -0500
    Subject: [PATCH 0267/1069] Change CRLF to LF for issue 356.
    
    ---
     .../restrict-user-agent-policy.xml            | 50 +++++++++----------
     1 file changed, 25 insertions(+), 25 deletions(-)
    
    diff --git a/src/test/resources/esapi/waf-policies/restrict-user-agent-policy.xml b/src/test/resources/esapi/waf-policies/restrict-user-agent-policy.xml
    index 7cdebd595..c550a2c61 100644
    --- a/src/test/resources/esapi/waf-policies/restrict-user-agent-policy.xml
    +++ b/src/test/resources/esapi/waf-policies/restrict-user-agent-policy.xml
    @@ -1,26 +1,26 @@
    -
    -
    -	
    -
    -
    -
    -	
    -		redirect
    -		
    -			/security/error.jsp
    -			403
    -		
    -	
    -
    -	
    -		
    -			/index.html
    -		
    -	
    -
    +
    +
    +	
    +
    +
    +
    +	
    +		redirect
    +		
    +			/security/error.jsp
    +			403
    +		
    +	
    +
    +	
    +		
    +			/index.html
    +		
    +	
    +
     
    \ No newline at end of file
    
    From 284c604f99c070f3966e977113a9794151ded02e Mon Sep 17 00:00:00 2001
    From: kwwall 
    Date: Mon, 18 Jan 2016 23:34:46 -0500
    Subject: [PATCH 0268/1069] Change CRLF to LF for issue 356.
    
    ---
     .../waf-policies/virtual-patch-policy.xml     | 52 +++++++++----------
     1 file changed, 26 insertions(+), 26 deletions(-)
    
    diff --git a/src/test/resources/esapi/waf-policies/virtual-patch-policy.xml b/src/test/resources/esapi/waf-policies/virtual-patch-policy.xml
    index 61c93fa88..8b989dd6e 100644
    --- a/src/test/resources/esapi/waf-policies/virtual-patch-policy.xml
    +++ b/src/test/resources/esapi/waf-policies/virtual-patch-policy.xml
    @@ -1,27 +1,27 @@
    -
    -
    -	
    -
    -
    -
    -	
    -		redirect
    -		
    -			/security/error.jsp
    -			403
    -		
    -	
    -
    -	
    -		
    -	
    -
    +
    +
    +	
    +
    +
    +
    +	
    +		redirect
    +		
    +			/security/error.jsp
    +			403
    +		
    +	
    +
    +	
    +		
    +	
    +
     
    \ No newline at end of file
    
    From 9aaaf809a1b77c9fd8bfdeaee2b85079d1fe7db4 Mon Sep 17 00:00:00 2001
    From: kwwall 
    Date: Mon, 18 Jan 2016 23:34:46 -0500
    Subject: [PATCH 0269/1069] Change CRLF to LF for issue 356.
    
    ---
     src/test/resources/esapi/waf-policy.xml | 296 ++++++++++++------------
     1 file changed, 148 insertions(+), 148 deletions(-)
    
    diff --git a/src/test/resources/esapi/waf-policy.xml b/src/test/resources/esapi/waf-policy.xml
    index 05d75b23d..7a1df159f 100644
    --- a/src/test/resources/esapi/waf-policy.xml
    +++ b/src/test/resources/esapi/waf-policy.xml
    @@ -1,149 +1,149 @@
    -
    -
    -	
    -
    -
    -
    -
    -	
    -	
    -		/security/input.jsp
    -		^/admin/.*
    -	
    -
    -
    -
    -	
    -	
    -		redirect
    -		JSESSIONID
    -		
    -			/security/error.jsp
    -			403
    -		
    -	
    -
    -
    -
    -	
    -	
    -		/
    -		/index.html
    -		/login.jsp
    -		/index.jsp
    -		/images/.*
    -		/css/.*
    -		/help/.*
    -	
    -
    -
    -		
    -		
    -	
    -	
    -	
    -	
    -		/admin/.*
    -		
    -	
    -
    -
    -
    -	
    -	
    -		
    -
    -		
    -		
    -
    -		
    -			/index.html
    -			/index.jsp
    -			/images/.*
    -			/css/.*
    -			/help/.*
    -		
    -	
    -
    -
    -
    -	
    -	
    -		
    -		
    -		
    -		
    -		
    -	
    -
    -
    -	
    -	
    -		
    -	
    -
    -
    -
    -	
    -
    -	
    -
    -		
    -			/foobar/.*
    -		
    -
    -		
    -			
    -		
    -
    -		
    -			
    -		
    -
    - 		
    -			
    -		
    -
    -		
    -			\1\2
    -		
    -
    -		
    -
    -	
    -
    +
    +
    +	
    +
    +
    +
    +
    +	
    +	
    +		/security/input.jsp
    +		^/admin/.*
    +	
    +
    +
    +
    +	
    +	
    +		redirect
    +		JSESSIONID
    +		
    +			/security/error.jsp
    +			403
    +		
    +	
    +
    +
    +
    +	
    +	
    +		/
    +		/index.html
    +		/login.jsp
    +		/index.jsp
    +		/images/.*
    +		/css/.*
    +		/help/.*
    +	
    +
    +
    +		
    +		
    +	
    +	
    +	
    +	
    +		/admin/.*
    +		
    +	
    +
    +
    +
    +	
    +	
    +		
    +
    +		
    +		
    +
    +		
    +			/index.html
    +			/index.jsp
    +			/images/.*
    +			/css/.*
    +			/help/.*
    +		
    +	
    +
    +
    +
    +	
    +	
    +		
    +		
    +		
    +		
    +		
    +	
    +
    +
    +	
    +	
    +		
    +	
    +
    +
    +
    +	
    +
    +	
    +
    +		
    +			/foobar/.*
    +		
    +
    +		
    +			
    +		
    +
    +		
    +			
    +		
    +
    + 		
    +			
    +		
    +
    +		
    +			\1\2
    +		
    +
    +		
    +
    +	
    +
     
    \ No newline at end of file
    
    From d571e4e6ce7e3639441569d6e359dfbb0860e778 Mon Sep 17 00:00:00 2001
    From: kwwall 
    Date: Mon, 18 Jan 2016 23:34:46 -0500
    Subject: [PATCH 0270/1069] Change CRLF to LF for issue 356.
    
    ---
     src/test/resources/log4j.xml | 110 +++++++++++++++++------------------
     1 file changed, 55 insertions(+), 55 deletions(-)
    
    diff --git a/src/test/resources/log4j.xml b/src/test/resources/log4j.xml
    index 04c2b4364..9cb7354fe 100644
    --- a/src/test/resources/log4j.xml
    +++ b/src/test/resources/log4j.xml
    @@ -1,55 +1,55 @@
    -
    -
    -
    -
    -
    -   
    -     
    -     
    -       
    -     
    -  
    -
    -    
    -        
    -        
    -          
    -        
    -    
    -
    -  
    -    
    -  
    -
    -  
    -    
    -  
    -
    -  
    -    
    - 
    -
    -  
    -    
    -  
    -
    -  
    -    
    -  
    -
    -  
    -    
    -  
    -
    -  
    -    
    -  
    -
    -  
    -    
    -    
    -  
    -
    -  
    -
    -
    +
    +
    +
    +
    +
    +   
    +     
    +     
    +       
    +     
    +  
    +
    +    
    +        
    +        
    +          
    +        
    +    
    +
    +  
    +    
    +  
    +
    +  
    +    
    +  
    +
    +  
    +    
    + 
    +
    +  
    +    
    +  
    +
    +  
    +    
    +  
    +
    +  
    +    
    +  
    +
    +  
    +    
    +  
    +
    +  
    +    
    +    
    +  
    +
    +  
    +
    +
    
    From 4a229d9fe665e60b5d7d51408caa59d8d2baf85d Mon Sep 17 00:00:00 2001
    From: kwwall 
    Date: Tue, 19 Jan 2016 01:10:08 -0500
    Subject: [PATCH 0271/1069] Continue to try to address issue 356.
    
    ---
     .gitattributes | 25 ++++++++++++++++++++-----
     1 file changed, 20 insertions(+), 5 deletions(-)
    
    diff --git a/.gitattributes b/.gitattributes
    index a90ca1d70..3645b7bba 100644
    --- a/.gitattributes
    +++ b/.gitattributes
    @@ -7,12 +7,9 @@
     #       git config --global core.autocrlf input
     * text=auto
     
    -# And configure default EOL terminators.
     #
    -# Force the following filetypes to have Unix eols, so Windows does not break them
    -# Those that are not explicitly set to use Windows EOL will use LF as EOL terminator.
    -# (e.g., see *.bat, below).
    -*.* text eol=lf
    +# And configure default EOL terminators for various text types
    +#
     
     # Explicitly declare text files you want to always be normalized and converted
     # to native line endings on checkout.
    @@ -42,6 +39,16 @@
     *.bsh text eol=lf
     *.ksh text eol=lf
     
    +# More
    +.gitattributes text
    +.settings/* text eol=crlf
    +.classpath text eol=crlf
    +*.MF text eol=crlf
    +LICENSE text eol=crlf
    +LICENSE-CONTENT text eol=crlf
    +LICENSE-README text eol=crlf
    +
    +
     # Denote all files that are truly binary and should not be modified,
     # or simply replaced in whole if committed.
     *.jpg binary
    @@ -56,3 +63,11 @@
     *.pptx binary
     *.odt binary
     *.pdf binary
    +*.zip binary
    +*.jar binary
    +*.war binary
    +*.ear binary
    +*.7z binary
    +*.rar binary
    +*.tgz binary
    +*.tar binary
    
    From f348b558d5cb059b806f4240f4c553e36ada06ad Mon Sep 17 00:00:00 2001
    From: kwwall 
    Date: Tue, 19 Jan 2016 01:12:42 -0500
    Subject: [PATCH 0272/1069] Address issue 356.
    
    ---
     configuration/esapi/ESAPI.properties | 930 +++++++++++++--------------
     1 file changed, 465 insertions(+), 465 deletions(-)
    
    diff --git a/configuration/esapi/ESAPI.properties b/configuration/esapi/ESAPI.properties
    index baa8e6933..f98cc5a49 100644
    --- a/configuration/esapi/ESAPI.properties
    +++ b/configuration/esapi/ESAPI.properties
    @@ -1,465 +1,465 @@
    -#
    -# OWASP Enterprise Security API (ESAPI) Properties file -- PRODUCTION Version
    -# 
    -# This file is part of the Open Web Application Security Project (OWASP)
    -# Enterprise Security API (ESAPI) project. For details, please see
    -# http://www.owasp.org/index.php/ESAPI.
    -#
    -# Copyright (c) 2008,2009 - The OWASP Foundation
    -#
    -# DISCUSS: This may cause a major backwards compatibility issue, etc. but
    -#		   from a name space perspective, we probably should have prefaced
    -#		   all the property names with ESAPI or at least OWASP. Otherwise
    -#		   there could be problems is someone loads this properties file into
    -#		   the System properties.  We could also put this file into the
    -#		   esapi.jar file (perhaps as a ResourceBundle) and then allow an external
    -#		   ESAPI properties be defined that would overwrite these defaults.
    -#		   That keeps the application's properties relatively simple as usually
    -#		   they will only want to override a few properties. If looks like we
    -#		   already support multiple override levels of this in the
    -#		   DefaultSecurityConfiguration class, but I'm suggesting placing the
    -#		   defaults in the esapi.jar itself. That way, if the jar is signed,
    -#		   we could detect if those properties had been tampered with. (The
    -#		   code to check the jar signatures is pretty simple... maybe 70-90 LOC,
    -#		   but off course there is an execution penalty (similar to the way
    -#		   that the separate sunjce.jar used to be when a class from it was
    -#		   first loaded). Thoughts?
    -###############################################################################
    -#
    -# WARNING: Operating system protection should be used to lock down the .esapi
    -# resources directory and all the files inside and all the directories all the
    -# way up to the root directory of the file system.  Note that if you are using
    -# file-based implementations, that some files may need to be read-write as they
    -# get updated dynamically.
    -#
    -# Before using, be sure to update the MasterKey and MasterSalt as described below.
    -# N.B.: If you had stored data that you have previously encrypted with ESAPI 1.4,
    -#		you *must* FIRST decrypt it using ESAPI 1.4 and then (if so desired)
    -#		re-encrypt it with ESAPI 2.0. If you fail to do this, you will NOT be
    -#		able to decrypt your data with ESAPI 2.0.
    -#
    -#		YOU HAVE BEEN WARNED!!! More details are in the ESAPI 2.0 Release Notes.
    -#
    -#===========================================================================
    -# ESAPI Configuration
    -#
    -# If true, then print all the ESAPI properties set here when they are loaded.
    -# If false, they are not printed. Useful to reduce output when running JUnit tests.
    -# If you need to troubleshoot a properties related problem, turning this on may help.
    -# This is 'false' in the src/test/resources/.esapi version. It is 'true' by
    -# default for reasons of backward compatibility with earlier ESAPI versions.
    -ESAPI.printProperties=true
    -
    -# ESAPI is designed to be easily extensible. You can use the reference implementation
    -# or implement your own providers to take advantage of your enterprise's security
    -# infrastructure. The functions in ESAPI are referenced using the ESAPI locator, like:
    -#
    -#    String ciphertext =
    -#		ESAPI.encryptor().encrypt("Secret message");   // Deprecated in 2.0
    -#    CipherText cipherText =
    -#		ESAPI.encryptor().encrypt(new PlainText("Secret message")); // Preferred
    -#
    -# Below you can specify the classname for the provider that you wish to use in your
    -# application. The only requirement is that it implement the appropriate ESAPI interface.
    -# This allows you to switch security implementations in the future without rewriting the
    -# entire application.
    -#
    -# ExperimentalAccessController requires ESAPI-AccessControlPolicy.xml in .esapi directory
    -ESAPI.AccessControl=org.owasp.esapi.reference.DefaultAccessController
    -# FileBasedAuthenticator requires users.txt file in .esapi directory
    -ESAPI.Authenticator=org.owasp.esapi.reference.FileBasedAuthenticator
    -ESAPI.Encoder=org.owasp.esapi.reference.DefaultEncoder
    -ESAPI.Encryptor=org.owasp.esapi.reference.crypto.JavaEncryptor
    -
    -ESAPI.Executor=org.owasp.esapi.reference.DefaultExecutor
    -ESAPI.HTTPUtilities=org.owasp.esapi.reference.DefaultHTTPUtilities
    -ESAPI.IntrusionDetector=org.owasp.esapi.reference.DefaultIntrusionDetector
    -# Log4JFactory Requires log4j.xml or log4j.properties in classpath - http://www.laliluna.de/log4j-tutorial.html
    -ESAPI.Logger=org.owasp.esapi.reference.Log4JLogFactory
    -#ESAPI.Logger=org.owasp.esapi.reference.JavaLogFactory
    -ESAPI.Randomizer=org.owasp.esapi.reference.DefaultRandomizer
    -ESAPI.Validator=org.owasp.esapi.reference.DefaultValidator
    -
    -#===========================================================================
    -# ESAPI Authenticator
    -#
    -Authenticator.AllowedLoginAttempts=3
    -Authenticator.MaxOldPasswordHashes=13
    -Authenticator.UsernameParameterName=username
    -Authenticator.PasswordParameterName=password
    -# RememberTokenDuration (in days)
    -Authenticator.RememberTokenDuration=14
    -# Session Timeouts (in minutes)
    -Authenticator.IdleTimeoutDuration=20
    -Authenticator.AbsoluteTimeoutDuration=120
    -
    -#===========================================================================
    -# ESAPI Encoder
    -#
    -# ESAPI canonicalizes input before validation to prevent bypassing filters with encoded attacks.
    -# Failure to canonicalize input is a very common mistake when implementing validation schemes.
    -# Canonicalization is automatic when using the ESAPI Validator, but you can also use the
    -# following code to canonicalize data.
    -#
    -#      ESAPI.Encoder().canonicalize( "%22hello world"" );
    -#  
    -# Multiple encoding is when a single encoding format is applied multiple times. Allowing
    -# multiple encoding is strongly discouraged.
    -Encoder.AllowMultipleEncoding=false
    -
    -# Mixed encoding is when multiple different encoding formats are applied, or when 
    -# multiple formats are nested. Allowing multiple encoding is strongly discouraged.
    -Encoder.AllowMixedEncoding=false
    -
    -# The default list of codecs to apply when canonicalizing untrusted data. The list should include the codecs
    -# for all downstream interpreters or decoders. For example, if the data is likely to end up in a URL, HTML, or
    -# inside JavaScript, then the list of codecs below is appropriate. The order of the list is not terribly important.
    -Encoder.DefaultCodecList=HTMLEntityCodec,PercentCodec,JavaScriptCodec
    -
    -
    -#===========================================================================
    -# ESAPI Encryption
    -#
    -# The ESAPI Encryptor provides basic cryptographic functions with a simplified API.
    -# To get started, generate a new key using java -classpath esapi.jar org.owasp.esapi.reference.crypto.JavaEncryptor
    -# There is not currently any support for key rotation, so be careful when changing your key and salt as it
    -# will invalidate all signed, encrypted, and hashed data.
    -#
    -# WARNING: Not all combinations of algorithms and key lengths are supported.
    -# If you choose to use a key length greater than 128, you MUST download the
    -# unlimited strength policy files and install in the lib directory of your JRE/JDK.
    -# See http://java.sun.com/javase/downloads/index.jsp for more information.
    -#
    -# Backward compatibility with ESAPI Java 1.4 is supported by the two deprecated API
    -# methods, Encryptor.encrypt(String) and Encryptor.decrypt(String). However, whenever
    -# possible, these methods should be avoided as they use ECB cipher mode, which in almost
    -# all circumstances a poor choice because of it's weakness. CBC cipher mode is the default
    -# for the new Encryptor encrypt / decrypt methods for ESAPI Java 2.0.  In general, you
    -# should only use this compatibility setting if you have persistent data encrypted with
    -# version 1.4 and even then, you should ONLY set this compatibility mode UNTIL
    -# you have decrypted all of your old encrypted data and then re-encrypted it with
    -# ESAPI 2.0 using CBC mode. If you have some reason to mix the deprecated 1.4 mode
    -# with the new 2.0 methods, make sure that you use the same cipher algorithm for both
    -# (256-bit AES was the default for 1.4; 128-bit is the default for 2.0; see below for
    -# more details.) Otherwise, you will have to use the new 2.0 encrypt / decrypt methods
    -# where you can specify a SecretKey. (Note that if you are using the 256-bit AES,
    -# that requires downloading the special jurisdiction policy files mentioned above.)
    -#
    -#		***** IMPORTANT: Do NOT forget to replace these with your own values! *****
    -# To calculate these values, you can run:
    -#		java -classpath esapi.jar org.owasp.esapi.reference.crypto.JavaEncryptor
    -#
    -#Encryptor.MasterKey=
    -#Encryptor.MasterSalt=
    -
    -# Provides the default JCE provider that ESAPI will "prefer" for its symmetric
    -# encryption and hashing. (That is it will look to this provider first, but it
    -# will defer to other providers if the requested algorithm is not implemented
    -# by this provider.) If left unset, ESAPI will just use your Java VM's current
    -# preferred JCE provider, which is generally set in the file
    -# "$JAVA_HOME/jre/lib/security/java.security".
    -#
    -# The main intent of this is to allow ESAPI symmetric encryption to be
    -# used with a FIPS 140-2 compliant crypto-module. For details, see the section
    -# "Using ESAPI Symmetric Encryption with FIPS 140-2 Cryptographic Modules" in
    -# the ESAPI 2.0 Symmetric Encryption User Guide, at:
    -# http://owasp-esapi-java.googlecode.com/svn/trunk/documentation/esapi4java-core-2.0-symmetric-crypto-user-guide.html
    -# However, this property also allows you to easily use an alternate JCE provider
    -# such as "Bouncy Castle" without having to make changes to "java.security".
    -# See Javadoc for SecurityProviderLoader for further details. If you wish to use
    -# a provider that is not known to SecurityProviderLoader, you may specify the
    -# fully-qualified class name of the JCE provider class that implements
    -# java.security.Provider. If the name contains a '.', this is interpreted as
    -# a fully-qualified class name that implements java.security.Provider.
    -#
    -# NOTE: Setting this property has the side-effect of changing it in your application
    -#       as well, so if you are using JCE in your application directly rather than
    -#       through ESAPI (you wouldn't do that, would you? ;-), it will change the
    -#       preferred JCE provider there as well.
    -#
    -# Default: Keeps the JCE provider set to whatever JVM sets it to.
    -Encryptor.PreferredJCEProvider=
    -
    -# AES is the most widely used and strongest encryption algorithm. This
    -# should agree with your Encryptor.CipherTransformation property.
    -# By default, ESAPI Java 1.4 uses "PBEWithMD5AndDES" and which is
    -# very weak. It is essentially a password-based encryption key, hashed
    -# with MD5 around 1K times and then encrypted with the weak DES algorithm
    -# (56-bits) using ECB mode and an unspecified padding (it is
    -# JCE provider specific, but most likely "NoPadding"). However, 2.0 uses
    -# "AES/CBC/PKCSPadding". If you want to change these, change them here.
    -# Warning: This property does not control the default reference implementation for
    -#		   ESAPI 2.0 using JavaEncryptor. Also, this property will be dropped
    -#		   in the future.
    -# @deprecated
    -Encryptor.EncryptionAlgorithm=AES
    -#		For ESAPI Java 2.0 - New encrypt / decrypt methods use this.
    -Encryptor.CipherTransformation=AES/CBC/PKCS5Padding
    -
    -# Applies to ESAPI 2.0 and later only!
    -# Comma-separated list of cipher modes that provide *BOTH*
    -# confidentiality *AND* message authenticity. (NIST refers to such cipher
    -# modes as "combined modes" so that's what we shall call them.) If any of these
    -# cipher modes are used then no MAC is calculated and stored
    -# in the CipherText upon encryption. Likewise, if one of these
    -# cipher modes is used with decryption, no attempt will be made
    -# to validate the MAC contained in the CipherText object regardless
    -# of whether it contains one or not. Since the expectation is that
    -# these cipher modes support support message authenticity already,
    -# injecting a MAC in the CipherText object would be at best redundant.
    -#
    -# Note that as of JDK 1.5, the SunJCE provider does not support *any*
    -# of these cipher modes. Of these listed, only GCM and CCM are currently
    -# NIST approved. YMMV for other JCE providers. E.g., Bouncy Castle supports
    -# GCM and CCM with "NoPadding" mode, but not with "PKCS5Padding" or other
    -# padding modes.
    -Encryptor.cipher_modes.combined_modes=GCM,CCM,IAPM,EAX,OCB,CWC
    -
    -# Applies to ESAPI 2.0 and later only!
    -# Additional cipher modes allowed for ESAPI 2.0 encryption. These
    -# cipher modes are in _addition_ to those specified by the property
    -# 'Encryptor.cipher_modes.combined_modes'.
    -# Note: We will add support for streaming modes like CFB & OFB once
    -# we add support for 'specified' to the property 'Encryptor.ChooseIVMethod'
    -# (probably in ESAPI 2.1).
    -# DISCUSS: Better name?
    -Encryptor.cipher_modes.additional_allowed=CBC
    -
    -# 128-bit is almost always sufficient and appears to be more resistant to
    -# related key attacks than is 256-bit AES. Use '_' to use default key size
    -# for cipher algorithms (where it makes sense because the algorithm supports
    -# a variable key size). Key length must agree to what's provided as the
    -# cipher transformation, otherwise this will be ignored after logging a
    -# warning.
    -#
    -# NOTE: This is what applies BOTH ESAPI 1.4 and 2.0. See warning above about mixing!
    -Encryptor.EncryptionKeyLength=128
    -
    -# Because 2.0 uses CBC mode by default, it requires an initialization vector (IV).
    -# (All cipher modes except ECB require an IV.) There are two choices: we can either
    -# use a fixed IV known to both parties or allow ESAPI to choose a random IV. While
    -# the IV does not need to be hidden from adversaries, it is important that the
    -# adversary not be allowed to choose it. Also, random IVs are generally much more
    -# secure than fixed IVs. (In fact, it is essential that feed-back cipher modes
    -# such as CFB and OFB use a different IV for each encryption with a given key so
    -# in such cases, random IVs are much preferred. By default, ESAPI 2.0 uses random
    -# IVs. If you wish to use 'fixed' IVs, set 'Encryptor.ChooseIVMethod=fixed' and
    -# uncomment the Encryptor.fixedIV.
    -#
    -# Valid values:		random|fixed|specified		'specified' not yet implemented; planned for 2.1
    -Encryptor.ChooseIVMethod=random
    -# If you choose to use a fixed IV, then you must place a fixed IV here that
    -# is known to all others who are sharing your secret key. The format should
    -# be a hex string that is the same length as the cipher block size for the
    -# cipher algorithm that you are using. The following is an *example* for AES
    -# from an AES test vector for AES-128/CBC as described in:
    -# NIST Special Publication 800-38A (2001 Edition)
    -# "Recommendation for Block Cipher Modes of Operation".
    -# (Note that the block size for AES is 16 bytes == 128 bits.)
    -#
    -Encryptor.fixedIV=0x000102030405060708090a0b0c0d0e0f
    -
    -# Whether or not CipherText should use a message authentication code (MAC) with it.
    -# This prevents an adversary from altering the IV as well as allowing a more
    -# fool-proof way of determining the decryption failed because of an incorrect
    -# key being supplied. This refers to the "separate" MAC calculated and stored
    -# in CipherText, not part of any MAC that is calculated as a result of a
    -# "combined mode" cipher mode.
    -#
    -# If you are using ESAPI with a FIPS 140-2 cryptographic module, you *must* also
    -# set this property to false.
    -Encryptor.CipherText.useMAC=true
    -
    -# Whether or not the PlainText object may be overwritten and then marked
    -# eligible for garbage collection. If not set, this is still treated as 'true'.
    -Encryptor.PlainText.overwrite=true
    -
    -# Do not use DES except in a legacy situations. 56-bit is way too small key size.
    -#Encryptor.EncryptionKeyLength=56
    -#Encryptor.EncryptionAlgorithm=DES
    -
    -# TripleDES is considered strong enough for most purposes.
    -#	Note:	There is also a 112-bit version of DESede. Using the 168-bit version
    -#			requires downloading the special jurisdiction policy from Sun.
    -#Encryptor.EncryptionKeyLength=168
    -#Encryptor.EncryptionAlgorithm=DESede
    -
    -Encryptor.HashAlgorithm=SHA-512
    -Encryptor.HashIterations=1024
    -Encryptor.DigitalSignatureAlgorithm=SHA1withDSA
    -Encryptor.DigitalSignatureKeyLength=1024
    -Encryptor.RandomAlgorithm=SHA1PRNG
    -Encryptor.CharacterEncoding=UTF-8
    -
    -# This is the Pseudo Random Function (PRF) that ESAPI's Key Derivation Function
    -# (KDF) normally uses. Note this is *only* the PRF used for ESAPI's KDF and
    -# *not* what is used for ESAPI's MAC. (Currently, HmacSHA1 is always used for
    -# the MAC, mostly to keep the overall size at a minimum.)
    -#
    -# Currently supported choices for JDK 1.5 and 1.6 are:
    -#	HmacSHA1 (160 bits), HmacSHA256 (256 bits), HmacSHA384 (384 bits), and
    -#	HmacSHA512 (512 bits).
    -# Note that HmacMD5 is *not* supported for the PRF used by the KDF even though
    -# the JDKs support it.  See the ESAPI 2.0 Symmetric Encryption User Guide
    -# further details.
    -Encryptor.KDF.PRF=HmacSHA256
    -#===========================================================================
    -# ESAPI HttpUtilties
    -#
    -# The HttpUtilities provide basic protections to HTTP requests and responses. Primarily these methods 
    -# protect against malicious data from attackers, such as unprintable characters, escaped characters,
    -# and other simple attacks. The HttpUtilities also provides utility methods for dealing with cookies,
    -# headers, and CSRF tokens.
    -#
    -# Default file upload location (remember to escape backslashes with \\)
    -HttpUtilities.UploadDir=C:\\ESAPI\\testUpload
    -HttpUtilities.UploadTempDir=C:\\temp
    -# Force flags on cookies, if you use HttpUtilities to set cookies
    -HttpUtilities.ForceHttpOnlySession=false
    -HttpUtilities.ForceSecureSession=false
    -HttpUtilities.ForceHttpOnlyCookies=true
    -HttpUtilities.ForceSecureCookies=true
    -# Maximum size of HTTP headers
    -HttpUtilities.MaxHeaderSize=4096
    -# File upload configuration
    -HttpUtilities.ApprovedUploadExtensions=.zip,.pdf,.doc,.docx,.ppt,.pptx,.tar,.gz,.tgz,.rar,.war,.jar,.ear,.xls,.rtf,.properties,.java,.class,.txt,.xml,.jsp,.jsf,.exe,.dll
    -HttpUtilities.MaxUploadFileBytes=500000000
    -# Using UTF-8 throughout your stack is highly recommended. That includes your database driver,
    -# container, and any other technologies you may be using. Failure to do this may expose you
    -# to Unicode transcoding injection attacks. Use of UTF-8 does not hinder internationalization.
    -HttpUtilities.ResponseContentType=text/html; charset=UTF-8
    -# This is the name of the cookie used to represent the HTTP session
    -# Typically this will be the default "JSESSIONID" 
    -HttpUtilities.HttpSessionIdName=JSESSIONID
    -
    -
    -
    -#===========================================================================
    -# ESAPI Executor
    -# CHECKME - This should be made OS independent. Don't use unsafe defaults.
    -# # Examples only -- do NOT blindly copy!
    -#   For Windows:
    -#     Executor.WorkingDirectory=C:\\Windows\\Temp
    -#     Executor.ApprovedExecutables=C:\\Windows\\System32\\cmd.exe,C:\\Windows\\System32\\runas.exe
    -#   For *nux, MacOS:
    -#     Executor.WorkingDirectory=/tmp
    -#     Executor.ApprovedExecutables=/bin/bash
    -Executor.WorkingDirectory=
    -Executor.ApprovedExecutables=
    -
    -
    -#===========================================================================
    -# ESAPI Logging
    -# Set the application name if these logs are combined with other applications
    -Logger.ApplicationName=ExampleApplication
    -# If you use an HTML log viewer that does not properly HTML escape log data, you can set LogEncodingRequired to true
    -Logger.LogEncodingRequired=false
    -# Determines whether ESAPI should log the application name. This might be clutter in some single-server/single-app environments.
    -Logger.LogApplicationName=true
    -# Determines whether ESAPI should log the server IP and port. This might be clutter in some single-server environments.
    -Logger.LogServerIP=true
    -# LogFileName, the name of the logging file. Provide a full directory path (e.g., C:\\ESAPI\\ESAPI_logging_file) if you
    -# want to place it in a specific directory.
    -Logger.LogFileName=ESAPI_logging_file
    -# MaxLogFileSize, the max size (in bytes) of a single log file before it cuts over to a new one (default is 10,000,000)
    -Logger.MaxLogFileSize=10000000
    -
    -
    -#===========================================================================
    -# ESAPI Intrusion Detection
    -#
    -# Each event has a base to which .count, .interval, and .action are added
    -# The IntrusionException will fire if we receive "count" events within "interval" seconds
    -# The IntrusionDetector is configurable to take the following actions: log, logout, and disable
    -#  (multiple actions separated by commas are allowed e.g. event.test.actions=log,disable
    -#
    -# Custom Events
    -# Names must start with "event." as the base
    -# Use IntrusionDetector.addEvent( "test" ) in your code to trigger "event.test" here
    -# You can also disable intrusion detection completely by changing
    -# the following parameter to true
    -#
    -IntrusionDetector.Disable=false
    -#
    -IntrusionDetector.event.test.count=2
    -IntrusionDetector.event.test.interval=10
    -IntrusionDetector.event.test.actions=disable,log
    -
    -# Exception Events
    -# All EnterpriseSecurityExceptions are registered automatically
    -# Call IntrusionDetector.getInstance().addException(e) for Exceptions that do not extend EnterpriseSecurityException
    -# Use the fully qualified classname of the exception as the base
    -
    -# any intrusion is an attack
    -IntrusionDetector.org.owasp.esapi.errors.IntrusionException.count=1
    -IntrusionDetector.org.owasp.esapi.errors.IntrusionException.interval=1
    -IntrusionDetector.org.owasp.esapi.errors.IntrusionException.actions=log,disable,logout
    -
    -# for test purposes
    -# CHECKME: Shouldn't there be something in the property name itself that designates
    -#		   that these are for testing???
    -IntrusionDetector.org.owasp.esapi.errors.IntegrityException.count=10
    -IntrusionDetector.org.owasp.esapi.errors.IntegrityException.interval=5
    -IntrusionDetector.org.owasp.esapi.errors.IntegrityException.actions=log,disable,logout
    -
    -# rapid validation errors indicate scans or attacks in progress
    -# org.owasp.esapi.errors.ValidationException.count=10
    -# org.owasp.esapi.errors.ValidationException.interval=10
    -# org.owasp.esapi.errors.ValidationException.actions=log,logout
    -
    -# sessions jumping between hosts indicates session hijacking
    -IntrusionDetector.org.owasp.esapi.errors.AuthenticationHostException.count=2
    -IntrusionDetector.org.owasp.esapi.errors.AuthenticationHostException.interval=10
    -IntrusionDetector.org.owasp.esapi.errors.AuthenticationHostException.actions=log,logout
    -
    -
    -#===========================================================================
    -# ESAPI Validation
    -#
    -# The ESAPI Validator works on regular expressions with defined names. You can define names
    -# either here, or you may define application specific patterns in a separate file defined below.
    -# This allows enterprises to specify both organizational standards as well as application specific
    -# validation rules.
    -#
    -# Use '\p{L}' (without the quotes) within the character class to match
    -# any Unicode LETTER. You can also use a range, like:  \u00C0-\u017F
    -# You can also use any of the regex flags as documented at
    -# https://docs.oracle.com/javase/tutorial/essential/regex/pattern.html, e.g. (?u)
    -#
    -Validator.ConfigurationFile=validation.properties
    -
    -# Validators used by ESAPI
    -Validator.AccountName=^[a-zA-Z0-9]{3,20}$
    -Validator.SystemCommand=^[a-zA-Z\\-\\/]{1,64}$
    -Validator.RoleName=^[a-z]{1,20}$
    -
    -#the word TEST below should be changed to your application 
    -#name - only relative URL's are supported
    -Validator.Redirect=^\\/test.*$
    -
    -# Global HTTP Validation Rules
    -# Values with Base64 encoded data (e.g. encrypted state) will need at least [a-zA-Z0-9\/+=]
    -Validator.HTTPScheme=^(http|https)$
    -Validator.HTTPServerName=^[a-zA-Z0-9_.\\-]*$
    -Validator.HTTPParameterName=^[a-zA-Z0-9_]{1,32}$
    -Validator.HTTPParameterValue=^[a-zA-Z0-9.\\-\\/+=@_ ]*$
    -Validator.HTTPCookieName=^[a-zA-Z0-9\\-_]{1,32}$
    -Validator.HTTPCookieValue=^[a-zA-Z0-9\\-\\/+=_ ]*$
    -# Note that max header name capped at 150 in SecurityRequestWrapper!
    -Validator.HTTPHeaderName=^[a-zA-Z0-9\\-_]{1,50}$
    -Validator.HTTPHeaderValue=^[a-zA-Z0-9()\\-=\\*\\.\\?;,+\\/:&_ ]*$
    -Validator.HTTPContextPath=^\\/?[a-zA-Z0-9.\\-\\/_]*$
    -Validator.HTTPServletPath=^[a-zA-Z0-9.\\-\\/_]*$
    -Validator.HTTPPath=^[a-zA-Z0-9.\\-_]*$
    -Validator.HTTPQueryString=^[a-zA-Z0-9()\\-=\\*\\.\\?;,+\\/:&_ %]*$
    -Validator.HTTPURI=^[a-zA-Z0-9()\\-=\\*\\.\\?;,+\\/:&_ ]*$
    -Validator.HTTPURL=^.*$
    -Validator.HTTPJSESSIONID=^[A-Z0-9]{10,30}$
    -
    -# Validation of file related input
    -Validator.FileName=^[a-zA-Z0-9!@#$%^&{}\\[\\]()_+\\-=,.~'` ]{1,255}$
    -Validator.DirectoryName=^[a-zA-Z0-9:/\\\\!@#$%^&{}\\[\\]()_+\\-=,.~'` ]{1,255}$
    -
    -# Validation of dates. Controls whether or not 'lenient' dates are accepted.
    -# See DataFormat.setLenient(boolean flag) for further details.
    -Validator.AcceptLenientDates=false
    +#
    +# OWASP Enterprise Security API (ESAPI) Properties file -- PRODUCTION Version
    +# 
    +# This file is part of the Open Web Application Security Project (OWASP)
    +# Enterprise Security API (ESAPI) project. For details, please see
    +# http://www.owasp.org/index.php/ESAPI.
    +#
    +# Copyright (c) 2008,2009 - The OWASP Foundation
    +#
    +# DISCUSS: This may cause a major backwards compatibility issue, etc. but
    +#		   from a name space perspective, we probably should have prefaced
    +#		   all the property names with ESAPI or at least OWASP. Otherwise
    +#		   there could be problems is someone loads this properties file into
    +#		   the System properties.  We could also put this file into the
    +#		   esapi.jar file (perhaps as a ResourceBundle) and then allow an external
    +#		   ESAPI properties be defined that would overwrite these defaults.
    +#		   That keeps the application's properties relatively simple as usually
    +#		   they will only want to override a few properties. If looks like we
    +#		   already support multiple override levels of this in the
    +#		   DefaultSecurityConfiguration class, but I'm suggesting placing the
    +#		   defaults in the esapi.jar itself. That way, if the jar is signed,
    +#		   we could detect if those properties had been tampered with. (The
    +#		   code to check the jar signatures is pretty simple... maybe 70-90 LOC,
    +#		   but off course there is an execution penalty (similar to the way
    +#		   that the separate sunjce.jar used to be when a class from it was
    +#		   first loaded). Thoughts?
    +###############################################################################
    +#
    +# WARNING: Operating system protection should be used to lock down the .esapi
    +# resources directory and all the files inside and all the directories all the
    +# way up to the root directory of the file system.  Note that if you are using
    +# file-based implementations, that some files may need to be read-write as they
    +# get updated dynamically.
    +#
    +# Before using, be sure to update the MasterKey and MasterSalt as described below.
    +# N.B.: If you had stored data that you have previously encrypted with ESAPI 1.4,
    +#		you *must* FIRST decrypt it using ESAPI 1.4 and then (if so desired)
    +#		re-encrypt it with ESAPI 2.0. If you fail to do this, you will NOT be
    +#		able to decrypt your data with ESAPI 2.0.
    +#
    +#		YOU HAVE BEEN WARNED!!! More details are in the ESAPI 2.0 Release Notes.
    +#
    +#===========================================================================
    +# ESAPI Configuration
    +#
    +# If true, then print all the ESAPI properties set here when they are loaded.
    +# If false, they are not printed. Useful to reduce output when running JUnit tests.
    +# If you need to troubleshoot a properties related problem, turning this on may help.
    +# This is 'false' in the src/test/resources/.esapi version. It is 'true' by
    +# default for reasons of backward compatibility with earlier ESAPI versions.
    +ESAPI.printProperties=true
    +
    +# ESAPI is designed to be easily extensible. You can use the reference implementation
    +# or implement your own providers to take advantage of your enterprise's security
    +# infrastructure. The functions in ESAPI are referenced using the ESAPI locator, like:
    +#
    +#    String ciphertext =
    +#		ESAPI.encryptor().encrypt("Secret message");   // Deprecated in 2.0
    +#    CipherText cipherText =
    +#		ESAPI.encryptor().encrypt(new PlainText("Secret message")); // Preferred
    +#
    +# Below you can specify the classname for the provider that you wish to use in your
    +# application. The only requirement is that it implement the appropriate ESAPI interface.
    +# This allows you to switch security implementations in the future without rewriting the
    +# entire application.
    +#
    +# ExperimentalAccessController requires ESAPI-AccessControlPolicy.xml in .esapi directory
    +ESAPI.AccessControl=org.owasp.esapi.reference.DefaultAccessController
    +# FileBasedAuthenticator requires users.txt file in .esapi directory
    +ESAPI.Authenticator=org.owasp.esapi.reference.FileBasedAuthenticator
    +ESAPI.Encoder=org.owasp.esapi.reference.DefaultEncoder
    +ESAPI.Encryptor=org.owasp.esapi.reference.crypto.JavaEncryptor
    +
    +ESAPI.Executor=org.owasp.esapi.reference.DefaultExecutor
    +ESAPI.HTTPUtilities=org.owasp.esapi.reference.DefaultHTTPUtilities
    +ESAPI.IntrusionDetector=org.owasp.esapi.reference.DefaultIntrusionDetector
    +# Log4JFactory Requires log4j.xml or log4j.properties in classpath - http://www.laliluna.de/log4j-tutorial.html
    +ESAPI.Logger=org.owasp.esapi.reference.Log4JLogFactory
    +#ESAPI.Logger=org.owasp.esapi.reference.JavaLogFactory
    +ESAPI.Randomizer=org.owasp.esapi.reference.DefaultRandomizer
    +ESAPI.Validator=org.owasp.esapi.reference.DefaultValidator
    +
    +#===========================================================================
    +# ESAPI Authenticator
    +#
    +Authenticator.AllowedLoginAttempts=3
    +Authenticator.MaxOldPasswordHashes=13
    +Authenticator.UsernameParameterName=username
    +Authenticator.PasswordParameterName=password
    +# RememberTokenDuration (in days)
    +Authenticator.RememberTokenDuration=14
    +# Session Timeouts (in minutes)
    +Authenticator.IdleTimeoutDuration=20
    +Authenticator.AbsoluteTimeoutDuration=120
    +
    +#===========================================================================
    +# ESAPI Encoder
    +#
    +# ESAPI canonicalizes input before validation to prevent bypassing filters with encoded attacks.
    +# Failure to canonicalize input is a very common mistake when implementing validation schemes.
    +# Canonicalization is automatic when using the ESAPI Validator, but you can also use the
    +# following code to canonicalize data.
    +#
    +#      ESAPI.Encoder().canonicalize( "%22hello world"" );
    +#  
    +# Multiple encoding is when a single encoding format is applied multiple times. Allowing
    +# multiple encoding is strongly discouraged.
    +Encoder.AllowMultipleEncoding=false
    +
    +# Mixed encoding is when multiple different encoding formats are applied, or when 
    +# multiple formats are nested. Allowing multiple encoding is strongly discouraged.
    +Encoder.AllowMixedEncoding=false
    +
    +# The default list of codecs to apply when canonicalizing untrusted data. The list should include the codecs
    +# for all downstream interpreters or decoders. For example, if the data is likely to end up in a URL, HTML, or
    +# inside JavaScript, then the list of codecs below is appropriate. The order of the list is not terribly important.
    +Encoder.DefaultCodecList=HTMLEntityCodec,PercentCodec,JavaScriptCodec
    +
    +
    +#===========================================================================
    +# ESAPI Encryption
    +#
    +# The ESAPI Encryptor provides basic cryptographic functions with a simplified API.
    +# To get started, generate a new key using java -classpath esapi.jar org.owasp.esapi.reference.crypto.JavaEncryptor
    +# There is not currently any support for key rotation, so be careful when changing your key and salt as it
    +# will invalidate all signed, encrypted, and hashed data.
    +#
    +# WARNING: Not all combinations of algorithms and key lengths are supported.
    +# If you choose to use a key length greater than 128, you MUST download the
    +# unlimited strength policy files and install in the lib directory of your JRE/JDK.
    +# See http://java.sun.com/javase/downloads/index.jsp for more information.
    +#
    +# Backward compatibility with ESAPI Java 1.4 is supported by the two deprecated API
    +# methods, Encryptor.encrypt(String) and Encryptor.decrypt(String). However, whenever
    +# possible, these methods should be avoided as they use ECB cipher mode, which in almost
    +# all circumstances a poor choice because of it's weakness. CBC cipher mode is the default
    +# for the new Encryptor encrypt / decrypt methods for ESAPI Java 2.0.  In general, you
    +# should only use this compatibility setting if you have persistent data encrypted with
    +# version 1.4 and even then, you should ONLY set this compatibility mode UNTIL
    +# you have decrypted all of your old encrypted data and then re-encrypted it with
    +# ESAPI 2.0 using CBC mode. If you have some reason to mix the deprecated 1.4 mode
    +# with the new 2.0 methods, make sure that you use the same cipher algorithm for both
    +# (256-bit AES was the default for 1.4; 128-bit is the default for 2.0; see below for
    +# more details.) Otherwise, you will have to use the new 2.0 encrypt / decrypt methods
    +# where you can specify a SecretKey. (Note that if you are using the 256-bit AES,
    +# that requires downloading the special jurisdiction policy files mentioned above.)
    +#
    +#		***** IMPORTANT: Do NOT forget to replace these with your own values! *****
    +# To calculate these values, you can run:
    +#		java -classpath esapi.jar org.owasp.esapi.reference.crypto.JavaEncryptor
    +#
    +#Encryptor.MasterKey=
    +#Encryptor.MasterSalt=
    +
    +# Provides the default JCE provider that ESAPI will "prefer" for its symmetric
    +# encryption and hashing. (That is it will look to this provider first, but it
    +# will defer to other providers if the requested algorithm is not implemented
    +# by this provider.) If left unset, ESAPI will just use your Java VM's current
    +# preferred JCE provider, which is generally set in the file
    +# "$JAVA_HOME/jre/lib/security/java.security".
    +#
    +# The main intent of this is to allow ESAPI symmetric encryption to be
    +# used with a FIPS 140-2 compliant crypto-module. For details, see the section
    +# "Using ESAPI Symmetric Encryption with FIPS 140-2 Cryptographic Modules" in
    +# the ESAPI 2.0 Symmetric Encryption User Guide, at:
    +# http://owasp-esapi-java.googlecode.com/svn/trunk/documentation/esapi4java-core-2.0-symmetric-crypto-user-guide.html
    +# However, this property also allows you to easily use an alternate JCE provider
    +# such as "Bouncy Castle" without having to make changes to "java.security".
    +# See Javadoc for SecurityProviderLoader for further details. If you wish to use
    +# a provider that is not known to SecurityProviderLoader, you may specify the
    +# fully-qualified class name of the JCE provider class that implements
    +# java.security.Provider. If the name contains a '.', this is interpreted as
    +# a fully-qualified class name that implements java.security.Provider.
    +#
    +# NOTE: Setting this property has the side-effect of changing it in your application
    +#       as well, so if you are using JCE in your application directly rather than
    +#       through ESAPI (you wouldn't do that, would you? ;-), it will change the
    +#       preferred JCE provider there as well.
    +#
    +# Default: Keeps the JCE provider set to whatever JVM sets it to.
    +Encryptor.PreferredJCEProvider=
    +
    +# AES is the most widely used and strongest encryption algorithm. This
    +# should agree with your Encryptor.CipherTransformation property.
    +# By default, ESAPI Java 1.4 uses "PBEWithMD5AndDES" and which is
    +# very weak. It is essentially a password-based encryption key, hashed
    +# with MD5 around 1K times and then encrypted with the weak DES algorithm
    +# (56-bits) using ECB mode and an unspecified padding (it is
    +# JCE provider specific, but most likely "NoPadding"). However, 2.0 uses
    +# "AES/CBC/PKCSPadding". If you want to change these, change them here.
    +# Warning: This property does not control the default reference implementation for
    +#		   ESAPI 2.0 using JavaEncryptor. Also, this property will be dropped
    +#		   in the future.
    +# @deprecated
    +Encryptor.EncryptionAlgorithm=AES
    +#		For ESAPI Java 2.0 - New encrypt / decrypt methods use this.
    +Encryptor.CipherTransformation=AES/CBC/PKCS5Padding
    +
    +# Applies to ESAPI 2.0 and later only!
    +# Comma-separated list of cipher modes that provide *BOTH*
    +# confidentiality *AND* message authenticity. (NIST refers to such cipher
    +# modes as "combined modes" so that's what we shall call them.) If any of these
    +# cipher modes are used then no MAC is calculated and stored
    +# in the CipherText upon encryption. Likewise, if one of these
    +# cipher modes is used with decryption, no attempt will be made
    +# to validate the MAC contained in the CipherText object regardless
    +# of whether it contains one or not. Since the expectation is that
    +# these cipher modes support support message authenticity already,
    +# injecting a MAC in the CipherText object would be at best redundant.
    +#
    +# Note that as of JDK 1.5, the SunJCE provider does not support *any*
    +# of these cipher modes. Of these listed, only GCM and CCM are currently
    +# NIST approved. YMMV for other JCE providers. E.g., Bouncy Castle supports
    +# GCM and CCM with "NoPadding" mode, but not with "PKCS5Padding" or other
    +# padding modes.
    +Encryptor.cipher_modes.combined_modes=GCM,CCM,IAPM,EAX,OCB,CWC
    +
    +# Applies to ESAPI 2.0 and later only!
    +# Additional cipher modes allowed for ESAPI 2.0 encryption. These
    +# cipher modes are in _addition_ to those specified by the property
    +# 'Encryptor.cipher_modes.combined_modes'.
    +# Note: We will add support for streaming modes like CFB & OFB once
    +# we add support for 'specified' to the property 'Encryptor.ChooseIVMethod'
    +# (probably in ESAPI 2.1).
    +# DISCUSS: Better name?
    +Encryptor.cipher_modes.additional_allowed=CBC
    +
    +# 128-bit is almost always sufficient and appears to be more resistant to
    +# related key attacks than is 256-bit AES. Use '_' to use default key size
    +# for cipher algorithms (where it makes sense because the algorithm supports
    +# a variable key size). Key length must agree to what's provided as the
    +# cipher transformation, otherwise this will be ignored after logging a
    +# warning.
    +#
    +# NOTE: This is what applies BOTH ESAPI 1.4 and 2.0. See warning above about mixing!
    +Encryptor.EncryptionKeyLength=128
    +
    +# Because 2.0 uses CBC mode by default, it requires an initialization vector (IV).
    +# (All cipher modes except ECB require an IV.) There are two choices: we can either
    +# use a fixed IV known to both parties or allow ESAPI to choose a random IV. While
    +# the IV does not need to be hidden from adversaries, it is important that the
    +# adversary not be allowed to choose it. Also, random IVs are generally much more
    +# secure than fixed IVs. (In fact, it is essential that feed-back cipher modes
    +# such as CFB and OFB use a different IV for each encryption with a given key so
    +# in such cases, random IVs are much preferred. By default, ESAPI 2.0 uses random
    +# IVs. If you wish to use 'fixed' IVs, set 'Encryptor.ChooseIVMethod=fixed' and
    +# uncomment the Encryptor.fixedIV.
    +#
    +# Valid values:		random|fixed|specified		'specified' not yet implemented; planned for 2.1
    +Encryptor.ChooseIVMethod=random
    +# If you choose to use a fixed IV, then you must place a fixed IV here that
    +# is known to all others who are sharing your secret key. The format should
    +# be a hex string that is the same length as the cipher block size for the
    +# cipher algorithm that you are using. The following is an *example* for AES
    +# from an AES test vector for AES-128/CBC as described in:
    +# NIST Special Publication 800-38A (2001 Edition)
    +# "Recommendation for Block Cipher Modes of Operation".
    +# (Note that the block size for AES is 16 bytes == 128 bits.)
    +#
    +Encryptor.fixedIV=0x000102030405060708090a0b0c0d0e0f
    +
    +# Whether or not CipherText should use a message authentication code (MAC) with it.
    +# This prevents an adversary from altering the IV as well as allowing a more
    +# fool-proof way of determining the decryption failed because of an incorrect
    +# key being supplied. This refers to the "separate" MAC calculated and stored
    +# in CipherText, not part of any MAC that is calculated as a result of a
    +# "combined mode" cipher mode.
    +#
    +# If you are using ESAPI with a FIPS 140-2 cryptographic module, you *must* also
    +# set this property to false.
    +Encryptor.CipherText.useMAC=true
    +
    +# Whether or not the PlainText object may be overwritten and then marked
    +# eligible for garbage collection. If not set, this is still treated as 'true'.
    +Encryptor.PlainText.overwrite=true
    +
    +# Do not use DES except in a legacy situations. 56-bit is way too small key size.
    +#Encryptor.EncryptionKeyLength=56
    +#Encryptor.EncryptionAlgorithm=DES
    +
    +# TripleDES is considered strong enough for most purposes.
    +#	Note:	There is also a 112-bit version of DESede. Using the 168-bit version
    +#			requires downloading the special jurisdiction policy from Sun.
    +#Encryptor.EncryptionKeyLength=168
    +#Encryptor.EncryptionAlgorithm=DESede
    +
    +Encryptor.HashAlgorithm=SHA-512
    +Encryptor.HashIterations=1024
    +Encryptor.DigitalSignatureAlgorithm=SHA1withDSA
    +Encryptor.DigitalSignatureKeyLength=1024
    +Encryptor.RandomAlgorithm=SHA1PRNG
    +Encryptor.CharacterEncoding=UTF-8
    +
    +# This is the Pseudo Random Function (PRF) that ESAPI's Key Derivation Function
    +# (KDF) normally uses. Note this is *only* the PRF used for ESAPI's KDF and
    +# *not* what is used for ESAPI's MAC. (Currently, HmacSHA1 is always used for
    +# the MAC, mostly to keep the overall size at a minimum.)
    +#
    +# Currently supported choices for JDK 1.5 and 1.6 are:
    +#	HmacSHA1 (160 bits), HmacSHA256 (256 bits), HmacSHA384 (384 bits), and
    +#	HmacSHA512 (512 bits).
    +# Note that HmacMD5 is *not* supported for the PRF used by the KDF even though
    +# the JDKs support it.  See the ESAPI 2.0 Symmetric Encryption User Guide
    +# further details.
    +Encryptor.KDF.PRF=HmacSHA256
    +#===========================================================================
    +# ESAPI HttpUtilties
    +#
    +# The HttpUtilities provide basic protections to HTTP requests and responses. Primarily these methods 
    +# protect against malicious data from attackers, such as unprintable characters, escaped characters,
    +# and other simple attacks. The HttpUtilities also provides utility methods for dealing with cookies,
    +# headers, and CSRF tokens.
    +#
    +# Default file upload location (remember to escape backslashes with \\)
    +HttpUtilities.UploadDir=C:\\ESAPI\\testUpload
    +HttpUtilities.UploadTempDir=C:\\temp
    +# Force flags on cookies, if you use HttpUtilities to set cookies
    +HttpUtilities.ForceHttpOnlySession=false
    +HttpUtilities.ForceSecureSession=false
    +HttpUtilities.ForceHttpOnlyCookies=true
    +HttpUtilities.ForceSecureCookies=true
    +# Maximum size of HTTP headers
    +HttpUtilities.MaxHeaderSize=4096
    +# File upload configuration
    +HttpUtilities.ApprovedUploadExtensions=.zip,.pdf,.doc,.docx,.ppt,.pptx,.tar,.gz,.tgz,.rar,.war,.jar,.ear,.xls,.rtf,.properties,.java,.class,.txt,.xml,.jsp,.jsf,.exe,.dll
    +HttpUtilities.MaxUploadFileBytes=500000000
    +# Using UTF-8 throughout your stack is highly recommended. That includes your database driver,
    +# container, and any other technologies you may be using. Failure to do this may expose you
    +# to Unicode transcoding injection attacks. Use of UTF-8 does not hinder internationalization.
    +HttpUtilities.ResponseContentType=text/html; charset=UTF-8
    +# This is the name of the cookie used to represent the HTTP session
    +# Typically this will be the default "JSESSIONID" 
    +HttpUtilities.HttpSessionIdName=JSESSIONID
    +
    +
    +
    +#===========================================================================
    +# ESAPI Executor
    +# CHECKME - This should be made OS independent. Don't use unsafe defaults.
    +# # Examples only -- do NOT blindly copy!
    +#   For Windows:
    +#     Executor.WorkingDirectory=C:\\Windows\\Temp
    +#     Executor.ApprovedExecutables=C:\\Windows\\System32\\cmd.exe,C:\\Windows\\System32\\runas.exe
    +#   For *nux, MacOS:
    +#     Executor.WorkingDirectory=/tmp
    +#     Executor.ApprovedExecutables=/bin/bash
    +Executor.WorkingDirectory=
    +Executor.ApprovedExecutables=
    +
    +
    +#===========================================================================
    +# ESAPI Logging
    +# Set the application name if these logs are combined with other applications
    +Logger.ApplicationName=ExampleApplication
    +# If you use an HTML log viewer that does not properly HTML escape log data, you can set LogEncodingRequired to true
    +Logger.LogEncodingRequired=false
    +# Determines whether ESAPI should log the application name. This might be clutter in some single-server/single-app environments.
    +Logger.LogApplicationName=true
    +# Determines whether ESAPI should log the server IP and port. This might be clutter in some single-server environments.
    +Logger.LogServerIP=true
    +# LogFileName, the name of the logging file. Provide a full directory path (e.g., C:\\ESAPI\\ESAPI_logging_file) if you
    +# want to place it in a specific directory.
    +Logger.LogFileName=ESAPI_logging_file
    +# MaxLogFileSize, the max size (in bytes) of a single log file before it cuts over to a new one (default is 10,000,000)
    +Logger.MaxLogFileSize=10000000
    +
    +
    +#===========================================================================
    +# ESAPI Intrusion Detection
    +#
    +# Each event has a base to which .count, .interval, and .action are added
    +# The IntrusionException will fire if we receive "count" events within "interval" seconds
    +# The IntrusionDetector is configurable to take the following actions: log, logout, and disable
    +#  (multiple actions separated by commas are allowed e.g. event.test.actions=log,disable
    +#
    +# Custom Events
    +# Names must start with "event." as the base
    +# Use IntrusionDetector.addEvent( "test" ) in your code to trigger "event.test" here
    +# You can also disable intrusion detection completely by changing
    +# the following parameter to true
    +#
    +IntrusionDetector.Disable=false
    +#
    +IntrusionDetector.event.test.count=2
    +IntrusionDetector.event.test.interval=10
    +IntrusionDetector.event.test.actions=disable,log
    +
    +# Exception Events
    +# All EnterpriseSecurityExceptions are registered automatically
    +# Call IntrusionDetector.getInstance().addException(e) for Exceptions that do not extend EnterpriseSecurityException
    +# Use the fully qualified classname of the exception as the base
    +
    +# any intrusion is an attack
    +IntrusionDetector.org.owasp.esapi.errors.IntrusionException.count=1
    +IntrusionDetector.org.owasp.esapi.errors.IntrusionException.interval=1
    +IntrusionDetector.org.owasp.esapi.errors.IntrusionException.actions=log,disable,logout
    +
    +# for test purposes
    +# CHECKME: Shouldn't there be something in the property name itself that designates
    +#		   that these are for testing???
    +IntrusionDetector.org.owasp.esapi.errors.IntegrityException.count=10
    +IntrusionDetector.org.owasp.esapi.errors.IntegrityException.interval=5
    +IntrusionDetector.org.owasp.esapi.errors.IntegrityException.actions=log,disable,logout
    +
    +# rapid validation errors indicate scans or attacks in progress
    +# org.owasp.esapi.errors.ValidationException.count=10
    +# org.owasp.esapi.errors.ValidationException.interval=10
    +# org.owasp.esapi.errors.ValidationException.actions=log,logout
    +
    +# sessions jumping between hosts indicates session hijacking
    +IntrusionDetector.org.owasp.esapi.errors.AuthenticationHostException.count=2
    +IntrusionDetector.org.owasp.esapi.errors.AuthenticationHostException.interval=10
    +IntrusionDetector.org.owasp.esapi.errors.AuthenticationHostException.actions=log,logout
    +
    +
    +#===========================================================================
    +# ESAPI Validation
    +#
    +# The ESAPI Validator works on regular expressions with defined names. You can define names
    +# either here, or you may define application specific patterns in a separate file defined below.
    +# This allows enterprises to specify both organizational standards as well as application specific
    +# validation rules.
    +#
    +# Use '\p{L}' (without the quotes) within the character class to match
    +# any Unicode LETTER. You can also use a range, like:  \u00C0-\u017F
    +# You can also use any of the regex flags as documented at
    +# https://docs.oracle.com/javase/tutorial/essential/regex/pattern.html, e.g. (?u)
    +#
    +Validator.ConfigurationFile=validation.properties
    +
    +# Validators used by ESAPI
    +Validator.AccountName=^[a-zA-Z0-9]{3,20}$
    +Validator.SystemCommand=^[a-zA-Z\\-\\/]{1,64}$
    +Validator.RoleName=^[a-z]{1,20}$
    +
    +#the word TEST below should be changed to your application 
    +#name - only relative URL's are supported
    +Validator.Redirect=^\\/test.*$
    +
    +# Global HTTP Validation Rules
    +# Values with Base64 encoded data (e.g. encrypted state) will need at least [a-zA-Z0-9\/+=]
    +Validator.HTTPScheme=^(http|https)$
    +Validator.HTTPServerName=^[a-zA-Z0-9_.\\-]*$
    +Validator.HTTPParameterName=^[a-zA-Z0-9_]{1,32}$
    +Validator.HTTPParameterValue=^[a-zA-Z0-9.\\-\\/+=@_ ]*$
    +Validator.HTTPCookieName=^[a-zA-Z0-9\\-_]{1,32}$
    +Validator.HTTPCookieValue=^[a-zA-Z0-9\\-\\/+=_ ]*$
    +# Note that max header name capped at 150 in SecurityRequestWrapper!
    +Validator.HTTPHeaderName=^[a-zA-Z0-9\\-_]{1,50}$
    +Validator.HTTPHeaderValue=^[a-zA-Z0-9()\\-=\\*\\.\\?;,+\\/:&_ ]*$
    +Validator.HTTPContextPath=^\\/?[a-zA-Z0-9.\\-\\/_]*$
    +Validator.HTTPServletPath=^[a-zA-Z0-9.\\-\\/_]*$
    +Validator.HTTPPath=^[a-zA-Z0-9.\\-_]*$
    +Validator.HTTPQueryString=^[a-zA-Z0-9()\\-=\\*\\.\\?;,+\\/:&_ %]*$
    +Validator.HTTPURI=^[a-zA-Z0-9()\\-=\\*\\.\\?;,+\\/:&_ ]*$
    +Validator.HTTPURL=^.*$
    +Validator.HTTPJSESSIONID=^[A-Z0-9]{10,30}$
    +
    +# Validation of file related input
    +Validator.FileName=^[a-zA-Z0-9!@#$%^&{}\\[\\]()_+\\-=,.~'` ]{1,255}$
    +Validator.DirectoryName=^[a-zA-Z0-9:/\\\\!@#$%^&{}\\[\\]()_+\\-=,.~'` ]{1,255}$
    +
    +# Validation of dates. Controls whether or not 'lenient' dates are accepted.
    +# See DataFormat.setLenient(boolean flag) for further details.
    +Validator.AcceptLenientDates=false
    
    From 2812549f07865efcce7ab741742291d8190d8e33 Mon Sep 17 00:00:00 2001
    From: kwwall 
    Date: Tue, 19 Jan 2016 01:12:42 -0500
    Subject: [PATCH 0273/1069] Address issue 356.
    
    ---
     configuration/esapi/validation.properties | 58 +++++++++++------------
     1 file changed, 29 insertions(+), 29 deletions(-)
    
    diff --git a/configuration/esapi/validation.properties b/configuration/esapi/validation.properties
    index 18e037f3b..433fa0b6b 100644
    --- a/configuration/esapi/validation.properties
    +++ b/configuration/esapi/validation.properties
    @@ -1,29 +1,29 @@
    -# The ESAPI validator does many security checks on input, such as canonicalization
    -# and whitelist validation. Note that all of these validation rules are applied *after*
    -# canonicalization. Double-encoded characters (even with different encodings involved,
    -# are never allowed.
    -#
    -# To use:
    -#
    -# First set up a pattern below. You can choose any name you want, prefixed by the word
    -# "Validation." For example:
    -#   Validation.Email=^[A-Za-z0-9._%-]+@[A-Za-z0-9.-]+\\.[a-zA-Z]{2,4}$
    -# 
    -# Then you can validate in your code against the pattern like this:
    -#     ESAPI.validator().isValidInput("User Email", input, "Email", maxLength, allowNull);
    -# Where maxLength and allowNull are set for you needs, respectively.
    -#
    -# But note, when you use boolean variants of validation functions, you lose critical 
    -# canonicalization. It is preferable to use the "get" methods (which throw exceptions) and 
    -# and use the returned user input which is in canonical form. Consider the following:
    -#  
    -# try {
    -#    someObject.setEmail(ESAPI.validator().getValidInput("User Email", input, "Email", maxLength, allowNull));
    -#
    -Validator.SafeString=^[.\\p{Alnum}\\p{Space}]{0,1024}$
    -Validator.Email=^[A-Za-z0-9._%'-]+@[A-Za-z0-9.-]+\\.[a-zA-Z]{2,4}$
    -Validator.IPAddress=^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$
    -Validator.URL=^(ht|f)tp(s?)\\:\\/\\/[0-9a-zA-Z]([-.\\w]*[0-9a-zA-Z])*(:(0-9)*)*(\\/?)([a-zA-Z0-9\\-\\.\\?\\,\\:\\'\\/\\\\\\+=&%\\$#_]*)?$
    -Validator.CreditCard=^(\\d{4}[- ]?){3}\\d{4}$
    -Validator.SSN=^(?!000)([0-6]\\d{2}|7([0-6]\\d|7[012]))([ -]?)(?!00)\\d\\d\\3(?!0000)\\d{4}$
    -
    +# The ESAPI validator does many security checks on input, such as canonicalization
    +# and whitelist validation. Note that all of these validation rules are applied *after*
    +# canonicalization. Double-encoded characters (even with different encodings involved,
    +# are never allowed.
    +#
    +# To use:
    +#
    +# First set up a pattern below. You can choose any name you want, prefixed by the word
    +# "Validation." For example:
    +#   Validation.Email=^[A-Za-z0-9._%-]+@[A-Za-z0-9.-]+\\.[a-zA-Z]{2,4}$
    +# 
    +# Then you can validate in your code against the pattern like this:
    +#     ESAPI.validator().isValidInput("User Email", input, "Email", maxLength, allowNull);
    +# Where maxLength and allowNull are set for you needs, respectively.
    +#
    +# But note, when you use boolean variants of validation functions, you lose critical 
    +# canonicalization. It is preferable to use the "get" methods (which throw exceptions) and 
    +# and use the returned user input which is in canonical form. Consider the following:
    +#  
    +# try {
    +#    someObject.setEmail(ESAPI.validator().getValidInput("User Email", input, "Email", maxLength, allowNull));
    +#
    +Validator.SafeString=^[.\\p{Alnum}\\p{Space}]{0,1024}$
    +Validator.Email=^[A-Za-z0-9._%'-]+@[A-Za-z0-9.-]+\\.[a-zA-Z]{2,4}$
    +Validator.IPAddress=^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$
    +Validator.URL=^(ht|f)tp(s?)\\:\\/\\/[0-9a-zA-Z]([-.\\w]*[0-9a-zA-Z])*(:(0-9)*)*(\\/?)([a-zA-Z0-9\\-\\.\\?\\,\\:\\'\\/\\\\\\+=&%\\$#_]*)?$
    +Validator.CreditCard=^(\\d{4}[- ]?){3}\\d{4}$
    +Validator.SSN=^(?!000)([0-6]\\d{2}|7([0-6]\\d|7[012]))([ -]?)(?!00)\\d\\d\\3(?!0000)\\d{4}$
    +
    
    From 53505707a49fad41ec3ff5b74fbf390741bed248 Mon Sep 17 00:00:00 2001
    From: kwwall 
    Date: Tue, 19 Jan 2016 01:12:42 -0500
    Subject: [PATCH 0274/1069] Address issue 356.
    
    ---
     .../properties/ESAPI_en_US.properties          | 18 +++++++++---------
     1 file changed, 9 insertions(+), 9 deletions(-)
    
    diff --git a/configuration/properties/ESAPI_en_US.properties b/configuration/properties/ESAPI_en_US.properties
    index 9b43d1a23..04f043b21 100644
    --- a/configuration/properties/ESAPI_en_US.properties
    +++ b/configuration/properties/ESAPI_en_US.properties
    @@ -1,9 +1,9 @@
    -# User Messages
    -Error.creating.randomizer=Error creating randomizer
    -
    -This.is.test.message=This {0} is {1} a test {2} message
    -
    -# Validation Messages
    -
    -# Log Messages
    -
    +# User Messages
    +Error.creating.randomizer=Error creating randomizer
    +
    +This.is.test.message=This {0} is {1} a test {2} message
    +
    +# Validation Messages
    +
    +# Log Messages
    +
    
    From 71ba3946427c154fa66d92e20ccaed042615cfea Mon Sep 17 00:00:00 2001
    From: kwwall 
    Date: Tue, 19 Jan 2016 01:12:42 -0500
    Subject: [PATCH 0275/1069] Address issue 356.
    
    ---
     .../properties/ESAPI_fr_FR.properties          | 18 +++++++++---------
     1 file changed, 9 insertions(+), 9 deletions(-)
    
    diff --git a/configuration/properties/ESAPI_fr_FR.properties b/configuration/properties/ESAPI_fr_FR.properties
    index 89ebcba7d..3e5a7a8f5 100644
    --- a/configuration/properties/ESAPI_fr_FR.properties
    +++ b/configuration/properties/ESAPI_fr_FR.properties
    @@ -1,9 +1,9 @@
    -# User Messages
    -Error.creating.randomizer=Erreur lors de la création aléatoire
    -
    -This.is.test.message=Ceci est un message de test message singh
    -
    -# Validation Messages
    -
    -# Log Messages
    -
    +# User Messages
    +Error.creating.randomizer=Erreur lors de la création aléatoire
    +
    +This.is.test.message=Ceci est un message de test message singh
    +
    +# Validation Messages
    +
    +# Log Messages
    +
    
    From b6a746b8589326aed5ef7208322f27a352c97869 Mon Sep 17 00:00:00 2001
    From: kwwall 
    Date: Tue, 19 Jan 2016 01:12:42 -0500
    Subject: [PATCH 0276/1069] Address issue 356.
    
    ---
     ...ESAPI-CommaValidatorFileChecker.properties | 930 +++++++++---------
     1 file changed, 465 insertions(+), 465 deletions(-)
    
    diff --git a/src/test/resources/esapi/ESAPI-CommaValidatorFileChecker.properties b/src/test/resources/esapi/ESAPI-CommaValidatorFileChecker.properties
    index 837d5a9d3..f120f748d 100644
    --- a/src/test/resources/esapi/ESAPI-CommaValidatorFileChecker.properties
    +++ b/src/test/resources/esapi/ESAPI-CommaValidatorFileChecker.properties
    @@ -1,466 +1,466 @@
    -#
    -# OWASP Enterprise Security API (ESAPI) Properties file -- TEST Version
    -# 
    -# This file is part of the Open Web Application Security Project (OWASP)
    -# Enterprise Security API (ESAPI) project. For details, please see
    -# http://www.owasp.org/index.php/ESAPI.
    -#
    -# Copyright (c) 2008,2009 - The OWASP Foundation
    -#
    -# DISCUSS: This may cause a major backwards compatibility issue, etc. but
    -#		   from a name space perspective, we probably should have prefaced
    -#		   all the property names with ESAPI or at least OWASP. Otherwise
    -#		   there could be problems is someone loads this properties file into
    -#		   the System properties.  We could also put this file into the
    -#		   esapi.jar file (perhaps as a ResourceBundle) and then allow an external
    -#		   ESAPI properties be defined that would overwrite these defaults.
    -#		   That keeps the application's properties relatively simple as usually
    -#		   they will only want to override a few properties. If looks like we
    -#		   already support multiple override levels of this in the
    -#		   DefaultSecurityConfiguration class, but I'm suggesting placing the
    -#		   defaults in the esapi.jar itself. That way, if the jar is signed,
    -#		   we could detect if those properties had been tampered with. (The
    -#		   code to check the jar signatures is pretty simple... maybe 70-90 LOC,
    -#		   but off course there is an execution penalty (similar to the way
    -#		   that the separate sunjce.jar used to be when a class from it was
    -#		   first loaded). Thoughts?
    -###############################################################################
    -#
    -# WARNING: Operating system protection should be used to lock down the .esapi
    -# resources directory and all the files inside and all the directories all the
    -# way up to the root directory of the file system.  Note that if you are using
    -# file-based implementations, that some files may need to be read-write as they
    -# get updated dynamically.
    -#
    -# Before using, be sure to update the MasterKey and MasterSalt as described below.
    -# N.B.: If you had stored data that you have previously encrypted with ESAPI 1.4,
    -#		you *must* FIRST decrypt it using ESAPI 1.4 and then (if so desired)
    -#		re-encrypt it with ESAPI 2.0. If you fail to do this, you will NOT be
    -#		able to decrypt your data with ESAPI 2.0.
    -#
    -#		YOU HAVE BEEN WARNED!!! More details are in the ESAPI 2.0 Release Notes.
    -#
    -#===========================================================================
    -# ESAPI Configuration
    -#
    -# If true, then print all the ESAPI properties set here when they are loaded.
    -# If false, they are not printed. Useful to reduce output when running JUnit tests.
    -# If you need to troubleshoot a properties related problem, turning this on may help,
    -# but we leave it off for running JUnit tests. (It will be 'true' in the one delivered
    -# as part of production ESAPI, mostly for backward compatibility.)
    -ESAPI.printProperties=false
    -
    -# ESAPI is designed to be easily extensible. You can use the reference implementation
    -# or implement your own providers to take advantage of your enterprise's security
    -# infrastructure. The functions in ESAPI are referenced using the ESAPI locator, like:
    -#
    -#    String ciphertext =
    -#		ESAPI.encryptor().encrypt("Secret message");   // Deprecated in 2.0
    -#    CipherText cipherText =
    -#		ESAPI.encryptor().encrypt(new PlainText("Secret message")); // Preferred
    -#
    -# Below you can specify the classname for the provider that you wish to use in your
    -# application. The only requirement is that it implement the appropriate ESAPI interface.
    -# This allows you to switch security implementations in the future without rewriting the
    -# entire application.
    -#
    -# ExperimentalAccessController requires ESAPI-AccessControlPolicy.xml in .esapi directory
    -ESAPI.AccessControl=org.owasp.esapi.reference.DefaultAccessController
    -# FileBasedAuthenticator requires users.txt file in .esapi directory
    -ESAPI.Authenticator=org.owasp.esapi.reference.FileBasedAuthenticator
    -ESAPI.Encoder=org.owasp.esapi.reference.DefaultEncoder
    -ESAPI.Encryptor=org.owasp.esapi.reference.crypto.JavaEncryptor
    -
    -ESAPI.Executor=org.owasp.esapi.reference.DefaultExecutor
    -ESAPI.HTTPUtilities=org.owasp.esapi.reference.DefaultHTTPUtilities
    -ESAPI.IntrusionDetector=org.owasp.esapi.reference.DefaultIntrusionDetector
    -# Log4JFactory Requires log4j.xml or log4j.properties in classpath - http://www.laliluna.de/log4j-tutorial.html
    -ESAPI.Logger=org.owasp.esapi.reference.Log4JLogFactory
    -#ESAPI.Logger=org.owasp.esapi.reference.JavaLogFactory
    -#ESAPI.Logger=org.owasp.esapi.reference.ExampleExtendedLog4JLogFactory
    -ESAPI.Randomizer=org.owasp.esapi.reference.DefaultRandomizer
    -ESAPI.Validator=org.owasp.esapi.reference.DefaultValidator
    -
    -#===========================================================================
    -# ESAPI Authenticator
    -#
    -Authenticator.AllowedLoginAttempts=3
    -Authenticator.MaxOldPasswordHashes=13
    -Authenticator.UsernameParameterName=username
    -Authenticator.PasswordParameterName=password
    -# RememberTokenDuration (in days)
    -Authenticator.RememberTokenDuration=14
    -# Session Timeouts (in minutes)
    -Authenticator.IdleTimeoutDuration=20
    -Authenticator.AbsoluteTimeoutDuration=120
    -
    -#===========================================================================
    -# ESAPI Encoder
    -#
    -# ESAPI canonicalizes input before validation to prevent bypassing filters with encoded attacks.
    -# Failure to canonicalize input is a very common mistake when implementing validation schemes.
    -# Canonicalization is automatic when using the ESAPI Validator, but you can also use the
    -# following code to canonicalize data.
    -#
    -#      ESAPI.Encoder().canonicalize( "%22hello world"" );
    -#  
    -# Multiple encoding is when a single encoding format is applied multiple times. Allowing
    -# multiple encoding is strongly discouraged.
    -Encoder.AllowMultipleEncoding=false
    -
    -# Mixed encoding is when multiple different encoding formats are applied, or when
    -# multiple formats are nested. Allowing multiple encoding is strongly discouraged.
    -Encoder.AllowMixedEncoding=false
    -
    -# The default list of codecs to apply when canonicalizing untrusted data. The list should include the codecs
    -# for all downstream interpreters or decoders. For example, if the data is likely to end up in a URL, HTML, or
    -# inside JavaScript, then the list of codecs below is appropriate. The order of the list is not terribly important.
    -Encoder.DefaultCodecList=HTMLEntityCodec,PercentCodec,JavaScriptCodec
    -
    -
    -#===========================================================================
    -# ESAPI Encryption
    -#
    -# The ESAPI Encryptor provides basic cryptographic functions with a simplified API.
    -# To get started, generate a new key using java -classpath esapi.jar org.owasp.esapi.reference.crypto.JavaEncryptor
    -# There is not currently any support for key rotation, so be careful when changing your key and salt as it
    -# will invalidate all signed, encrypted, and hashed data.
    -#
    -# WARNING: Not all combinations of algorithms and key lengths are supported.
    -# If you choose to use a key length greater than 128, you MUST download the
    -# unlimited strength policy files and install in the lib directory of your JRE/JDK.
    -# See http://java.sun.com/javase/downloads/index.jsp for more information.
    -#
    -# Backward compatibility with ESAPI Java 1.4 is supported by the two deprecated API
    -# methods, Encryptor.encrypt(String) and Encryptor.decrypt(String). However, whenever
    -# possible, these methods should be avoided as they use ECB cipher mode, which in almost
    -# all circumstances a poor choice because of it's weakness. CBC cipher mode is the default
    -# for the new Encryptor encrypt / decrypt methods for ESAPI Java 2.0.  In general, you
    -# should only use this compatibility setting if you have persistent data encrypted with
    -# version 1.4 and even then, you should ONLY set this compatibility mode UNTIL
    -# you have decrypted all of your old encrypted data and then re-encrypted it with
    -# ESAPI 2.0 using CBC mode. If you have some reason to mix the deprecated 1.4 mode
    -# with the new 2.0 methods, make sure that you use the same cipher algorithm for both
    -# (256-bit AES was the default for 1.4; 128-bit is the default for 2.0; see below for
    -# more details.) Otherwise, you will have to use the new 2.0 encrypt / decrypt methods
    -# where you can specify a SecretKey. (Note that if you are using the 256-bit AES,
    -# that requires downloading the special jurisdiction policy files mentioned above.)
    -#
    -#		***** IMPORTANT: These are for JUnit testing. Test files may have been
    -#						 encrypted using these values so do not change these or
    -#						 those tests will fail. The version under
    -#							src/main/resources/.esapi/ESAPI.properties
    -#						 will be delivered with Encryptor.MasterKey and
    -#						 Encryptor.MasterSalt set to the empty string.
    -#
    -#						 FINAL NOTE:
    -#                           If Maven changes these when run, that needs to be fixed.
    -#       256-bit key... requires unlimited strength jurisdiction policy files
    -### Encryptor.MasterKey=pJhlri8JbuFYDgkqtHmm9s0Ziug2PE7ovZDyEPm4j14=
    -#       128-bit key
    -Encryptor.MasterKey=a6H9is3hEVGKB4Jut+lOVA==
    -Encryptor.MasterSalt=SbftnvmEWD5ZHHP+pX3fqugNysc=
    -# Encryptor.MasterSalt=
    -
    -# Provides the default JCE provider that ESAPI will "prefer" for its symmetric
    -# encryption and hashing. (That is it will look to this provider first, but it
    -# will defer to other providers if the requested algorithm is not implemented
    -# by this provider.) If left unset, ESAPI will just use your Java VM's current
    -# preferred JCE provider, which is generally set in the file
    -# "$JAVA_HOME/jre/lib/security/java.security".
    -#
    -# The main intent of this is to allow ESAPI symmetric encryption to be
    -# used with a FIPS 140-2 compliant crypto-module. For details, see the section
    -# "Using ESAPI Symmetric Encryption with FIPS 140-2 Cryptographic Modules" in
    -# the ESAPI 2.0 Symmetric Encryption User Guide, at:
    -# http://owasp-esapi-java.googlecode.com/svn/trunk/documentation/esapi4java-core-2.0-symmetric-crypto-user-guide.html
    -# However, this property also allows you to easily use an alternate JCE provider
    -# such as "Bouncy Castle" without having to make changes to "java.security".
    -# See Javadoc for SecurityProviderLoader for further details. If you wish to use
    -# a provider that is not known to SecurityProviderLoader, you may specify the
    -# fully-qualified class name of the JCE provider class that implements
    -# java.security.Provider. If the name contains a '.', this is interpreted as
    -# a fully-qualified class name that implements java.security.Provider.
    -#
    -# NOTE: Setting this property has the side-effect of changing it in your application
    -#       as well, so if you are using JCE in your application directly rather than
    -#       through ESAPI (you wouldn't do that, would you? ;-), it will change the
    -#       preferred JCE provider there as well.
    -#
    -# Default: Keeps the JCE provider set to whatever JVM sets it to.
    -Encryptor.PreferredJCEProvider=
    -
    -# AES is the most widely used and strongest encryption algorithm. This
    -# should agree with your Encryptor.CipherTransformation property.
    -# By default, ESAPI Java 1.4 uses "PBEWithMD5AndDES" and which is
    -# very weak. It is essentially a password-based encryption key, hashed
    -# with MD5 around 1K times and then encrypted with the weak DES algorithm
    -# (56-bits) using ECB mode and an unspecified padding (it is
    -# JCE provider specific, but most likely "NoPadding"). However, 2.0 uses
    -# "AES/CBC/PKCSPadding". If you want to change these, change them here.
    -# Warning: This property does not control the default reference implementation for
    -#		   ESAPI 2.0 using JavaEncryptor. Also, this property will be dropped
    -#		   in the future.
    -# @deprecated
    -Encryptor.EncryptionAlgorithm=AES
    -#		For ESAPI Java 2.0 - New encrypt / decrypt methods use this.
    -Encryptor.CipherTransformation=AES/CBC/PKCS5Padding
    -
    -# Applies to ESAPI 2.0 and later only!
    -# Comma-separated list of cipher modes that provide *BOTH*
    -# confidentiality *AND* message authenticity. (NIST refers to such cipher
    -# modes as "combined modes" so that's what we shall call them.) If any of these
    -# cipher modes are used then no MAC is calculated and stored
    -# in the CipherText upon encryption. Likewise, if one of these
    -# cipher modes is used with decryption, no attempt will be made
    -# to validate the MAC contained in the CipherText object regardless
    -# of whether it contains one or not. Since the expectation is that
    -# these cipher modes support support message authenticity already,
    -# injecting a MAC in the CipherText object would be at best redundant.
    -#
    -# Note that as of JDK 1.5, the SunJCE provider does not support *any*
    -# of these cipher modes. Of these listed, only GCM and CCM are currently
    -# NIST approved. YMMV for other JCE providers. E.g., Bouncy Castle supports
    -# GCM and CCM with "NoPadding" mode, but not with "PKCS5Padding" or other
    -# padding modes.
    -Encryptor.cipher_modes.combined_modes=GCM,CCM,IAPM,EAX,OCB,CWC
    -
    -# Applies to ESAPI 2.0 and later only!
    -# Additional cipher modes allowed for ESAPI 2.0 encryption. These
    -# cipher modes are in _addition_ to those specified by the property
    -# 'Encryptor.cipher_modes.combined_modes'.
    -# Note: We will add support for streaming modes like CFB & OFB once
    -# we add support for 'specified' to the property 'Encryptor.ChooseIVMethod'
    -# (probably in ESAPI 2.1).
    -#
    -#	IMPORTANT NOTE:	In the official ESAPI.properties we do *NOT* include ECB
    -#					here as this is an extremely weak mode. However, we *must*
    -#					allow it here so we can test ECB mode. That is important
    -#					since the logic is somewhat different (i.e., ECB mode does
    -#					not use an IV).
    -# DISCUSS: Better name?
    -#	NOTE: ECB added only for testing purposes. Don't try this at home!
    -Encryptor.cipher_modes.additional_allowed=CBC,ECB
    -
    -# 128-bit is almost always sufficient and appears to be more resistant to
    -# related key attacks than is 256-bit AES. Use '_' to use default key size
    -# for cipher algorithms (where it makes sense because the algorithm supports
    -# a variable key size). Key length must agree to what's provided as the
    -# cipher transformation, otherwise this will be ignored after logging a
    -# warning.
    -#
    -# NOTE: This is what applies BOTH ESAPI 1.4 and 2.0. See warning above about mixing!
    -Encryptor.EncryptionKeyLength=128
    -
    -# Because 2.0 uses CBC mode by default, it requires an initialization vector (IV).
    -# (All cipher modes except ECB require an IV.) There are two choices: we can either
    -# use a fixed IV known to both parties or allow ESAPI to choose a random IV. While
    -# the IV does not need to be hidden from adversaries, it is important that the
    -# adversary not be allowed to choose it. Also, random IVs are generally much more
    -# secure than fixed IVs. (In fact, it is essential that feed-back cipher modes
    -# such as CFB and OFB use a different IV for each encryption with a given key so
    -# in such cases, random IVs are much preferred. By default, ESAPI 2.0 uses random
    -# IVs. If you wish to use 'fixed' IVs, set 'Encryptor.ChooseIVMethod=fixed' and
    -# uncomment the Encryptor.fixedIV.
    -#
    -# Valid values:		random|fixed|specified		'specified' not yet implemented; planned for 2.1
    -Encryptor.ChooseIVMethod=random
    -# If you choose to use a fixed IV, then you must place a fixed IV here that
    -# is known to all others who are sharing your secret key. The format should
    -# be a hex string that is the same length as the cipher block size for the
    -# cipher algorithm that you are using. The following is an example for AES
    -# from an AES test vector for AES-128/CBC as described in:
    -# NIST Special Publication 800-38A (2001 Edition)
    -# "Recommendation for Block Cipher Modes of Operation".
    -# (Note that the block size for AES is 16 bytes == 128 bits.)
    -#
    -Encryptor.fixedIV=0x000102030405060708090a0b0c0d0e0f
    -
    -# Whether or not CipherText should use a message authentication code (MAC) with it.
    -# This prevents an adversary from altering the IV as well as allowing a more
    -# fool-proof way of determining the decryption failed because of an incorrect
    -# key being supplied. This refers to the "separate" MAC calculated and stored
    -# in CipherText, not part of any MAC that is calculated as a result of a
    -# "combined mode" cipher mode.
    -#
    -# If you are using ESAPI with a FIPS 140-2 cryptographic module, you *must* also
    -# set this property to false.
    -Encryptor.CipherText.useMAC=true
    -
    -# Whether or not the PlainText object may be overwritten and then marked
    -# eligible for garbage collection. If not set, this is still treated as 'true'.
    -Encryptor.PlainText.overwrite=true
    -
    -# Do not use DES except in a legacy situations. 56-bit is way too small key size.
    -#Encryptor.EncryptionKeyLength=56
    -#Encryptor.EncryptionAlgorithm=DES
    -
    -# TripleDES is considered strong enough for most purposes.
    -#	Note:	There is also a 112-bit version of DESede. Using the 168-bit version
    -#			requires downloading the special jurisdiction policy from Sun.
    -#Encryptor.EncryptionKeyLength=168
    -#Encryptor.EncryptionAlgorithm=DESede
    -
    -Encryptor.HashAlgorithm=SHA-512
    -Encryptor.HashIterations=1024
    -Encryptor.DigitalSignatureAlgorithm=SHA1withDSA
    -Encryptor.DigitalSignatureKeyLength=1024
    -Encryptor.RandomAlgorithm=SHA1PRNG
    -Encryptor.CharacterEncoding=UTF-8
    -# Currently supported choices for JDK 1.5 and 1.6 are:
    -#	HmacSHA1 (160 bits), HmacSHA256 (256 bits), HmacSHA384 (384 bits), and
    -#	HmacSHA512 (512 bits).
    -# Note that HmacMD5 is *not* supported for the PRF used by the KDF even though
    -# these JDKs support it.
    -Encryptor.KDF.PRF=HmacSHA256
    -
    -#===========================================================================
    -# ESAPI HttpUtilties
    -#
    -# The HttpUtilities provide basic protections to HTTP requests and responses. Primarily these methods 
    -# protect against malicious data from attackers, such as unprintable characters, escaped characters,
    -# and other simple attacks. The HttpUtilities also provides utility methods for dealing with cookies,
    -# headers, and CSRF tokens.
    -#
    -# Default file upload location (remember to escape backslashes with \\)
    -HttpUtilities.UploadDir=C:\\ESAPI\\testUpload
    -# let this default to java.io.tmpdir for testing
    -#HttpUtilities.UploadTempDir=C:\\temp
    -# Force flags on cookies, if you use HttpUtilities to set cookies
    -HttpUtilities.ForceHttpOnlySession=false
    -HttpUtilities.ForceSecureSession=false
    -HttpUtilities.ForceHttpOnlyCookies=true
    -HttpUtilities.ForceSecureCookies=true
    -# Maximum size of HTTP headers
    -HttpUtilities.MaxHeaderSize=4096
    -# File upload configuration
    -HttpUtilities.ApprovedUploadExtensions=.zip,.pdf,.doc,.docx,.ppt,.pptx,.tar,.gz,.tgz,.rar,.war,.jar,.ear,.xls,.rtf,.properties,.java,.class,.txt,.xml,.jsp,.jsf,.exe,.dll
    -HttpUtilities.MaxUploadFileBytes=500000000
    -# Using UTF-8 throughout your stack is highly recommended. That includes your database driver,
    -# container, and any other technologies you may be using. Failure to do this may expose you
    -# to Unicode transcoding injection attacks. Use of UTF-8 does not hinder internationalization.
    -HttpUtilities.ResponseContentType=text/html; charset=UTF-8
    -# This is the name of the cookie used to represent the HTTP session
    -# Typically this will be the default "JSESSIONID" 
    -HttpUtilities.HttpSessionIdName=JSESSIONID
    -
    -
    -
    -#===========================================================================
    -# ESAPI Executor
    -# CHECKME - Not sure what this is used for, but surely it should be made OS independent.
    -Executor.WorkingDirectory=C:\\Windows\\Temp
    -Executor.ApprovedExecutables=C:\\Windows\\System32\\cmd.exe,C:\\Windows\\System32\\runas.exe
    -
    -
    -#===========================================================================
    -# ESAPI Logging
    -# Set the application name if these logs are combined with other applications
    -Logger.ApplicationName=ExampleApplication
    -# If you use an HTML log viewer that does not properly HTML escape log data, you can set LogEncodingRequired to true
    -Logger.LogEncodingRequired=false
    -# Determines whether ESAPI should log the application name. This might be clutter in some single-server/single-app environments.
    -Logger.LogApplicationName=true
    -# Determines whether ESAPI should log the server IP and port. This might be clutter in some single-server environments.
    -Logger.LogServerIP=true
    -# LogFileName, the name of the logging file. Provide a full directory path (e.g., C:\\ESAPI\\ESAPI_logging_file) if you
    -# want to place it in a specific directory.
    -Logger.LogFileName=ESAPI_logging_file
    -# MaxLogFileSize, the max size (in bytes) of a single log file before it cuts over to a new one (default is 10,000,000)
    -Logger.MaxLogFileSize=10000000
    -
    -
    -#===========================================================================
    -# ESAPI Intrusion Detection
    -#
    -# Each event has a base to which .count, .interval, and .action are added
    -# The IntrusionException will fire if we receive "count" events within "interval" seconds
    -# The IntrusionDetector is configurable to take the following actions: log, logout, and disable
    -#  (multiple actions separated by commas are allowed e.g. event.test.actions=log,disable
    -#
    -# Custom Events
    -# Names must start with "event." as the base
    -# Use IntrusionDetector.addEvent( "test" ) in your code to trigger "event.test" here
    -# You can also disable intrusion detection completely by changing
    -# the following parameter to true
    -#
    -IntrusionDetector.Disable=false
    -#
    -IntrusionDetector.event.test.count=2
    -IntrusionDetector.event.test.interval=10
    -IntrusionDetector.event.test.actions=disable,log
    -
    -# Exception Events
    -# All EnterpriseSecurityExceptions are registered automatically
    -# Call IntrusionDetector.getInstance().addException(e) for Exceptions that do not extend EnterpriseSecurityException
    -# Use the fully qualified classname of the exception as the base
    -
    -# any intrusion is an attack
    -IntrusionDetector.org.owasp.esapi.errors.IntrusionException.count=1
    -IntrusionDetector.org.owasp.esapi.errors.IntrusionException.interval=1
    -IntrusionDetector.org.owasp.esapi.errors.IntrusionException.actions=log,disable,logout
    -
    -# for test purposes
    -# CHECKME: Shouldn't there be something in the property name itself that designates
    -#		   that these are for testing???
    -IntrusionDetector.org.owasp.esapi.errors.IntegrityException.count=10
    -IntrusionDetector.org.owasp.esapi.errors.IntegrityException.interval=5
    -IntrusionDetector.org.owasp.esapi.errors.IntegrityException.actions=log,disable,logout
    -
    -# rapid validation errors indicate scans or attacks in progress
    -# org.owasp.esapi.errors.ValidationException.count=10
    -# org.owasp.esapi.errors.ValidationException.interval=10
    -# org.owasp.esapi.errors.ValidationException.actions=log,logout
    -
    -# sessions jumping between hosts indicates session hijacking
    -IntrusionDetector.org.owasp.esapi.errors.AuthenticationHostException.count=2
    -IntrusionDetector.org.owasp.esapi.errors.AuthenticationHostException.interval=10
    -IntrusionDetector.org.owasp.esapi.errors.AuthenticationHostException.actions=log,logout
    -
    -
    -#===========================================================================
    -# ESAPI Validation
    -#
    -# The ESAPI Validator works on regular expressions with defined names. You can define names
    -# either here, or you may define application specific patterns in a separate file defined below.
    -# This allows enterprises to specify both organizational standards as well as application specific
    -# validation rules.
    -#
    -#Validator.ConfigurationFile.MultiValued=false
    -Validator.ConfigurationFile=validation-test,comma.properties
    -
    -# Validators used by ESAPI
    -Validator.AccountName=^[a-zA-Z0-9]{3,20}$
    -Validator.SystemCommand=^[a-zA-Z\\-\\/]{1,64}$
    -Validator.RoleName=^[a-z]{1,20}$
    -Validator.Redirect=^\\/test.*$
    -
    -# Global HTTP Validation Rules
    -# Values with Base64 encoded data (e.g. encrypted state) will need at least [a-zA-Z0-9\/+=]
    -Validator.HTTPScheme=^(http|https)$
    -Validator.HTTPServerName=^[a-zA-Z0-9_.\\-]*$
    -Validator.HTTPCookieName=^[a-zA-Z0-9\\-_]{1,32}$
    -Validator.HTTPCookieValue=^[a-zA-Z0-9\\-\\/+=_ ]*$
    -Validator.HTTPHeaderName=^[a-zA-Z0-9\\-_]{1,32}$
    -Validator.HTTPHeaderValue=^[a-zA-Z0-9()\\-=\\*\\.\\?;,+\\/:&_ ]*$
    -Validator.HTTPServletPath=^[a-zA-Z0-9.\\-\\/_]*$
    -Validator.HTTPPath=^[a-zA-Z0-9.\\-_]*$
    -Validator.HTTPURL=^.*$
    -Validator.HTTPJSESSIONID=^[A-Z0-9]{10,30}$
    -
    -# Contributed by Fraenku@gmx.ch
    -# Googlecode Issue 116 (http://code.google.com/p/owasp-esapi-java/issues/detail?id=116)
    -Validator.HTTPParameterName=^[a-zA-Z0-9_\\-]{1,32}$
    -Validator.HTTPParameterValue=^[\\p{L}\\p{N}.\\-/+=_ !$*?@]{0,1000}$
    -Validator.HTTPContextPath=^/[a-zA-Z0-9.\\-_]*$
    -Validator.HTTPQueryString=^([a-zA-Z0-9_\\-]{1,32}=[\\p{L}\\p{N}.\\-/+=_ !$*?@%]*&?)*$
    -Validator.HTTPURI=^/([a-zA-Z0-9.\\-_]*/?)*$
    -
    -
    -# Validation of file related input
    -Validator.FileName=^[a-zA-Z0-9!@#$%^&{}\\[\\]()_+\\-=,.~'` ]{1,255}$
    -Validator.DirectoryName=^[a-zA-Z0-9:/\\\\!@#$%^&{}\\[\\]()_+\\-=,.~'` ]{1,255}$
    -
    -# Validation of dates. Controls whether or not 'lenient' dates are accepted.
    -# See DataFormat.setLenient(boolean flag) for further details.
    +#
    +# OWASP Enterprise Security API (ESAPI) Properties file -- TEST Version
    +# 
    +# This file is part of the Open Web Application Security Project (OWASP)
    +# Enterprise Security API (ESAPI) project. For details, please see
    +# http://www.owasp.org/index.php/ESAPI.
    +#
    +# Copyright (c) 2008,2009 - The OWASP Foundation
    +#
    +# DISCUSS: This may cause a major backwards compatibility issue, etc. but
    +#		   from a name space perspective, we probably should have prefaced
    +#		   all the property names with ESAPI or at least OWASP. Otherwise
    +#		   there could be problems is someone loads this properties file into
    +#		   the System properties.  We could also put this file into the
    +#		   esapi.jar file (perhaps as a ResourceBundle) and then allow an external
    +#		   ESAPI properties be defined that would overwrite these defaults.
    +#		   That keeps the application's properties relatively simple as usually
    +#		   they will only want to override a few properties. If looks like we
    +#		   already support multiple override levels of this in the
    +#		   DefaultSecurityConfiguration class, but I'm suggesting placing the
    +#		   defaults in the esapi.jar itself. That way, if the jar is signed,
    +#		   we could detect if those properties had been tampered with. (The
    +#		   code to check the jar signatures is pretty simple... maybe 70-90 LOC,
    +#		   but off course there is an execution penalty (similar to the way
    +#		   that the separate sunjce.jar used to be when a class from it was
    +#		   first loaded). Thoughts?
    +###############################################################################
    +#
    +# WARNING: Operating system protection should be used to lock down the .esapi
    +# resources directory and all the files inside and all the directories all the
    +# way up to the root directory of the file system.  Note that if you are using
    +# file-based implementations, that some files may need to be read-write as they
    +# get updated dynamically.
    +#
    +# Before using, be sure to update the MasterKey and MasterSalt as described below.
    +# N.B.: If you had stored data that you have previously encrypted with ESAPI 1.4,
    +#		you *must* FIRST decrypt it using ESAPI 1.4 and then (if so desired)
    +#		re-encrypt it with ESAPI 2.0. If you fail to do this, you will NOT be
    +#		able to decrypt your data with ESAPI 2.0.
    +#
    +#		YOU HAVE BEEN WARNED!!! More details are in the ESAPI 2.0 Release Notes.
    +#
    +#===========================================================================
    +# ESAPI Configuration
    +#
    +# If true, then print all the ESAPI properties set here when they are loaded.
    +# If false, they are not printed. Useful to reduce output when running JUnit tests.
    +# If you need to troubleshoot a properties related problem, turning this on may help,
    +# but we leave it off for running JUnit tests. (It will be 'true' in the one delivered
    +# as part of production ESAPI, mostly for backward compatibility.)
    +ESAPI.printProperties=false
    +
    +# ESAPI is designed to be easily extensible. You can use the reference implementation
    +# or implement your own providers to take advantage of your enterprise's security
    +# infrastructure. The functions in ESAPI are referenced using the ESAPI locator, like:
    +#
    +#    String ciphertext =
    +#		ESAPI.encryptor().encrypt("Secret message");   // Deprecated in 2.0
    +#    CipherText cipherText =
    +#		ESAPI.encryptor().encrypt(new PlainText("Secret message")); // Preferred
    +#
    +# Below you can specify the classname for the provider that you wish to use in your
    +# application. The only requirement is that it implement the appropriate ESAPI interface.
    +# This allows you to switch security implementations in the future without rewriting the
    +# entire application.
    +#
    +# ExperimentalAccessController requires ESAPI-AccessControlPolicy.xml in .esapi directory
    +ESAPI.AccessControl=org.owasp.esapi.reference.DefaultAccessController
    +# FileBasedAuthenticator requires users.txt file in .esapi directory
    +ESAPI.Authenticator=org.owasp.esapi.reference.FileBasedAuthenticator
    +ESAPI.Encoder=org.owasp.esapi.reference.DefaultEncoder
    +ESAPI.Encryptor=org.owasp.esapi.reference.crypto.JavaEncryptor
    +
    +ESAPI.Executor=org.owasp.esapi.reference.DefaultExecutor
    +ESAPI.HTTPUtilities=org.owasp.esapi.reference.DefaultHTTPUtilities
    +ESAPI.IntrusionDetector=org.owasp.esapi.reference.DefaultIntrusionDetector
    +# Log4JFactory Requires log4j.xml or log4j.properties in classpath - http://www.laliluna.de/log4j-tutorial.html
    +ESAPI.Logger=org.owasp.esapi.reference.Log4JLogFactory
    +#ESAPI.Logger=org.owasp.esapi.reference.JavaLogFactory
    +#ESAPI.Logger=org.owasp.esapi.reference.ExampleExtendedLog4JLogFactory
    +ESAPI.Randomizer=org.owasp.esapi.reference.DefaultRandomizer
    +ESAPI.Validator=org.owasp.esapi.reference.DefaultValidator
    +
    +#===========================================================================
    +# ESAPI Authenticator
    +#
    +Authenticator.AllowedLoginAttempts=3
    +Authenticator.MaxOldPasswordHashes=13
    +Authenticator.UsernameParameterName=username
    +Authenticator.PasswordParameterName=password
    +# RememberTokenDuration (in days)
    +Authenticator.RememberTokenDuration=14
    +# Session Timeouts (in minutes)
    +Authenticator.IdleTimeoutDuration=20
    +Authenticator.AbsoluteTimeoutDuration=120
    +
    +#===========================================================================
    +# ESAPI Encoder
    +#
    +# ESAPI canonicalizes input before validation to prevent bypassing filters with encoded attacks.
    +# Failure to canonicalize input is a very common mistake when implementing validation schemes.
    +# Canonicalization is automatic when using the ESAPI Validator, but you can also use the
    +# following code to canonicalize data.
    +#
    +#      ESAPI.Encoder().canonicalize( "%22hello world"" );
    +#  
    +# Multiple encoding is when a single encoding format is applied multiple times. Allowing
    +# multiple encoding is strongly discouraged.
    +Encoder.AllowMultipleEncoding=false
    +
    +# Mixed encoding is when multiple different encoding formats are applied, or when
    +# multiple formats are nested. Allowing multiple encoding is strongly discouraged.
    +Encoder.AllowMixedEncoding=false
    +
    +# The default list of codecs to apply when canonicalizing untrusted data. The list should include the codecs
    +# for all downstream interpreters or decoders. For example, if the data is likely to end up in a URL, HTML, or
    +# inside JavaScript, then the list of codecs below is appropriate. The order of the list is not terribly important.
    +Encoder.DefaultCodecList=HTMLEntityCodec,PercentCodec,JavaScriptCodec
    +
    +
    +#===========================================================================
    +# ESAPI Encryption
    +#
    +# The ESAPI Encryptor provides basic cryptographic functions with a simplified API.
    +# To get started, generate a new key using java -classpath esapi.jar org.owasp.esapi.reference.crypto.JavaEncryptor
    +# There is not currently any support for key rotation, so be careful when changing your key and salt as it
    +# will invalidate all signed, encrypted, and hashed data.
    +#
    +# WARNING: Not all combinations of algorithms and key lengths are supported.
    +# If you choose to use a key length greater than 128, you MUST download the
    +# unlimited strength policy files and install in the lib directory of your JRE/JDK.
    +# See http://java.sun.com/javase/downloads/index.jsp for more information.
    +#
    +# Backward compatibility with ESAPI Java 1.4 is supported by the two deprecated API
    +# methods, Encryptor.encrypt(String) and Encryptor.decrypt(String). However, whenever
    +# possible, these methods should be avoided as they use ECB cipher mode, which in almost
    +# all circumstances a poor choice because of it's weakness. CBC cipher mode is the default
    +# for the new Encryptor encrypt / decrypt methods for ESAPI Java 2.0.  In general, you
    +# should only use this compatibility setting if you have persistent data encrypted with
    +# version 1.4 and even then, you should ONLY set this compatibility mode UNTIL
    +# you have decrypted all of your old encrypted data and then re-encrypted it with
    +# ESAPI 2.0 using CBC mode. If you have some reason to mix the deprecated 1.4 mode
    +# with the new 2.0 methods, make sure that you use the same cipher algorithm for both
    +# (256-bit AES was the default for 1.4; 128-bit is the default for 2.0; see below for
    +# more details.) Otherwise, you will have to use the new 2.0 encrypt / decrypt methods
    +# where you can specify a SecretKey. (Note that if you are using the 256-bit AES,
    +# that requires downloading the special jurisdiction policy files mentioned above.)
    +#
    +#		***** IMPORTANT: These are for JUnit testing. Test files may have been
    +#						 encrypted using these values so do not change these or
    +#						 those tests will fail. The version under
    +#							src/main/resources/.esapi/ESAPI.properties
    +#						 will be delivered with Encryptor.MasterKey and
    +#						 Encryptor.MasterSalt set to the empty string.
    +#
    +#						 FINAL NOTE:
    +#                           If Maven changes these when run, that needs to be fixed.
    +#       256-bit key... requires unlimited strength jurisdiction policy files
    +### Encryptor.MasterKey=pJhlri8JbuFYDgkqtHmm9s0Ziug2PE7ovZDyEPm4j14=
    +#       128-bit key
    +Encryptor.MasterKey=a6H9is3hEVGKB4Jut+lOVA==
    +Encryptor.MasterSalt=SbftnvmEWD5ZHHP+pX3fqugNysc=
    +# Encryptor.MasterSalt=
    +
    +# Provides the default JCE provider that ESAPI will "prefer" for its symmetric
    +# encryption and hashing. (That is it will look to this provider first, but it
    +# will defer to other providers if the requested algorithm is not implemented
    +# by this provider.) If left unset, ESAPI will just use your Java VM's current
    +# preferred JCE provider, which is generally set in the file
    +# "$JAVA_HOME/jre/lib/security/java.security".
    +#
    +# The main intent of this is to allow ESAPI symmetric encryption to be
    +# used with a FIPS 140-2 compliant crypto-module. For details, see the section
    +# "Using ESAPI Symmetric Encryption with FIPS 140-2 Cryptographic Modules" in
    +# the ESAPI 2.0 Symmetric Encryption User Guide, at:
    +# http://owasp-esapi-java.googlecode.com/svn/trunk/documentation/esapi4java-core-2.0-symmetric-crypto-user-guide.html
    +# However, this property also allows you to easily use an alternate JCE provider
    +# such as "Bouncy Castle" without having to make changes to "java.security".
    +# See Javadoc for SecurityProviderLoader for further details. If you wish to use
    +# a provider that is not known to SecurityProviderLoader, you may specify the
    +# fully-qualified class name of the JCE provider class that implements
    +# java.security.Provider. If the name contains a '.', this is interpreted as
    +# a fully-qualified class name that implements java.security.Provider.
    +#
    +# NOTE: Setting this property has the side-effect of changing it in your application
    +#       as well, so if you are using JCE in your application directly rather than
    +#       through ESAPI (you wouldn't do that, would you? ;-), it will change the
    +#       preferred JCE provider there as well.
    +#
    +# Default: Keeps the JCE provider set to whatever JVM sets it to.
    +Encryptor.PreferredJCEProvider=
    +
    +# AES is the most widely used and strongest encryption algorithm. This
    +# should agree with your Encryptor.CipherTransformation property.
    +# By default, ESAPI Java 1.4 uses "PBEWithMD5AndDES" and which is
    +# very weak. It is essentially a password-based encryption key, hashed
    +# with MD5 around 1K times and then encrypted with the weak DES algorithm
    +# (56-bits) using ECB mode and an unspecified padding (it is
    +# JCE provider specific, but most likely "NoPadding"). However, 2.0 uses
    +# "AES/CBC/PKCSPadding". If you want to change these, change them here.
    +# Warning: This property does not control the default reference implementation for
    +#		   ESAPI 2.0 using JavaEncryptor. Also, this property will be dropped
    +#		   in the future.
    +# @deprecated
    +Encryptor.EncryptionAlgorithm=AES
    +#		For ESAPI Java 2.0 - New encrypt / decrypt methods use this.
    +Encryptor.CipherTransformation=AES/CBC/PKCS5Padding
    +
    +# Applies to ESAPI 2.0 and later only!
    +# Comma-separated list of cipher modes that provide *BOTH*
    +# confidentiality *AND* message authenticity. (NIST refers to such cipher
    +# modes as "combined modes" so that's what we shall call them.) If any of these
    +# cipher modes are used then no MAC is calculated and stored
    +# in the CipherText upon encryption. Likewise, if one of these
    +# cipher modes is used with decryption, no attempt will be made
    +# to validate the MAC contained in the CipherText object regardless
    +# of whether it contains one or not. Since the expectation is that
    +# these cipher modes support support message authenticity already,
    +# injecting a MAC in the CipherText object would be at best redundant.
    +#
    +# Note that as of JDK 1.5, the SunJCE provider does not support *any*
    +# of these cipher modes. Of these listed, only GCM and CCM are currently
    +# NIST approved. YMMV for other JCE providers. E.g., Bouncy Castle supports
    +# GCM and CCM with "NoPadding" mode, but not with "PKCS5Padding" or other
    +# padding modes.
    +Encryptor.cipher_modes.combined_modes=GCM,CCM,IAPM,EAX,OCB,CWC
    +
    +# Applies to ESAPI 2.0 and later only!
    +# Additional cipher modes allowed for ESAPI 2.0 encryption. These
    +# cipher modes are in _addition_ to those specified by the property
    +# 'Encryptor.cipher_modes.combined_modes'.
    +# Note: We will add support for streaming modes like CFB & OFB once
    +# we add support for 'specified' to the property 'Encryptor.ChooseIVMethod'
    +# (probably in ESAPI 2.1).
    +#
    +#	IMPORTANT NOTE:	In the official ESAPI.properties we do *NOT* include ECB
    +#					here as this is an extremely weak mode. However, we *must*
    +#					allow it here so we can test ECB mode. That is important
    +#					since the logic is somewhat different (i.e., ECB mode does
    +#					not use an IV).
    +# DISCUSS: Better name?
    +#	NOTE: ECB added only for testing purposes. Don't try this at home!
    +Encryptor.cipher_modes.additional_allowed=CBC,ECB
    +
    +# 128-bit is almost always sufficient and appears to be more resistant to
    +# related key attacks than is 256-bit AES. Use '_' to use default key size
    +# for cipher algorithms (where it makes sense because the algorithm supports
    +# a variable key size). Key length must agree to what's provided as the
    +# cipher transformation, otherwise this will be ignored after logging a
    +# warning.
    +#
    +# NOTE: This is what applies BOTH ESAPI 1.4 and 2.0. See warning above about mixing!
    +Encryptor.EncryptionKeyLength=128
    +
    +# Because 2.0 uses CBC mode by default, it requires an initialization vector (IV).
    +# (All cipher modes except ECB require an IV.) There are two choices: we can either
    +# use a fixed IV known to both parties or allow ESAPI to choose a random IV. While
    +# the IV does not need to be hidden from adversaries, it is important that the
    +# adversary not be allowed to choose it. Also, random IVs are generally much more
    +# secure than fixed IVs. (In fact, it is essential that feed-back cipher modes
    +# such as CFB and OFB use a different IV for each encryption with a given key so
    +# in such cases, random IVs are much preferred. By default, ESAPI 2.0 uses random
    +# IVs. If you wish to use 'fixed' IVs, set 'Encryptor.ChooseIVMethod=fixed' and
    +# uncomment the Encryptor.fixedIV.
    +#
    +# Valid values:		random|fixed|specified		'specified' not yet implemented; planned for 2.1
    +Encryptor.ChooseIVMethod=random
    +# If you choose to use a fixed IV, then you must place a fixed IV here that
    +# is known to all others who are sharing your secret key. The format should
    +# be a hex string that is the same length as the cipher block size for the
    +# cipher algorithm that you are using. The following is an example for AES
    +# from an AES test vector for AES-128/CBC as described in:
    +# NIST Special Publication 800-38A (2001 Edition)
    +# "Recommendation for Block Cipher Modes of Operation".
    +# (Note that the block size for AES is 16 bytes == 128 bits.)
    +#
    +Encryptor.fixedIV=0x000102030405060708090a0b0c0d0e0f
    +
    +# Whether or not CipherText should use a message authentication code (MAC) with it.
    +# This prevents an adversary from altering the IV as well as allowing a more
    +# fool-proof way of determining the decryption failed because of an incorrect
    +# key being supplied. This refers to the "separate" MAC calculated and stored
    +# in CipherText, not part of any MAC that is calculated as a result of a
    +# "combined mode" cipher mode.
    +#
    +# If you are using ESAPI with a FIPS 140-2 cryptographic module, you *must* also
    +# set this property to false.
    +Encryptor.CipherText.useMAC=true
    +
    +# Whether or not the PlainText object may be overwritten and then marked
    +# eligible for garbage collection. If not set, this is still treated as 'true'.
    +Encryptor.PlainText.overwrite=true
    +
    +# Do not use DES except in a legacy situations. 56-bit is way too small key size.
    +#Encryptor.EncryptionKeyLength=56
    +#Encryptor.EncryptionAlgorithm=DES
    +
    +# TripleDES is considered strong enough for most purposes.
    +#	Note:	There is also a 112-bit version of DESede. Using the 168-bit version
    +#			requires downloading the special jurisdiction policy from Sun.
    +#Encryptor.EncryptionKeyLength=168
    +#Encryptor.EncryptionAlgorithm=DESede
    +
    +Encryptor.HashAlgorithm=SHA-512
    +Encryptor.HashIterations=1024
    +Encryptor.DigitalSignatureAlgorithm=SHA1withDSA
    +Encryptor.DigitalSignatureKeyLength=1024
    +Encryptor.RandomAlgorithm=SHA1PRNG
    +Encryptor.CharacterEncoding=UTF-8
    +# Currently supported choices for JDK 1.5 and 1.6 are:
    +#	HmacSHA1 (160 bits), HmacSHA256 (256 bits), HmacSHA384 (384 bits), and
    +#	HmacSHA512 (512 bits).
    +# Note that HmacMD5 is *not* supported for the PRF used by the KDF even though
    +# these JDKs support it.
    +Encryptor.KDF.PRF=HmacSHA256
    +
    +#===========================================================================
    +# ESAPI HttpUtilties
    +#
    +# The HttpUtilities provide basic protections to HTTP requests and responses. Primarily these methods 
    +# protect against malicious data from attackers, such as unprintable characters, escaped characters,
    +# and other simple attacks. The HttpUtilities also provides utility methods for dealing with cookies,
    +# headers, and CSRF tokens.
    +#
    +# Default file upload location (remember to escape backslashes with \\)
    +HttpUtilities.UploadDir=C:\\ESAPI\\testUpload
    +# let this default to java.io.tmpdir for testing
    +#HttpUtilities.UploadTempDir=C:\\temp
    +# Force flags on cookies, if you use HttpUtilities to set cookies
    +HttpUtilities.ForceHttpOnlySession=false
    +HttpUtilities.ForceSecureSession=false
    +HttpUtilities.ForceHttpOnlyCookies=true
    +HttpUtilities.ForceSecureCookies=true
    +# Maximum size of HTTP headers
    +HttpUtilities.MaxHeaderSize=4096
    +# File upload configuration
    +HttpUtilities.ApprovedUploadExtensions=.zip,.pdf,.doc,.docx,.ppt,.pptx,.tar,.gz,.tgz,.rar,.war,.jar,.ear,.xls,.rtf,.properties,.java,.class,.txt,.xml,.jsp,.jsf,.exe,.dll
    +HttpUtilities.MaxUploadFileBytes=500000000
    +# Using UTF-8 throughout your stack is highly recommended. That includes your database driver,
    +# container, and any other technologies you may be using. Failure to do this may expose you
    +# to Unicode transcoding injection attacks. Use of UTF-8 does not hinder internationalization.
    +HttpUtilities.ResponseContentType=text/html; charset=UTF-8
    +# This is the name of the cookie used to represent the HTTP session
    +# Typically this will be the default "JSESSIONID" 
    +HttpUtilities.HttpSessionIdName=JSESSIONID
    +
    +
    +
    +#===========================================================================
    +# ESAPI Executor
    +# CHECKME - Not sure what this is used for, but surely it should be made OS independent.
    +Executor.WorkingDirectory=C:\\Windows\\Temp
    +Executor.ApprovedExecutables=C:\\Windows\\System32\\cmd.exe,C:\\Windows\\System32\\runas.exe
    +
    +
    +#===========================================================================
    +# ESAPI Logging
    +# Set the application name if these logs are combined with other applications
    +Logger.ApplicationName=ExampleApplication
    +# If you use an HTML log viewer that does not properly HTML escape log data, you can set LogEncodingRequired to true
    +Logger.LogEncodingRequired=false
    +# Determines whether ESAPI should log the application name. This might be clutter in some single-server/single-app environments.
    +Logger.LogApplicationName=true
    +# Determines whether ESAPI should log the server IP and port. This might be clutter in some single-server environments.
    +Logger.LogServerIP=true
    +# LogFileName, the name of the logging file. Provide a full directory path (e.g., C:\\ESAPI\\ESAPI_logging_file) if you
    +# want to place it in a specific directory.
    +Logger.LogFileName=ESAPI_logging_file
    +# MaxLogFileSize, the max size (in bytes) of a single log file before it cuts over to a new one (default is 10,000,000)
    +Logger.MaxLogFileSize=10000000
    +
    +
    +#===========================================================================
    +# ESAPI Intrusion Detection
    +#
    +# Each event has a base to which .count, .interval, and .action are added
    +# The IntrusionException will fire if we receive "count" events within "interval" seconds
    +# The IntrusionDetector is configurable to take the following actions: log, logout, and disable
    +#  (multiple actions separated by commas are allowed e.g. event.test.actions=log,disable
    +#
    +# Custom Events
    +# Names must start with "event." as the base
    +# Use IntrusionDetector.addEvent( "test" ) in your code to trigger "event.test" here
    +# You can also disable intrusion detection completely by changing
    +# the following parameter to true
    +#
    +IntrusionDetector.Disable=false
    +#
    +IntrusionDetector.event.test.count=2
    +IntrusionDetector.event.test.interval=10
    +IntrusionDetector.event.test.actions=disable,log
    +
    +# Exception Events
    +# All EnterpriseSecurityExceptions are registered automatically
    +# Call IntrusionDetector.getInstance().addException(e) for Exceptions that do not extend EnterpriseSecurityException
    +# Use the fully qualified classname of the exception as the base
    +
    +# any intrusion is an attack
    +IntrusionDetector.org.owasp.esapi.errors.IntrusionException.count=1
    +IntrusionDetector.org.owasp.esapi.errors.IntrusionException.interval=1
    +IntrusionDetector.org.owasp.esapi.errors.IntrusionException.actions=log,disable,logout
    +
    +# for test purposes
    +# CHECKME: Shouldn't there be something in the property name itself that designates
    +#		   that these are for testing???
    +IntrusionDetector.org.owasp.esapi.errors.IntegrityException.count=10
    +IntrusionDetector.org.owasp.esapi.errors.IntegrityException.interval=5
    +IntrusionDetector.org.owasp.esapi.errors.IntegrityException.actions=log,disable,logout
    +
    +# rapid validation errors indicate scans or attacks in progress
    +# org.owasp.esapi.errors.ValidationException.count=10
    +# org.owasp.esapi.errors.ValidationException.interval=10
    +# org.owasp.esapi.errors.ValidationException.actions=log,logout
    +
    +# sessions jumping between hosts indicates session hijacking
    +IntrusionDetector.org.owasp.esapi.errors.AuthenticationHostException.count=2
    +IntrusionDetector.org.owasp.esapi.errors.AuthenticationHostException.interval=10
    +IntrusionDetector.org.owasp.esapi.errors.AuthenticationHostException.actions=log,logout
    +
    +
    +#===========================================================================
    +# ESAPI Validation
    +#
    +# The ESAPI Validator works on regular expressions with defined names. You can define names
    +# either here, or you may define application specific patterns in a separate file defined below.
    +# This allows enterprises to specify both organizational standards as well as application specific
    +# validation rules.
    +#
    +#Validator.ConfigurationFile.MultiValued=false
    +Validator.ConfigurationFile=validation-test,comma.properties
    +
    +# Validators used by ESAPI
    +Validator.AccountName=^[a-zA-Z0-9]{3,20}$
    +Validator.SystemCommand=^[a-zA-Z\\-\\/]{1,64}$
    +Validator.RoleName=^[a-z]{1,20}$
    +Validator.Redirect=^\\/test.*$
    +
    +# Global HTTP Validation Rules
    +# Values with Base64 encoded data (e.g. encrypted state) will need at least [a-zA-Z0-9\/+=]
    +Validator.HTTPScheme=^(http|https)$
    +Validator.HTTPServerName=^[a-zA-Z0-9_.\\-]*$
    +Validator.HTTPCookieName=^[a-zA-Z0-9\\-_]{1,32}$
    +Validator.HTTPCookieValue=^[a-zA-Z0-9\\-\\/+=_ ]*$
    +Validator.HTTPHeaderName=^[a-zA-Z0-9\\-_]{1,32}$
    +Validator.HTTPHeaderValue=^[a-zA-Z0-9()\\-=\\*\\.\\?;,+\\/:&_ ]*$
    +Validator.HTTPServletPath=^[a-zA-Z0-9.\\-\\/_]*$
    +Validator.HTTPPath=^[a-zA-Z0-9.\\-_]*$
    +Validator.HTTPURL=^.*$
    +Validator.HTTPJSESSIONID=^[A-Z0-9]{10,30}$
    +
    +# Contributed by Fraenku@gmx.ch
    +# Googlecode Issue 116 (http://code.google.com/p/owasp-esapi-java/issues/detail?id=116)
    +Validator.HTTPParameterName=^[a-zA-Z0-9_\\-]{1,32}$
    +Validator.HTTPParameterValue=^[\\p{L}\\p{N}.\\-/+=_ !$*?@]{0,1000}$
    +Validator.HTTPContextPath=^/[a-zA-Z0-9.\\-_]*$
    +Validator.HTTPQueryString=^([a-zA-Z0-9_\\-]{1,32}=[\\p{L}\\p{N}.\\-/+=_ !$*?@%]*&?)*$
    +Validator.HTTPURI=^/([a-zA-Z0-9.\\-_]*/?)*$
    +
    +
    +# Validation of file related input
    +Validator.FileName=^[a-zA-Z0-9!@#$%^&{}\\[\\]()_+\\-=,.~'` ]{1,255}$
    +Validator.DirectoryName=^[a-zA-Z0-9:/\\\\!@#$%^&{}\\[\\]()_+\\-=,.~'` ]{1,255}$
    +
    +# Validation of dates. Controls whether or not 'lenient' dates are accepted.
    +# See DataFormat.setLenient(boolean flag) for further details.
     Validator.AcceptLenientDates=false
    \ No newline at end of file
    
    From 23e6d43a552369983297c38bfc2e181320495fd8 Mon Sep 17 00:00:00 2001
    From: kwwall 
    Date: Tue, 19 Jan 2016 01:12:42 -0500
    Subject: [PATCH 0277/1069] Address issue 356.
    
    ---
     .../ESAPI-DualValidatorFileChecker.properties | 930 +++++++++---------
     1 file changed, 465 insertions(+), 465 deletions(-)
    
    diff --git a/src/test/resources/esapi/ESAPI-DualValidatorFileChecker.properties b/src/test/resources/esapi/ESAPI-DualValidatorFileChecker.properties
    index afa1d75f0..040de2038 100644
    --- a/src/test/resources/esapi/ESAPI-DualValidatorFileChecker.properties
    +++ b/src/test/resources/esapi/ESAPI-DualValidatorFileChecker.properties
    @@ -1,466 +1,466 @@
    -#
    -# OWASP Enterprise Security API (ESAPI) Properties file -- TEST Version
    -# 
    -# This file is part of the Open Web Application Security Project (OWASP)
    -# Enterprise Security API (ESAPI) project. For details, please see
    -# http://www.owasp.org/index.php/ESAPI.
    -#
    -# Copyright (c) 2008,2009 - The OWASP Foundation
    -#
    -# DISCUSS: This may cause a major backwards compatibility issue, etc. but
    -#		   from a name space perspective, we probably should have prefaced
    -#		   all the property names with ESAPI or at least OWASP. Otherwise
    -#		   there could be problems is someone loads this properties file into
    -#		   the System properties.  We could also put this file into the
    -#		   esapi.jar file (perhaps as a ResourceBundle) and then allow an external
    -#		   ESAPI properties be defined that would overwrite these defaults.
    -#		   That keeps the application's properties relatively simple as usually
    -#		   they will only want to override a few properties. If looks like we
    -#		   already support multiple override levels of this in the
    -#		   DefaultSecurityConfiguration class, but I'm suggesting placing the
    -#		   defaults in the esapi.jar itself. That way, if the jar is signed,
    -#		   we could detect if those properties had been tampered with. (The
    -#		   code to check the jar signatures is pretty simple... maybe 70-90 LOC,
    -#		   but off course there is an execution penalty (similar to the way
    -#		   that the separate sunjce.jar used to be when a class from it was
    -#		   first loaded). Thoughts?
    -###############################################################################
    -#
    -# WARNING: Operating system protection should be used to lock down the .esapi
    -# resources directory and all the files inside and all the directories all the
    -# way up to the root directory of the file system.  Note that if you are using
    -# file-based implementations, that some files may need to be read-write as they
    -# get updated dynamically.
    -#
    -# Before using, be sure to update the MasterKey and MasterSalt as described below.
    -# N.B.: If you had stored data that you have previously encrypted with ESAPI 1.4,
    -#		you *must* FIRST decrypt it using ESAPI 1.4 and then (if so desired)
    -#		re-encrypt it with ESAPI 2.0. If you fail to do this, you will NOT be
    -#		able to decrypt your data with ESAPI 2.0.
    -#
    -#		YOU HAVE BEEN WARNED!!! More details are in the ESAPI 2.0 Release Notes.
    -#
    -#===========================================================================
    -# ESAPI Configuration
    -#
    -# If true, then print all the ESAPI properties set here when they are loaded.
    -# If false, they are not printed. Useful to reduce output when running JUnit tests.
    -# If you need to troubleshoot a properties related problem, turning this on may help,
    -# but we leave it off for running JUnit tests. (It will be 'true' in the one delivered
    -# as part of production ESAPI, mostly for backward compatibility.)
    -ESAPI.printProperties=false
    -
    -# ESAPI is designed to be easily extensible. You can use the reference implementation
    -# or implement your own providers to take advantage of your enterprise's security
    -# infrastructure. The functions in ESAPI are referenced using the ESAPI locator, like:
    -#
    -#    String ciphertext =
    -#		ESAPI.encryptor().encrypt("Secret message");   // Deprecated in 2.0
    -#    CipherText cipherText =
    -#		ESAPI.encryptor().encrypt(new PlainText("Secret message")); // Preferred
    -#
    -# Below you can specify the classname for the provider that you wish to use in your
    -# application. The only requirement is that it implement the appropriate ESAPI interface.
    -# This allows you to switch security implementations in the future without rewriting the
    -# entire application.
    -#
    -# ExperimentalAccessController requires ESAPI-AccessControlPolicy.xml in .esapi directory
    -ESAPI.AccessControl=org.owasp.esapi.reference.DefaultAccessController
    -# FileBasedAuthenticator requires users.txt file in .esapi directory
    -ESAPI.Authenticator=org.owasp.esapi.reference.FileBasedAuthenticator
    -ESAPI.Encoder=org.owasp.esapi.reference.DefaultEncoder
    -ESAPI.Encryptor=org.owasp.esapi.reference.crypto.JavaEncryptor
    -
    -ESAPI.Executor=org.owasp.esapi.reference.DefaultExecutor
    -ESAPI.HTTPUtilities=org.owasp.esapi.reference.DefaultHTTPUtilities
    -ESAPI.IntrusionDetector=org.owasp.esapi.reference.DefaultIntrusionDetector
    -# Log4JFactory Requires log4j.xml or log4j.properties in classpath - http://www.laliluna.de/log4j-tutorial.html
    -ESAPI.Logger=org.owasp.esapi.reference.Log4JLogFactory
    -#ESAPI.Logger=org.owasp.esapi.reference.JavaLogFactory
    -#ESAPI.Logger=org.owasp.esapi.reference.ExampleExtendedLog4JLogFactory
    -ESAPI.Randomizer=org.owasp.esapi.reference.DefaultRandomizer
    -ESAPI.Validator=org.owasp.esapi.reference.DefaultValidator
    -
    -#===========================================================================
    -# ESAPI Authenticator
    -#
    -Authenticator.AllowedLoginAttempts=3
    -Authenticator.MaxOldPasswordHashes=13
    -Authenticator.UsernameParameterName=username
    -Authenticator.PasswordParameterName=password
    -# RememberTokenDuration (in days)
    -Authenticator.RememberTokenDuration=14
    -# Session Timeouts (in minutes)
    -Authenticator.IdleTimeoutDuration=20
    -Authenticator.AbsoluteTimeoutDuration=120
    -
    -#===========================================================================
    -# ESAPI Encoder
    -#
    -# ESAPI canonicalizes input before validation to prevent bypassing filters with encoded attacks.
    -# Failure to canonicalize input is a very common mistake when implementing validation schemes.
    -# Canonicalization is automatic when using the ESAPI Validator, but you can also use the
    -# following code to canonicalize data.
    -#
    -#      ESAPI.Encoder().canonicalize( "%22hello world"" );
    -#  
    -# Multiple encoding is when a single encoding format is applied multiple times. Allowing
    -# multiple encoding is strongly discouraged.
    -Encoder.AllowMultipleEncoding=false
    -
    -# Mixed encoding is when multiple different encoding formats are applied, or when
    -# multiple formats are nested. Allowing multiple encoding is strongly discouraged.
    -Encoder.AllowMixedEncoding=false
    -
    -# The default list of codecs to apply when canonicalizing untrusted data. The list should include the codecs
    -# for all downstream interpreters or decoders. For example, if the data is likely to end up in a URL, HTML, or
    -# inside JavaScript, then the list of codecs below is appropriate. The order of the list is not terribly important.
    -Encoder.DefaultCodecList=HTMLEntityCodec,PercentCodec,JavaScriptCodec
    -
    -
    -#===========================================================================
    -# ESAPI Encryption
    -#
    -# The ESAPI Encryptor provides basic cryptographic functions with a simplified API.
    -# To get started, generate a new key using java -classpath esapi.jar org.owasp.esapi.reference.crypto.JavaEncryptor
    -# There is not currently any support for key rotation, so be careful when changing your key and salt as it
    -# will invalidate all signed, encrypted, and hashed data.
    -#
    -# WARNING: Not all combinations of algorithms and key lengths are supported.
    -# If you choose to use a key length greater than 128, you MUST download the
    -# unlimited strength policy files and install in the lib directory of your JRE/JDK.
    -# See http://java.sun.com/javase/downloads/index.jsp for more information.
    -#
    -# Backward compatibility with ESAPI Java 1.4 is supported by the two deprecated API
    -# methods, Encryptor.encrypt(String) and Encryptor.decrypt(String). However, whenever
    -# possible, these methods should be avoided as they use ECB cipher mode, which in almost
    -# all circumstances a poor choice because of it's weakness. CBC cipher mode is the default
    -# for the new Encryptor encrypt / decrypt methods for ESAPI Java 2.0.  In general, you
    -# should only use this compatibility setting if you have persistent data encrypted with
    -# version 1.4 and even then, you should ONLY set this compatibility mode UNTIL
    -# you have decrypted all of your old encrypted data and then re-encrypted it with
    -# ESAPI 2.0 using CBC mode. If you have some reason to mix the deprecated 1.4 mode
    -# with the new 2.0 methods, make sure that you use the same cipher algorithm for both
    -# (256-bit AES was the default for 1.4; 128-bit is the default for 2.0; see below for
    -# more details.) Otherwise, you will have to use the new 2.0 encrypt / decrypt methods
    -# where you can specify a SecretKey. (Note that if you are using the 256-bit AES,
    -# that requires downloading the special jurisdiction policy files mentioned above.)
    -#
    -#		***** IMPORTANT: These are for JUnit testing. Test files may have been
    -#						 encrypted using these values so do not change these or
    -#						 those tests will fail. The version under
    -#							src/main/resources/.esapi/ESAPI.properties
    -#						 will be delivered with Encryptor.MasterKey and
    -#						 Encryptor.MasterSalt set to the empty string.
    -#
    -#						 FINAL NOTE:
    -#                           If Maven changes these when run, that needs to be fixed.
    -#       256-bit key... requires unlimited strength jurisdiction policy files
    -### Encryptor.MasterKey=pJhlri8JbuFYDgkqtHmm9s0Ziug2PE7ovZDyEPm4j14=
    -#       128-bit key
    -Encryptor.MasterKey=a6H9is3hEVGKB4Jut+lOVA==
    -Encryptor.MasterSalt=SbftnvmEWD5ZHHP+pX3fqugNysc=
    -# Encryptor.MasterSalt=
    -
    -# Provides the default JCE provider that ESAPI will "prefer" for its symmetric
    -# encryption and hashing. (That is it will look to this provider first, but it
    -# will defer to other providers if the requested algorithm is not implemented
    -# by this provider.) If left unset, ESAPI will just use your Java VM's current
    -# preferred JCE provider, which is generally set in the file
    -# "$JAVA_HOME/jre/lib/security/java.security".
    -#
    -# The main intent of this is to allow ESAPI symmetric encryption to be
    -# used with a FIPS 140-2 compliant crypto-module. For details, see the section
    -# "Using ESAPI Symmetric Encryption with FIPS 140-2 Cryptographic Modules" in
    -# the ESAPI 2.0 Symmetric Encryption User Guide, at:
    -# http://owasp-esapi-java.googlecode.com/svn/trunk/documentation/esapi4java-core-2.0-symmetric-crypto-user-guide.html
    -# However, this property also allows you to easily use an alternate JCE provider
    -# such as "Bouncy Castle" without having to make changes to "java.security".
    -# See Javadoc for SecurityProviderLoader for further details. If you wish to use
    -# a provider that is not known to SecurityProviderLoader, you may specify the
    -# fully-qualified class name of the JCE provider class that implements
    -# java.security.Provider. If the name contains a '.', this is interpreted as
    -# a fully-qualified class name that implements java.security.Provider.
    -#
    -# NOTE: Setting this property has the side-effect of changing it in your application
    -#       as well, so if you are using JCE in your application directly rather than
    -#       through ESAPI (you wouldn't do that, would you? ;-), it will change the
    -#       preferred JCE provider there as well.
    -#
    -# Default: Keeps the JCE provider set to whatever JVM sets it to.
    -Encryptor.PreferredJCEProvider=
    -
    -# AES is the most widely used and strongest encryption algorithm. This
    -# should agree with your Encryptor.CipherTransformation property.
    -# By default, ESAPI Java 1.4 uses "PBEWithMD5AndDES" and which is
    -# very weak. It is essentially a password-based encryption key, hashed
    -# with MD5 around 1K times and then encrypted with the weak DES algorithm
    -# (56-bits) using ECB mode and an unspecified padding (it is
    -# JCE provider specific, but most likely "NoPadding"). However, 2.0 uses
    -# "AES/CBC/PKCSPadding". If you want to change these, change them here.
    -# Warning: This property does not control the default reference implementation for
    -#		   ESAPI 2.0 using JavaEncryptor. Also, this property will be dropped
    -#		   in the future.
    -# @deprecated
    -Encryptor.EncryptionAlgorithm=AES
    -#		For ESAPI Java 2.0 - New encrypt / decrypt methods use this.
    -Encryptor.CipherTransformation=AES/CBC/PKCS5Padding
    -
    -# Applies to ESAPI 2.0 and later only!
    -# Comma-separated list of cipher modes that provide *BOTH*
    -# confidentiality *AND* message authenticity. (NIST refers to such cipher
    -# modes as "combined modes" so that's what we shall call them.) If any of these
    -# cipher modes are used then no MAC is calculated and stored
    -# in the CipherText upon encryption. Likewise, if one of these
    -# cipher modes is used with decryption, no attempt will be made
    -# to validate the MAC contained in the CipherText object regardless
    -# of whether it contains one or not. Since the expectation is that
    -# these cipher modes support support message authenticity already,
    -# injecting a MAC in the CipherText object would be at best redundant.
    -#
    -# Note that as of JDK 1.5, the SunJCE provider does not support *any*
    -# of these cipher modes. Of these listed, only GCM and CCM are currently
    -# NIST approved. YMMV for other JCE providers. E.g., Bouncy Castle supports
    -# GCM and CCM with "NoPadding" mode, but not with "PKCS5Padding" or other
    -# padding modes.
    -Encryptor.cipher_modes.combined_modes=GCM,CCM,IAPM,EAX,OCB,CWC
    -
    -# Applies to ESAPI 2.0 and later only!
    -# Additional cipher modes allowed for ESAPI 2.0 encryption. These
    -# cipher modes are in _addition_ to those specified by the property
    -# 'Encryptor.cipher_modes.combined_modes'.
    -# Note: We will add support for streaming modes like CFB & OFB once
    -# we add support for 'specified' to the property 'Encryptor.ChooseIVMethod'
    -# (probably in ESAPI 2.1).
    -#
    -#	IMPORTANT NOTE:	In the official ESAPI.properties we do *NOT* include ECB
    -#					here as this is an extremely weak mode. However, we *must*
    -#					allow it here so we can test ECB mode. That is important
    -#					since the logic is somewhat different (i.e., ECB mode does
    -#					not use an IV).
    -# DISCUSS: Better name?
    -#	NOTE: ECB added only for testing purposes. Don't try this at home!
    -Encryptor.cipher_modes.additional_allowed=CBC,ECB
    -
    -# 128-bit is almost always sufficient and appears to be more resistant to
    -# related key attacks than is 256-bit AES. Use '_' to use default key size
    -# for cipher algorithms (where it makes sense because the algorithm supports
    -# a variable key size). Key length must agree to what's provided as the
    -# cipher transformation, otherwise this will be ignored after logging a
    -# warning.
    -#
    -# NOTE: This is what applies BOTH ESAPI 1.4 and 2.0. See warning above about mixing!
    -Encryptor.EncryptionKeyLength=128
    -
    -# Because 2.0 uses CBC mode by default, it requires an initialization vector (IV).
    -# (All cipher modes except ECB require an IV.) There are two choices: we can either
    -# use a fixed IV known to both parties or allow ESAPI to choose a random IV. While
    -# the IV does not need to be hidden from adversaries, it is important that the
    -# adversary not be allowed to choose it. Also, random IVs are generally much more
    -# secure than fixed IVs. (In fact, it is essential that feed-back cipher modes
    -# such as CFB and OFB use a different IV for each encryption with a given key so
    -# in such cases, random IVs are much preferred. By default, ESAPI 2.0 uses random
    -# IVs. If you wish to use 'fixed' IVs, set 'Encryptor.ChooseIVMethod=fixed' and
    -# uncomment the Encryptor.fixedIV.
    -#
    -# Valid values:		random|fixed|specified		'specified' not yet implemented; planned for 2.1
    -Encryptor.ChooseIVMethod=random
    -# If you choose to use a fixed IV, then you must place a fixed IV here that
    -# is known to all others who are sharing your secret key. The format should
    -# be a hex string that is the same length as the cipher block size for the
    -# cipher algorithm that you are using. The following is an example for AES
    -# from an AES test vector for AES-128/CBC as described in:
    -# NIST Special Publication 800-38A (2001 Edition)
    -# "Recommendation for Block Cipher Modes of Operation".
    -# (Note that the block size for AES is 16 bytes == 128 bits.)
    -#
    -Encryptor.fixedIV=0x000102030405060708090a0b0c0d0e0f
    -
    -# Whether or not CipherText should use a message authentication code (MAC) with it.
    -# This prevents an adversary from altering the IV as well as allowing a more
    -# fool-proof way of determining the decryption failed because of an incorrect
    -# key being supplied. This refers to the "separate" MAC calculated and stored
    -# in CipherText, not part of any MAC that is calculated as a result of a
    -# "combined mode" cipher mode.
    -#
    -# If you are using ESAPI with a FIPS 140-2 cryptographic module, you *must* also
    -# set this property to false.
    -Encryptor.CipherText.useMAC=true
    -
    -# Whether or not the PlainText object may be overwritten and then marked
    -# eligible for garbage collection. If not set, this is still treated as 'true'.
    -Encryptor.PlainText.overwrite=true
    -
    -# Do not use DES except in a legacy situations. 56-bit is way too small key size.
    -#Encryptor.EncryptionKeyLength=56
    -#Encryptor.EncryptionAlgorithm=DES
    -
    -# TripleDES is considered strong enough for most purposes.
    -#	Note:	There is also a 112-bit version of DESede. Using the 168-bit version
    -#			requires downloading the special jurisdiction policy from Sun.
    -#Encryptor.EncryptionKeyLength=168
    -#Encryptor.EncryptionAlgorithm=DESede
    -
    -Encryptor.HashAlgorithm=SHA-512
    -Encryptor.HashIterations=1024
    -Encryptor.DigitalSignatureAlgorithm=SHA1withDSA
    -Encryptor.DigitalSignatureKeyLength=1024
    -Encryptor.RandomAlgorithm=SHA1PRNG
    -Encryptor.CharacterEncoding=UTF-8
    -# Currently supported choices for JDK 1.5 and 1.6 are:
    -#	HmacSHA1 (160 bits), HmacSHA256 (256 bits), HmacSHA384 (384 bits), and
    -#	HmacSHA512 (512 bits).
    -# Note that HmacMD5 is *not* supported for the PRF used by the KDF even though
    -# these JDKs support it.
    -Encryptor.KDF.PRF=HmacSHA256
    -
    -#===========================================================================
    -# ESAPI HttpUtilties
    -#
    -# The HttpUtilities provide basic protections to HTTP requests and responses. Primarily these methods 
    -# protect against malicious data from attackers, such as unprintable characters, escaped characters,
    -# and other simple attacks. The HttpUtilities also provides utility methods for dealing with cookies,
    -# headers, and CSRF tokens.
    -#
    -# Default file upload location (remember to escape backslashes with \\)
    -HttpUtilities.UploadDir=C:\\ESAPI\\testUpload
    -# let this default to java.io.tmpdir for testing
    -#HttpUtilities.UploadTempDir=C:\\temp
    -# Force flags on cookies, if you use HttpUtilities to set cookies
    -HttpUtilities.ForceHttpOnlySession=false
    -HttpUtilities.ForceSecureSession=false
    -HttpUtilities.ForceHttpOnlyCookies=true
    -HttpUtilities.ForceSecureCookies=true
    -# Maximum size of HTTP headers
    -HttpUtilities.MaxHeaderSize=4096
    -# File upload configuration
    -HttpUtilities.ApprovedUploadExtensions=.zip,.pdf,.doc,.docx,.ppt,.pptx,.tar,.gz,.tgz,.rar,.war,.jar,.ear,.xls,.rtf,.properties,.java,.class,.txt,.xml,.jsp,.jsf,.exe,.dll
    -HttpUtilities.MaxUploadFileBytes=500000000
    -# Using UTF-8 throughout your stack is highly recommended. That includes your database driver,
    -# container, and any other technologies you may be using. Failure to do this may expose you
    -# to Unicode transcoding injection attacks. Use of UTF-8 does not hinder internationalization.
    -HttpUtilities.ResponseContentType=text/html; charset=UTF-8
    -# This is the name of the cookie used to represent the HTTP session
    -# Typically this will be the default "JSESSIONID" 
    -HttpUtilities.HttpSessionIdName=JSESSIONID
    -
    -
    -
    -#===========================================================================
    -# ESAPI Executor
    -# CHECKME - Not sure what this is used for, but surely it should be made OS independent.
    -Executor.WorkingDirectory=C:\\Windows\\Temp
    -Executor.ApprovedExecutables=C:\\Windows\\System32\\cmd.exe,C:\\Windows\\System32\\runas.exe
    -
    -
    -#===========================================================================
    -# ESAPI Logging
    -# Set the application name if these logs are combined with other applications
    -Logger.ApplicationName=ExampleApplication
    -# If you use an HTML log viewer that does not properly HTML escape log data, you can set LogEncodingRequired to true
    -Logger.LogEncodingRequired=false
    -# Determines whether ESAPI should log the application name. This might be clutter in some single-server/single-app environments.
    -Logger.LogApplicationName=true
    -# Determines whether ESAPI should log the server IP and port. This might be clutter in some single-server environments.
    -Logger.LogServerIP=true
    -# LogFileName, the name of the logging file. Provide a full directory path (e.g., C:\\ESAPI\\ESAPI_logging_file) if you
    -# want to place it in a specific directory.
    -Logger.LogFileName=ESAPI_logging_file
    -# MaxLogFileSize, the max size (in bytes) of a single log file before it cuts over to a new one (default is 10,000,000)
    -Logger.MaxLogFileSize=10000000
    -
    -
    -#===========================================================================
    -# ESAPI Intrusion Detection
    -#
    -# Each event has a base to which .count, .interval, and .action are added
    -# The IntrusionException will fire if we receive "count" events within "interval" seconds
    -# The IntrusionDetector is configurable to take the following actions: log, logout, and disable
    -#  (multiple actions separated by commas are allowed e.g. event.test.actions=log,disable
    -#
    -# Custom Events
    -# Names must start with "event." as the base
    -# Use IntrusionDetector.addEvent( "test" ) in your code to trigger "event.test" here
    -# You can also disable intrusion detection completely by changing
    -# the following parameter to true
    -#
    -IntrusionDetector.Disable=false
    -#
    -IntrusionDetector.event.test.count=2
    -IntrusionDetector.event.test.interval=10
    -IntrusionDetector.event.test.actions=disable,log
    -
    -# Exception Events
    -# All EnterpriseSecurityExceptions are registered automatically
    -# Call IntrusionDetector.getInstance().addException(e) for Exceptions that do not extend EnterpriseSecurityException
    -# Use the fully qualified classname of the exception as the base
    -
    -# any intrusion is an attack
    -IntrusionDetector.org.owasp.esapi.errors.IntrusionException.count=1
    -IntrusionDetector.org.owasp.esapi.errors.IntrusionException.interval=1
    -IntrusionDetector.org.owasp.esapi.errors.IntrusionException.actions=log,disable,logout
    -
    -# for test purposes
    -# CHECKME: Shouldn't there be something in the property name itself that designates
    -#		   that these are for testing???
    -IntrusionDetector.org.owasp.esapi.errors.IntegrityException.count=10
    -IntrusionDetector.org.owasp.esapi.errors.IntegrityException.interval=5
    -IntrusionDetector.org.owasp.esapi.errors.IntegrityException.actions=log,disable,logout
    -
    -# rapid validation errors indicate scans or attacks in progress
    -# org.owasp.esapi.errors.ValidationException.count=10
    -# org.owasp.esapi.errors.ValidationException.interval=10
    -# org.owasp.esapi.errors.ValidationException.actions=log,logout
    -
    -# sessions jumping between hosts indicates session hijacking
    -IntrusionDetector.org.owasp.esapi.errors.AuthenticationHostException.count=2
    -IntrusionDetector.org.owasp.esapi.errors.AuthenticationHostException.interval=10
    -IntrusionDetector.org.owasp.esapi.errors.AuthenticationHostException.actions=log,logout
    -
    -
    -#===========================================================================
    -# ESAPI Validation
    -#
    -# The ESAPI Validator works on regular expressions with defined names. You can define names
    -# either here, or you may define application specific patterns in a separate file defined below.
    -# This allows enterprises to specify both organizational standards as well as application specific
    -# validation rules.
    -#
    -Validator.ConfigurationFile.MultiValued=true
    -Validator.ConfigurationFile=validation-test1.properties,validation-test2.properties
    -
    -# Validators used by ESAPI
    -Validator.AccountName=^[a-zA-Z0-9]{3,20}$
    -Validator.SystemCommand=^[a-zA-Z\\-\\/]{1,64}$
    -Validator.RoleName=^[a-z]{1,20}$
    -Validator.Redirect=^\\/test.*$
    -
    -# Global HTTP Validation Rules
    -# Values with Base64 encoded data (e.g. encrypted state) will need at least [a-zA-Z0-9\/+=]
    -Validator.HTTPScheme=^(http|https)$
    -Validator.HTTPServerName=^[a-zA-Z0-9_.\\-]*$
    -Validator.HTTPCookieName=^[a-zA-Z0-9\\-_]{1,32}$
    -Validator.HTTPCookieValue=^[a-zA-Z0-9\\-\\/+=_ ]*$
    -Validator.HTTPHeaderName=^[a-zA-Z0-9\\-_]{1,32}$
    -Validator.HTTPHeaderValue=^[a-zA-Z0-9()\\-=\\*\\.\\?;,+\\/:&_ ]*$
    -Validator.HTTPServletPath=^[a-zA-Z0-9.\\-\\/_]*$
    -Validator.HTTPPath=^[a-zA-Z0-9.\\-_]*$
    -Validator.HTTPURL=^.*$
    -Validator.HTTPJSESSIONID=^[A-Z0-9]{10,30}$
    -
    -# Contributed by Fraenku@gmx.ch
    -# Googlecode Issue 116 (http://code.google.com/p/owasp-esapi-java/issues/detail?id=116)
    -Validator.HTTPParameterName=^[a-zA-Z0-9_\\-]{1,32}$
    -Validator.HTTPParameterValue=^[\\p{L}\\p{N}.\\-/+=_ !$*?@]{0,1000}$
    -Validator.HTTPContextPath=^/[a-zA-Z0-9.\\-_]*$
    -Validator.HTTPQueryString=^([a-zA-Z0-9_\\-]{1,32}=[\\p{L}\\p{N}.\\-/+=_ !$*?@%]*&?)*$
    -Validator.HTTPURI=^/([a-zA-Z0-9.\\-_]*/?)*$
    -
    -
    -# Validation of file related input
    -Validator.FileName=^[a-zA-Z0-9!@#$%^&{}\\[\\]()_+\\-=,.~'` ]{1,255}$
    -Validator.DirectoryName=^[a-zA-Z0-9:/\\\\!@#$%^&{}\\[\\]()_+\\-=,.~'` ]{1,255}$
    -
    -# Validation of dates. Controls whether or not 'lenient' dates are accepted.
    -# See DataFormat.setLenient(boolean flag) for further details.
    +#
    +# OWASP Enterprise Security API (ESAPI) Properties file -- TEST Version
    +# 
    +# This file is part of the Open Web Application Security Project (OWASP)
    +# Enterprise Security API (ESAPI) project. For details, please see
    +# http://www.owasp.org/index.php/ESAPI.
    +#
    +# Copyright (c) 2008,2009 - The OWASP Foundation
    +#
    +# DISCUSS: This may cause a major backwards compatibility issue, etc. but
    +#		   from a name space perspective, we probably should have prefaced
    +#		   all the property names with ESAPI or at least OWASP. Otherwise
    +#		   there could be problems is someone loads this properties file into
    +#		   the System properties.  We could also put this file into the
    +#		   esapi.jar file (perhaps as a ResourceBundle) and then allow an external
    +#		   ESAPI properties be defined that would overwrite these defaults.
    +#		   That keeps the application's properties relatively simple as usually
    +#		   they will only want to override a few properties. If looks like we
    +#		   already support multiple override levels of this in the
    +#		   DefaultSecurityConfiguration class, but I'm suggesting placing the
    +#		   defaults in the esapi.jar itself. That way, if the jar is signed,
    +#		   we could detect if those properties had been tampered with. (The
    +#		   code to check the jar signatures is pretty simple... maybe 70-90 LOC,
    +#		   but off course there is an execution penalty (similar to the way
    +#		   that the separate sunjce.jar used to be when a class from it was
    +#		   first loaded). Thoughts?
    +###############################################################################
    +#
    +# WARNING: Operating system protection should be used to lock down the .esapi
    +# resources directory and all the files inside and all the directories all the
    +# way up to the root directory of the file system.  Note that if you are using
    +# file-based implementations, that some files may need to be read-write as they
    +# get updated dynamically.
    +#
    +# Before using, be sure to update the MasterKey and MasterSalt as described below.
    +# N.B.: If you had stored data that you have previously encrypted with ESAPI 1.4,
    +#		you *must* FIRST decrypt it using ESAPI 1.4 and then (if so desired)
    +#		re-encrypt it with ESAPI 2.0. If you fail to do this, you will NOT be
    +#		able to decrypt your data with ESAPI 2.0.
    +#
    +#		YOU HAVE BEEN WARNED!!! More details are in the ESAPI 2.0 Release Notes.
    +#
    +#===========================================================================
    +# ESAPI Configuration
    +#
    +# If true, then print all the ESAPI properties set here when they are loaded.
    +# If false, they are not printed. Useful to reduce output when running JUnit tests.
    +# If you need to troubleshoot a properties related problem, turning this on may help,
    +# but we leave it off for running JUnit tests. (It will be 'true' in the one delivered
    +# as part of production ESAPI, mostly for backward compatibility.)
    +ESAPI.printProperties=false
    +
    +# ESAPI is designed to be easily extensible. You can use the reference implementation
    +# or implement your own providers to take advantage of your enterprise's security
    +# infrastructure. The functions in ESAPI are referenced using the ESAPI locator, like:
    +#
    +#    String ciphertext =
    +#		ESAPI.encryptor().encrypt("Secret message");   // Deprecated in 2.0
    +#    CipherText cipherText =
    +#		ESAPI.encryptor().encrypt(new PlainText("Secret message")); // Preferred
    +#
    +# Below you can specify the classname for the provider that you wish to use in your
    +# application. The only requirement is that it implement the appropriate ESAPI interface.
    +# This allows you to switch security implementations in the future without rewriting the
    +# entire application.
    +#
    +# ExperimentalAccessController requires ESAPI-AccessControlPolicy.xml in .esapi directory
    +ESAPI.AccessControl=org.owasp.esapi.reference.DefaultAccessController
    +# FileBasedAuthenticator requires users.txt file in .esapi directory
    +ESAPI.Authenticator=org.owasp.esapi.reference.FileBasedAuthenticator
    +ESAPI.Encoder=org.owasp.esapi.reference.DefaultEncoder
    +ESAPI.Encryptor=org.owasp.esapi.reference.crypto.JavaEncryptor
    +
    +ESAPI.Executor=org.owasp.esapi.reference.DefaultExecutor
    +ESAPI.HTTPUtilities=org.owasp.esapi.reference.DefaultHTTPUtilities
    +ESAPI.IntrusionDetector=org.owasp.esapi.reference.DefaultIntrusionDetector
    +# Log4JFactory Requires log4j.xml or log4j.properties in classpath - http://www.laliluna.de/log4j-tutorial.html
    +ESAPI.Logger=org.owasp.esapi.reference.Log4JLogFactory
    +#ESAPI.Logger=org.owasp.esapi.reference.JavaLogFactory
    +#ESAPI.Logger=org.owasp.esapi.reference.ExampleExtendedLog4JLogFactory
    +ESAPI.Randomizer=org.owasp.esapi.reference.DefaultRandomizer
    +ESAPI.Validator=org.owasp.esapi.reference.DefaultValidator
    +
    +#===========================================================================
    +# ESAPI Authenticator
    +#
    +Authenticator.AllowedLoginAttempts=3
    +Authenticator.MaxOldPasswordHashes=13
    +Authenticator.UsernameParameterName=username
    +Authenticator.PasswordParameterName=password
    +# RememberTokenDuration (in days)
    +Authenticator.RememberTokenDuration=14
    +# Session Timeouts (in minutes)
    +Authenticator.IdleTimeoutDuration=20
    +Authenticator.AbsoluteTimeoutDuration=120
    +
    +#===========================================================================
    +# ESAPI Encoder
    +#
    +# ESAPI canonicalizes input before validation to prevent bypassing filters with encoded attacks.
    +# Failure to canonicalize input is a very common mistake when implementing validation schemes.
    +# Canonicalization is automatic when using the ESAPI Validator, but you can also use the
    +# following code to canonicalize data.
    +#
    +#      ESAPI.Encoder().canonicalize( "%22hello world"" );
    +#  
    +# Multiple encoding is when a single encoding format is applied multiple times. Allowing
    +# multiple encoding is strongly discouraged.
    +Encoder.AllowMultipleEncoding=false
    +
    +# Mixed encoding is when multiple different encoding formats are applied, or when
    +# multiple formats are nested. Allowing multiple encoding is strongly discouraged.
    +Encoder.AllowMixedEncoding=false
    +
    +# The default list of codecs to apply when canonicalizing untrusted data. The list should include the codecs
    +# for all downstream interpreters or decoders. For example, if the data is likely to end up in a URL, HTML, or
    +# inside JavaScript, then the list of codecs below is appropriate. The order of the list is not terribly important.
    +Encoder.DefaultCodecList=HTMLEntityCodec,PercentCodec,JavaScriptCodec
    +
    +
    +#===========================================================================
    +# ESAPI Encryption
    +#
    +# The ESAPI Encryptor provides basic cryptographic functions with a simplified API.
    +# To get started, generate a new key using java -classpath esapi.jar org.owasp.esapi.reference.crypto.JavaEncryptor
    +# There is not currently any support for key rotation, so be careful when changing your key and salt as it
    +# will invalidate all signed, encrypted, and hashed data.
    +#
    +# WARNING: Not all combinations of algorithms and key lengths are supported.
    +# If you choose to use a key length greater than 128, you MUST download the
    +# unlimited strength policy files and install in the lib directory of your JRE/JDK.
    +# See http://java.sun.com/javase/downloads/index.jsp for more information.
    +#
    +# Backward compatibility with ESAPI Java 1.4 is supported by the two deprecated API
    +# methods, Encryptor.encrypt(String) and Encryptor.decrypt(String). However, whenever
    +# possible, these methods should be avoided as they use ECB cipher mode, which in almost
    +# all circumstances a poor choice because of it's weakness. CBC cipher mode is the default
    +# for the new Encryptor encrypt / decrypt methods for ESAPI Java 2.0.  In general, you
    +# should only use this compatibility setting if you have persistent data encrypted with
    +# version 1.4 and even then, you should ONLY set this compatibility mode UNTIL
    +# you have decrypted all of your old encrypted data and then re-encrypted it with
    +# ESAPI 2.0 using CBC mode. If you have some reason to mix the deprecated 1.4 mode
    +# with the new 2.0 methods, make sure that you use the same cipher algorithm for both
    +# (256-bit AES was the default for 1.4; 128-bit is the default for 2.0; see below for
    +# more details.) Otherwise, you will have to use the new 2.0 encrypt / decrypt methods
    +# where you can specify a SecretKey. (Note that if you are using the 256-bit AES,
    +# that requires downloading the special jurisdiction policy files mentioned above.)
    +#
    +#		***** IMPORTANT: These are for JUnit testing. Test files may have been
    +#						 encrypted using these values so do not change these or
    +#						 those tests will fail. The version under
    +#							src/main/resources/.esapi/ESAPI.properties
    +#						 will be delivered with Encryptor.MasterKey and
    +#						 Encryptor.MasterSalt set to the empty string.
    +#
    +#						 FINAL NOTE:
    +#                           If Maven changes these when run, that needs to be fixed.
    +#       256-bit key... requires unlimited strength jurisdiction policy files
    +### Encryptor.MasterKey=pJhlri8JbuFYDgkqtHmm9s0Ziug2PE7ovZDyEPm4j14=
    +#       128-bit key
    +Encryptor.MasterKey=a6H9is3hEVGKB4Jut+lOVA==
    +Encryptor.MasterSalt=SbftnvmEWD5ZHHP+pX3fqugNysc=
    +# Encryptor.MasterSalt=
    +
    +# Provides the default JCE provider that ESAPI will "prefer" for its symmetric
    +# encryption and hashing. (That is it will look to this provider first, but it
    +# will defer to other providers if the requested algorithm is not implemented
    +# by this provider.) If left unset, ESAPI will just use your Java VM's current
    +# preferred JCE provider, which is generally set in the file
    +# "$JAVA_HOME/jre/lib/security/java.security".
    +#
    +# The main intent of this is to allow ESAPI symmetric encryption to be
    +# used with a FIPS 140-2 compliant crypto-module. For details, see the section
    +# "Using ESAPI Symmetric Encryption with FIPS 140-2 Cryptographic Modules" in
    +# the ESAPI 2.0 Symmetric Encryption User Guide, at:
    +# http://owasp-esapi-java.googlecode.com/svn/trunk/documentation/esapi4java-core-2.0-symmetric-crypto-user-guide.html
    +# However, this property also allows you to easily use an alternate JCE provider
    +# such as "Bouncy Castle" without having to make changes to "java.security".
    +# See Javadoc for SecurityProviderLoader for further details. If you wish to use
    +# a provider that is not known to SecurityProviderLoader, you may specify the
    +# fully-qualified class name of the JCE provider class that implements
    +# java.security.Provider. If the name contains a '.', this is interpreted as
    +# a fully-qualified class name that implements java.security.Provider.
    +#
    +# NOTE: Setting this property has the side-effect of changing it in your application
    +#       as well, so if you are using JCE in your application directly rather than
    +#       through ESAPI (you wouldn't do that, would you? ;-), it will change the
    +#       preferred JCE provider there as well.
    +#
    +# Default: Keeps the JCE provider set to whatever JVM sets it to.
    +Encryptor.PreferredJCEProvider=
    +
    +# AES is the most widely used and strongest encryption algorithm. This
    +# should agree with your Encryptor.CipherTransformation property.
    +# By default, ESAPI Java 1.4 uses "PBEWithMD5AndDES" and which is
    +# very weak. It is essentially a password-based encryption key, hashed
    +# with MD5 around 1K times and then encrypted with the weak DES algorithm
    +# (56-bits) using ECB mode and an unspecified padding (it is
    +# JCE provider specific, but most likely "NoPadding"). However, 2.0 uses
    +# "AES/CBC/PKCSPadding". If you want to change these, change them here.
    +# Warning: This property does not control the default reference implementation for
    +#		   ESAPI 2.0 using JavaEncryptor. Also, this property will be dropped
    +#		   in the future.
    +# @deprecated
    +Encryptor.EncryptionAlgorithm=AES
    +#		For ESAPI Java 2.0 - New encrypt / decrypt methods use this.
    +Encryptor.CipherTransformation=AES/CBC/PKCS5Padding
    +
    +# Applies to ESAPI 2.0 and later only!
    +# Comma-separated list of cipher modes that provide *BOTH*
    +# confidentiality *AND* message authenticity. (NIST refers to such cipher
    +# modes as "combined modes" so that's what we shall call them.) If any of these
    +# cipher modes are used then no MAC is calculated and stored
    +# in the CipherText upon encryption. Likewise, if one of these
    +# cipher modes is used with decryption, no attempt will be made
    +# to validate the MAC contained in the CipherText object regardless
    +# of whether it contains one or not. Since the expectation is that
    +# these cipher modes support support message authenticity already,
    +# injecting a MAC in the CipherText object would be at best redundant.
    +#
    +# Note that as of JDK 1.5, the SunJCE provider does not support *any*
    +# of these cipher modes. Of these listed, only GCM and CCM are currently
    +# NIST approved. YMMV for other JCE providers. E.g., Bouncy Castle supports
    +# GCM and CCM with "NoPadding" mode, but not with "PKCS5Padding" or other
    +# padding modes.
    +Encryptor.cipher_modes.combined_modes=GCM,CCM,IAPM,EAX,OCB,CWC
    +
    +# Applies to ESAPI 2.0 and later only!
    +# Additional cipher modes allowed for ESAPI 2.0 encryption. These
    +# cipher modes are in _addition_ to those specified by the property
    +# 'Encryptor.cipher_modes.combined_modes'.
    +# Note: We will add support for streaming modes like CFB & OFB once
    +# we add support for 'specified' to the property 'Encryptor.ChooseIVMethod'
    +# (probably in ESAPI 2.1).
    +#
    +#	IMPORTANT NOTE:	In the official ESAPI.properties we do *NOT* include ECB
    +#					here as this is an extremely weak mode. However, we *must*
    +#					allow it here so we can test ECB mode. That is important
    +#					since the logic is somewhat different (i.e., ECB mode does
    +#					not use an IV).
    +# DISCUSS: Better name?
    +#	NOTE: ECB added only for testing purposes. Don't try this at home!
    +Encryptor.cipher_modes.additional_allowed=CBC,ECB
    +
    +# 128-bit is almost always sufficient and appears to be more resistant to
    +# related key attacks than is 256-bit AES. Use '_' to use default key size
    +# for cipher algorithms (where it makes sense because the algorithm supports
    +# a variable key size). Key length must agree to what's provided as the
    +# cipher transformation, otherwise this will be ignored after logging a
    +# warning.
    +#
    +# NOTE: This is what applies BOTH ESAPI 1.4 and 2.0. See warning above about mixing!
    +Encryptor.EncryptionKeyLength=128
    +
    +# Because 2.0 uses CBC mode by default, it requires an initialization vector (IV).
    +# (All cipher modes except ECB require an IV.) There are two choices: we can either
    +# use a fixed IV known to both parties or allow ESAPI to choose a random IV. While
    +# the IV does not need to be hidden from adversaries, it is important that the
    +# adversary not be allowed to choose it. Also, random IVs are generally much more
    +# secure than fixed IVs. (In fact, it is essential that feed-back cipher modes
    +# such as CFB and OFB use a different IV for each encryption with a given key so
    +# in such cases, random IVs are much preferred. By default, ESAPI 2.0 uses random
    +# IVs. If you wish to use 'fixed' IVs, set 'Encryptor.ChooseIVMethod=fixed' and
    +# uncomment the Encryptor.fixedIV.
    +#
    +# Valid values:		random|fixed|specified		'specified' not yet implemented; planned for 2.1
    +Encryptor.ChooseIVMethod=random
    +# If you choose to use a fixed IV, then you must place a fixed IV here that
    +# is known to all others who are sharing your secret key. The format should
    +# be a hex string that is the same length as the cipher block size for the
    +# cipher algorithm that you are using. The following is an example for AES
    +# from an AES test vector for AES-128/CBC as described in:
    +# NIST Special Publication 800-38A (2001 Edition)
    +# "Recommendation for Block Cipher Modes of Operation".
    +# (Note that the block size for AES is 16 bytes == 128 bits.)
    +#
    +Encryptor.fixedIV=0x000102030405060708090a0b0c0d0e0f
    +
    +# Whether or not CipherText should use a message authentication code (MAC) with it.
    +# This prevents an adversary from altering the IV as well as allowing a more
    +# fool-proof way of determining the decryption failed because of an incorrect
    +# key being supplied. This refers to the "separate" MAC calculated and stored
    +# in CipherText, not part of any MAC that is calculated as a result of a
    +# "combined mode" cipher mode.
    +#
    +# If you are using ESAPI with a FIPS 140-2 cryptographic module, you *must* also
    +# set this property to false.
    +Encryptor.CipherText.useMAC=true
    +
    +# Whether or not the PlainText object may be overwritten and then marked
    +# eligible for garbage collection. If not set, this is still treated as 'true'.
    +Encryptor.PlainText.overwrite=true
    +
    +# Do not use DES except in a legacy situations. 56-bit is way too small key size.
    +#Encryptor.EncryptionKeyLength=56
    +#Encryptor.EncryptionAlgorithm=DES
    +
    +# TripleDES is considered strong enough for most purposes.
    +#	Note:	There is also a 112-bit version of DESede. Using the 168-bit version
    +#			requires downloading the special jurisdiction policy from Sun.
    +#Encryptor.EncryptionKeyLength=168
    +#Encryptor.EncryptionAlgorithm=DESede
    +
    +Encryptor.HashAlgorithm=SHA-512
    +Encryptor.HashIterations=1024
    +Encryptor.DigitalSignatureAlgorithm=SHA1withDSA
    +Encryptor.DigitalSignatureKeyLength=1024
    +Encryptor.RandomAlgorithm=SHA1PRNG
    +Encryptor.CharacterEncoding=UTF-8
    +# Currently supported choices for JDK 1.5 and 1.6 are:
    +#	HmacSHA1 (160 bits), HmacSHA256 (256 bits), HmacSHA384 (384 bits), and
    +#	HmacSHA512 (512 bits).
    +# Note that HmacMD5 is *not* supported for the PRF used by the KDF even though
    +# these JDKs support it.
    +Encryptor.KDF.PRF=HmacSHA256
    +
    +#===========================================================================
    +# ESAPI HttpUtilties
    +#
    +# The HttpUtilities provide basic protections to HTTP requests and responses. Primarily these methods 
    +# protect against malicious data from attackers, such as unprintable characters, escaped characters,
    +# and other simple attacks. The HttpUtilities also provides utility methods for dealing with cookies,
    +# headers, and CSRF tokens.
    +#
    +# Default file upload location (remember to escape backslashes with \\)
    +HttpUtilities.UploadDir=C:\\ESAPI\\testUpload
    +# let this default to java.io.tmpdir for testing
    +#HttpUtilities.UploadTempDir=C:\\temp
    +# Force flags on cookies, if you use HttpUtilities to set cookies
    +HttpUtilities.ForceHttpOnlySession=false
    +HttpUtilities.ForceSecureSession=false
    +HttpUtilities.ForceHttpOnlyCookies=true
    +HttpUtilities.ForceSecureCookies=true
    +# Maximum size of HTTP headers
    +HttpUtilities.MaxHeaderSize=4096
    +# File upload configuration
    +HttpUtilities.ApprovedUploadExtensions=.zip,.pdf,.doc,.docx,.ppt,.pptx,.tar,.gz,.tgz,.rar,.war,.jar,.ear,.xls,.rtf,.properties,.java,.class,.txt,.xml,.jsp,.jsf,.exe,.dll
    +HttpUtilities.MaxUploadFileBytes=500000000
    +# Using UTF-8 throughout your stack is highly recommended. That includes your database driver,
    +# container, and any other technologies you may be using. Failure to do this may expose you
    +# to Unicode transcoding injection attacks. Use of UTF-8 does not hinder internationalization.
    +HttpUtilities.ResponseContentType=text/html; charset=UTF-8
    +# This is the name of the cookie used to represent the HTTP session
    +# Typically this will be the default "JSESSIONID" 
    +HttpUtilities.HttpSessionIdName=JSESSIONID
    +
    +
    +
    +#===========================================================================
    +# ESAPI Executor
    +# CHECKME - Not sure what this is used for, but surely it should be made OS independent.
    +Executor.WorkingDirectory=C:\\Windows\\Temp
    +Executor.ApprovedExecutables=C:\\Windows\\System32\\cmd.exe,C:\\Windows\\System32\\runas.exe
    +
    +
    +#===========================================================================
    +# ESAPI Logging
    +# Set the application name if these logs are combined with other applications
    +Logger.ApplicationName=ExampleApplication
    +# If you use an HTML log viewer that does not properly HTML escape log data, you can set LogEncodingRequired to true
    +Logger.LogEncodingRequired=false
    +# Determines whether ESAPI should log the application name. This might be clutter in some single-server/single-app environments.
    +Logger.LogApplicationName=true
    +# Determines whether ESAPI should log the server IP and port. This might be clutter in some single-server environments.
    +Logger.LogServerIP=true
    +# LogFileName, the name of the logging file. Provide a full directory path (e.g., C:\\ESAPI\\ESAPI_logging_file) if you
    +# want to place it in a specific directory.
    +Logger.LogFileName=ESAPI_logging_file
    +# MaxLogFileSize, the max size (in bytes) of a single log file before it cuts over to a new one (default is 10,000,000)
    +Logger.MaxLogFileSize=10000000
    +
    +
    +#===========================================================================
    +# ESAPI Intrusion Detection
    +#
    +# Each event has a base to which .count, .interval, and .action are added
    +# The IntrusionException will fire if we receive "count" events within "interval" seconds
    +# The IntrusionDetector is configurable to take the following actions: log, logout, and disable
    +#  (multiple actions separated by commas are allowed e.g. event.test.actions=log,disable
    +#
    +# Custom Events
    +# Names must start with "event." as the base
    +# Use IntrusionDetector.addEvent( "test" ) in your code to trigger "event.test" here
    +# You can also disable intrusion detection completely by changing
    +# the following parameter to true
    +#
    +IntrusionDetector.Disable=false
    +#
    +IntrusionDetector.event.test.count=2
    +IntrusionDetector.event.test.interval=10
    +IntrusionDetector.event.test.actions=disable,log
    +
    +# Exception Events
    +# All EnterpriseSecurityExceptions are registered automatically
    +# Call IntrusionDetector.getInstance().addException(e) for Exceptions that do not extend EnterpriseSecurityException
    +# Use the fully qualified classname of the exception as the base
    +
    +# any intrusion is an attack
    +IntrusionDetector.org.owasp.esapi.errors.IntrusionException.count=1
    +IntrusionDetector.org.owasp.esapi.errors.IntrusionException.interval=1
    +IntrusionDetector.org.owasp.esapi.errors.IntrusionException.actions=log,disable,logout
    +
    +# for test purposes
    +# CHECKME: Shouldn't there be something in the property name itself that designates
    +#		   that these are for testing???
    +IntrusionDetector.org.owasp.esapi.errors.IntegrityException.count=10
    +IntrusionDetector.org.owasp.esapi.errors.IntegrityException.interval=5
    +IntrusionDetector.org.owasp.esapi.errors.IntegrityException.actions=log,disable,logout
    +
    +# rapid validation errors indicate scans or attacks in progress
    +# org.owasp.esapi.errors.ValidationException.count=10
    +# org.owasp.esapi.errors.ValidationException.interval=10
    +# org.owasp.esapi.errors.ValidationException.actions=log,logout
    +
    +# sessions jumping between hosts indicates session hijacking
    +IntrusionDetector.org.owasp.esapi.errors.AuthenticationHostException.count=2
    +IntrusionDetector.org.owasp.esapi.errors.AuthenticationHostException.interval=10
    +IntrusionDetector.org.owasp.esapi.errors.AuthenticationHostException.actions=log,logout
    +
    +
    +#===========================================================================
    +# ESAPI Validation
    +#
    +# The ESAPI Validator works on regular expressions with defined names. You can define names
    +# either here, or you may define application specific patterns in a separate file defined below.
    +# This allows enterprises to specify both organizational standards as well as application specific
    +# validation rules.
    +#
    +Validator.ConfigurationFile.MultiValued=true
    +Validator.ConfigurationFile=validation-test1.properties,validation-test2.properties
    +
    +# Validators used by ESAPI
    +Validator.AccountName=^[a-zA-Z0-9]{3,20}$
    +Validator.SystemCommand=^[a-zA-Z\\-\\/]{1,64}$
    +Validator.RoleName=^[a-z]{1,20}$
    +Validator.Redirect=^\\/test.*$
    +
    +# Global HTTP Validation Rules
    +# Values with Base64 encoded data (e.g. encrypted state) will need at least [a-zA-Z0-9\/+=]
    +Validator.HTTPScheme=^(http|https)$
    +Validator.HTTPServerName=^[a-zA-Z0-9_.\\-]*$
    +Validator.HTTPCookieName=^[a-zA-Z0-9\\-_]{1,32}$
    +Validator.HTTPCookieValue=^[a-zA-Z0-9\\-\\/+=_ ]*$
    +Validator.HTTPHeaderName=^[a-zA-Z0-9\\-_]{1,32}$
    +Validator.HTTPHeaderValue=^[a-zA-Z0-9()\\-=\\*\\.\\?;,+\\/:&_ ]*$
    +Validator.HTTPServletPath=^[a-zA-Z0-9.\\-\\/_]*$
    +Validator.HTTPPath=^[a-zA-Z0-9.\\-_]*$
    +Validator.HTTPURL=^.*$
    +Validator.HTTPJSESSIONID=^[A-Z0-9]{10,30}$
    +
    +# Contributed by Fraenku@gmx.ch
    +# Googlecode Issue 116 (http://code.google.com/p/owasp-esapi-java/issues/detail?id=116)
    +Validator.HTTPParameterName=^[a-zA-Z0-9_\\-]{1,32}$
    +Validator.HTTPParameterValue=^[\\p{L}\\p{N}.\\-/+=_ !$*?@]{0,1000}$
    +Validator.HTTPContextPath=^/[a-zA-Z0-9.\\-_]*$
    +Validator.HTTPQueryString=^([a-zA-Z0-9_\\-]{1,32}=[\\p{L}\\p{N}.\\-/+=_ !$*?@%]*&?)*$
    +Validator.HTTPURI=^/([a-zA-Z0-9.\\-_]*/?)*$
    +
    +
    +# Validation of file related input
    +Validator.FileName=^[a-zA-Z0-9!@#$%^&{}\\[\\]()_+\\-=,.~'` ]{1,255}$
    +Validator.DirectoryName=^[a-zA-Z0-9:/\\\\!@#$%^&{}\\[\\]()_+\\-=,.~'` ]{1,255}$
    +
    +# Validation of dates. Controls whether or not 'lenient' dates are accepted.
    +# See DataFormat.setLenient(boolean flag) for further details.
     Validator.AcceptLenientDates=false
    \ No newline at end of file
    
    From 1678c989de38e716b376790186cad5dce2ef05f9 Mon Sep 17 00:00:00 2001
    From: kwwall 
    Date: Tue, 19 Jan 2016 01:12:42 -0500
    Subject: [PATCH 0278/1069] Address issue 356.
    
    ---
     ...SAPI-QuotedValidatorFileChecker.properties | 930 +++++++++---------
     1 file changed, 465 insertions(+), 465 deletions(-)
    
    diff --git a/src/test/resources/esapi/ESAPI-QuotedValidatorFileChecker.properties b/src/test/resources/esapi/ESAPI-QuotedValidatorFileChecker.properties
    index 2dc8c5d15..a3a66ba60 100644
    --- a/src/test/resources/esapi/ESAPI-QuotedValidatorFileChecker.properties
    +++ b/src/test/resources/esapi/ESAPI-QuotedValidatorFileChecker.properties
    @@ -1,466 +1,466 @@
    -#
    -# OWASP Enterprise Security API (ESAPI) Properties file -- TEST Version
    -# 
    -# This file is part of the Open Web Application Security Project (OWASP)
    -# Enterprise Security API (ESAPI) project. For details, please see
    -# http://www.owasp.org/index.php/ESAPI.
    -#
    -# Copyright (c) 2008,2009 - The OWASP Foundation
    -#
    -# DISCUSS: This may cause a major backwards compatibility issue, etc. but
    -#		   from a name space perspective, we probably should have prefaced
    -#		   all the property names with ESAPI or at least OWASP. Otherwise
    -#		   there could be problems is someone loads this properties file into
    -#		   the System properties.  We could also put this file into the
    -#		   esapi.jar file (perhaps as a ResourceBundle) and then allow an external
    -#		   ESAPI properties be defined that would overwrite these defaults.
    -#		   That keeps the application's properties relatively simple as usually
    -#		   they will only want to override a few properties. If looks like we
    -#		   already support multiple override levels of this in the
    -#		   DefaultSecurityConfiguration class, but I'm suggesting placing the
    -#		   defaults in the esapi.jar itself. That way, if the jar is signed,
    -#		   we could detect if those properties had been tampered with. (The
    -#		   code to check the jar signatures is pretty simple... maybe 70-90 LOC,
    -#		   but off course there is an execution penalty (similar to the way
    -#		   that the separate sunjce.jar used to be when a class from it was
    -#		   first loaded). Thoughts?
    -###############################################################################
    -#
    -# WARNING: Operating system protection should be used to lock down the .esapi
    -# resources directory and all the files inside and all the directories all the
    -# way up to the root directory of the file system.  Note that if you are using
    -# file-based implementations, that some files may need to be read-write as they
    -# get updated dynamically.
    -#
    -# Before using, be sure to update the MasterKey and MasterSalt as described below.
    -# N.B.: If you had stored data that you have previously encrypted with ESAPI 1.4,
    -#		you *must* FIRST decrypt it using ESAPI 1.4 and then (if so desired)
    -#		re-encrypt it with ESAPI 2.0. If you fail to do this, you will NOT be
    -#		able to decrypt your data with ESAPI 2.0.
    -#
    -#		YOU HAVE BEEN WARNED!!! More details are in the ESAPI 2.0 Release Notes.
    -#
    -#===========================================================================
    -# ESAPI Configuration
    -#
    -# If true, then print all the ESAPI properties set here when they are loaded.
    -# If false, they are not printed. Useful to reduce output when running JUnit tests.
    -# If you need to troubleshoot a properties related problem, turning this on may help,
    -# but we leave it off for running JUnit tests. (It will be 'true' in the one delivered
    -# as part of production ESAPI, mostly for backward compatibility.)
    -ESAPI.printProperties=false
    -
    -# ESAPI is designed to be easily extensible. You can use the reference implementation
    -# or implement your own providers to take advantage of your enterprise's security
    -# infrastructure. The functions in ESAPI are referenced using the ESAPI locator, like:
    -#
    -#    String ciphertext =
    -#		ESAPI.encryptor().encrypt("Secret message");   // Deprecated in 2.0
    -#    CipherText cipherText =
    -#		ESAPI.encryptor().encrypt(new PlainText("Secret message")); // Preferred
    -#
    -# Below you can specify the classname for the provider that you wish to use in your
    -# application. The only requirement is that it implement the appropriate ESAPI interface.
    -# This allows you to switch security implementations in the future without rewriting the
    -# entire application.
    -#
    -# ExperimentalAccessController requires ESAPI-AccessControlPolicy.xml in .esapi directory
    -ESAPI.AccessControl=org.owasp.esapi.reference.DefaultAccessController
    -# FileBasedAuthenticator requires users.txt file in .esapi directory
    -ESAPI.Authenticator=org.owasp.esapi.reference.FileBasedAuthenticator
    -ESAPI.Encoder=org.owasp.esapi.reference.DefaultEncoder
    -ESAPI.Encryptor=org.owasp.esapi.reference.crypto.JavaEncryptor
    -
    -ESAPI.Executor=org.owasp.esapi.reference.DefaultExecutor
    -ESAPI.HTTPUtilities=org.owasp.esapi.reference.DefaultHTTPUtilities
    -ESAPI.IntrusionDetector=org.owasp.esapi.reference.DefaultIntrusionDetector
    -# Log4JFactory Requires log4j.xml or log4j.properties in classpath - http://www.laliluna.de/log4j-tutorial.html
    -ESAPI.Logger=org.owasp.esapi.reference.Log4JLogFactory
    -#ESAPI.Logger=org.owasp.esapi.reference.JavaLogFactory
    -#ESAPI.Logger=org.owasp.esapi.reference.ExampleExtendedLog4JLogFactory
    -ESAPI.Randomizer=org.owasp.esapi.reference.DefaultRandomizer
    -ESAPI.Validator=org.owasp.esapi.reference.DefaultValidator
    -
    -#===========================================================================
    -# ESAPI Authenticator
    -#
    -Authenticator.AllowedLoginAttempts=3
    -Authenticator.MaxOldPasswordHashes=13
    -Authenticator.UsernameParameterName=username
    -Authenticator.PasswordParameterName=password
    -# RememberTokenDuration (in days)
    -Authenticator.RememberTokenDuration=14
    -# Session Timeouts (in minutes)
    -Authenticator.IdleTimeoutDuration=20
    -Authenticator.AbsoluteTimeoutDuration=120
    -
    -#===========================================================================
    -# ESAPI Encoder
    -#
    -# ESAPI canonicalizes input before validation to prevent bypassing filters with encoded attacks.
    -# Failure to canonicalize input is a very common mistake when implementing validation schemes.
    -# Canonicalization is automatic when using the ESAPI Validator, but you can also use the
    -# following code to canonicalize data.
    -#
    -#      ESAPI.Encoder().canonicalize( "%22hello world"" );
    -#  
    -# Multiple encoding is when a single encoding format is applied multiple times. Allowing
    -# multiple encoding is strongly discouraged.
    -Encoder.AllowMultipleEncoding=false
    -
    -# Mixed encoding is when multiple different encoding formats are applied, or when
    -# multiple formats are nested. Allowing multiple encoding is strongly discouraged.
    -Encoder.AllowMixedEncoding=false
    -
    -# The default list of codecs to apply when canonicalizing untrusted data. The list should include the codecs
    -# for all downstream interpreters or decoders. For example, if the data is likely to end up in a URL, HTML, or
    -# inside JavaScript, then the list of codecs below is appropriate. The order of the list is not terribly important.
    -Encoder.DefaultCodecList=HTMLEntityCodec,PercentCodec,JavaScriptCodec
    -
    -
    -#===========================================================================
    -# ESAPI Encryption
    -#
    -# The ESAPI Encryptor provides basic cryptographic functions with a simplified API.
    -# To get started, generate a new key using java -classpath esapi.jar org.owasp.esapi.reference.crypto.JavaEncryptor
    -# There is not currently any support for key rotation, so be careful when changing your key and salt as it
    -# will invalidate all signed, encrypted, and hashed data.
    -#
    -# WARNING: Not all combinations of algorithms and key lengths are supported.
    -# If you choose to use a key length greater than 128, you MUST download the
    -# unlimited strength policy files and install in the lib directory of your JRE/JDK.
    -# See http://java.sun.com/javase/downloads/index.jsp for more information.
    -#
    -# Backward compatibility with ESAPI Java 1.4 is supported by the two deprecated API
    -# methods, Encryptor.encrypt(String) and Encryptor.decrypt(String). However, whenever
    -# possible, these methods should be avoided as they use ECB cipher mode, which in almost
    -# all circumstances a poor choice because of it's weakness. CBC cipher mode is the default
    -# for the new Encryptor encrypt / decrypt methods for ESAPI Java 2.0.  In general, you
    -# should only use this compatibility setting if you have persistent data encrypted with
    -# version 1.4 and even then, you should ONLY set this compatibility mode UNTIL
    -# you have decrypted all of your old encrypted data and then re-encrypted it with
    -# ESAPI 2.0 using CBC mode. If you have some reason to mix the deprecated 1.4 mode
    -# with the new 2.0 methods, make sure that you use the same cipher algorithm for both
    -# (256-bit AES was the default for 1.4; 128-bit is the default for 2.0; see below for
    -# more details.) Otherwise, you will have to use the new 2.0 encrypt / decrypt methods
    -# where you can specify a SecretKey. (Note that if you are using the 256-bit AES,
    -# that requires downloading the special jurisdiction policy files mentioned above.)
    -#
    -#		***** IMPORTANT: These are for JUnit testing. Test files may have been
    -#						 encrypted using these values so do not change these or
    -#						 those tests will fail. The version under
    -#							src/main/resources/.esapi/ESAPI.properties
    -#						 will be delivered with Encryptor.MasterKey and
    -#						 Encryptor.MasterSalt set to the empty string.
    -#
    -#						 FINAL NOTE:
    -#                           If Maven changes these when run, that needs to be fixed.
    -#       256-bit key... requires unlimited strength jurisdiction policy files
    -### Encryptor.MasterKey=pJhlri8JbuFYDgkqtHmm9s0Ziug2PE7ovZDyEPm4j14=
    -#       128-bit key
    -Encryptor.MasterKey=a6H9is3hEVGKB4Jut+lOVA==
    -Encryptor.MasterSalt=SbftnvmEWD5ZHHP+pX3fqugNysc=
    -# Encryptor.MasterSalt=
    -
    -# Provides the default JCE provider that ESAPI will "prefer" for its symmetric
    -# encryption and hashing. (That is it will look to this provider first, but it
    -# will defer to other providers if the requested algorithm is not implemented
    -# by this provider.) If left unset, ESAPI will just use your Java VM's current
    -# preferred JCE provider, which is generally set in the file
    -# "$JAVA_HOME/jre/lib/security/java.security".
    -#
    -# The main intent of this is to allow ESAPI symmetric encryption to be
    -# used with a FIPS 140-2 compliant crypto-module. For details, see the section
    -# "Using ESAPI Symmetric Encryption with FIPS 140-2 Cryptographic Modules" in
    -# the ESAPI 2.0 Symmetric Encryption User Guide, at:
    -# http://owasp-esapi-java.googlecode.com/svn/trunk/documentation/esapi4java-core-2.0-symmetric-crypto-user-guide.html
    -# However, this property also allows you to easily use an alternate JCE provider
    -# such as "Bouncy Castle" without having to make changes to "java.security".
    -# See Javadoc for SecurityProviderLoader for further details. If you wish to use
    -# a provider that is not known to SecurityProviderLoader, you may specify the
    -# fully-qualified class name of the JCE provider class that implements
    -# java.security.Provider. If the name contains a '.', this is interpreted as
    -# a fully-qualified class name that implements java.security.Provider.
    -#
    -# NOTE: Setting this property has the side-effect of changing it in your application
    -#       as well, so if you are using JCE in your application directly rather than
    -#       through ESAPI (you wouldn't do that, would you? ;-), it will change the
    -#       preferred JCE provider there as well.
    -#
    -# Default: Keeps the JCE provider set to whatever JVM sets it to.
    -Encryptor.PreferredJCEProvider=
    -
    -# AES is the most widely used and strongest encryption algorithm. This
    -# should agree with your Encryptor.CipherTransformation property.
    -# By default, ESAPI Java 1.4 uses "PBEWithMD5AndDES" and which is
    -# very weak. It is essentially a password-based encryption key, hashed
    -# with MD5 around 1K times and then encrypted with the weak DES algorithm
    -# (56-bits) using ECB mode and an unspecified padding (it is
    -# JCE provider specific, but most likely "NoPadding"). However, 2.0 uses
    -# "AES/CBC/PKCSPadding". If you want to change these, change them here.
    -# Warning: This property does not control the default reference implementation for
    -#		   ESAPI 2.0 using JavaEncryptor. Also, this property will be dropped
    -#		   in the future.
    -# @deprecated
    -Encryptor.EncryptionAlgorithm=AES
    -#		For ESAPI Java 2.0 - New encrypt / decrypt methods use this.
    -Encryptor.CipherTransformation=AES/CBC/PKCS5Padding
    -
    -# Applies to ESAPI 2.0 and later only!
    -# Comma-separated list of cipher modes that provide *BOTH*
    -# confidentiality *AND* message authenticity. (NIST refers to such cipher
    -# modes as "combined modes" so that's what we shall call them.) If any of these
    -# cipher modes are used then no MAC is calculated and stored
    -# in the CipherText upon encryption. Likewise, if one of these
    -# cipher modes is used with decryption, no attempt will be made
    -# to validate the MAC contained in the CipherText object regardless
    -# of whether it contains one or not. Since the expectation is that
    -# these cipher modes support support message authenticity already,
    -# injecting a MAC in the CipherText object would be at best redundant.
    -#
    -# Note that as of JDK 1.5, the SunJCE provider does not support *any*
    -# of these cipher modes. Of these listed, only GCM and CCM are currently
    -# NIST approved. YMMV for other JCE providers. E.g., Bouncy Castle supports
    -# GCM and CCM with "NoPadding" mode, but not with "PKCS5Padding" or other
    -# padding modes.
    -Encryptor.cipher_modes.combined_modes=GCM,CCM,IAPM,EAX,OCB,CWC
    -
    -# Applies to ESAPI 2.0 and later only!
    -# Additional cipher modes allowed for ESAPI 2.0 encryption. These
    -# cipher modes are in _addition_ to those specified by the property
    -# 'Encryptor.cipher_modes.combined_modes'.
    -# Note: We will add support for streaming modes like CFB & OFB once
    -# we add support for 'specified' to the property 'Encryptor.ChooseIVMethod'
    -# (probably in ESAPI 2.1).
    -#
    -#	IMPORTANT NOTE:	In the official ESAPI.properties we do *NOT* include ECB
    -#					here as this is an extremely weak mode. However, we *must*
    -#					allow it here so we can test ECB mode. That is important
    -#					since the logic is somewhat different (i.e., ECB mode does
    -#					not use an IV).
    -# DISCUSS: Better name?
    -#	NOTE: ECB added only for testing purposes. Don't try this at home!
    -Encryptor.cipher_modes.additional_allowed=CBC,ECB
    -
    -# 128-bit is almost always sufficient and appears to be more resistant to
    -# related key attacks than is 256-bit AES. Use '_' to use default key size
    -# for cipher algorithms (where it makes sense because the algorithm supports
    -# a variable key size). Key length must agree to what's provided as the
    -# cipher transformation, otherwise this will be ignored after logging a
    -# warning.
    -#
    -# NOTE: This is what applies BOTH ESAPI 1.4 and 2.0. See warning above about mixing!
    -Encryptor.EncryptionKeyLength=128
    -
    -# Because 2.0 uses CBC mode by default, it requires an initialization vector (IV).
    -# (All cipher modes except ECB require an IV.) There are two choices: we can either
    -# use a fixed IV known to both parties or allow ESAPI to choose a random IV. While
    -# the IV does not need to be hidden from adversaries, it is important that the
    -# adversary not be allowed to choose it. Also, random IVs are generally much more
    -# secure than fixed IVs. (In fact, it is essential that feed-back cipher modes
    -# such as CFB and OFB use a different IV for each encryption with a given key so
    -# in such cases, random IVs are much preferred. By default, ESAPI 2.0 uses random
    -# IVs. If you wish to use 'fixed' IVs, set 'Encryptor.ChooseIVMethod=fixed' and
    -# uncomment the Encryptor.fixedIV.
    -#
    -# Valid values:		random|fixed|specified		'specified' not yet implemented; planned for 2.1
    -Encryptor.ChooseIVMethod=random
    -# If you choose to use a fixed IV, then you must place a fixed IV here that
    -# is known to all others who are sharing your secret key. The format should
    -# be a hex string that is the same length as the cipher block size for the
    -# cipher algorithm that you are using. The following is an example for AES
    -# from an AES test vector for AES-128/CBC as described in:
    -# NIST Special Publication 800-38A (2001 Edition)
    -# "Recommendation for Block Cipher Modes of Operation".
    -# (Note that the block size for AES is 16 bytes == 128 bits.)
    -#
    -Encryptor.fixedIV=0x000102030405060708090a0b0c0d0e0f
    -
    -# Whether or not CipherText should use a message authentication code (MAC) with it.
    -# This prevents an adversary from altering the IV as well as allowing a more
    -# fool-proof way of determining the decryption failed because of an incorrect
    -# key being supplied. This refers to the "separate" MAC calculated and stored
    -# in CipherText, not part of any MAC that is calculated as a result of a
    -# "combined mode" cipher mode.
    -#
    -# If you are using ESAPI with a FIPS 140-2 cryptographic module, you *must* also
    -# set this property to false.
    -Encryptor.CipherText.useMAC=true
    -
    -# Whether or not the PlainText object may be overwritten and then marked
    -# eligible for garbage collection. If not set, this is still treated as 'true'.
    -Encryptor.PlainText.overwrite=true
    -
    -# Do not use DES except in a legacy situations. 56-bit is way too small key size.
    -#Encryptor.EncryptionKeyLength=56
    -#Encryptor.EncryptionAlgorithm=DES
    -
    -# TripleDES is considered strong enough for most purposes.
    -#	Note:	There is also a 112-bit version of DESede. Using the 168-bit version
    -#			requires downloading the special jurisdiction policy from Sun.
    -#Encryptor.EncryptionKeyLength=168
    -#Encryptor.EncryptionAlgorithm=DESede
    -
    -Encryptor.HashAlgorithm=SHA-512
    -Encryptor.HashIterations=1024
    -Encryptor.DigitalSignatureAlgorithm=SHA1withDSA
    -Encryptor.DigitalSignatureKeyLength=1024
    -Encryptor.RandomAlgorithm=SHA1PRNG
    -Encryptor.CharacterEncoding=UTF-8
    -# Currently supported choices for JDK 1.5 and 1.6 are:
    -#	HmacSHA1 (160 bits), HmacSHA256 (256 bits), HmacSHA384 (384 bits), and
    -#	HmacSHA512 (512 bits).
    -# Note that HmacMD5 is *not* supported for the PRF used by the KDF even though
    -# these JDKs support it.
    -Encryptor.KDF.PRF=HmacSHA256
    -
    -#===========================================================================
    -# ESAPI HttpUtilties
    -#
    -# The HttpUtilities provide basic protections to HTTP requests and responses. Primarily these methods 
    -# protect against malicious data from attackers, such as unprintable characters, escaped characters,
    -# and other simple attacks. The HttpUtilities also provides utility methods for dealing with cookies,
    -# headers, and CSRF tokens.
    -#
    -# Default file upload location (remember to escape backslashes with \\)
    -HttpUtilities.UploadDir=C:\\ESAPI\\testUpload
    -# let this default to java.io.tmpdir for testing
    -#HttpUtilities.UploadTempDir=C:\\temp
    -# Force flags on cookies, if you use HttpUtilities to set cookies
    -HttpUtilities.ForceHttpOnlySession=false
    -HttpUtilities.ForceSecureSession=false
    -HttpUtilities.ForceHttpOnlyCookies=true
    -HttpUtilities.ForceSecureCookies=true
    -# Maximum size of HTTP headers
    -HttpUtilities.MaxHeaderSize=4096
    -# File upload configuration
    -HttpUtilities.ApprovedUploadExtensions=.zip,.pdf,.doc,.docx,.ppt,.pptx,.tar,.gz,.tgz,.rar,.war,.jar,.ear,.xls,.rtf,.properties,.java,.class,.txt,.xml,.jsp,.jsf,.exe,.dll
    -HttpUtilities.MaxUploadFileBytes=500000000
    -# Using UTF-8 throughout your stack is highly recommended. That includes your database driver,
    -# container, and any other technologies you may be using. Failure to do this may expose you
    -# to Unicode transcoding injection attacks. Use of UTF-8 does not hinder internationalization.
    -HttpUtilities.ResponseContentType=text/html; charset=UTF-8
    -# This is the name of the cookie used to represent the HTTP session
    -# Typically this will be the default "JSESSIONID" 
    -HttpUtilities.HttpSessionIdName=JSESSIONID
    -
    -
    -
    -#===========================================================================
    -# ESAPI Executor
    -# CHECKME - Not sure what this is used for, but surely it should be made OS independent.
    -Executor.WorkingDirectory=C:\\Windows\\Temp
    -Executor.ApprovedExecutables=C:\\Windows\\System32\\cmd.exe,C:\\Windows\\System32\\runas.exe
    -
    -
    -#===========================================================================
    -# ESAPI Logging
    -# Set the application name if these logs are combined with other applications
    -Logger.ApplicationName=ExampleApplication
    -# If you use an HTML log viewer that does not properly HTML escape log data, you can set LogEncodingRequired to true
    -Logger.LogEncodingRequired=false
    -# Determines whether ESAPI should log the application name. This might be clutter in some single-server/single-app environments.
    -Logger.LogApplicationName=true
    -# Determines whether ESAPI should log the server IP and port. This might be clutter in some single-server environments.
    -Logger.LogServerIP=true
    -# LogFileName, the name of the logging file. Provide a full directory path (e.g., C:\\ESAPI\\ESAPI_logging_file) if you
    -# want to place it in a specific directory.
    -Logger.LogFileName=ESAPI_logging_file
    -# MaxLogFileSize, the max size (in bytes) of a single log file before it cuts over to a new one (default is 10,000,000)
    -Logger.MaxLogFileSize=10000000
    -
    -
    -#===========================================================================
    -# ESAPI Intrusion Detection
    -#
    -# Each event has a base to which .count, .interval, and .action are added
    -# The IntrusionException will fire if we receive "count" events within "interval" seconds
    -# The IntrusionDetector is configurable to take the following actions: log, logout, and disable
    -#  (multiple actions separated by commas are allowed e.g. event.test.actions=log,disable
    -#
    -# Custom Events
    -# Names must start with "event." as the base
    -# Use IntrusionDetector.addEvent( "test" ) in your code to trigger "event.test" here
    -# You can also disable intrusion detection completely by changing
    -# the following parameter to true
    -#
    -IntrusionDetector.Disable=false
    -#
    -IntrusionDetector.event.test.count=2
    -IntrusionDetector.event.test.interval=10
    -IntrusionDetector.event.test.actions=disable,log
    -
    -# Exception Events
    -# All EnterpriseSecurityExceptions are registered automatically
    -# Call IntrusionDetector.getInstance().addException(e) for Exceptions that do not extend EnterpriseSecurityException
    -# Use the fully qualified classname of the exception as the base
    -
    -# any intrusion is an attack
    -IntrusionDetector.org.owasp.esapi.errors.IntrusionException.count=1
    -IntrusionDetector.org.owasp.esapi.errors.IntrusionException.interval=1
    -IntrusionDetector.org.owasp.esapi.errors.IntrusionException.actions=log,disable,logout
    -
    -# for test purposes
    -# CHECKME: Shouldn't there be something in the property name itself that designates
    -#		   that these are for testing???
    -IntrusionDetector.org.owasp.esapi.errors.IntegrityException.count=10
    -IntrusionDetector.org.owasp.esapi.errors.IntegrityException.interval=5
    -IntrusionDetector.org.owasp.esapi.errors.IntegrityException.actions=log,disable,logout
    -
    -# rapid validation errors indicate scans or attacks in progress
    -# org.owasp.esapi.errors.ValidationException.count=10
    -# org.owasp.esapi.errors.ValidationException.interval=10
    -# org.owasp.esapi.errors.ValidationException.actions=log,logout
    -
    -# sessions jumping between hosts indicates session hijacking
    -IntrusionDetector.org.owasp.esapi.errors.AuthenticationHostException.count=2
    -IntrusionDetector.org.owasp.esapi.errors.AuthenticationHostException.interval=10
    -IntrusionDetector.org.owasp.esapi.errors.AuthenticationHostException.actions=log,logout
    -
    -
    -#===========================================================================
    -# ESAPI Validation
    -#
    -# The ESAPI Validator works on regular expressions with defined names. You can define names
    -# either here, or you may define application specific patterns in a separate file defined below.
    -# This allows enterprises to specify both organizational standards as well as application specific
    -# validation rules.
    -#
    -Validator.ConfigurationFile.MultiValued=true
    -Validator.ConfigurationFile="validation-test,comma.properties",validation-test1.properties,validation-test2.properties
    -
    -# Validators used by ESAPI
    -Validator.AccountName=^[a-zA-Z0-9]{3,20}$
    -Validator.SystemCommand=^[a-zA-Z\\-\\/]{1,64}$
    -Validator.RoleName=^[a-z]{1,20}$
    -Validator.Redirect=^\\/test.*$
    -
    -# Global HTTP Validation Rules
    -# Values with Base64 encoded data (e.g. encrypted state) will need at least [a-zA-Z0-9\/+=]
    -Validator.HTTPScheme=^(http|https)$
    -Validator.HTTPServerName=^[a-zA-Z0-9_.\\-]*$
    -Validator.HTTPCookieName=^[a-zA-Z0-9\\-_]{1,32}$
    -Validator.HTTPCookieValue=^[a-zA-Z0-9\\-\\/+=_ ]*$
    -Validator.HTTPHeaderName=^[a-zA-Z0-9\\-_]{1,32}$
    -Validator.HTTPHeaderValue=^[a-zA-Z0-9()\\-=\\*\\.\\?;,+\\/:&_ ]*$
    -Validator.HTTPServletPath=^[a-zA-Z0-9.\\-\\/_]*$
    -Validator.HTTPPath=^[a-zA-Z0-9.\\-_]*$
    -Validator.HTTPURL=^.*$
    -Validator.HTTPJSESSIONID=^[A-Z0-9]{10,30}$
    -
    -# Contributed by Fraenku@gmx.ch
    -# Googlecode Issue 116 (http://code.google.com/p/owasp-esapi-java/issues/detail?id=116)
    -Validator.HTTPParameterName=^[a-zA-Z0-9_\\-]{1,32}$
    -Validator.HTTPParameterValue=^[\\p{L}\\p{N}.\\-/+=_ !$*?@]{0,1000}$
    -Validator.HTTPContextPath=^/[a-zA-Z0-9.\\-_]*$
    -Validator.HTTPQueryString=^([a-zA-Z0-9_\\-]{1,32}=[\\p{L}\\p{N}.\\-/+=_ !$*?@%]*&?)*$
    -Validator.HTTPURI=^/([a-zA-Z0-9.\\-_]*/?)*$
    -
    -
    -# Validation of file related input
    -Validator.FileName=^[a-zA-Z0-9!@#$%^&{}\\[\\]()_+\\-=,.~'` ]{1,255}$
    -Validator.DirectoryName=^[a-zA-Z0-9:/\\\\!@#$%^&{}\\[\\]()_+\\-=,.~'` ]{1,255}$
    -
    -# Validation of dates. Controls whether or not 'lenient' dates are accepted.
    -# See DataFormat.setLenient(boolean flag) for further details.
    +#
    +# OWASP Enterprise Security API (ESAPI) Properties file -- TEST Version
    +# 
    +# This file is part of the Open Web Application Security Project (OWASP)
    +# Enterprise Security API (ESAPI) project. For details, please see
    +# http://www.owasp.org/index.php/ESAPI.
    +#
    +# Copyright (c) 2008,2009 - The OWASP Foundation
    +#
    +# DISCUSS: This may cause a major backwards compatibility issue, etc. but
    +#		   from a name space perspective, we probably should have prefaced
    +#		   all the property names with ESAPI or at least OWASP. Otherwise
    +#		   there could be problems is someone loads this properties file into
    +#		   the System properties.  We could also put this file into the
    +#		   esapi.jar file (perhaps as a ResourceBundle) and then allow an external
    +#		   ESAPI properties be defined that would overwrite these defaults.
    +#		   That keeps the application's properties relatively simple as usually
    +#		   they will only want to override a few properties. If looks like we
    +#		   already support multiple override levels of this in the
    +#		   DefaultSecurityConfiguration class, but I'm suggesting placing the
    +#		   defaults in the esapi.jar itself. That way, if the jar is signed,
    +#		   we could detect if those properties had been tampered with. (The
    +#		   code to check the jar signatures is pretty simple... maybe 70-90 LOC,
    +#		   but off course there is an execution penalty (similar to the way
    +#		   that the separate sunjce.jar used to be when a class from it was
    +#		   first loaded). Thoughts?
    +###############################################################################
    +#
    +# WARNING: Operating system protection should be used to lock down the .esapi
    +# resources directory and all the files inside and all the directories all the
    +# way up to the root directory of the file system.  Note that if you are using
    +# file-based implementations, that some files may need to be read-write as they
    +# get updated dynamically.
    +#
    +# Before using, be sure to update the MasterKey and MasterSalt as described below.
    +# N.B.: If you had stored data that you have previously encrypted with ESAPI 1.4,
    +#		you *must* FIRST decrypt it using ESAPI 1.4 and then (if so desired)
    +#		re-encrypt it with ESAPI 2.0. If you fail to do this, you will NOT be
    +#		able to decrypt your data with ESAPI 2.0.
    +#
    +#		YOU HAVE BEEN WARNED!!! More details are in the ESAPI 2.0 Release Notes.
    +#
    +#===========================================================================
    +# ESAPI Configuration
    +#
    +# If true, then print all the ESAPI properties set here when they are loaded.
    +# If false, they are not printed. Useful to reduce output when running JUnit tests.
    +# If you need to troubleshoot a properties related problem, turning this on may help,
    +# but we leave it off for running JUnit tests. (It will be 'true' in the one delivered
    +# as part of production ESAPI, mostly for backward compatibility.)
    +ESAPI.printProperties=false
    +
    +# ESAPI is designed to be easily extensible. You can use the reference implementation
    +# or implement your own providers to take advantage of your enterprise's security
    +# infrastructure. The functions in ESAPI are referenced using the ESAPI locator, like:
    +#
    +#    String ciphertext =
    +#		ESAPI.encryptor().encrypt("Secret message");   // Deprecated in 2.0
    +#    CipherText cipherText =
    +#		ESAPI.encryptor().encrypt(new PlainText("Secret message")); // Preferred
    +#
    +# Below you can specify the classname for the provider that you wish to use in your
    +# application. The only requirement is that it implement the appropriate ESAPI interface.
    +# This allows you to switch security implementations in the future without rewriting the
    +# entire application.
    +#
    +# ExperimentalAccessController requires ESAPI-AccessControlPolicy.xml in .esapi directory
    +ESAPI.AccessControl=org.owasp.esapi.reference.DefaultAccessController
    +# FileBasedAuthenticator requires users.txt file in .esapi directory
    +ESAPI.Authenticator=org.owasp.esapi.reference.FileBasedAuthenticator
    +ESAPI.Encoder=org.owasp.esapi.reference.DefaultEncoder
    +ESAPI.Encryptor=org.owasp.esapi.reference.crypto.JavaEncryptor
    +
    +ESAPI.Executor=org.owasp.esapi.reference.DefaultExecutor
    +ESAPI.HTTPUtilities=org.owasp.esapi.reference.DefaultHTTPUtilities
    +ESAPI.IntrusionDetector=org.owasp.esapi.reference.DefaultIntrusionDetector
    +# Log4JFactory Requires log4j.xml or log4j.properties in classpath - http://www.laliluna.de/log4j-tutorial.html
    +ESAPI.Logger=org.owasp.esapi.reference.Log4JLogFactory
    +#ESAPI.Logger=org.owasp.esapi.reference.JavaLogFactory
    +#ESAPI.Logger=org.owasp.esapi.reference.ExampleExtendedLog4JLogFactory
    +ESAPI.Randomizer=org.owasp.esapi.reference.DefaultRandomizer
    +ESAPI.Validator=org.owasp.esapi.reference.DefaultValidator
    +
    +#===========================================================================
    +# ESAPI Authenticator
    +#
    +Authenticator.AllowedLoginAttempts=3
    +Authenticator.MaxOldPasswordHashes=13
    +Authenticator.UsernameParameterName=username
    +Authenticator.PasswordParameterName=password
    +# RememberTokenDuration (in days)
    +Authenticator.RememberTokenDuration=14
    +# Session Timeouts (in minutes)
    +Authenticator.IdleTimeoutDuration=20
    +Authenticator.AbsoluteTimeoutDuration=120
    +
    +#===========================================================================
    +# ESAPI Encoder
    +#
    +# ESAPI canonicalizes input before validation to prevent bypassing filters with encoded attacks.
    +# Failure to canonicalize input is a very common mistake when implementing validation schemes.
    +# Canonicalization is automatic when using the ESAPI Validator, but you can also use the
    +# following code to canonicalize data.
    +#
    +#      ESAPI.Encoder().canonicalize( "%22hello world"" );
    +#  
    +# Multiple encoding is when a single encoding format is applied multiple times. Allowing
    +# multiple encoding is strongly discouraged.
    +Encoder.AllowMultipleEncoding=false
    +
    +# Mixed encoding is when multiple different encoding formats are applied, or when
    +# multiple formats are nested. Allowing multiple encoding is strongly discouraged.
    +Encoder.AllowMixedEncoding=false
    +
    +# The default list of codecs to apply when canonicalizing untrusted data. The list should include the codecs
    +# for all downstream interpreters or decoders. For example, if the data is likely to end up in a URL, HTML, or
    +# inside JavaScript, then the list of codecs below is appropriate. The order of the list is not terribly important.
    +Encoder.DefaultCodecList=HTMLEntityCodec,PercentCodec,JavaScriptCodec
    +
    +
    +#===========================================================================
    +# ESAPI Encryption
    +#
    +# The ESAPI Encryptor provides basic cryptographic functions with a simplified API.
    +# To get started, generate a new key using java -classpath esapi.jar org.owasp.esapi.reference.crypto.JavaEncryptor
    +# There is not currently any support for key rotation, so be careful when changing your key and salt as it
    +# will invalidate all signed, encrypted, and hashed data.
    +#
    +# WARNING: Not all combinations of algorithms and key lengths are supported.
    +# If you choose to use a key length greater than 128, you MUST download the
    +# unlimited strength policy files and install in the lib directory of your JRE/JDK.
    +# See http://java.sun.com/javase/downloads/index.jsp for more information.
    +#
    +# Backward compatibility with ESAPI Java 1.4 is supported by the two deprecated API
    +# methods, Encryptor.encrypt(String) and Encryptor.decrypt(String). However, whenever
    +# possible, these methods should be avoided as they use ECB cipher mode, which in almost
    +# all circumstances a poor choice because of it's weakness. CBC cipher mode is the default
    +# for the new Encryptor encrypt / decrypt methods for ESAPI Java 2.0.  In general, you
    +# should only use this compatibility setting if you have persistent data encrypted with
    +# version 1.4 and even then, you should ONLY set this compatibility mode UNTIL
    +# you have decrypted all of your old encrypted data and then re-encrypted it with
    +# ESAPI 2.0 using CBC mode. If you have some reason to mix the deprecated 1.4 mode
    +# with the new 2.0 methods, make sure that you use the same cipher algorithm for both
    +# (256-bit AES was the default for 1.4; 128-bit is the default for 2.0; see below for
    +# more details.) Otherwise, you will have to use the new 2.0 encrypt / decrypt methods
    +# where you can specify a SecretKey. (Note that if you are using the 256-bit AES,
    +# that requires downloading the special jurisdiction policy files mentioned above.)
    +#
    +#		***** IMPORTANT: These are for JUnit testing. Test files may have been
    +#						 encrypted using these values so do not change these or
    +#						 those tests will fail. The version under
    +#							src/main/resources/.esapi/ESAPI.properties
    +#						 will be delivered with Encryptor.MasterKey and
    +#						 Encryptor.MasterSalt set to the empty string.
    +#
    +#						 FINAL NOTE:
    +#                           If Maven changes these when run, that needs to be fixed.
    +#       256-bit key... requires unlimited strength jurisdiction policy files
    +### Encryptor.MasterKey=pJhlri8JbuFYDgkqtHmm9s0Ziug2PE7ovZDyEPm4j14=
    +#       128-bit key
    +Encryptor.MasterKey=a6H9is3hEVGKB4Jut+lOVA==
    +Encryptor.MasterSalt=SbftnvmEWD5ZHHP+pX3fqugNysc=
    +# Encryptor.MasterSalt=
    +
    +# Provides the default JCE provider that ESAPI will "prefer" for its symmetric
    +# encryption and hashing. (That is it will look to this provider first, but it
    +# will defer to other providers if the requested algorithm is not implemented
    +# by this provider.) If left unset, ESAPI will just use your Java VM's current
    +# preferred JCE provider, which is generally set in the file
    +# "$JAVA_HOME/jre/lib/security/java.security".
    +#
    +# The main intent of this is to allow ESAPI symmetric encryption to be
    +# used with a FIPS 140-2 compliant crypto-module. For details, see the section
    +# "Using ESAPI Symmetric Encryption with FIPS 140-2 Cryptographic Modules" in
    +# the ESAPI 2.0 Symmetric Encryption User Guide, at:
    +# http://owasp-esapi-java.googlecode.com/svn/trunk/documentation/esapi4java-core-2.0-symmetric-crypto-user-guide.html
    +# However, this property also allows you to easily use an alternate JCE provider
    +# such as "Bouncy Castle" without having to make changes to "java.security".
    +# See Javadoc for SecurityProviderLoader for further details. If you wish to use
    +# a provider that is not known to SecurityProviderLoader, you may specify the
    +# fully-qualified class name of the JCE provider class that implements
    +# java.security.Provider. If the name contains a '.', this is interpreted as
    +# a fully-qualified class name that implements java.security.Provider.
    +#
    +# NOTE: Setting this property has the side-effect of changing it in your application
    +#       as well, so if you are using JCE in your application directly rather than
    +#       through ESAPI (you wouldn't do that, would you? ;-), it will change the
    +#       preferred JCE provider there as well.
    +#
    +# Default: Keeps the JCE provider set to whatever JVM sets it to.
    +Encryptor.PreferredJCEProvider=
    +
    +# AES is the most widely used and strongest encryption algorithm. This
    +# should agree with your Encryptor.CipherTransformation property.
    +# By default, ESAPI Java 1.4 uses "PBEWithMD5AndDES" and which is
    +# very weak. It is essentially a password-based encryption key, hashed
    +# with MD5 around 1K times and then encrypted with the weak DES algorithm
    +# (56-bits) using ECB mode and an unspecified padding (it is
    +# JCE provider specific, but most likely "NoPadding"). However, 2.0 uses
    +# "AES/CBC/PKCSPadding". If you want to change these, change them here.
    +# Warning: This property does not control the default reference implementation for
    +#		   ESAPI 2.0 using JavaEncryptor. Also, this property will be dropped
    +#		   in the future.
    +# @deprecated
    +Encryptor.EncryptionAlgorithm=AES
    +#		For ESAPI Java 2.0 - New encrypt / decrypt methods use this.
    +Encryptor.CipherTransformation=AES/CBC/PKCS5Padding
    +
    +# Applies to ESAPI 2.0 and later only!
    +# Comma-separated list of cipher modes that provide *BOTH*
    +# confidentiality *AND* message authenticity. (NIST refers to such cipher
    +# modes as "combined modes" so that's what we shall call them.) If any of these
    +# cipher modes are used then no MAC is calculated and stored
    +# in the CipherText upon encryption. Likewise, if one of these
    +# cipher modes is used with decryption, no attempt will be made
    +# to validate the MAC contained in the CipherText object regardless
    +# of whether it contains one or not. Since the expectation is that
    +# these cipher modes support support message authenticity already,
    +# injecting a MAC in the CipherText object would be at best redundant.
    +#
    +# Note that as of JDK 1.5, the SunJCE provider does not support *any*
    +# of these cipher modes. Of these listed, only GCM and CCM are currently
    +# NIST approved. YMMV for other JCE providers. E.g., Bouncy Castle supports
    +# GCM and CCM with "NoPadding" mode, but not with "PKCS5Padding" or other
    +# padding modes.
    +Encryptor.cipher_modes.combined_modes=GCM,CCM,IAPM,EAX,OCB,CWC
    +
    +# Applies to ESAPI 2.0 and later only!
    +# Additional cipher modes allowed for ESAPI 2.0 encryption. These
    +# cipher modes are in _addition_ to those specified by the property
    +# 'Encryptor.cipher_modes.combined_modes'.
    +# Note: We will add support for streaming modes like CFB & OFB once
    +# we add support for 'specified' to the property 'Encryptor.ChooseIVMethod'
    +# (probably in ESAPI 2.1).
    +#
    +#	IMPORTANT NOTE:	In the official ESAPI.properties we do *NOT* include ECB
    +#					here as this is an extremely weak mode. However, we *must*
    +#					allow it here so we can test ECB mode. That is important
    +#					since the logic is somewhat different (i.e., ECB mode does
    +#					not use an IV).
    +# DISCUSS: Better name?
    +#	NOTE: ECB added only for testing purposes. Don't try this at home!
    +Encryptor.cipher_modes.additional_allowed=CBC,ECB
    +
    +# 128-bit is almost always sufficient and appears to be more resistant to
    +# related key attacks than is 256-bit AES. Use '_' to use default key size
    +# for cipher algorithms (where it makes sense because the algorithm supports
    +# a variable key size). Key length must agree to what's provided as the
    +# cipher transformation, otherwise this will be ignored after logging a
    +# warning.
    +#
    +# NOTE: This is what applies BOTH ESAPI 1.4 and 2.0. See warning above about mixing!
    +Encryptor.EncryptionKeyLength=128
    +
    +# Because 2.0 uses CBC mode by default, it requires an initialization vector (IV).
    +# (All cipher modes except ECB require an IV.) There are two choices: we can either
    +# use a fixed IV known to both parties or allow ESAPI to choose a random IV. While
    +# the IV does not need to be hidden from adversaries, it is important that the
    +# adversary not be allowed to choose it. Also, random IVs are generally much more
    +# secure than fixed IVs. (In fact, it is essential that feed-back cipher modes
    +# such as CFB and OFB use a different IV for each encryption with a given key so
    +# in such cases, random IVs are much preferred. By default, ESAPI 2.0 uses random
    +# IVs. If you wish to use 'fixed' IVs, set 'Encryptor.ChooseIVMethod=fixed' and
    +# uncomment the Encryptor.fixedIV.
    +#
    +# Valid values:		random|fixed|specified		'specified' not yet implemented; planned for 2.1
    +Encryptor.ChooseIVMethod=random
    +# If you choose to use a fixed IV, then you must place a fixed IV here that
    +# is known to all others who are sharing your secret key. The format should
    +# be a hex string that is the same length as the cipher block size for the
    +# cipher algorithm that you are using. The following is an example for AES
    +# from an AES test vector for AES-128/CBC as described in:
    +# NIST Special Publication 800-38A (2001 Edition)
    +# "Recommendation for Block Cipher Modes of Operation".
    +# (Note that the block size for AES is 16 bytes == 128 bits.)
    +#
    +Encryptor.fixedIV=0x000102030405060708090a0b0c0d0e0f
    +
    +# Whether or not CipherText should use a message authentication code (MAC) with it.
    +# This prevents an adversary from altering the IV as well as allowing a more
    +# fool-proof way of determining the decryption failed because of an incorrect
    +# key being supplied. This refers to the "separate" MAC calculated and stored
    +# in CipherText, not part of any MAC that is calculated as a result of a
    +# "combined mode" cipher mode.
    +#
    +# If you are using ESAPI with a FIPS 140-2 cryptographic module, you *must* also
    +# set this property to false.
    +Encryptor.CipherText.useMAC=true
    +
    +# Whether or not the PlainText object may be overwritten and then marked
    +# eligible for garbage collection. If not set, this is still treated as 'true'.
    +Encryptor.PlainText.overwrite=true
    +
    +# Do not use DES except in a legacy situations. 56-bit is way too small key size.
    +#Encryptor.EncryptionKeyLength=56
    +#Encryptor.EncryptionAlgorithm=DES
    +
    +# TripleDES is considered strong enough for most purposes.
    +#	Note:	There is also a 112-bit version of DESede. Using the 168-bit version
    +#			requires downloading the special jurisdiction policy from Sun.
    +#Encryptor.EncryptionKeyLength=168
    +#Encryptor.EncryptionAlgorithm=DESede
    +
    +Encryptor.HashAlgorithm=SHA-512
    +Encryptor.HashIterations=1024
    +Encryptor.DigitalSignatureAlgorithm=SHA1withDSA
    +Encryptor.DigitalSignatureKeyLength=1024
    +Encryptor.RandomAlgorithm=SHA1PRNG
    +Encryptor.CharacterEncoding=UTF-8
    +# Currently supported choices for JDK 1.5 and 1.6 are:
    +#	HmacSHA1 (160 bits), HmacSHA256 (256 bits), HmacSHA384 (384 bits), and
    +#	HmacSHA512 (512 bits).
    +# Note that HmacMD5 is *not* supported for the PRF used by the KDF even though
    +# these JDKs support it.
    +Encryptor.KDF.PRF=HmacSHA256
    +
    +#===========================================================================
    +# ESAPI HttpUtilties
    +#
    +# The HttpUtilities provide basic protections to HTTP requests and responses. Primarily these methods 
    +# protect against malicious data from attackers, such as unprintable characters, escaped characters,
    +# and other simple attacks. The HttpUtilities also provides utility methods for dealing with cookies,
    +# headers, and CSRF tokens.
    +#
    +# Default file upload location (remember to escape backslashes with \\)
    +HttpUtilities.UploadDir=C:\\ESAPI\\testUpload
    +# let this default to java.io.tmpdir for testing
    +#HttpUtilities.UploadTempDir=C:\\temp
    +# Force flags on cookies, if you use HttpUtilities to set cookies
    +HttpUtilities.ForceHttpOnlySession=false
    +HttpUtilities.ForceSecureSession=false
    +HttpUtilities.ForceHttpOnlyCookies=true
    +HttpUtilities.ForceSecureCookies=true
    +# Maximum size of HTTP headers
    +HttpUtilities.MaxHeaderSize=4096
    +# File upload configuration
    +HttpUtilities.ApprovedUploadExtensions=.zip,.pdf,.doc,.docx,.ppt,.pptx,.tar,.gz,.tgz,.rar,.war,.jar,.ear,.xls,.rtf,.properties,.java,.class,.txt,.xml,.jsp,.jsf,.exe,.dll
    +HttpUtilities.MaxUploadFileBytes=500000000
    +# Using UTF-8 throughout your stack is highly recommended. That includes your database driver,
    +# container, and any other technologies you may be using. Failure to do this may expose you
    +# to Unicode transcoding injection attacks. Use of UTF-8 does not hinder internationalization.
    +HttpUtilities.ResponseContentType=text/html; charset=UTF-8
    +# This is the name of the cookie used to represent the HTTP session
    +# Typically this will be the default "JSESSIONID" 
    +HttpUtilities.HttpSessionIdName=JSESSIONID
    +
    +
    +
    +#===========================================================================
    +# ESAPI Executor
    +# CHECKME - Not sure what this is used for, but surely it should be made OS independent.
    +Executor.WorkingDirectory=C:\\Windows\\Temp
    +Executor.ApprovedExecutables=C:\\Windows\\System32\\cmd.exe,C:\\Windows\\System32\\runas.exe
    +
    +
    +#===========================================================================
    +# ESAPI Logging
    +# Set the application name if these logs are combined with other applications
    +Logger.ApplicationName=ExampleApplication
    +# If you use an HTML log viewer that does not properly HTML escape log data, you can set LogEncodingRequired to true
    +Logger.LogEncodingRequired=false
    +# Determines whether ESAPI should log the application name. This might be clutter in some single-server/single-app environments.
    +Logger.LogApplicationName=true
    +# Determines whether ESAPI should log the server IP and port. This might be clutter in some single-server environments.
    +Logger.LogServerIP=true
    +# LogFileName, the name of the logging file. Provide a full directory path (e.g., C:\\ESAPI\\ESAPI_logging_file) if you
    +# want to place it in a specific directory.
    +Logger.LogFileName=ESAPI_logging_file
    +# MaxLogFileSize, the max size (in bytes) of a single log file before it cuts over to a new one (default is 10,000,000)
    +Logger.MaxLogFileSize=10000000
    +
    +
    +#===========================================================================
    +# ESAPI Intrusion Detection
    +#
    +# Each event has a base to which .count, .interval, and .action are added
    +# The IntrusionException will fire if we receive "count" events within "interval" seconds
    +# The IntrusionDetector is configurable to take the following actions: log, logout, and disable
    +#  (multiple actions separated by commas are allowed e.g. event.test.actions=log,disable
    +#
    +# Custom Events
    +# Names must start with "event." as the base
    +# Use IntrusionDetector.addEvent( "test" ) in your code to trigger "event.test" here
    +# You can also disable intrusion detection completely by changing
    +# the following parameter to true
    +#
    +IntrusionDetector.Disable=false
    +#
    +IntrusionDetector.event.test.count=2
    +IntrusionDetector.event.test.interval=10
    +IntrusionDetector.event.test.actions=disable,log
    +
    +# Exception Events
    +# All EnterpriseSecurityExceptions are registered automatically
    +# Call IntrusionDetector.getInstance().addException(e) for Exceptions that do not extend EnterpriseSecurityException
    +# Use the fully qualified classname of the exception as the base
    +
    +# any intrusion is an attack
    +IntrusionDetector.org.owasp.esapi.errors.IntrusionException.count=1
    +IntrusionDetector.org.owasp.esapi.errors.IntrusionException.interval=1
    +IntrusionDetector.org.owasp.esapi.errors.IntrusionException.actions=log,disable,logout
    +
    +# for test purposes
    +# CHECKME: Shouldn't there be something in the property name itself that designates
    +#		   that these are for testing???
    +IntrusionDetector.org.owasp.esapi.errors.IntegrityException.count=10
    +IntrusionDetector.org.owasp.esapi.errors.IntegrityException.interval=5
    +IntrusionDetector.org.owasp.esapi.errors.IntegrityException.actions=log,disable,logout
    +
    +# rapid validation errors indicate scans or attacks in progress
    +# org.owasp.esapi.errors.ValidationException.count=10
    +# org.owasp.esapi.errors.ValidationException.interval=10
    +# org.owasp.esapi.errors.ValidationException.actions=log,logout
    +
    +# sessions jumping between hosts indicates session hijacking
    +IntrusionDetector.org.owasp.esapi.errors.AuthenticationHostException.count=2
    +IntrusionDetector.org.owasp.esapi.errors.AuthenticationHostException.interval=10
    +IntrusionDetector.org.owasp.esapi.errors.AuthenticationHostException.actions=log,logout
    +
    +
    +#===========================================================================
    +# ESAPI Validation
    +#
    +# The ESAPI Validator works on regular expressions with defined names. You can define names
    +# either here, or you may define application specific patterns in a separate file defined below.
    +# This allows enterprises to specify both organizational standards as well as application specific
    +# validation rules.
    +#
    +Validator.ConfigurationFile.MultiValued=true
    +Validator.ConfigurationFile="validation-test,comma.properties",validation-test1.properties,validation-test2.properties
    +
    +# Validators used by ESAPI
    +Validator.AccountName=^[a-zA-Z0-9]{3,20}$
    +Validator.SystemCommand=^[a-zA-Z\\-\\/]{1,64}$
    +Validator.RoleName=^[a-z]{1,20}$
    +Validator.Redirect=^\\/test.*$
    +
    +# Global HTTP Validation Rules
    +# Values with Base64 encoded data (e.g. encrypted state) will need at least [a-zA-Z0-9\/+=]
    +Validator.HTTPScheme=^(http|https)$
    +Validator.HTTPServerName=^[a-zA-Z0-9_.\\-]*$
    +Validator.HTTPCookieName=^[a-zA-Z0-9\\-_]{1,32}$
    +Validator.HTTPCookieValue=^[a-zA-Z0-9\\-\\/+=_ ]*$
    +Validator.HTTPHeaderName=^[a-zA-Z0-9\\-_]{1,32}$
    +Validator.HTTPHeaderValue=^[a-zA-Z0-9()\\-=\\*\\.\\?;,+\\/:&_ ]*$
    +Validator.HTTPServletPath=^[a-zA-Z0-9.\\-\\/_]*$
    +Validator.HTTPPath=^[a-zA-Z0-9.\\-_]*$
    +Validator.HTTPURL=^.*$
    +Validator.HTTPJSESSIONID=^[A-Z0-9]{10,30}$
    +
    +# Contributed by Fraenku@gmx.ch
    +# Googlecode Issue 116 (http://code.google.com/p/owasp-esapi-java/issues/detail?id=116)
    +Validator.HTTPParameterName=^[a-zA-Z0-9_\\-]{1,32}$
    +Validator.HTTPParameterValue=^[\\p{L}\\p{N}.\\-/+=_ !$*?@]{0,1000}$
    +Validator.HTTPContextPath=^/[a-zA-Z0-9.\\-_]*$
    +Validator.HTTPQueryString=^([a-zA-Z0-9_\\-]{1,32}=[\\p{L}\\p{N}.\\-/+=_ !$*?@%]*&?)*$
    +Validator.HTTPURI=^/([a-zA-Z0-9.\\-_]*/?)*$
    +
    +
    +# Validation of file related input
    +Validator.FileName=^[a-zA-Z0-9!@#$%^&{}\\[\\]()_+\\-=,.~'` ]{1,255}$
    +Validator.DirectoryName=^[a-zA-Z0-9:/\\\\!@#$%^&{}\\[\\]()_+\\-=,.~'` ]{1,255}$
    +
    +# Validation of dates. Controls whether or not 'lenient' dates are accepted.
    +# See DataFormat.setLenient(boolean flag) for further details.
     Validator.AcceptLenientDates=false
    \ No newline at end of file
    
    From c52a1f7981194a828bdc064268c349a950dd720f Mon Sep 17 00:00:00 2001
    From: kwwall 
    Date: Tue, 19 Jan 2016 01:12:42 -0500
    Subject: [PATCH 0279/1069] Address issue 356.
    
    ---
     ...SAPI-SingleValidatorFileChecker.properties | 930 +++++++++---------
     1 file changed, 465 insertions(+), 465 deletions(-)
    
    diff --git a/src/test/resources/esapi/ESAPI-SingleValidatorFileChecker.properties b/src/test/resources/esapi/ESAPI-SingleValidatorFileChecker.properties
    index 95cdb0056..d741f3608 100644
    --- a/src/test/resources/esapi/ESAPI-SingleValidatorFileChecker.properties
    +++ b/src/test/resources/esapi/ESAPI-SingleValidatorFileChecker.properties
    @@ -1,466 +1,466 @@
    -#
    -# OWASP Enterprise Security API (ESAPI) Properties file -- TEST Version
    -# 
    -# This file is part of the Open Web Application Security Project (OWASP)
    -# Enterprise Security API (ESAPI) project. For details, please see
    -# http://www.owasp.org/index.php/ESAPI.
    -#
    -# Copyright (c) 2008,2009 - The OWASP Foundation
    -#
    -# DISCUSS: This may cause a major backwards compatibility issue, etc. but
    -#		   from a name space perspective, we probably should have prefaced
    -#		   all the property names with ESAPI or at least OWASP. Otherwise
    -#		   there could be problems is someone loads this properties file into
    -#		   the System properties.  We could also put this file into the
    -#		   esapi.jar file (perhaps as a ResourceBundle) and then allow an external
    -#		   ESAPI properties be defined that would overwrite these defaults.
    -#		   That keeps the application's properties relatively simple as usually
    -#		   they will only want to override a few properties. If looks like we
    -#		   already support multiple override levels of this in the
    -#		   DefaultSecurityConfiguration class, but I'm suggesting placing the
    -#		   defaults in the esapi.jar itself. That way, if the jar is signed,
    -#		   we could detect if those properties had been tampered with. (The
    -#		   code to check the jar signatures is pretty simple... maybe 70-90 LOC,
    -#		   but off course there is an execution penalty (similar to the way
    -#		   that the separate sunjce.jar used to be when a class from it was
    -#		   first loaded). Thoughts?
    -###############################################################################
    -#
    -# WARNING: Operating system protection should be used to lock down the .esapi
    -# resources directory and all the files inside and all the directories all the
    -# way up to the root directory of the file system.  Note that if you are using
    -# file-based implementations, that some files may need to be read-write as they
    -# get updated dynamically.
    -#
    -# Before using, be sure to update the MasterKey and MasterSalt as described below.
    -# N.B.: If you had stored data that you have previously encrypted with ESAPI 1.4,
    -#		you *must* FIRST decrypt it using ESAPI 1.4 and then (if so desired)
    -#		re-encrypt it with ESAPI 2.0. If you fail to do this, you will NOT be
    -#		able to decrypt your data with ESAPI 2.0.
    -#
    -#		YOU HAVE BEEN WARNED!!! More details are in the ESAPI 2.0 Release Notes.
    -#
    -#===========================================================================
    -# ESAPI Configuration
    -#
    -# If true, then print all the ESAPI properties set here when they are loaded.
    -# If false, they are not printed. Useful to reduce output when running JUnit tests.
    -# If you need to troubleshoot a properties related problem, turning this on may help,
    -# but we leave it off for running JUnit tests. (It will be 'true' in the one delivered
    -# as part of production ESAPI, mostly for backward compatibility.)
    -ESAPI.printProperties=false
    -
    -# ESAPI is designed to be easily extensible. You can use the reference implementation
    -# or implement your own providers to take advantage of your enterprise's security
    -# infrastructure. The functions in ESAPI are referenced using the ESAPI locator, like:
    -#
    -#    String ciphertext =
    -#		ESAPI.encryptor().encrypt("Secret message");   // Deprecated in 2.0
    -#    CipherText cipherText =
    -#		ESAPI.encryptor().encrypt(new PlainText("Secret message")); // Preferred
    -#
    -# Below you can specify the classname for the provider that you wish to use in your
    -# application. The only requirement is that it implement the appropriate ESAPI interface.
    -# This allows you to switch security implementations in the future without rewriting the
    -# entire application.
    -#
    -# ExperimentalAccessController requires ESAPI-AccessControlPolicy.xml in .esapi directory
    -ESAPI.AccessControl=org.owasp.esapi.reference.DefaultAccessController
    -# FileBasedAuthenticator requires users.txt file in .esapi directory
    -ESAPI.Authenticator=org.owasp.esapi.reference.FileBasedAuthenticator
    -ESAPI.Encoder=org.owasp.esapi.reference.DefaultEncoder
    -ESAPI.Encryptor=org.owasp.esapi.reference.crypto.JavaEncryptor
    -
    -ESAPI.Executor=org.owasp.esapi.reference.DefaultExecutor
    -ESAPI.HTTPUtilities=org.owasp.esapi.reference.DefaultHTTPUtilities
    -ESAPI.IntrusionDetector=org.owasp.esapi.reference.DefaultIntrusionDetector
    -# Log4JFactory Requires log4j.xml or log4j.properties in classpath - http://www.laliluna.de/log4j-tutorial.html
    -ESAPI.Logger=org.owasp.esapi.reference.Log4JLogFactory
    -#ESAPI.Logger=org.owasp.esapi.reference.JavaLogFactory
    -#ESAPI.Logger=org.owasp.esapi.reference.ExampleExtendedLog4JLogFactory
    -ESAPI.Randomizer=org.owasp.esapi.reference.DefaultRandomizer
    -ESAPI.Validator=org.owasp.esapi.reference.DefaultValidator
    -
    -#===========================================================================
    -# ESAPI Authenticator
    -#
    -Authenticator.AllowedLoginAttempts=3
    -Authenticator.MaxOldPasswordHashes=13
    -Authenticator.UsernameParameterName=username
    -Authenticator.PasswordParameterName=password
    -# RememberTokenDuration (in days)
    -Authenticator.RememberTokenDuration=14
    -# Session Timeouts (in minutes)
    -Authenticator.IdleTimeoutDuration=20
    -Authenticator.AbsoluteTimeoutDuration=120
    -
    -#===========================================================================
    -# ESAPI Encoder
    -#
    -# ESAPI canonicalizes input before validation to prevent bypassing filters with encoded attacks.
    -# Failure to canonicalize input is a very common mistake when implementing validation schemes.
    -# Canonicalization is automatic when using the ESAPI Validator, but you can also use the
    -# following code to canonicalize data.
    -#
    -#      ESAPI.Encoder().canonicalize( "%22hello world"" );
    -#  
    -# Multiple encoding is when a single encoding format is applied multiple times. Allowing
    -# multiple encoding is strongly discouraged.
    -Encoder.AllowMultipleEncoding=false
    -
    -# Mixed encoding is when multiple different encoding formats are applied, or when
    -# multiple formats are nested. Allowing multiple encoding is strongly discouraged.
    -Encoder.AllowMixedEncoding=false
    -
    -# The default list of codecs to apply when canonicalizing untrusted data. The list should include the codecs
    -# for all downstream interpreters or decoders. For example, if the data is likely to end up in a URL, HTML, or
    -# inside JavaScript, then the list of codecs below is appropriate. The order of the list is not terribly important.
    -Encoder.DefaultCodecList=HTMLEntityCodec,PercentCodec,JavaScriptCodec
    -
    -
    -#===========================================================================
    -# ESAPI Encryption
    -#
    -# The ESAPI Encryptor provides basic cryptographic functions with a simplified API.
    -# To get started, generate a new key using java -classpath esapi.jar org.owasp.esapi.reference.crypto.JavaEncryptor
    -# There is not currently any support for key rotation, so be careful when changing your key and salt as it
    -# will invalidate all signed, encrypted, and hashed data.
    -#
    -# WARNING: Not all combinations of algorithms and key lengths are supported.
    -# If you choose to use a key length greater than 128, you MUST download the
    -# unlimited strength policy files and install in the lib directory of your JRE/JDK.
    -# See http://java.sun.com/javase/downloads/index.jsp for more information.
    -#
    -# Backward compatibility with ESAPI Java 1.4 is supported by the two deprecated API
    -# methods, Encryptor.encrypt(String) and Encryptor.decrypt(String). However, whenever
    -# possible, these methods should be avoided as they use ECB cipher mode, which in almost
    -# all circumstances a poor choice because of it's weakness. CBC cipher mode is the default
    -# for the new Encryptor encrypt / decrypt methods for ESAPI Java 2.0.  In general, you
    -# should only use this compatibility setting if you have persistent data encrypted with
    -# version 1.4 and even then, you should ONLY set this compatibility mode UNTIL
    -# you have decrypted all of your old encrypted data and then re-encrypted it with
    -# ESAPI 2.0 using CBC mode. If you have some reason to mix the deprecated 1.4 mode
    -# with the new 2.0 methods, make sure that you use the same cipher algorithm for both
    -# (256-bit AES was the default for 1.4; 128-bit is the default for 2.0; see below for
    -# more details.) Otherwise, you will have to use the new 2.0 encrypt / decrypt methods
    -# where you can specify a SecretKey. (Note that if you are using the 256-bit AES,
    -# that requires downloading the special jurisdiction policy files mentioned above.)
    -#
    -#		***** IMPORTANT: These are for JUnit testing. Test files may have been
    -#						 encrypted using these values so do not change these or
    -#						 those tests will fail. The version under
    -#							src/main/resources/.esapi/ESAPI.properties
    -#						 will be delivered with Encryptor.MasterKey and
    -#						 Encryptor.MasterSalt set to the empty string.
    -#
    -#						 FINAL NOTE:
    -#                           If Maven changes these when run, that needs to be fixed.
    -#       256-bit key... requires unlimited strength jurisdiction policy files
    -### Encryptor.MasterKey=pJhlri8JbuFYDgkqtHmm9s0Ziug2PE7ovZDyEPm4j14=
    -#       128-bit key
    -Encryptor.MasterKey=a6H9is3hEVGKB4Jut+lOVA==
    -Encryptor.MasterSalt=SbftnvmEWD5ZHHP+pX3fqugNysc=
    -# Encryptor.MasterSalt=
    -
    -# Provides the default JCE provider that ESAPI will "prefer" for its symmetric
    -# encryption and hashing. (That is it will look to this provider first, but it
    -# will defer to other providers if the requested algorithm is not implemented
    -# by this provider.) If left unset, ESAPI will just use your Java VM's current
    -# preferred JCE provider, which is generally set in the file
    -# "$JAVA_HOME/jre/lib/security/java.security".
    -#
    -# The main intent of this is to allow ESAPI symmetric encryption to be
    -# used with a FIPS 140-2 compliant crypto-module. For details, see the section
    -# "Using ESAPI Symmetric Encryption with FIPS 140-2 Cryptographic Modules" in
    -# the ESAPI 2.0 Symmetric Encryption User Guide, at:
    -# http://owasp-esapi-java.googlecode.com/svn/trunk/documentation/esapi4java-core-2.0-symmetric-crypto-user-guide.html
    -# However, this property also allows you to easily use an alternate JCE provider
    -# such as "Bouncy Castle" without having to make changes to "java.security".
    -# See Javadoc for SecurityProviderLoader for further details. If you wish to use
    -# a provider that is not known to SecurityProviderLoader, you may specify the
    -# fully-qualified class name of the JCE provider class that implements
    -# java.security.Provider. If the name contains a '.', this is interpreted as
    -# a fully-qualified class name that implements java.security.Provider.
    -#
    -# NOTE: Setting this property has the side-effect of changing it in your application
    -#       as well, so if you are using JCE in your application directly rather than
    -#       through ESAPI (you wouldn't do that, would you? ;-), it will change the
    -#       preferred JCE provider there as well.
    -#
    -# Default: Keeps the JCE provider set to whatever JVM sets it to.
    -Encryptor.PreferredJCEProvider=
    -
    -# AES is the most widely used and strongest encryption algorithm. This
    -# should agree with your Encryptor.CipherTransformation property.
    -# By default, ESAPI Java 1.4 uses "PBEWithMD5AndDES" and which is
    -# very weak. It is essentially a password-based encryption key, hashed
    -# with MD5 around 1K times and then encrypted with the weak DES algorithm
    -# (56-bits) using ECB mode and an unspecified padding (it is
    -# JCE provider specific, but most likely "NoPadding"). However, 2.0 uses
    -# "AES/CBC/PKCSPadding". If you want to change these, change them here.
    -# Warning: This property does not control the default reference implementation for
    -#		   ESAPI 2.0 using JavaEncryptor. Also, this property will be dropped
    -#		   in the future.
    -# @deprecated
    -Encryptor.EncryptionAlgorithm=AES
    -#		For ESAPI Java 2.0 - New encrypt / decrypt methods use this.
    -Encryptor.CipherTransformation=AES/CBC/PKCS5Padding
    -
    -# Applies to ESAPI 2.0 and later only!
    -# Comma-separated list of cipher modes that provide *BOTH*
    -# confidentiality *AND* message authenticity. (NIST refers to such cipher
    -# modes as "combined modes" so that's what we shall call them.) If any of these
    -# cipher modes are used then no MAC is calculated and stored
    -# in the CipherText upon encryption. Likewise, if one of these
    -# cipher modes is used with decryption, no attempt will be made
    -# to validate the MAC contained in the CipherText object regardless
    -# of whether it contains one or not. Since the expectation is that
    -# these cipher modes support support message authenticity already,
    -# injecting a MAC in the CipherText object would be at best redundant.
    -#
    -# Note that as of JDK 1.5, the SunJCE provider does not support *any*
    -# of these cipher modes. Of these listed, only GCM and CCM are currently
    -# NIST approved. YMMV for other JCE providers. E.g., Bouncy Castle supports
    -# GCM and CCM with "NoPadding" mode, but not with "PKCS5Padding" or other
    -# padding modes.
    -Encryptor.cipher_modes.combined_modes=GCM,CCM,IAPM,EAX,OCB,CWC
    -
    -# Applies to ESAPI 2.0 and later only!
    -# Additional cipher modes allowed for ESAPI 2.0 encryption. These
    -# cipher modes are in _addition_ to those specified by the property
    -# 'Encryptor.cipher_modes.combined_modes'.
    -# Note: We will add support for streaming modes like CFB & OFB once
    -# we add support for 'specified' to the property 'Encryptor.ChooseIVMethod'
    -# (probably in ESAPI 2.1).
    -#
    -#	IMPORTANT NOTE:	In the official ESAPI.properties we do *NOT* include ECB
    -#					here as this is an extremely weak mode. However, we *must*
    -#					allow it here so we can test ECB mode. That is important
    -#					since the logic is somewhat different (i.e., ECB mode does
    -#					not use an IV).
    -# DISCUSS: Better name?
    -#	NOTE: ECB added only for testing purposes. Don't try this at home!
    -Encryptor.cipher_modes.additional_allowed=CBC,ECB
    -
    -# 128-bit is almost always sufficient and appears to be more resistant to
    -# related key attacks than is 256-bit AES. Use '_' to use default key size
    -# for cipher algorithms (where it makes sense because the algorithm supports
    -# a variable key size). Key length must agree to what's provided as the
    -# cipher transformation, otherwise this will be ignored after logging a
    -# warning.
    -#
    -# NOTE: This is what applies BOTH ESAPI 1.4 and 2.0. See warning above about mixing!
    -Encryptor.EncryptionKeyLength=128
    -
    -# Because 2.0 uses CBC mode by default, it requires an initialization vector (IV).
    -# (All cipher modes except ECB require an IV.) There are two choices: we can either
    -# use a fixed IV known to both parties or allow ESAPI to choose a random IV. While
    -# the IV does not need to be hidden from adversaries, it is important that the
    -# adversary not be allowed to choose it. Also, random IVs are generally much more
    -# secure than fixed IVs. (In fact, it is essential that feed-back cipher modes
    -# such as CFB and OFB use a different IV for each encryption with a given key so
    -# in such cases, random IVs are much preferred. By default, ESAPI 2.0 uses random
    -# IVs. If you wish to use 'fixed' IVs, set 'Encryptor.ChooseIVMethod=fixed' and
    -# uncomment the Encryptor.fixedIV.
    -#
    -# Valid values:		random|fixed|specified		'specified' not yet implemented; planned for 2.1
    -Encryptor.ChooseIVMethod=random
    -# If you choose to use a fixed IV, then you must place a fixed IV here that
    -# is known to all others who are sharing your secret key. The format should
    -# be a hex string that is the same length as the cipher block size for the
    -# cipher algorithm that you are using. The following is an example for AES
    -# from an AES test vector for AES-128/CBC as described in:
    -# NIST Special Publication 800-38A (2001 Edition)
    -# "Recommendation for Block Cipher Modes of Operation".
    -# (Note that the block size for AES is 16 bytes == 128 bits.)
    -#
    -Encryptor.fixedIV=0x000102030405060708090a0b0c0d0e0f
    -
    -# Whether or not CipherText should use a message authentication code (MAC) with it.
    -# This prevents an adversary from altering the IV as well as allowing a more
    -# fool-proof way of determining the decryption failed because of an incorrect
    -# key being supplied. This refers to the "separate" MAC calculated and stored
    -# in CipherText, not part of any MAC that is calculated as a result of a
    -# "combined mode" cipher mode.
    -#
    -# If you are using ESAPI with a FIPS 140-2 cryptographic module, you *must* also
    -# set this property to false.
    -Encryptor.CipherText.useMAC=true
    -
    -# Whether or not the PlainText object may be overwritten and then marked
    -# eligible for garbage collection. If not set, this is still treated as 'true'.
    -Encryptor.PlainText.overwrite=true
    -
    -# Do not use DES except in a legacy situations. 56-bit is way too small key size.
    -#Encryptor.EncryptionKeyLength=56
    -#Encryptor.EncryptionAlgorithm=DES
    -
    -# TripleDES is considered strong enough for most purposes.
    -#	Note:	There is also a 112-bit version of DESede. Using the 168-bit version
    -#			requires downloading the special jurisdiction policy from Sun.
    -#Encryptor.EncryptionKeyLength=168
    -#Encryptor.EncryptionAlgorithm=DESede
    -
    -Encryptor.HashAlgorithm=SHA-512
    -Encryptor.HashIterations=1024
    -Encryptor.DigitalSignatureAlgorithm=SHA1withDSA
    -Encryptor.DigitalSignatureKeyLength=1024
    -Encryptor.RandomAlgorithm=SHA1PRNG
    -Encryptor.CharacterEncoding=UTF-8
    -# Currently supported choices for JDK 1.5 and 1.6 are:
    -#	HmacSHA1 (160 bits), HmacSHA256 (256 bits), HmacSHA384 (384 bits), and
    -#	HmacSHA512 (512 bits).
    -# Note that HmacMD5 is *not* supported for the PRF used by the KDF even though
    -# these JDKs support it.
    -Encryptor.KDF.PRF=HmacSHA256
    -
    -#===========================================================================
    -# ESAPI HttpUtilties
    -#
    -# The HttpUtilities provide basic protections to HTTP requests and responses. Primarily these methods 
    -# protect against malicious data from attackers, such as unprintable characters, escaped characters,
    -# and other simple attacks. The HttpUtilities also provides utility methods for dealing with cookies,
    -# headers, and CSRF tokens.
    -#
    -# Default file upload location (remember to escape backslashes with \\)
    -HttpUtilities.UploadDir=C:\\ESAPI\\testUpload
    -# let this default to java.io.tmpdir for testing
    -#HttpUtilities.UploadTempDir=C:\\temp
    -# Force flags on cookies, if you use HttpUtilities to set cookies
    -HttpUtilities.ForceHttpOnlySession=false
    -HttpUtilities.ForceSecureSession=false
    -HttpUtilities.ForceHttpOnlyCookies=true
    -HttpUtilities.ForceSecureCookies=true
    -# Maximum size of HTTP headers
    -HttpUtilities.MaxHeaderSize=4096
    -# File upload configuration
    -HttpUtilities.ApprovedUploadExtensions=.zip,.pdf,.doc,.docx,.ppt,.pptx,.tar,.gz,.tgz,.rar,.war,.jar,.ear,.xls,.rtf,.properties,.java,.class,.txt,.xml,.jsp,.jsf,.exe,.dll
    -HttpUtilities.MaxUploadFileBytes=500000000
    -# Using UTF-8 throughout your stack is highly recommended. That includes your database driver,
    -# container, and any other technologies you may be using. Failure to do this may expose you
    -# to Unicode transcoding injection attacks. Use of UTF-8 does not hinder internationalization.
    -HttpUtilities.ResponseContentType=text/html; charset=UTF-8
    -# This is the name of the cookie used to represent the HTTP session
    -# Typically this will be the default "JSESSIONID" 
    -HttpUtilities.HttpSessionIdName=JSESSIONID
    -
    -
    -
    -#===========================================================================
    -# ESAPI Executor
    -# CHECKME - Not sure what this is used for, but surely it should be made OS independent.
    -Executor.WorkingDirectory=C:\\Windows\\Temp
    -Executor.ApprovedExecutables=C:\\Windows\\System32\\cmd.exe,C:\\Windows\\System32\\runas.exe
    -
    -
    -#===========================================================================
    -# ESAPI Logging
    -# Set the application name if these logs are combined with other applications
    -Logger.ApplicationName=ExampleApplication
    -# If you use an HTML log viewer that does not properly HTML escape log data, you can set LogEncodingRequired to true
    -Logger.LogEncodingRequired=false
    -# Determines whether ESAPI should log the application name. This might be clutter in some single-server/single-app environments.
    -Logger.LogApplicationName=true
    -# Determines whether ESAPI should log the server IP and port. This might be clutter in some single-server environments.
    -Logger.LogServerIP=true
    -# LogFileName, the name of the logging file. Provide a full directory path (e.g., C:\\ESAPI\\ESAPI_logging_file) if you
    -# want to place it in a specific directory.
    -Logger.LogFileName=ESAPI_logging_file
    -# MaxLogFileSize, the max size (in bytes) of a single log file before it cuts over to a new one (default is 10,000,000)
    -Logger.MaxLogFileSize=10000000
    -
    -
    -#===========================================================================
    -# ESAPI Intrusion Detection
    -#
    -# Each event has a base to which .count, .interval, and .action are added
    -# The IntrusionException will fire if we receive "count" events within "interval" seconds
    -# The IntrusionDetector is configurable to take the following actions: log, logout, and disable
    -#  (multiple actions separated by commas are allowed e.g. event.test.actions=log,disable
    -#
    -# Custom Events
    -# Names must start with "event." as the base
    -# Use IntrusionDetector.addEvent( "test" ) in your code to trigger "event.test" here
    -# You can also disable intrusion detection completely by changing
    -# the following parameter to true
    -#
    -IntrusionDetector.Disable=false
    -#
    -IntrusionDetector.event.test.count=2
    -IntrusionDetector.event.test.interval=10
    -IntrusionDetector.event.test.actions=disable,log
    -
    -# Exception Events
    -# All EnterpriseSecurityExceptions are registered automatically
    -# Call IntrusionDetector.getInstance().addException(e) for Exceptions that do not extend EnterpriseSecurityException
    -# Use the fully qualified classname of the exception as the base
    -
    -# any intrusion is an attack
    -IntrusionDetector.org.owasp.esapi.errors.IntrusionException.count=1
    -IntrusionDetector.org.owasp.esapi.errors.IntrusionException.interval=1
    -IntrusionDetector.org.owasp.esapi.errors.IntrusionException.actions=log,disable,logout
    -
    -# for test purposes
    -# CHECKME: Shouldn't there be something in the property name itself that designates
    -#		   that these are for testing???
    -IntrusionDetector.org.owasp.esapi.errors.IntegrityException.count=10
    -IntrusionDetector.org.owasp.esapi.errors.IntegrityException.interval=5
    -IntrusionDetector.org.owasp.esapi.errors.IntegrityException.actions=log,disable,logout
    -
    -# rapid validation errors indicate scans or attacks in progress
    -# org.owasp.esapi.errors.ValidationException.count=10
    -# org.owasp.esapi.errors.ValidationException.interval=10
    -# org.owasp.esapi.errors.ValidationException.actions=log,logout
    -
    -# sessions jumping between hosts indicates session hijacking
    -IntrusionDetector.org.owasp.esapi.errors.AuthenticationHostException.count=2
    -IntrusionDetector.org.owasp.esapi.errors.AuthenticationHostException.interval=10
    -IntrusionDetector.org.owasp.esapi.errors.AuthenticationHostException.actions=log,logout
    -
    -
    -#===========================================================================
    -# ESAPI Validation
    -#
    -# The ESAPI Validator works on regular expressions with defined names. You can define names
    -# either here, or you may define application specific patterns in a separate file defined below.
    -# This allows enterprises to specify both organizational standards as well as application specific
    -# validation rules.
    -#
    -#Validator.ConfigurationFile.MultiValued=false
    -Validator.ConfigurationFile=validation-test1.properties
    -
    -# Validators used by ESAPI
    -Validator.AccountName=^[a-zA-Z0-9]{3,20}$
    -Validator.SystemCommand=^[a-zA-Z\\-\\/]{1,64}$
    -Validator.RoleName=^[a-z]{1,20}$
    -Validator.Redirect=^\\/test.*$
    -
    -# Global HTTP Validation Rules
    -# Values with Base64 encoded data (e.g. encrypted state) will need at least [a-zA-Z0-9\/+=]
    -Validator.HTTPScheme=^(http|https)$
    -Validator.HTTPServerName=^[a-zA-Z0-9_.\\-]*$
    -Validator.HTTPCookieName=^[a-zA-Z0-9\\-_]{1,32}$
    -Validator.HTTPCookieValue=^[a-zA-Z0-9\\-\\/+=_ ]*$
    -Validator.HTTPHeaderName=^[a-zA-Z0-9\\-_]{1,32}$
    -Validator.HTTPHeaderValue=^[a-zA-Z0-9()\\-=\\*\\.\\?;,+\\/:&_ ]*$
    -Validator.HTTPServletPath=^[a-zA-Z0-9.\\-\\/_]*$
    -Validator.HTTPPath=^[a-zA-Z0-9.\\-_]*$
    -Validator.HTTPURL=^.*$
    -Validator.HTTPJSESSIONID=^[A-Z0-9]{10,30}$
    -
    -# Contributed by Fraenku@gmx.ch
    -# Googlecode Issue 116 (http://code.google.com/p/owasp-esapi-java/issues/detail?id=116)
    -Validator.HTTPParameterName=^[a-zA-Z0-9_\\-]{1,32}$
    -Validator.HTTPParameterValue=^[\\p{L}\\p{N}.\\-/+=_ !$*?@]{0,1000}$
    -Validator.HTTPContextPath=^/[a-zA-Z0-9.\\-_]*$
    -Validator.HTTPQueryString=^([a-zA-Z0-9_\\-]{1,32}=[\\p{L}\\p{N}.\\-/+=_ !$*?@%]*&?)*$
    -Validator.HTTPURI=^/([a-zA-Z0-9.\\-_]*/?)*$
    -
    -
    -# Validation of file related input
    -Validator.FileName=^[a-zA-Z0-9!@#$%^&{}\\[\\]()_+\\-=,.~'` ]{1,255}$
    -Validator.DirectoryName=^[a-zA-Z0-9:/\\\\!@#$%^&{}\\[\\]()_+\\-=,.~'` ]{1,255}$
    -
    -# Validation of dates. Controls whether or not 'lenient' dates are accepted.
    -# See DataFormat.setLenient(boolean flag) for further details.
    +#
    +# OWASP Enterprise Security API (ESAPI) Properties file -- TEST Version
    +# 
    +# This file is part of the Open Web Application Security Project (OWASP)
    +# Enterprise Security API (ESAPI) project. For details, please see
    +# http://www.owasp.org/index.php/ESAPI.
    +#
    +# Copyright (c) 2008,2009 - The OWASP Foundation
    +#
    +# DISCUSS: This may cause a major backwards compatibility issue, etc. but
    +#		   from a name space perspective, we probably should have prefaced
    +#		   all the property names with ESAPI or at least OWASP. Otherwise
    +#		   there could be problems is someone loads this properties file into
    +#		   the System properties.  We could also put this file into the
    +#		   esapi.jar file (perhaps as a ResourceBundle) and then allow an external
    +#		   ESAPI properties be defined that would overwrite these defaults.
    +#		   That keeps the application's properties relatively simple as usually
    +#		   they will only want to override a few properties. If looks like we
    +#		   already support multiple override levels of this in the
    +#		   DefaultSecurityConfiguration class, but I'm suggesting placing the
    +#		   defaults in the esapi.jar itself. That way, if the jar is signed,
    +#		   we could detect if those properties had been tampered with. (The
    +#		   code to check the jar signatures is pretty simple... maybe 70-90 LOC,
    +#		   but off course there is an execution penalty (similar to the way
    +#		   that the separate sunjce.jar used to be when a class from it was
    +#		   first loaded). Thoughts?
    +###############################################################################
    +#
    +# WARNING: Operating system protection should be used to lock down the .esapi
    +# resources directory and all the files inside and all the directories all the
    +# way up to the root directory of the file system.  Note that if you are using
    +# file-based implementations, that some files may need to be read-write as they
    +# get updated dynamically.
    +#
    +# Before using, be sure to update the MasterKey and MasterSalt as described below.
    +# N.B.: If you had stored data that you have previously encrypted with ESAPI 1.4,
    +#		you *must* FIRST decrypt it using ESAPI 1.4 and then (if so desired)
    +#		re-encrypt it with ESAPI 2.0. If you fail to do this, you will NOT be
    +#		able to decrypt your data with ESAPI 2.0.
    +#
    +#		YOU HAVE BEEN WARNED!!! More details are in the ESAPI 2.0 Release Notes.
    +#
    +#===========================================================================
    +# ESAPI Configuration
    +#
    +# If true, then print all the ESAPI properties set here when they are loaded.
    +# If false, they are not printed. Useful to reduce output when running JUnit tests.
    +# If you need to troubleshoot a properties related problem, turning this on may help,
    +# but we leave it off for running JUnit tests. (It will be 'true' in the one delivered
    +# as part of production ESAPI, mostly for backward compatibility.)
    +ESAPI.printProperties=false
    +
    +# ESAPI is designed to be easily extensible. You can use the reference implementation
    +# or implement your own providers to take advantage of your enterprise's security
    +# infrastructure. The functions in ESAPI are referenced using the ESAPI locator, like:
    +#
    +#    String ciphertext =
    +#		ESAPI.encryptor().encrypt("Secret message");   // Deprecated in 2.0
    +#    CipherText cipherText =
    +#		ESAPI.encryptor().encrypt(new PlainText("Secret message")); // Preferred
    +#
    +# Below you can specify the classname for the provider that you wish to use in your
    +# application. The only requirement is that it implement the appropriate ESAPI interface.
    +# This allows you to switch security implementations in the future without rewriting the
    +# entire application.
    +#
    +# ExperimentalAccessController requires ESAPI-AccessControlPolicy.xml in .esapi directory
    +ESAPI.AccessControl=org.owasp.esapi.reference.DefaultAccessController
    +# FileBasedAuthenticator requires users.txt file in .esapi directory
    +ESAPI.Authenticator=org.owasp.esapi.reference.FileBasedAuthenticator
    +ESAPI.Encoder=org.owasp.esapi.reference.DefaultEncoder
    +ESAPI.Encryptor=org.owasp.esapi.reference.crypto.JavaEncryptor
    +
    +ESAPI.Executor=org.owasp.esapi.reference.DefaultExecutor
    +ESAPI.HTTPUtilities=org.owasp.esapi.reference.DefaultHTTPUtilities
    +ESAPI.IntrusionDetector=org.owasp.esapi.reference.DefaultIntrusionDetector
    +# Log4JFactory Requires log4j.xml or log4j.properties in classpath - http://www.laliluna.de/log4j-tutorial.html
    +ESAPI.Logger=org.owasp.esapi.reference.Log4JLogFactory
    +#ESAPI.Logger=org.owasp.esapi.reference.JavaLogFactory
    +#ESAPI.Logger=org.owasp.esapi.reference.ExampleExtendedLog4JLogFactory
    +ESAPI.Randomizer=org.owasp.esapi.reference.DefaultRandomizer
    +ESAPI.Validator=org.owasp.esapi.reference.DefaultValidator
    +
    +#===========================================================================
    +# ESAPI Authenticator
    +#
    +Authenticator.AllowedLoginAttempts=3
    +Authenticator.MaxOldPasswordHashes=13
    +Authenticator.UsernameParameterName=username
    +Authenticator.PasswordParameterName=password
    +# RememberTokenDuration (in days)
    +Authenticator.RememberTokenDuration=14
    +# Session Timeouts (in minutes)
    +Authenticator.IdleTimeoutDuration=20
    +Authenticator.AbsoluteTimeoutDuration=120
    +
    +#===========================================================================
    +# ESAPI Encoder
    +#
    +# ESAPI canonicalizes input before validation to prevent bypassing filters with encoded attacks.
    +# Failure to canonicalize input is a very common mistake when implementing validation schemes.
    +# Canonicalization is automatic when using the ESAPI Validator, but you can also use the
    +# following code to canonicalize data.
    +#
    +#      ESAPI.Encoder().canonicalize( "%22hello world"" );
    +#  
    +# Multiple encoding is when a single encoding format is applied multiple times. Allowing
    +# multiple encoding is strongly discouraged.
    +Encoder.AllowMultipleEncoding=false
    +
    +# Mixed encoding is when multiple different encoding formats are applied, or when
    +# multiple formats are nested. Allowing multiple encoding is strongly discouraged.
    +Encoder.AllowMixedEncoding=false
    +
    +# The default list of codecs to apply when canonicalizing untrusted data. The list should include the codecs
    +# for all downstream interpreters or decoders. For example, if the data is likely to end up in a URL, HTML, or
    +# inside JavaScript, then the list of codecs below is appropriate. The order of the list is not terribly important.
    +Encoder.DefaultCodecList=HTMLEntityCodec,PercentCodec,JavaScriptCodec
    +
    +
    +#===========================================================================
    +# ESAPI Encryption
    +#
    +# The ESAPI Encryptor provides basic cryptographic functions with a simplified API.
    +# To get started, generate a new key using java -classpath esapi.jar org.owasp.esapi.reference.crypto.JavaEncryptor
    +# There is not currently any support for key rotation, so be careful when changing your key and salt as it
    +# will invalidate all signed, encrypted, and hashed data.
    +#
    +# WARNING: Not all combinations of algorithms and key lengths are supported.
    +# If you choose to use a key length greater than 128, you MUST download the
    +# unlimited strength policy files and install in the lib directory of your JRE/JDK.
    +# See http://java.sun.com/javase/downloads/index.jsp for more information.
    +#
    +# Backward compatibility with ESAPI Java 1.4 is supported by the two deprecated API
    +# methods, Encryptor.encrypt(String) and Encryptor.decrypt(String). However, whenever
    +# possible, these methods should be avoided as they use ECB cipher mode, which in almost
    +# all circumstances a poor choice because of it's weakness. CBC cipher mode is the default
    +# for the new Encryptor encrypt / decrypt methods for ESAPI Java 2.0.  In general, you
    +# should only use this compatibility setting if you have persistent data encrypted with
    +# version 1.4 and even then, you should ONLY set this compatibility mode UNTIL
    +# you have decrypted all of your old encrypted data and then re-encrypted it with
    +# ESAPI 2.0 using CBC mode. If you have some reason to mix the deprecated 1.4 mode
    +# with the new 2.0 methods, make sure that you use the same cipher algorithm for both
    +# (256-bit AES was the default for 1.4; 128-bit is the default for 2.0; see below for
    +# more details.) Otherwise, you will have to use the new 2.0 encrypt / decrypt methods
    +# where you can specify a SecretKey. (Note that if you are using the 256-bit AES,
    +# that requires downloading the special jurisdiction policy files mentioned above.)
    +#
    +#		***** IMPORTANT: These are for JUnit testing. Test files may have been
    +#						 encrypted using these values so do not change these or
    +#						 those tests will fail. The version under
    +#							src/main/resources/.esapi/ESAPI.properties
    +#						 will be delivered with Encryptor.MasterKey and
    +#						 Encryptor.MasterSalt set to the empty string.
    +#
    +#						 FINAL NOTE:
    +#                           If Maven changes these when run, that needs to be fixed.
    +#       256-bit key... requires unlimited strength jurisdiction policy files
    +### Encryptor.MasterKey=pJhlri8JbuFYDgkqtHmm9s0Ziug2PE7ovZDyEPm4j14=
    +#       128-bit key
    +Encryptor.MasterKey=a6H9is3hEVGKB4Jut+lOVA==
    +Encryptor.MasterSalt=SbftnvmEWD5ZHHP+pX3fqugNysc=
    +# Encryptor.MasterSalt=
    +
    +# Provides the default JCE provider that ESAPI will "prefer" for its symmetric
    +# encryption and hashing. (That is it will look to this provider first, but it
    +# will defer to other providers if the requested algorithm is not implemented
    +# by this provider.) If left unset, ESAPI will just use your Java VM's current
    +# preferred JCE provider, which is generally set in the file
    +# "$JAVA_HOME/jre/lib/security/java.security".
    +#
    +# The main intent of this is to allow ESAPI symmetric encryption to be
    +# used with a FIPS 140-2 compliant crypto-module. For details, see the section
    +# "Using ESAPI Symmetric Encryption with FIPS 140-2 Cryptographic Modules" in
    +# the ESAPI 2.0 Symmetric Encryption User Guide, at:
    +# http://owasp-esapi-java.googlecode.com/svn/trunk/documentation/esapi4java-core-2.0-symmetric-crypto-user-guide.html
    +# However, this property also allows you to easily use an alternate JCE provider
    +# such as "Bouncy Castle" without having to make changes to "java.security".
    +# See Javadoc for SecurityProviderLoader for further details. If you wish to use
    +# a provider that is not known to SecurityProviderLoader, you may specify the
    +# fully-qualified class name of the JCE provider class that implements
    +# java.security.Provider. If the name contains a '.', this is interpreted as
    +# a fully-qualified class name that implements java.security.Provider.
    +#
    +# NOTE: Setting this property has the side-effect of changing it in your application
    +#       as well, so if you are using JCE in your application directly rather than
    +#       through ESAPI (you wouldn't do that, would you? ;-), it will change the
    +#       preferred JCE provider there as well.
    +#
    +# Default: Keeps the JCE provider set to whatever JVM sets it to.
    +Encryptor.PreferredJCEProvider=
    +
    +# AES is the most widely used and strongest encryption algorithm. This
    +# should agree with your Encryptor.CipherTransformation property.
    +# By default, ESAPI Java 1.4 uses "PBEWithMD5AndDES" and which is
    +# very weak. It is essentially a password-based encryption key, hashed
    +# with MD5 around 1K times and then encrypted with the weak DES algorithm
    +# (56-bits) using ECB mode and an unspecified padding (it is
    +# JCE provider specific, but most likely "NoPadding"). However, 2.0 uses
    +# "AES/CBC/PKCSPadding". If you want to change these, change them here.
    +# Warning: This property does not control the default reference implementation for
    +#		   ESAPI 2.0 using JavaEncryptor. Also, this property will be dropped
    +#		   in the future.
    +# @deprecated
    +Encryptor.EncryptionAlgorithm=AES
    +#		For ESAPI Java 2.0 - New encrypt / decrypt methods use this.
    +Encryptor.CipherTransformation=AES/CBC/PKCS5Padding
    +
    +# Applies to ESAPI 2.0 and later only!
    +# Comma-separated list of cipher modes that provide *BOTH*
    +# confidentiality *AND* message authenticity. (NIST refers to such cipher
    +# modes as "combined modes" so that's what we shall call them.) If any of these
    +# cipher modes are used then no MAC is calculated and stored
    +# in the CipherText upon encryption. Likewise, if one of these
    +# cipher modes is used with decryption, no attempt will be made
    +# to validate the MAC contained in the CipherText object regardless
    +# of whether it contains one or not. Since the expectation is that
    +# these cipher modes support support message authenticity already,
    +# injecting a MAC in the CipherText object would be at best redundant.
    +#
    +# Note that as of JDK 1.5, the SunJCE provider does not support *any*
    +# of these cipher modes. Of these listed, only GCM and CCM are currently
    +# NIST approved. YMMV for other JCE providers. E.g., Bouncy Castle supports
    +# GCM and CCM with "NoPadding" mode, but not with "PKCS5Padding" or other
    +# padding modes.
    +Encryptor.cipher_modes.combined_modes=GCM,CCM,IAPM,EAX,OCB,CWC
    +
    +# Applies to ESAPI 2.0 and later only!
    +# Additional cipher modes allowed for ESAPI 2.0 encryption. These
    +# cipher modes are in _addition_ to those specified by the property
    +# 'Encryptor.cipher_modes.combined_modes'.
    +# Note: We will add support for streaming modes like CFB & OFB once
    +# we add support for 'specified' to the property 'Encryptor.ChooseIVMethod'
    +# (probably in ESAPI 2.1).
    +#
    +#	IMPORTANT NOTE:	In the official ESAPI.properties we do *NOT* include ECB
    +#					here as this is an extremely weak mode. However, we *must*
    +#					allow it here so we can test ECB mode. That is important
    +#					since the logic is somewhat different (i.e., ECB mode does
    +#					not use an IV).
    +# DISCUSS: Better name?
    +#	NOTE: ECB added only for testing purposes. Don't try this at home!
    +Encryptor.cipher_modes.additional_allowed=CBC,ECB
    +
    +# 128-bit is almost always sufficient and appears to be more resistant to
    +# related key attacks than is 256-bit AES. Use '_' to use default key size
    +# for cipher algorithms (where it makes sense because the algorithm supports
    +# a variable key size). Key length must agree to what's provided as the
    +# cipher transformation, otherwise this will be ignored after logging a
    +# warning.
    +#
    +# NOTE: This is what applies BOTH ESAPI 1.4 and 2.0. See warning above about mixing!
    +Encryptor.EncryptionKeyLength=128
    +
    +# Because 2.0 uses CBC mode by default, it requires an initialization vector (IV).
    +# (All cipher modes except ECB require an IV.) There are two choices: we can either
    +# use a fixed IV known to both parties or allow ESAPI to choose a random IV. While
    +# the IV does not need to be hidden from adversaries, it is important that the
    +# adversary not be allowed to choose it. Also, random IVs are generally much more
    +# secure than fixed IVs. (In fact, it is essential that feed-back cipher modes
    +# such as CFB and OFB use a different IV for each encryption with a given key so
    +# in such cases, random IVs are much preferred. By default, ESAPI 2.0 uses random
    +# IVs. If you wish to use 'fixed' IVs, set 'Encryptor.ChooseIVMethod=fixed' and
    +# uncomment the Encryptor.fixedIV.
    +#
    +# Valid values:		random|fixed|specified		'specified' not yet implemented; planned for 2.1
    +Encryptor.ChooseIVMethod=random
    +# If you choose to use a fixed IV, then you must place a fixed IV here that
    +# is known to all others who are sharing your secret key. The format should
    +# be a hex string that is the same length as the cipher block size for the
    +# cipher algorithm that you are using. The following is an example for AES
    +# from an AES test vector for AES-128/CBC as described in:
    +# NIST Special Publication 800-38A (2001 Edition)
    +# "Recommendation for Block Cipher Modes of Operation".
    +# (Note that the block size for AES is 16 bytes == 128 bits.)
    +#
    +Encryptor.fixedIV=0x000102030405060708090a0b0c0d0e0f
    +
    +# Whether or not CipherText should use a message authentication code (MAC) with it.
    +# This prevents an adversary from altering the IV as well as allowing a more
    +# fool-proof way of determining the decryption failed because of an incorrect
    +# key being supplied. This refers to the "separate" MAC calculated and stored
    +# in CipherText, not part of any MAC that is calculated as a result of a
    +# "combined mode" cipher mode.
    +#
    +# If you are using ESAPI with a FIPS 140-2 cryptographic module, you *must* also
    +# set this property to false.
    +Encryptor.CipherText.useMAC=true
    +
    +# Whether or not the PlainText object may be overwritten and then marked
    +# eligible for garbage collection. If not set, this is still treated as 'true'.
    +Encryptor.PlainText.overwrite=true
    +
    +# Do not use DES except in a legacy situations. 56-bit is way too small key size.
    +#Encryptor.EncryptionKeyLength=56
    +#Encryptor.EncryptionAlgorithm=DES
    +
    +# TripleDES is considered strong enough for most purposes.
    +#	Note:	There is also a 112-bit version of DESede. Using the 168-bit version
    +#			requires downloading the special jurisdiction policy from Sun.
    +#Encryptor.EncryptionKeyLength=168
    +#Encryptor.EncryptionAlgorithm=DESede
    +
    +Encryptor.HashAlgorithm=SHA-512
    +Encryptor.HashIterations=1024
    +Encryptor.DigitalSignatureAlgorithm=SHA1withDSA
    +Encryptor.DigitalSignatureKeyLength=1024
    +Encryptor.RandomAlgorithm=SHA1PRNG
    +Encryptor.CharacterEncoding=UTF-8
    +# Currently supported choices for JDK 1.5 and 1.6 are:
    +#	HmacSHA1 (160 bits), HmacSHA256 (256 bits), HmacSHA384 (384 bits), and
    +#	HmacSHA512 (512 bits).
    +# Note that HmacMD5 is *not* supported for the PRF used by the KDF even though
    +# these JDKs support it.
    +Encryptor.KDF.PRF=HmacSHA256
    +
    +#===========================================================================
    +# ESAPI HttpUtilties
    +#
    +# The HttpUtilities provide basic protections to HTTP requests and responses. Primarily these methods 
    +# protect against malicious data from attackers, such as unprintable characters, escaped characters,
    +# and other simple attacks. The HttpUtilities also provides utility methods for dealing with cookies,
    +# headers, and CSRF tokens.
    +#
    +# Default file upload location (remember to escape backslashes with \\)
    +HttpUtilities.UploadDir=C:\\ESAPI\\testUpload
    +# let this default to java.io.tmpdir for testing
    +#HttpUtilities.UploadTempDir=C:\\temp
    +# Force flags on cookies, if you use HttpUtilities to set cookies
    +HttpUtilities.ForceHttpOnlySession=false
    +HttpUtilities.ForceSecureSession=false
    +HttpUtilities.ForceHttpOnlyCookies=true
    +HttpUtilities.ForceSecureCookies=true
    +# Maximum size of HTTP headers
    +HttpUtilities.MaxHeaderSize=4096
    +# File upload configuration
    +HttpUtilities.ApprovedUploadExtensions=.zip,.pdf,.doc,.docx,.ppt,.pptx,.tar,.gz,.tgz,.rar,.war,.jar,.ear,.xls,.rtf,.properties,.java,.class,.txt,.xml,.jsp,.jsf,.exe,.dll
    +HttpUtilities.MaxUploadFileBytes=500000000
    +# Using UTF-8 throughout your stack is highly recommended. That includes your database driver,
    +# container, and any other technologies you may be using. Failure to do this may expose you
    +# to Unicode transcoding injection attacks. Use of UTF-8 does not hinder internationalization.
    +HttpUtilities.ResponseContentType=text/html; charset=UTF-8
    +# This is the name of the cookie used to represent the HTTP session
    +# Typically this will be the default "JSESSIONID" 
    +HttpUtilities.HttpSessionIdName=JSESSIONID
    +
    +
    +
    +#===========================================================================
    +# ESAPI Executor
    +# CHECKME - Not sure what this is used for, but surely it should be made OS independent.
    +Executor.WorkingDirectory=C:\\Windows\\Temp
    +Executor.ApprovedExecutables=C:\\Windows\\System32\\cmd.exe,C:\\Windows\\System32\\runas.exe
    +
    +
    +#===========================================================================
    +# ESAPI Logging
    +# Set the application name if these logs are combined with other applications
    +Logger.ApplicationName=ExampleApplication
    +# If you use an HTML log viewer that does not properly HTML escape log data, you can set LogEncodingRequired to true
    +Logger.LogEncodingRequired=false
    +# Determines whether ESAPI should log the application name. This might be clutter in some single-server/single-app environments.
    +Logger.LogApplicationName=true
    +# Determines whether ESAPI should log the server IP and port. This might be clutter in some single-server environments.
    +Logger.LogServerIP=true
    +# LogFileName, the name of the logging file. Provide a full directory path (e.g., C:\\ESAPI\\ESAPI_logging_file) if you
    +# want to place it in a specific directory.
    +Logger.LogFileName=ESAPI_logging_file
    +# MaxLogFileSize, the max size (in bytes) of a single log file before it cuts over to a new one (default is 10,000,000)
    +Logger.MaxLogFileSize=10000000
    +
    +
    +#===========================================================================
    +# ESAPI Intrusion Detection
    +#
    +# Each event has a base to which .count, .interval, and .action are added
    +# The IntrusionException will fire if we receive "count" events within "interval" seconds
    +# The IntrusionDetector is configurable to take the following actions: log, logout, and disable
    +#  (multiple actions separated by commas are allowed e.g. event.test.actions=log,disable
    +#
    +# Custom Events
    +# Names must start with "event." as the base
    +# Use IntrusionDetector.addEvent( "test" ) in your code to trigger "event.test" here
    +# You can also disable intrusion detection completely by changing
    +# the following parameter to true
    +#
    +IntrusionDetector.Disable=false
    +#
    +IntrusionDetector.event.test.count=2
    +IntrusionDetector.event.test.interval=10
    +IntrusionDetector.event.test.actions=disable,log
    +
    +# Exception Events
    +# All EnterpriseSecurityExceptions are registered automatically
    +# Call IntrusionDetector.getInstance().addException(e) for Exceptions that do not extend EnterpriseSecurityException
    +# Use the fully qualified classname of the exception as the base
    +
    +# any intrusion is an attack
    +IntrusionDetector.org.owasp.esapi.errors.IntrusionException.count=1
    +IntrusionDetector.org.owasp.esapi.errors.IntrusionException.interval=1
    +IntrusionDetector.org.owasp.esapi.errors.IntrusionException.actions=log,disable,logout
    +
    +# for test purposes
    +# CHECKME: Shouldn't there be something in the property name itself that designates
    +#		   that these are for testing???
    +IntrusionDetector.org.owasp.esapi.errors.IntegrityException.count=10
    +IntrusionDetector.org.owasp.esapi.errors.IntegrityException.interval=5
    +IntrusionDetector.org.owasp.esapi.errors.IntegrityException.actions=log,disable,logout
    +
    +# rapid validation errors indicate scans or attacks in progress
    +# org.owasp.esapi.errors.ValidationException.count=10
    +# org.owasp.esapi.errors.ValidationException.interval=10
    +# org.owasp.esapi.errors.ValidationException.actions=log,logout
    +
    +# sessions jumping between hosts indicates session hijacking
    +IntrusionDetector.org.owasp.esapi.errors.AuthenticationHostException.count=2
    +IntrusionDetector.org.owasp.esapi.errors.AuthenticationHostException.interval=10
    +IntrusionDetector.org.owasp.esapi.errors.AuthenticationHostException.actions=log,logout
    +
    +
    +#===========================================================================
    +# ESAPI Validation
    +#
    +# The ESAPI Validator works on regular expressions with defined names. You can define names
    +# either here, or you may define application specific patterns in a separate file defined below.
    +# This allows enterprises to specify both organizational standards as well as application specific
    +# validation rules.
    +#
    +#Validator.ConfigurationFile.MultiValued=false
    +Validator.ConfigurationFile=validation-test1.properties
    +
    +# Validators used by ESAPI
    +Validator.AccountName=^[a-zA-Z0-9]{3,20}$
    +Validator.SystemCommand=^[a-zA-Z\\-\\/]{1,64}$
    +Validator.RoleName=^[a-z]{1,20}$
    +Validator.Redirect=^\\/test.*$
    +
    +# Global HTTP Validation Rules
    +# Values with Base64 encoded data (e.g. encrypted state) will need at least [a-zA-Z0-9\/+=]
    +Validator.HTTPScheme=^(http|https)$
    +Validator.HTTPServerName=^[a-zA-Z0-9_.\\-]*$
    +Validator.HTTPCookieName=^[a-zA-Z0-9\\-_]{1,32}$
    +Validator.HTTPCookieValue=^[a-zA-Z0-9\\-\\/+=_ ]*$
    +Validator.HTTPHeaderName=^[a-zA-Z0-9\\-_]{1,32}$
    +Validator.HTTPHeaderValue=^[a-zA-Z0-9()\\-=\\*\\.\\?;,+\\/:&_ ]*$
    +Validator.HTTPServletPath=^[a-zA-Z0-9.\\-\\/_]*$
    +Validator.HTTPPath=^[a-zA-Z0-9.\\-_]*$
    +Validator.HTTPURL=^.*$
    +Validator.HTTPJSESSIONID=^[A-Z0-9]{10,30}$
    +
    +# Contributed by Fraenku@gmx.ch
    +# Googlecode Issue 116 (http://code.google.com/p/owasp-esapi-java/issues/detail?id=116)
    +Validator.HTTPParameterName=^[a-zA-Z0-9_\\-]{1,32}$
    +Validator.HTTPParameterValue=^[\\p{L}\\p{N}.\\-/+=_ !$*?@]{0,1000}$
    +Validator.HTTPContextPath=^/[a-zA-Z0-9.\\-_]*$
    +Validator.HTTPQueryString=^([a-zA-Z0-9_\\-]{1,32}=[\\p{L}\\p{N}.\\-/+=_ !$*?@%]*&?)*$
    +Validator.HTTPURI=^/([a-zA-Z0-9.\\-_]*/?)*$
    +
    +
    +# Validation of file related input
    +Validator.FileName=^[a-zA-Z0-9!@#$%^&{}\\[\\]()_+\\-=,.~'` ]{1,255}$
    +Validator.DirectoryName=^[a-zA-Z0-9:/\\\\!@#$%^&{}\\[\\]()_+\\-=,.~'` ]{1,255}$
    +
    +# Validation of dates. Controls whether or not 'lenient' dates are accepted.
    +# See DataFormat.setLenient(boolean flag) for further details.
     Validator.AcceptLenientDates=false
    \ No newline at end of file
    
    From 770fb79545bc6ea869809121ab4b86f32212ec2a Mon Sep 17 00:00:00 2001
    From: kwwall 
    Date: Tue, 19 Jan 2016 01:12:42 -0500
    Subject: [PATCH 0280/1069] Address issue 356.
    
    ---
     src/test/resources/esapi/ESAPI.properties | 942 +++++++++++-----------
     1 file changed, 471 insertions(+), 471 deletions(-)
    
    diff --git a/src/test/resources/esapi/ESAPI.properties b/src/test/resources/esapi/ESAPI.properties
    index 1a0506ebf..2727d4ada 100644
    --- a/src/test/resources/esapi/ESAPI.properties
    +++ b/src/test/resources/esapi/ESAPI.properties
    @@ -1,471 +1,471 @@
    -#
    -# OWASP Enterprise Security API (ESAPI) Properties file -- TEST Version
    -# 
    -# This file is part of the Open Web Application Security Project (OWASP)
    -# Enterprise Security API (ESAPI) project. For details, please see
    -# http://www.owasp.org/index.php/ESAPI.
    -#
    -# Copyright (c) 2008,2009 - The OWASP Foundation
    -#
    -# DISCUSS: This may cause a major backwards compatibility issue, etc. but
    -#		   from a name space perspective, we probably should have prefaced
    -#		   all the property names with ESAPI or at least OWASP. Otherwise
    -#		   there could be problems is someone loads this properties file into
    -#		   the System properties.  We could also put this file into the
    -#		   esapi.jar file (perhaps as a ResourceBundle) and then allow an external
    -#		   ESAPI properties be defined that would overwrite these defaults.
    -#		   That keeps the application's properties relatively simple as usually
    -#		   they will only want to override a few properties. If looks like we
    -#		   already support multiple override levels of this in the
    -#		   DefaultSecurityConfiguration class, but I'm suggesting placing the
    -#		   defaults in the esapi.jar itself. That way, if the jar is signed,
    -#		   we could detect if those properties had been tampered with. (The
    -#		   code to check the jar signatures is pretty simple... maybe 70-90 LOC,
    -#		   but off course there is an execution penalty (similar to the way
    -#		   that the separate sunjce.jar used to be when a class from it was
    -#		   first loaded). Thoughts?
    -###############################################################################
    -#
    -# WARNING: Operating system protection should be used to lock down the .esapi
    -# resources directory and all the files inside and all the directories all the
    -# way up to the root directory of the file system.  Note that if you are using
    -# file-based implementations, that some files may need to be read-write as they
    -# get updated dynamically.
    -#
    -# Before using, be sure to update the MasterKey and MasterSalt as described below.
    -# N.B.: If you had stored data that you have previously encrypted with ESAPI 1.4,
    -#		you *must* FIRST decrypt it using ESAPI 1.4 and then (if so desired)
    -#		re-encrypt it with ESAPI 2.0. If you fail to do this, you will NOT be
    -#		able to decrypt your data with ESAPI 2.0.
    -#
    -#		YOU HAVE BEEN WARNED!!! More details are in the ESAPI 2.0 Release Notes.
    -#
    -#===========================================================================
    -# ESAPI Configuration
    -#
    -# If true, then print all the ESAPI properties set here when they are loaded.
    -# If false, they are not printed. Useful to reduce output when running JUnit tests.
    -# If you need to troubleshoot a properties related problem, turning this on may help,
    -# but we leave it off for running JUnit tests. (It will be 'true' in the one delivered
    -# as part of production ESAPI, mostly for backward compatibility.)
    -ESAPI.printProperties=false
    -
    -# ESAPI is designed to be easily extensible. You can use the reference implementation
    -# or implement your own providers to take advantage of your enterprise's security
    -# infrastructure. The functions in ESAPI are referenced using the ESAPI locator, like:
    -#
    -#    String ciphertext =
    -#		ESAPI.encryptor().encrypt("Secret message");   // Deprecated in 2.0
    -#    CipherText cipherText =
    -#		ESAPI.encryptor().encrypt(new PlainText("Secret message")); // Preferred
    -#
    -# Below you can specify the classname for the provider that you wish to use in your
    -# application. The only requirement is that it implement the appropriate ESAPI interface.
    -# This allows you to switch security implementations in the future without rewriting the
    -# entire application.
    -#
    -# ExperimentalAccessController requires ESAPI-AccessControlPolicy.xml in .esapi directory
    -ESAPI.AccessControl=org.owasp.esapi.reference.DefaultAccessController
    -# FileBasedAuthenticator requires users.txt file in .esapi directory
    -ESAPI.Authenticator=org.owasp.esapi.reference.FileBasedAuthenticator
    -ESAPI.Encoder=org.owasp.esapi.reference.DefaultEncoder
    -ESAPI.Encryptor=org.owasp.esapi.reference.crypto.JavaEncryptor
    -
    -ESAPI.Executor=org.owasp.esapi.reference.DefaultExecutor
    -ESAPI.HTTPUtilities=org.owasp.esapi.reference.DefaultHTTPUtilities
    -ESAPI.IntrusionDetector=org.owasp.esapi.reference.DefaultIntrusionDetector
    -# Log4JFactory Requires log4j.xml or log4j.properties in classpath - http://www.laliluna.de/log4j-tutorial.html
    -ESAPI.Logger=org.owasp.esapi.reference.Log4JLogFactory
    -#ESAPI.Logger=org.owasp.esapi.reference.JavaLogFactory
    -#ESAPI.Logger=org.owasp.esapi.reference.ExampleExtendedLog4JLogFactory
    -ESAPI.Randomizer=org.owasp.esapi.reference.DefaultRandomizer
    -ESAPI.Validator=org.owasp.esapi.reference.DefaultValidator
    -
    -#===========================================================================
    -# ESAPI Authenticator
    -#
    -Authenticator.AllowedLoginAttempts=3
    -Authenticator.MaxOldPasswordHashes=13
    -Authenticator.UsernameParameterName=username
    -Authenticator.PasswordParameterName=password
    -# RememberTokenDuration (in days)
    -Authenticator.RememberTokenDuration=14
    -# Session Timeouts (in minutes)
    -Authenticator.IdleTimeoutDuration=20
    -Authenticator.AbsoluteTimeoutDuration=120
    -
    -#===========================================================================
    -# ESAPI Encoder
    -#
    -# ESAPI canonicalizes input before validation to prevent bypassing filters with encoded attacks.
    -# Failure to canonicalize input is a very common mistake when implementing validation schemes.
    -# Canonicalization is automatic when using the ESAPI Validator, but you can also use the
    -# following code to canonicalize data.
    -#
    -#      ESAPI.Encoder().canonicalize( "%22hello world"" );
    -#  
    -# Multiple encoding is when a single encoding format is applied multiple times. Allowing
    -# multiple encoding is strongly discouraged.
    -Encoder.AllowMultipleEncoding=false
    -
    -# Mixed encoding is when multiple different encoding formats are applied, or when
    -# multiple formats are nested. Allowing multiple encoding is strongly discouraged.
    -Encoder.AllowMixedEncoding=false
    -
    -# The default list of codecs to apply when canonicalizing untrusted data. The list should include the codecs
    -# for all downstream interpreters or decoders. For example, if the data is likely to end up in a URL, HTML, or
    -# inside JavaScript, then the list of codecs below is appropriate. The order of the list is not terribly important.
    -Encoder.DefaultCodecList=HTMLEntityCodec,PercentCodec,JavaScriptCodec
    -
    -
    -#===========================================================================
    -# ESAPI Encryption
    -#
    -# The ESAPI Encryptor provides basic cryptographic functions with a simplified API.
    -# To get started, generate a new key using java -classpath esapi.jar org.owasp.esapi.reference.crypto.JavaEncryptor
    -# There is not currently any support for key rotation, so be careful when changing your key and salt as it
    -# will invalidate all signed, encrypted, and hashed data.
    -#
    -# WARNING: Not all combinations of algorithms and key lengths are supported.
    -# If you choose to use a key length greater than 128, you MUST download the
    -# unlimited strength policy files and install in the lib directory of your JRE/JDK.
    -# See http://java.sun.com/javase/downloads/index.jsp for more information.
    -#
    -# Backward compatibility with ESAPI Java 1.4 is supported by the two deprecated API
    -# methods, Encryptor.encrypt(String) and Encryptor.decrypt(String). However, whenever
    -# possible, these methods should be avoided as they use ECB cipher mode, which in almost
    -# all circumstances a poor choice because of it's weakness. CBC cipher mode is the default
    -# for the new Encryptor encrypt / decrypt methods for ESAPI Java 2.0.  In general, you
    -# should only use this compatibility setting if you have persistent data encrypted with
    -# version 1.4 and even then, you should ONLY set this compatibility mode UNTIL
    -# you have decrypted all of your old encrypted data and then re-encrypted it with
    -# ESAPI 2.0 using CBC mode. If you have some reason to mix the deprecated 1.4 mode
    -# with the new 2.0 methods, make sure that you use the same cipher algorithm for both
    -# (256-bit AES was the default for 1.4; 128-bit is the default for 2.0; see below for
    -# more details.) Otherwise, you will have to use the new 2.0 encrypt / decrypt methods
    -# where you can specify a SecretKey. (Note that if you are using the 256-bit AES,
    -# that requires downloading the special jurisdiction policy files mentioned above.)
    -#
    -#		***** IMPORTANT: These are for JUnit testing. Test files may have been
    -#						 encrypted using these values so do not change these or
    -#						 those tests will fail. The version under
    -#							src/main/resources/.esapi/ESAPI.properties
    -#						 will be delivered with Encryptor.MasterKey and
    -#						 Encryptor.MasterSalt set to the empty string.
    -#
    -#						 FINAL NOTE:
    -#                           If Maven changes these when run, that needs to be fixed.
    -#       256-bit key... requires unlimited strength jurisdiction policy files
    -### Encryptor.MasterKey=pJhlri8JbuFYDgkqtHmm9s0Ziug2PE7ovZDyEPm4j14=
    -#       128-bit key
    -Encryptor.MasterKey=a6H9is3hEVGKB4Jut+lOVA==
    -Encryptor.MasterSalt=SbftnvmEWD5ZHHP+pX3fqugNysc=
    -# Encryptor.MasterSalt=
    -
    -# Provides the default JCE provider that ESAPI will "prefer" for its symmetric
    -# encryption and hashing. (That is it will look to this provider first, but it
    -# will defer to other providers if the requested algorithm is not implemented
    -# by this provider.) If left unset, ESAPI will just use your Java VM's current
    -# preferred JCE provider, which is generally set in the file
    -# "$JAVA_HOME/jre/lib/security/java.security".
    -#
    -# The main intent of this is to allow ESAPI symmetric encryption to be
    -# used with a FIPS 140-2 compliant crypto-module. For details, see the section
    -# "Using ESAPI Symmetric Encryption with FIPS 140-2 Cryptographic Modules" in
    -# the ESAPI 2.0 Symmetric Encryption User Guide, at:
    -# http://owasp-esapi-java.googlecode.com/svn/trunk/documentation/esapi4java-core-2.0-symmetric-crypto-user-guide.html
    -# However, this property also allows you to easily use an alternate JCE provider
    -# such as "Bouncy Castle" without having to make changes to "java.security".
    -# See Javadoc for SecurityProviderLoader for further details. If you wish to use
    -# a provider that is not known to SecurityProviderLoader, you may specify the
    -# fully-qualified class name of the JCE provider class that implements
    -# java.security.Provider. If the name contains a '.', this is interpreted as
    -# a fully-qualified class name that implements java.security.Provider.
    -#
    -# NOTE: Setting this property has the side-effect of changing it in your application
    -#       as well, so if you are using JCE in your application directly rather than
    -#       through ESAPI (you wouldn't do that, would you? ;-), it will change the
    -#       preferred JCE provider there as well.
    -#
    -# Default: Keeps the JCE provider set to whatever JVM sets it to.
    -Encryptor.PreferredJCEProvider=
    -
    -# AES is the most widely used and strongest encryption algorithm. This
    -# should agree with your Encryptor.CipherTransformation property.
    -# By default, ESAPI Java 1.4 uses "PBEWithMD5AndDES" and which is
    -# very weak. It is essentially a password-based encryption key, hashed
    -# with MD5 around 1K times and then encrypted with the weak DES algorithm
    -# (56-bits) using ECB mode and an unspecified padding (it is
    -# JCE provider specific, but most likely "NoPadding"). However, 2.0 uses
    -# "AES/CBC/PKCSPadding". If you want to change these, change them here.
    -# Warning: This property does not control the default reference implementation for
    -#		   ESAPI 2.0 using JavaEncryptor. Also, this property will be dropped
    -#		   in the future.
    -# @deprecated
    -Encryptor.EncryptionAlgorithm=AES
    -#		For ESAPI Java 2.0 - New encrypt / decrypt methods use this.
    -Encryptor.CipherTransformation=AES/CBC/PKCS5Padding
    -
    -# Applies to ESAPI 2.0 and later only!
    -# Comma-separated list of cipher modes that provide *BOTH*
    -# confidentiality *AND* message authenticity. (NIST refers to such cipher
    -# modes as "combined modes" so that's what we shall call them.) If any of these
    -# cipher modes are used then no MAC is calculated and stored
    -# in the CipherText upon encryption. Likewise, if one of these
    -# cipher modes is used with decryption, no attempt will be made
    -# to validate the MAC contained in the CipherText object regardless
    -# of whether it contains one or not. Since the expectation is that
    -# these cipher modes support support message authenticity already,
    -# injecting a MAC in the CipherText object would be at best redundant.
    -#
    -# Note that as of JDK 1.5, the SunJCE provider does not support *any*
    -# of these cipher modes. Of these listed, only GCM and CCM are currently
    -# NIST approved. YMMV for other JCE providers. E.g., Bouncy Castle supports
    -# GCM and CCM with "NoPadding" mode, but not with "PKCS5Padding" or other
    -# padding modes.
    -Encryptor.cipher_modes.combined_modes=GCM,CCM,IAPM,EAX,OCB,CWC
    -
    -# Applies to ESAPI 2.0 and later only!
    -# Additional cipher modes allowed for ESAPI 2.0 encryption. These
    -# cipher modes are in _addition_ to those specified by the property
    -# 'Encryptor.cipher_modes.combined_modes'.
    -# Note: We will add support for streaming modes like CFB & OFB once
    -# we add support for 'specified' to the property 'Encryptor.ChooseIVMethod'
    -# (probably in ESAPI 2.1).
    -#
    -#	IMPORTANT NOTE:	In the official ESAPI.properties we do *NOT* include ECB
    -#					here as this is an extremely weak mode. However, we *must*
    -#					allow it here so we can test ECB mode. That is important
    -#					since the logic is somewhat different (i.e., ECB mode does
    -#					not use an IV).
    -# DISCUSS: Better name?
    -#	NOTE: ECB added only for testing purposes. Don't try this at home!
    -Encryptor.cipher_modes.additional_allowed=CBC,ECB
    -
    -# 128-bit is almost always sufficient and appears to be more resistant to
    -# related key attacks than is 256-bit AES. Use '_' to use default key size
    -# for cipher algorithms (where it makes sense because the algorithm supports
    -# a variable key size). Key length must agree to what's provided as the
    -# cipher transformation, otherwise this will be ignored after logging a
    -# warning.
    -#
    -# NOTE: This is what applies BOTH ESAPI 1.4 and 2.0. See warning above about mixing!
    -Encryptor.EncryptionKeyLength=128
    -
    -# Because 2.0 uses CBC mode by default, it requires an initialization vector (IV).
    -# (All cipher modes except ECB require an IV.) There are two choices: we can either
    -# use a fixed IV known to both parties or allow ESAPI to choose a random IV. While
    -# the IV does not need to be hidden from adversaries, it is important that the
    -# adversary not be allowed to choose it. Also, random IVs are generally much more
    -# secure than fixed IVs. (In fact, it is essential that feed-back cipher modes
    -# such as CFB and OFB use a different IV for each encryption with a given key so
    -# in such cases, random IVs are much preferred. By default, ESAPI 2.0 uses random
    -# IVs. If you wish to use 'fixed' IVs, set 'Encryptor.ChooseIVMethod=fixed' and
    -# uncomment the Encryptor.fixedIV.
    -#
    -# Valid values:		random|fixed|specified		'specified' not yet implemented; planned for 2.1
    -Encryptor.ChooseIVMethod=random
    -# If you choose to use a fixed IV, then you must place a fixed IV here that
    -# is known to all others who are sharing your secret key. The format should
    -# be a hex string that is the same length as the cipher block size for the
    -# cipher algorithm that you are using. The following is an example for AES
    -# from an AES test vector for AES-128/CBC as described in:
    -# NIST Special Publication 800-38A (2001 Edition)
    -# "Recommendation for Block Cipher Modes of Operation".
    -# (Note that the block size for AES is 16 bytes == 128 bits.)
    -#
    -Encryptor.fixedIV=0x000102030405060708090a0b0c0d0e0f
    -
    -# Whether or not CipherText should use a message authentication code (MAC) with it.
    -# This prevents an adversary from altering the IV as well as allowing a more
    -# fool-proof way of determining the decryption failed because of an incorrect
    -# key being supplied. This refers to the "separate" MAC calculated and stored
    -# in CipherText, not part of any MAC that is calculated as a result of a
    -# "combined mode" cipher mode.
    -#
    -# If you are using ESAPI with a FIPS 140-2 cryptographic module, you *must* also
    -# set this property to false.
    -Encryptor.CipherText.useMAC=true
    -
    -# Whether or not the PlainText object may be overwritten and then marked
    -# eligible for garbage collection. If not set, this is still treated as 'true'.
    -Encryptor.PlainText.overwrite=true
    -
    -# Do not use DES except in a legacy situations. 56-bit is way too small key size.
    -#Encryptor.EncryptionKeyLength=56
    -#Encryptor.EncryptionAlgorithm=DES
    -
    -# TripleDES is considered strong enough for most purposes.
    -#	Note:	There is also a 112-bit version of DESede. Using the 168-bit version
    -#			requires downloading the special jurisdiction policy from Sun.
    -#Encryptor.EncryptionKeyLength=168
    -#Encryptor.EncryptionAlgorithm=DESede
    -
    -Encryptor.HashAlgorithm=SHA-512
    -Encryptor.HashIterations=1024
    -Encryptor.DigitalSignatureAlgorithm=SHA1withDSA
    -Encryptor.DigitalSignatureKeyLength=1024
    -Encryptor.RandomAlgorithm=SHA1PRNG
    -Encryptor.CharacterEncoding=UTF-8
    -# Currently supported choices for JDK 1.5 and 1.6 are:
    -#	HmacSHA1 (160 bits), HmacSHA256 (256 bits), HmacSHA384 (384 bits), and
    -#	HmacSHA512 (512 bits).
    -# Note that HmacMD5 is *not* supported for the PRF used by the KDF even though
    -# these JDKs support it.
    -Encryptor.KDF.PRF=HmacSHA256
    -
    -#===========================================================================
    -# ESAPI HttpUtilties
    -#
    -# The HttpUtilities provide basic protections to HTTP requests and responses. Primarily these methods 
    -# protect against malicious data from attackers, such as unprintable characters, escaped characters,
    -# and other simple attacks. The HttpUtilities also provides utility methods for dealing with cookies,
    -# headers, and CSRF tokens.
    -#
    -# Default file upload location (remember to escape backslashes with \\)
    -HttpUtilities.UploadDir=C:\\ESAPI\\testUpload
    -# let this default to java.io.tmpdir for testing
    -#HttpUtilities.UploadTempDir=C:\\temp
    -# Force flags on cookies, if you use HttpUtilities to set cookies
    -HttpUtilities.ForceHttpOnlySession=false
    -HttpUtilities.ForceSecureSession=false
    -HttpUtilities.ForceHttpOnlyCookies=true
    -HttpUtilities.ForceSecureCookies=true
    -# Maximum size of HTTP headers
    -HttpUtilities.MaxHeaderSize=4096
    -# File upload configuration
    -HttpUtilities.ApprovedUploadExtensions=.zip,.pdf,.doc,.docx,.ppt,.pptx,.tar,.gz,.tgz,.rar,.war,.jar,.ear,.xls,.rtf,.properties,.java,.class,.txt,.xml,.jsp,.jsf,.exe,.dll
    -HttpUtilities.MaxUploadFileBytes=500000000
    -# Using UTF-8 throughout your stack is highly recommended. That includes your database driver,
    -# container, and any other technologies you may be using. Failure to do this may expose you
    -# to Unicode transcoding injection attacks. Use of UTF-8 does not hinder internationalization.
    -HttpUtilities.ResponseContentType=text/html; charset=UTF-8
    -# This is the name of the cookie used to represent the HTTP session
    -# Typically this will be the default "JSESSIONID" 
    -HttpUtilities.HttpSessionIdName=JSESSIONID
    -
    -
    -
    -#===========================================================================
    -# ESAPI Executor
    -# CHECKME - Not sure what this is used for, but surely it should be made OS independent.
    -Executor.WorkingDirectory=C:\\Windows\\Temp
    -Executor.ApprovedExecutables=C:\\Windows\\System32\\cmd.exe,C:\\Windows\\System32\\runas.exe
    -
    -
    -#===========================================================================
    -# ESAPI Logging
    -# Set the application name if these logs are combined with other applications
    -Logger.ApplicationName=ExampleApplication
    -# If you use an HTML log viewer that does not properly HTML escape log data, you can set LogEncodingRequired to true
    -Logger.LogEncodingRequired=false
    -# Determines whether ESAPI should log the application name. This might be clutter in some single-server/single-app environments.
    -Logger.LogApplicationName=true
    -# Determines whether ESAPI should log the server IP and port. This might be clutter in some single-server environments.
    -Logger.LogServerIP=true
    -# LogFileName, the name of the logging file. Provide a full directory path (e.g., C:\\ESAPI\\ESAPI_logging_file) if you
    -# want to place it in a specific directory.
    -Logger.LogFileName=ESAPI_logging_file
    -# MaxLogFileSize, the max size (in bytes) of a single log file before it cuts over to a new one (default is 10,000,000)
    -Logger.MaxLogFileSize=10000000
    -
    -
    -#===========================================================================
    -# ESAPI Intrusion Detection
    -#
    -# Each event has a base to which .count, .interval, and .action are added
    -# The IntrusionException will fire if we receive "count" events within "interval" seconds
    -# The IntrusionDetector is configurable to take the following actions: log, logout, and disable
    -#  (multiple actions separated by commas are allowed e.g. event.test.actions=log,disable
    -#
    -# Custom Events
    -# Names must start with "event." as the base
    -# Use IntrusionDetector.addEvent( "test" ) in your code to trigger "event.test" here
    -# You can also disable intrusion detection completely by changing
    -# the following parameter to true
    -#
    -IntrusionDetector.Disable=false
    -#
    -IntrusionDetector.event.test.count=2
    -IntrusionDetector.event.test.interval=10
    -IntrusionDetector.event.test.actions=disable,log
    -
    -# Exception Events
    -# All EnterpriseSecurityExceptions are registered automatically
    -# Call IntrusionDetector.getInstance().addException(e) for Exceptions that do not extend EnterpriseSecurityException
    -# Use the fully qualified classname of the exception as the base
    -
    -# any intrusion is an attack
    -IntrusionDetector.org.owasp.esapi.errors.IntrusionException.count=1
    -IntrusionDetector.org.owasp.esapi.errors.IntrusionException.interval=1
    -IntrusionDetector.org.owasp.esapi.errors.IntrusionException.actions=log,disable,logout
    -
    -# for test purposes
    -# CHECKME: Shouldn't there be something in the property name itself that designates
    -#		   that these are for testing???
    -IntrusionDetector.org.owasp.esapi.errors.IntegrityException.count=10
    -IntrusionDetector.org.owasp.esapi.errors.IntegrityException.interval=5
    -IntrusionDetector.org.owasp.esapi.errors.IntegrityException.actions=log,disable,logout
    -
    -# rapid validation errors indicate scans or attacks in progress
    -# org.owasp.esapi.errors.ValidationException.count=10
    -# org.owasp.esapi.errors.ValidationException.interval=10
    -# org.owasp.esapi.errors.ValidationException.actions=log,logout
    -
    -# sessions jumping between hosts indicates session hijacking
    -IntrusionDetector.org.owasp.esapi.errors.AuthenticationHostException.count=2
    -IntrusionDetector.org.owasp.esapi.errors.AuthenticationHostException.interval=10
    -IntrusionDetector.org.owasp.esapi.errors.AuthenticationHostException.actions=log,logout
    -
    -
    -#===========================================================================
    -# ESAPI Validation
    -#
    -# The ESAPI Validator works on regular expressions with defined names. You can define names
    -# either here, or you may define application specific patterns in a separate file defined below.
    -# This allows enterprises to specify both organizational standards as well as application specific
    -# validation rules.
    -#
    -# Use '\p{L}' (without the quotes) within the character class to match
    -# any Unicode LETTER. You can also use a range, like:  \u00C0-\u017F
    -# You can also use any of the regex flags as documented at
    -# https://docs.oracle.com/javase/tutorial/essential/regex/pattern.html, e.g. (?u)
    -#
    -Validator.ConfigurationFile=validation.properties
    -
    -# Validators used by ESAPI
    -Validator.AccountName=^[a-zA-Z0-9]{3,20}$
    -Validator.SystemCommand=^[a-zA-Z\\-\\/]{1,64}$
    -Validator.RoleName=^[a-z]{1,20}$
    -Validator.Redirect=^\\/test.*$
    -
    -# Global HTTP Validation Rules
    -# Values with Base64 encoded data (e.g. encrypted state) will need at least [a-zA-Z0-9\/+=]
    -Validator.HTTPScheme=^(http|https)$
    -Validator.HTTPServerName=^[a-zA-Z0-9_.\\-]*$
    -Validator.HTTPCookieName=^[a-zA-Z0-9\\-_]{1,32}$
    -Validator.HTTPCookieValue=^[a-zA-Z0-9\\-\\/+=_ ]*$
    -# Note that max header name capped at 150 in SecurityRequestWrapper!
    -Validator.HTTPHeaderName=^[a-zA-Z0-9\\-_]{1,50}$
    -Validator.HTTPHeaderValue=^[a-zA-Z0-9()\\-=\\*\\.\\?;,+\\/:&_ ]*$
    -Validator.HTTPServletPath=^[a-zA-Z0-9.\\-\\/_]*$
    -Validator.HTTPPath=^[a-zA-Z0-9.\\-_]*$
    -Validator.HTTPURL=^.*$
    -Validator.HTTPJSESSIONID=^[A-Z0-9]{10,30}$
    -
    -# Contributed by Fraenku@gmx.ch
    -# Googlecode Issue 116 (http://code.google.com/p/owasp-esapi-java/issues/detail?id=116)
    -Validator.HTTPParameterName=^[a-zA-Z0-9_\\-]{1,32}$
    -Validator.HTTPParameterValue=^[\\p{L}\\p{N}.\\-/+=_ !$*?@]{0,1000}$
    -Validator.HTTPContextPath=^/[a-zA-Z0-9.\\-_]*$
    -Validator.HTTPQueryString=^([a-zA-Z0-9_\\-]{1,32}=[\\p{L}\\p{N}.\\-/+=_ !$*?@%]*&?)*$
    -Validator.HTTPURI=^/([a-zA-Z0-9.\\-_]*/?)*$
    -
    -
    -# Validation of file related input
    -Validator.FileName=^[a-zA-Z0-9!@#$%^&{}\\[\\]()_+\\-=,.~'` ]{1,255}$
    -Validator.DirectoryName=^[a-zA-Z0-9:/\\\\!@#$%^&{}\\[\\]()_+\\-=,.~'` ]{1,255}$
    -
    -# Validation of dates. Controls whether or not 'lenient' dates are accepted.
    -# See DataFormat.setLenient(boolean flag) for further details.
    -Validator.AcceptLenientDates=false
    +#
    +# OWASP Enterprise Security API (ESAPI) Properties file -- TEST Version
    +# 
    +# This file is part of the Open Web Application Security Project (OWASP)
    +# Enterprise Security API (ESAPI) project. For details, please see
    +# http://www.owasp.org/index.php/ESAPI.
    +#
    +# Copyright (c) 2008,2009 - The OWASP Foundation
    +#
    +# DISCUSS: This may cause a major backwards compatibility issue, etc. but
    +#		   from a name space perspective, we probably should have prefaced
    +#		   all the property names with ESAPI or at least OWASP. Otherwise
    +#		   there could be problems is someone loads this properties file into
    +#		   the System properties.  We could also put this file into the
    +#		   esapi.jar file (perhaps as a ResourceBundle) and then allow an external
    +#		   ESAPI properties be defined that would overwrite these defaults.
    +#		   That keeps the application's properties relatively simple as usually
    +#		   they will only want to override a few properties. If looks like we
    +#		   already support multiple override levels of this in the
    +#		   DefaultSecurityConfiguration class, but I'm suggesting placing the
    +#		   defaults in the esapi.jar itself. That way, if the jar is signed,
    +#		   we could detect if those properties had been tampered with. (The
    +#		   code to check the jar signatures is pretty simple... maybe 70-90 LOC,
    +#		   but off course there is an execution penalty (similar to the way
    +#		   that the separate sunjce.jar used to be when a class from it was
    +#		   first loaded). Thoughts?
    +###############################################################################
    +#
    +# WARNING: Operating system protection should be used to lock down the .esapi
    +# resources directory and all the files inside and all the directories all the
    +# way up to the root directory of the file system.  Note that if you are using
    +# file-based implementations, that some files may need to be read-write as they
    +# get updated dynamically.
    +#
    +# Before using, be sure to update the MasterKey and MasterSalt as described below.
    +# N.B.: If you had stored data that you have previously encrypted with ESAPI 1.4,
    +#		you *must* FIRST decrypt it using ESAPI 1.4 and then (if so desired)
    +#		re-encrypt it with ESAPI 2.0. If you fail to do this, you will NOT be
    +#		able to decrypt your data with ESAPI 2.0.
    +#
    +#		YOU HAVE BEEN WARNED!!! More details are in the ESAPI 2.0 Release Notes.
    +#
    +#===========================================================================
    +# ESAPI Configuration
    +#
    +# If true, then print all the ESAPI properties set here when they are loaded.
    +# If false, they are not printed. Useful to reduce output when running JUnit tests.
    +# If you need to troubleshoot a properties related problem, turning this on may help,
    +# but we leave it off for running JUnit tests. (It will be 'true' in the one delivered
    +# as part of production ESAPI, mostly for backward compatibility.)
    +ESAPI.printProperties=false
    +
    +# ESAPI is designed to be easily extensible. You can use the reference implementation
    +# or implement your own providers to take advantage of your enterprise's security
    +# infrastructure. The functions in ESAPI are referenced using the ESAPI locator, like:
    +#
    +#    String ciphertext =
    +#		ESAPI.encryptor().encrypt("Secret message");   // Deprecated in 2.0
    +#    CipherText cipherText =
    +#		ESAPI.encryptor().encrypt(new PlainText("Secret message")); // Preferred
    +#
    +# Below you can specify the classname for the provider that you wish to use in your
    +# application. The only requirement is that it implement the appropriate ESAPI interface.
    +# This allows you to switch security implementations in the future without rewriting the
    +# entire application.
    +#
    +# ExperimentalAccessController requires ESAPI-AccessControlPolicy.xml in .esapi directory
    +ESAPI.AccessControl=org.owasp.esapi.reference.DefaultAccessController
    +# FileBasedAuthenticator requires users.txt file in .esapi directory
    +ESAPI.Authenticator=org.owasp.esapi.reference.FileBasedAuthenticator
    +ESAPI.Encoder=org.owasp.esapi.reference.DefaultEncoder
    +ESAPI.Encryptor=org.owasp.esapi.reference.crypto.JavaEncryptor
    +
    +ESAPI.Executor=org.owasp.esapi.reference.DefaultExecutor
    +ESAPI.HTTPUtilities=org.owasp.esapi.reference.DefaultHTTPUtilities
    +ESAPI.IntrusionDetector=org.owasp.esapi.reference.DefaultIntrusionDetector
    +# Log4JFactory Requires log4j.xml or log4j.properties in classpath - http://www.laliluna.de/log4j-tutorial.html
    +ESAPI.Logger=org.owasp.esapi.reference.Log4JLogFactory
    +#ESAPI.Logger=org.owasp.esapi.reference.JavaLogFactory
    +#ESAPI.Logger=org.owasp.esapi.reference.ExampleExtendedLog4JLogFactory
    +ESAPI.Randomizer=org.owasp.esapi.reference.DefaultRandomizer
    +ESAPI.Validator=org.owasp.esapi.reference.DefaultValidator
    +
    +#===========================================================================
    +# ESAPI Authenticator
    +#
    +Authenticator.AllowedLoginAttempts=3
    +Authenticator.MaxOldPasswordHashes=13
    +Authenticator.UsernameParameterName=username
    +Authenticator.PasswordParameterName=password
    +# RememberTokenDuration (in days)
    +Authenticator.RememberTokenDuration=14
    +# Session Timeouts (in minutes)
    +Authenticator.IdleTimeoutDuration=20
    +Authenticator.AbsoluteTimeoutDuration=120
    +
    +#===========================================================================
    +# ESAPI Encoder
    +#
    +# ESAPI canonicalizes input before validation to prevent bypassing filters with encoded attacks.
    +# Failure to canonicalize input is a very common mistake when implementing validation schemes.
    +# Canonicalization is automatic when using the ESAPI Validator, but you can also use the
    +# following code to canonicalize data.
    +#
    +#      ESAPI.Encoder().canonicalize( "%22hello world"" );
    +#  
    +# Multiple encoding is when a single encoding format is applied multiple times. Allowing
    +# multiple encoding is strongly discouraged.
    +Encoder.AllowMultipleEncoding=false
    +
    +# Mixed encoding is when multiple different encoding formats are applied, or when
    +# multiple formats are nested. Allowing multiple encoding is strongly discouraged.
    +Encoder.AllowMixedEncoding=false
    +
    +# The default list of codecs to apply when canonicalizing untrusted data. The list should include the codecs
    +# for all downstream interpreters or decoders. For example, if the data is likely to end up in a URL, HTML, or
    +# inside JavaScript, then the list of codecs below is appropriate. The order of the list is not terribly important.
    +Encoder.DefaultCodecList=HTMLEntityCodec,PercentCodec,JavaScriptCodec
    +
    +
    +#===========================================================================
    +# ESAPI Encryption
    +#
    +# The ESAPI Encryptor provides basic cryptographic functions with a simplified API.
    +# To get started, generate a new key using java -classpath esapi.jar org.owasp.esapi.reference.crypto.JavaEncryptor
    +# There is not currently any support for key rotation, so be careful when changing your key and salt as it
    +# will invalidate all signed, encrypted, and hashed data.
    +#
    +# WARNING: Not all combinations of algorithms and key lengths are supported.
    +# If you choose to use a key length greater than 128, you MUST download the
    +# unlimited strength policy files and install in the lib directory of your JRE/JDK.
    +# See http://java.sun.com/javase/downloads/index.jsp for more information.
    +#
    +# Backward compatibility with ESAPI Java 1.4 is supported by the two deprecated API
    +# methods, Encryptor.encrypt(String) and Encryptor.decrypt(String). However, whenever
    +# possible, these methods should be avoided as they use ECB cipher mode, which in almost
    +# all circumstances a poor choice because of it's weakness. CBC cipher mode is the default
    +# for the new Encryptor encrypt / decrypt methods for ESAPI Java 2.0.  In general, you
    +# should only use this compatibility setting if you have persistent data encrypted with
    +# version 1.4 and even then, you should ONLY set this compatibility mode UNTIL
    +# you have decrypted all of your old encrypted data and then re-encrypted it with
    +# ESAPI 2.0 using CBC mode. If you have some reason to mix the deprecated 1.4 mode
    +# with the new 2.0 methods, make sure that you use the same cipher algorithm for both
    +# (256-bit AES was the default for 1.4; 128-bit is the default for 2.0; see below for
    +# more details.) Otherwise, you will have to use the new 2.0 encrypt / decrypt methods
    +# where you can specify a SecretKey. (Note that if you are using the 256-bit AES,
    +# that requires downloading the special jurisdiction policy files mentioned above.)
    +#
    +#		***** IMPORTANT: These are for JUnit testing. Test files may have been
    +#						 encrypted using these values so do not change these or
    +#						 those tests will fail. The version under
    +#							src/main/resources/.esapi/ESAPI.properties
    +#						 will be delivered with Encryptor.MasterKey and
    +#						 Encryptor.MasterSalt set to the empty string.
    +#
    +#						 FINAL NOTE:
    +#                           If Maven changes these when run, that needs to be fixed.
    +#       256-bit key... requires unlimited strength jurisdiction policy files
    +### Encryptor.MasterKey=pJhlri8JbuFYDgkqtHmm9s0Ziug2PE7ovZDyEPm4j14=
    +#       128-bit key
    +Encryptor.MasterKey=a6H9is3hEVGKB4Jut+lOVA==
    +Encryptor.MasterSalt=SbftnvmEWD5ZHHP+pX3fqugNysc=
    +# Encryptor.MasterSalt=
    +
    +# Provides the default JCE provider that ESAPI will "prefer" for its symmetric
    +# encryption and hashing. (That is it will look to this provider first, but it
    +# will defer to other providers if the requested algorithm is not implemented
    +# by this provider.) If left unset, ESAPI will just use your Java VM's current
    +# preferred JCE provider, which is generally set in the file
    +# "$JAVA_HOME/jre/lib/security/java.security".
    +#
    +# The main intent of this is to allow ESAPI symmetric encryption to be
    +# used with a FIPS 140-2 compliant crypto-module. For details, see the section
    +# "Using ESAPI Symmetric Encryption with FIPS 140-2 Cryptographic Modules" in
    +# the ESAPI 2.0 Symmetric Encryption User Guide, at:
    +# http://owasp-esapi-java.googlecode.com/svn/trunk/documentation/esapi4java-core-2.0-symmetric-crypto-user-guide.html
    +# However, this property also allows you to easily use an alternate JCE provider
    +# such as "Bouncy Castle" without having to make changes to "java.security".
    +# See Javadoc for SecurityProviderLoader for further details. If you wish to use
    +# a provider that is not known to SecurityProviderLoader, you may specify the
    +# fully-qualified class name of the JCE provider class that implements
    +# java.security.Provider. If the name contains a '.', this is interpreted as
    +# a fully-qualified class name that implements java.security.Provider.
    +#
    +# NOTE: Setting this property has the side-effect of changing it in your application
    +#       as well, so if you are using JCE in your application directly rather than
    +#       through ESAPI (you wouldn't do that, would you? ;-), it will change the
    +#       preferred JCE provider there as well.
    +#
    +# Default: Keeps the JCE provider set to whatever JVM sets it to.
    +Encryptor.PreferredJCEProvider=
    +
    +# AES is the most widely used and strongest encryption algorithm. This
    +# should agree with your Encryptor.CipherTransformation property.
    +# By default, ESAPI Java 1.4 uses "PBEWithMD5AndDES" and which is
    +# very weak. It is essentially a password-based encryption key, hashed
    +# with MD5 around 1K times and then encrypted with the weak DES algorithm
    +# (56-bits) using ECB mode and an unspecified padding (it is
    +# JCE provider specific, but most likely "NoPadding"). However, 2.0 uses
    +# "AES/CBC/PKCSPadding". If you want to change these, change them here.
    +# Warning: This property does not control the default reference implementation for
    +#		   ESAPI 2.0 using JavaEncryptor. Also, this property will be dropped
    +#		   in the future.
    +# @deprecated
    +Encryptor.EncryptionAlgorithm=AES
    +#		For ESAPI Java 2.0 - New encrypt / decrypt methods use this.
    +Encryptor.CipherTransformation=AES/CBC/PKCS5Padding
    +
    +# Applies to ESAPI 2.0 and later only!
    +# Comma-separated list of cipher modes that provide *BOTH*
    +# confidentiality *AND* message authenticity. (NIST refers to such cipher
    +# modes as "combined modes" so that's what we shall call them.) If any of these
    +# cipher modes are used then no MAC is calculated and stored
    +# in the CipherText upon encryption. Likewise, if one of these
    +# cipher modes is used with decryption, no attempt will be made
    +# to validate the MAC contained in the CipherText object regardless
    +# of whether it contains one or not. Since the expectation is that
    +# these cipher modes support support message authenticity already,
    +# injecting a MAC in the CipherText object would be at best redundant.
    +#
    +# Note that as of JDK 1.5, the SunJCE provider does not support *any*
    +# of these cipher modes. Of these listed, only GCM and CCM are currently
    +# NIST approved. YMMV for other JCE providers. E.g., Bouncy Castle supports
    +# GCM and CCM with "NoPadding" mode, but not with "PKCS5Padding" or other
    +# padding modes.
    +Encryptor.cipher_modes.combined_modes=GCM,CCM,IAPM,EAX,OCB,CWC
    +
    +# Applies to ESAPI 2.0 and later only!
    +# Additional cipher modes allowed for ESAPI 2.0 encryption. These
    +# cipher modes are in _addition_ to those specified by the property
    +# 'Encryptor.cipher_modes.combined_modes'.
    +# Note: We will add support for streaming modes like CFB & OFB once
    +# we add support for 'specified' to the property 'Encryptor.ChooseIVMethod'
    +# (probably in ESAPI 2.1).
    +#
    +#	IMPORTANT NOTE:	In the official ESAPI.properties we do *NOT* include ECB
    +#					here as this is an extremely weak mode. However, we *must*
    +#					allow it here so we can test ECB mode. That is important
    +#					since the logic is somewhat different (i.e., ECB mode does
    +#					not use an IV).
    +# DISCUSS: Better name?
    +#	NOTE: ECB added only for testing purposes. Don't try this at home!
    +Encryptor.cipher_modes.additional_allowed=CBC,ECB
    +
    +# 128-bit is almost always sufficient and appears to be more resistant to
    +# related key attacks than is 256-bit AES. Use '_' to use default key size
    +# for cipher algorithms (where it makes sense because the algorithm supports
    +# a variable key size). Key length must agree to what's provided as the
    +# cipher transformation, otherwise this will be ignored after logging a
    +# warning.
    +#
    +# NOTE: This is what applies BOTH ESAPI 1.4 and 2.0. See warning above about mixing!
    +Encryptor.EncryptionKeyLength=128
    +
    +# Because 2.0 uses CBC mode by default, it requires an initialization vector (IV).
    +# (All cipher modes except ECB require an IV.) There are two choices: we can either
    +# use a fixed IV known to both parties or allow ESAPI to choose a random IV. While
    +# the IV does not need to be hidden from adversaries, it is important that the
    +# adversary not be allowed to choose it. Also, random IVs are generally much more
    +# secure than fixed IVs. (In fact, it is essential that feed-back cipher modes
    +# such as CFB and OFB use a different IV for each encryption with a given key so
    +# in such cases, random IVs are much preferred. By default, ESAPI 2.0 uses random
    +# IVs. If you wish to use 'fixed' IVs, set 'Encryptor.ChooseIVMethod=fixed' and
    +# uncomment the Encryptor.fixedIV.
    +#
    +# Valid values:		random|fixed|specified		'specified' not yet implemented; planned for 2.1
    +Encryptor.ChooseIVMethod=random
    +# If you choose to use a fixed IV, then you must place a fixed IV here that
    +# is known to all others who are sharing your secret key. The format should
    +# be a hex string that is the same length as the cipher block size for the
    +# cipher algorithm that you are using. The following is an example for AES
    +# from an AES test vector for AES-128/CBC as described in:
    +# NIST Special Publication 800-38A (2001 Edition)
    +# "Recommendation for Block Cipher Modes of Operation".
    +# (Note that the block size for AES is 16 bytes == 128 bits.)
    +#
    +Encryptor.fixedIV=0x000102030405060708090a0b0c0d0e0f
    +
    +# Whether or not CipherText should use a message authentication code (MAC) with it.
    +# This prevents an adversary from altering the IV as well as allowing a more
    +# fool-proof way of determining the decryption failed because of an incorrect
    +# key being supplied. This refers to the "separate" MAC calculated and stored
    +# in CipherText, not part of any MAC that is calculated as a result of a
    +# "combined mode" cipher mode.
    +#
    +# If you are using ESAPI with a FIPS 140-2 cryptographic module, you *must* also
    +# set this property to false.
    +Encryptor.CipherText.useMAC=true
    +
    +# Whether or not the PlainText object may be overwritten and then marked
    +# eligible for garbage collection. If not set, this is still treated as 'true'.
    +Encryptor.PlainText.overwrite=true
    +
    +# Do not use DES except in a legacy situations. 56-bit is way too small key size.
    +#Encryptor.EncryptionKeyLength=56
    +#Encryptor.EncryptionAlgorithm=DES
    +
    +# TripleDES is considered strong enough for most purposes.
    +#	Note:	There is also a 112-bit version of DESede. Using the 168-bit version
    +#			requires downloading the special jurisdiction policy from Sun.
    +#Encryptor.EncryptionKeyLength=168
    +#Encryptor.EncryptionAlgorithm=DESede
    +
    +Encryptor.HashAlgorithm=SHA-512
    +Encryptor.HashIterations=1024
    +Encryptor.DigitalSignatureAlgorithm=SHA1withDSA
    +Encryptor.DigitalSignatureKeyLength=1024
    +Encryptor.RandomAlgorithm=SHA1PRNG
    +Encryptor.CharacterEncoding=UTF-8
    +# Currently supported choices for JDK 1.5 and 1.6 are:
    +#	HmacSHA1 (160 bits), HmacSHA256 (256 bits), HmacSHA384 (384 bits), and
    +#	HmacSHA512 (512 bits).
    +# Note that HmacMD5 is *not* supported for the PRF used by the KDF even though
    +# these JDKs support it.
    +Encryptor.KDF.PRF=HmacSHA256
    +
    +#===========================================================================
    +# ESAPI HttpUtilties
    +#
    +# The HttpUtilities provide basic protections to HTTP requests and responses. Primarily these methods 
    +# protect against malicious data from attackers, such as unprintable characters, escaped characters,
    +# and other simple attacks. The HttpUtilities also provides utility methods for dealing with cookies,
    +# headers, and CSRF tokens.
    +#
    +# Default file upload location (remember to escape backslashes with \\)
    +HttpUtilities.UploadDir=C:\\ESAPI\\testUpload
    +# let this default to java.io.tmpdir for testing
    +#HttpUtilities.UploadTempDir=C:\\temp
    +# Force flags on cookies, if you use HttpUtilities to set cookies
    +HttpUtilities.ForceHttpOnlySession=false
    +HttpUtilities.ForceSecureSession=false
    +HttpUtilities.ForceHttpOnlyCookies=true
    +HttpUtilities.ForceSecureCookies=true
    +# Maximum size of HTTP headers
    +HttpUtilities.MaxHeaderSize=4096
    +# File upload configuration
    +HttpUtilities.ApprovedUploadExtensions=.zip,.pdf,.doc,.docx,.ppt,.pptx,.tar,.gz,.tgz,.rar,.war,.jar,.ear,.xls,.rtf,.properties,.java,.class,.txt,.xml,.jsp,.jsf,.exe,.dll
    +HttpUtilities.MaxUploadFileBytes=500000000
    +# Using UTF-8 throughout your stack is highly recommended. That includes your database driver,
    +# container, and any other technologies you may be using. Failure to do this may expose you
    +# to Unicode transcoding injection attacks. Use of UTF-8 does not hinder internationalization.
    +HttpUtilities.ResponseContentType=text/html; charset=UTF-8
    +# This is the name of the cookie used to represent the HTTP session
    +# Typically this will be the default "JSESSIONID" 
    +HttpUtilities.HttpSessionIdName=JSESSIONID
    +
    +
    +
    +#===========================================================================
    +# ESAPI Executor
    +# CHECKME - Not sure what this is used for, but surely it should be made OS independent.
    +Executor.WorkingDirectory=C:\\Windows\\Temp
    +Executor.ApprovedExecutables=C:\\Windows\\System32\\cmd.exe,C:\\Windows\\System32\\runas.exe
    +
    +
    +#===========================================================================
    +# ESAPI Logging
    +# Set the application name if these logs are combined with other applications
    +Logger.ApplicationName=ExampleApplication
    +# If you use an HTML log viewer that does not properly HTML escape log data, you can set LogEncodingRequired to true
    +Logger.LogEncodingRequired=false
    +# Determines whether ESAPI should log the application name. This might be clutter in some single-server/single-app environments.
    +Logger.LogApplicationName=true
    +# Determines whether ESAPI should log the server IP and port. This might be clutter in some single-server environments.
    +Logger.LogServerIP=true
    +# LogFileName, the name of the logging file. Provide a full directory path (e.g., C:\\ESAPI\\ESAPI_logging_file) if you
    +# want to place it in a specific directory.
    +Logger.LogFileName=ESAPI_logging_file
    +# MaxLogFileSize, the max size (in bytes) of a single log file before it cuts over to a new one (default is 10,000,000)
    +Logger.MaxLogFileSize=10000000
    +
    +
    +#===========================================================================
    +# ESAPI Intrusion Detection
    +#
    +# Each event has a base to which .count, .interval, and .action are added
    +# The IntrusionException will fire if we receive "count" events within "interval" seconds
    +# The IntrusionDetector is configurable to take the following actions: log, logout, and disable
    +#  (multiple actions separated by commas are allowed e.g. event.test.actions=log,disable
    +#
    +# Custom Events
    +# Names must start with "event." as the base
    +# Use IntrusionDetector.addEvent( "test" ) in your code to trigger "event.test" here
    +# You can also disable intrusion detection completely by changing
    +# the following parameter to true
    +#
    +IntrusionDetector.Disable=false
    +#
    +IntrusionDetector.event.test.count=2
    +IntrusionDetector.event.test.interval=10
    +IntrusionDetector.event.test.actions=disable,log
    +
    +# Exception Events
    +# All EnterpriseSecurityExceptions are registered automatically
    +# Call IntrusionDetector.getInstance().addException(e) for Exceptions that do not extend EnterpriseSecurityException
    +# Use the fully qualified classname of the exception as the base
    +
    +# any intrusion is an attack
    +IntrusionDetector.org.owasp.esapi.errors.IntrusionException.count=1
    +IntrusionDetector.org.owasp.esapi.errors.IntrusionException.interval=1
    +IntrusionDetector.org.owasp.esapi.errors.IntrusionException.actions=log,disable,logout
    +
    +# for test purposes
    +# CHECKME: Shouldn't there be something in the property name itself that designates
    +#		   that these are for testing???
    +IntrusionDetector.org.owasp.esapi.errors.IntegrityException.count=10
    +IntrusionDetector.org.owasp.esapi.errors.IntegrityException.interval=5
    +IntrusionDetector.org.owasp.esapi.errors.IntegrityException.actions=log,disable,logout
    +
    +# rapid validation errors indicate scans or attacks in progress
    +# org.owasp.esapi.errors.ValidationException.count=10
    +# org.owasp.esapi.errors.ValidationException.interval=10
    +# org.owasp.esapi.errors.ValidationException.actions=log,logout
    +
    +# sessions jumping between hosts indicates session hijacking
    +IntrusionDetector.org.owasp.esapi.errors.AuthenticationHostException.count=2
    +IntrusionDetector.org.owasp.esapi.errors.AuthenticationHostException.interval=10
    +IntrusionDetector.org.owasp.esapi.errors.AuthenticationHostException.actions=log,logout
    +
    +
    +#===========================================================================
    +# ESAPI Validation
    +#
    +# The ESAPI Validator works on regular expressions with defined names. You can define names
    +# either here, or you may define application specific patterns in a separate file defined below.
    +# This allows enterprises to specify both organizational standards as well as application specific
    +# validation rules.
    +#
    +# Use '\p{L}' (without the quotes) within the character class to match
    +# any Unicode LETTER. You can also use a range, like:  \u00C0-\u017F
    +# You can also use any of the regex flags as documented at
    +# https://docs.oracle.com/javase/tutorial/essential/regex/pattern.html, e.g. (?u)
    +#
    +Validator.ConfigurationFile=validation.properties
    +
    +# Validators used by ESAPI
    +Validator.AccountName=^[a-zA-Z0-9]{3,20}$
    +Validator.SystemCommand=^[a-zA-Z\\-\\/]{1,64}$
    +Validator.RoleName=^[a-z]{1,20}$
    +Validator.Redirect=^\\/test.*$
    +
    +# Global HTTP Validation Rules
    +# Values with Base64 encoded data (e.g. encrypted state) will need at least [a-zA-Z0-9\/+=]
    +Validator.HTTPScheme=^(http|https)$
    +Validator.HTTPServerName=^[a-zA-Z0-9_.\\-]*$
    +Validator.HTTPCookieName=^[a-zA-Z0-9\\-_]{1,32}$
    +Validator.HTTPCookieValue=^[a-zA-Z0-9\\-\\/+=_ ]*$
    +# Note that max header name capped at 150 in SecurityRequestWrapper!
    +Validator.HTTPHeaderName=^[a-zA-Z0-9\\-_]{1,50}$
    +Validator.HTTPHeaderValue=^[a-zA-Z0-9()\\-=\\*\\.\\?;,+\\/:&_ ]*$
    +Validator.HTTPServletPath=^[a-zA-Z0-9.\\-\\/_]*$
    +Validator.HTTPPath=^[a-zA-Z0-9.\\-_]*$
    +Validator.HTTPURL=^.*$
    +Validator.HTTPJSESSIONID=^[A-Z0-9]{10,30}$
    +
    +# Contributed by Fraenku@gmx.ch
    +# Googlecode Issue 116 (http://code.google.com/p/owasp-esapi-java/issues/detail?id=116)
    +Validator.HTTPParameterName=^[a-zA-Z0-9_\\-]{1,32}$
    +Validator.HTTPParameterValue=^[\\p{L}\\p{N}.\\-/+=_ !$*?@]{0,1000}$
    +Validator.HTTPContextPath=^/[a-zA-Z0-9.\\-_]*$
    +Validator.HTTPQueryString=^([a-zA-Z0-9_\\-]{1,32}=[\\p{L}\\p{N}.\\-/+=_ !$*?@%]*&?)*$
    +Validator.HTTPURI=^/([a-zA-Z0-9.\\-_]*/?)*$
    +
    +
    +# Validation of file related input
    +Validator.FileName=^[a-zA-Z0-9!@#$%^&{}\\[\\]()_+\\-=,.~'` ]{1,255}$
    +Validator.DirectoryName=^[a-zA-Z0-9:/\\\\!@#$%^&{}\\[\\]()_+\\-=,.~'` ]{1,255}$
    +
    +# Validation of dates. Controls whether or not 'lenient' dates are accepted.
    +# See DataFormat.setLenient(boolean flag) for further details.
    +Validator.AcceptLenientDates=false
    
    From ab4d74b374242a350a7716c51385d850e00b9215 Mon Sep 17 00:00:00 2001
    From: kwwall 
    Date: Tue, 19 Jan 2016 01:12:42 -0500
    Subject: [PATCH 0281/1069] Address issue 356.
    
    ---
     .../resources/esapi/validation.properties     | 58 +++++++++----------
     1 file changed, 29 insertions(+), 29 deletions(-)
    
    diff --git a/src/test/resources/esapi/validation.properties b/src/test/resources/esapi/validation.properties
    index 18e037f3b..433fa0b6b 100644
    --- a/src/test/resources/esapi/validation.properties
    +++ b/src/test/resources/esapi/validation.properties
    @@ -1,29 +1,29 @@
    -# The ESAPI validator does many security checks on input, such as canonicalization
    -# and whitelist validation. Note that all of these validation rules are applied *after*
    -# canonicalization. Double-encoded characters (even with different encodings involved,
    -# are never allowed.
    -#
    -# To use:
    -#
    -# First set up a pattern below. You can choose any name you want, prefixed by the word
    -# "Validation." For example:
    -#   Validation.Email=^[A-Za-z0-9._%-]+@[A-Za-z0-9.-]+\\.[a-zA-Z]{2,4}$
    -# 
    -# Then you can validate in your code against the pattern like this:
    -#     ESAPI.validator().isValidInput("User Email", input, "Email", maxLength, allowNull);
    -# Where maxLength and allowNull are set for you needs, respectively.
    -#
    -# But note, when you use boolean variants of validation functions, you lose critical 
    -# canonicalization. It is preferable to use the "get" methods (which throw exceptions) and 
    -# and use the returned user input which is in canonical form. Consider the following:
    -#  
    -# try {
    -#    someObject.setEmail(ESAPI.validator().getValidInput("User Email", input, "Email", maxLength, allowNull));
    -#
    -Validator.SafeString=^[.\\p{Alnum}\\p{Space}]{0,1024}$
    -Validator.Email=^[A-Za-z0-9._%'-]+@[A-Za-z0-9.-]+\\.[a-zA-Z]{2,4}$
    -Validator.IPAddress=^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$
    -Validator.URL=^(ht|f)tp(s?)\\:\\/\\/[0-9a-zA-Z]([-.\\w]*[0-9a-zA-Z])*(:(0-9)*)*(\\/?)([a-zA-Z0-9\\-\\.\\?\\,\\:\\'\\/\\\\\\+=&%\\$#_]*)?$
    -Validator.CreditCard=^(\\d{4}[- ]?){3}\\d{4}$
    -Validator.SSN=^(?!000)([0-6]\\d{2}|7([0-6]\\d|7[012]))([ -]?)(?!00)\\d\\d\\3(?!0000)\\d{4}$
    -
    +# The ESAPI validator does many security checks on input, such as canonicalization
    +# and whitelist validation. Note that all of these validation rules are applied *after*
    +# canonicalization. Double-encoded characters (even with different encodings involved,
    +# are never allowed.
    +#
    +# To use:
    +#
    +# First set up a pattern below. You can choose any name you want, prefixed by the word
    +# "Validation." For example:
    +#   Validation.Email=^[A-Za-z0-9._%-]+@[A-Za-z0-9.-]+\\.[a-zA-Z]{2,4}$
    +# 
    +# Then you can validate in your code against the pattern like this:
    +#     ESAPI.validator().isValidInput("User Email", input, "Email", maxLength, allowNull);
    +# Where maxLength and allowNull are set for you needs, respectively.
    +#
    +# But note, when you use boolean variants of validation functions, you lose critical 
    +# canonicalization. It is preferable to use the "get" methods (which throw exceptions) and 
    +# and use the returned user input which is in canonical form. Consider the following:
    +#  
    +# try {
    +#    someObject.setEmail(ESAPI.validator().getValidInput("User Email", input, "Email", maxLength, allowNull));
    +#
    +Validator.SafeString=^[.\\p{Alnum}\\p{Space}]{0,1024}$
    +Validator.Email=^[A-Za-z0-9._%'-]+@[A-Za-z0-9.-]+\\.[a-zA-Z]{2,4}$
    +Validator.IPAddress=^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$
    +Validator.URL=^(ht|f)tp(s?)\\:\\/\\/[0-9a-zA-Z]([-.\\w]*[0-9a-zA-Z])*(:(0-9)*)*(\\/?)([a-zA-Z0-9\\-\\.\\?\\,\\:\\'\\/\\\\\\+=&%\\$#_]*)?$
    +Validator.CreditCard=^(\\d{4}[- ]?){3}\\d{4}$
    +Validator.SSN=^(?!000)([0-6]\\d{2}|7([0-6]\\d|7[012]))([ -]?)(?!00)\\d\\d\\3(?!0000)\\d{4}$
    +
    
    From 392eae1efc6d078bebd4d3a8e287ec6f0055a225 Mon Sep 17 00:00:00 2001
    From: kwwall 
    Date: Tue, 19 Jan 2016 01:12:42 -0500
    Subject: [PATCH 0282/1069] Address issue 356.
    
    ---
     .../properties/ESAPI_en_US.properties          | 18 +++++++++---------
     1 file changed, 9 insertions(+), 9 deletions(-)
    
    diff --git a/src/test/resources/properties/ESAPI_en_US.properties b/src/test/resources/properties/ESAPI_en_US.properties
    index 9b43d1a23..04f043b21 100644
    --- a/src/test/resources/properties/ESAPI_en_US.properties
    +++ b/src/test/resources/properties/ESAPI_en_US.properties
    @@ -1,9 +1,9 @@
    -# User Messages
    -Error.creating.randomizer=Error creating randomizer
    -
    -This.is.test.message=This {0} is {1} a test {2} message
    -
    -# Validation Messages
    -
    -# Log Messages
    -
    +# User Messages
    +Error.creating.randomizer=Error creating randomizer
    +
    +This.is.test.message=This {0} is {1} a test {2} message
    +
    +# Validation Messages
    +
    +# Log Messages
    +
    
    From c4c9281679689b396f5d3fbff743193fd3879b31 Mon Sep 17 00:00:00 2001
    From: kwwall 
    Date: Tue, 19 Jan 2016 01:12:43 -0500
    Subject: [PATCH 0283/1069] Address issue 356.
    
    ---
     .../properties/ESAPI_fr_FR.properties          | 18 +++++++++---------
     1 file changed, 9 insertions(+), 9 deletions(-)
    
    diff --git a/src/test/resources/properties/ESAPI_fr_FR.properties b/src/test/resources/properties/ESAPI_fr_FR.properties
    index 89ebcba7d..3e5a7a8f5 100644
    --- a/src/test/resources/properties/ESAPI_fr_FR.properties
    +++ b/src/test/resources/properties/ESAPI_fr_FR.properties
    @@ -1,9 +1,9 @@
    -# User Messages
    -Error.creating.randomizer=Erreur lors de la création aléatoire
    -
    -This.is.test.message=Ceci est un message de test message singh
    -
    -# Validation Messages
    -
    -# Log Messages
    -
    +# User Messages
    +Error.creating.randomizer=Erreur lors de la création aléatoire
    +
    +This.is.test.message=Ceci est un message de test message singh
    +
    +# Validation Messages
    +
    +# Log Messages
    +
    
    From 3dd0aecb6fed7c2c32f66083d0728d612e785d8a Mon Sep 17 00:00:00 2001
    From: kwwall 
    Date: Tue, 19 Jan 2016 01:15:37 -0500
    Subject: [PATCH 0284/1069] Continue to try to address issue 356.
    
    ---
     .gitattributes | 8 ++++++--
     1 file changed, 6 insertions(+), 2 deletions(-)
    
    diff --git a/.gitattributes b/.gitattributes
    index 3645b7bba..74f99db34 100644
    --- a/.gitattributes
    +++ b/.gitattributes
    @@ -39,10 +39,14 @@
     *.bsh text eol=lf
     *.ksh text eol=lf
     
    -# More
    -.gitattributes text
    +# Eclipse stuff
     .settings/* text eol=crlf
     .classpath text eol=crlf
    +.project text eol=crlf
    +
    +# Miscellaneous text
    +.gitattributes text eol=lf
    +.gitignore text eol=lf
     *.MF text eol=crlf
     LICENSE text eol=crlf
     LICENSE-CONTENT text eol=crlf
    
    From 2f3650d87ec45bd56ee8999e850c0beb1a9950cc Mon Sep 17 00:00:00 2001
    From: kwwall 
    Date: Tue, 19 Jan 2016 01:17:57 -0500
    Subject: [PATCH 0285/1069] Issue 356 EOL matters
    
    ---
     configuration/esapi/waf-policies/bean-shell-rule.bsh | 8 ++++----
     1 file changed, 4 insertions(+), 4 deletions(-)
    
    diff --git a/configuration/esapi/waf-policies/bean-shell-rule.bsh b/configuration/esapi/waf-policies/bean-shell-rule.bsh
    index 2a1f423e7..08c8424c4 100644
    --- a/configuration/esapi/waf-policies/bean-shell-rule.bsh
    +++ b/configuration/esapi/waf-policies/bean-shell-rule.bsh
    @@ -1,5 +1,5 @@
    -import org.owasp.esapi.waf.actions.*;
    -
    -session.setAttribute("simple_waf_test", "true");
    -
    +import org.owasp.esapi.waf.actions.*;
    +
    +session.setAttribute("simple_waf_test", "true");
    +
     action = new RedirectAction();
    \ No newline at end of file
    
    From ce5cbf3058c9c283afbb11dde8661edc77a87ca3 Mon Sep 17 00:00:00 2001
    From: kwwall 
    Date: Tue, 19 Jan 2016 01:17:57 -0500
    Subject: [PATCH 0286/1069] Issue 356 EOL matters
    
    ---
     .../java/org/owasp/esapi/errors/package.html  | 34 +++++++++----------
     1 file changed, 17 insertions(+), 17 deletions(-)
    
    diff --git a/src/main/java/org/owasp/esapi/errors/package.html b/src/main/java/org/owasp/esapi/errors/package.html
    index cb35f9c0b..4e061dd18 100644
    --- a/src/main/java/org/owasp/esapi/errors/package.html
    +++ b/src/main/java/org/owasp/esapi/errors/package.html
    @@ -1,17 +1,17 @@
    -
    -
    -
    -
    -
    -
    -
    -A set of exception classes designed to model the error conditions that
    -frequently arise in enterprise web applications and web services. The
    -root class is the EnterpriseSecurityException and all the other
    -exception classes extend this basic class. The
    -EnterpriseSecurityException automatically logs a message in the
    -constructor so that a full set of security events are captured in the
    -logs.
    -
    -
    -
    +
    +
    +
    +
    +
    +
    +
    +A set of exception classes designed to model the error conditions that
    +frequently arise in enterprise web applications and web services. The
    +root class is the EnterpriseSecurityException and all the other
    +exception classes extend this basic class. The
    +EnterpriseSecurityException automatically logs a message in the
    +constructor so that a full set of security events are captured in the
    +logs.
    +
    +
    +
    
    From f0fbbb1f9c86f5006e79d07fca92afc5a8becb2e Mon Sep 17 00:00:00 2001
    From: kwwall 
    Date: Tue, 19 Jan 2016 01:17:57 -0500
    Subject: [PATCH 0287/1069] Issue 356 EOL matters
    
    ---
     src/main/java/org/owasp/esapi/reference/package.html | 10 +++++-----
     1 file changed, 5 insertions(+), 5 deletions(-)
    
    diff --git a/src/main/java/org/owasp/esapi/reference/package.html b/src/main/java/org/owasp/esapi/reference/package.html
    index 0e110cb91..e1fe0b048 100644
    --- a/src/main/java/org/owasp/esapi/reference/package.html
    +++ b/src/main/java/org/owasp/esapi/reference/package.html
    @@ -5,11 +5,11 @@
     
     
     
    -This package contains reference implementations of the ESAPI interfaces. These are intended to
    -serve as examples of how your enterprise might implement these functions. The reference implementations
    -are high quality and pass all of the ESAPI test cases. Many of the reference implementations are
    -likely to be useful in your enterprise without change (Validator, Encoder, Encryptor, etc...).
    -Implementing other classes (Authenticator, User, AccessController, Logger, etc...) will likely need to
    +This package contains reference implementations of the ESAPI interfaces. These are intended to
    +serve as examples of how your enterprise might implement these functions. The reference implementations
    +are high quality and pass all of the ESAPI test cases. Many of the reference implementations are
    +likely to be useful in your enterprise without change (Validator, Encoder, Encryptor, etc...).
    +Implementing other classes (Authenticator, User, AccessController, Logger, etc...) will likely need to
     be customized for your enterprise, to integrate with your backend systems and policies.
     
     
    
    From 3c4385286f866fe92ecdcdf00408a52a25874fc2 Mon Sep 17 00:00:00 2001
    From: kwwall 
    Date: Tue, 19 Jan 2016 01:17:57 -0500
    Subject: [PATCH 0288/1069] Issue 356 EOL matters
    
    ---
     .../esapi/reference/validation/package.html   | 22 +++++++++----------
     1 file changed, 11 insertions(+), 11 deletions(-)
    
    diff --git a/src/main/java/org/owasp/esapi/reference/validation/package.html b/src/main/java/org/owasp/esapi/reference/validation/package.html
    index 050397abf..b950c12a7 100644
    --- a/src/main/java/org/owasp/esapi/reference/validation/package.html
    +++ b/src/main/java/org/owasp/esapi/reference/validation/package.html
    @@ -1,11 +1,11 @@
    -
    -
    -
    -
    -
    -
    -
    -This package contains data format-specific validation rule functions.
    - 
    -
    -
    +
    +
    +
    +
    +
    +
    +
    +This package contains data format-specific validation rule functions.
    + 
    +
    +
    
    From d0d363ebd2b78b40f131823d3666a4275fe5745b Mon Sep 17 00:00:00 2001
    From: kwwall 
    Date: Tue, 19 Jan 2016 01:17:58 -0500
    Subject: [PATCH 0289/1069] Issue 356 EOL matters
    
    ---
     .../org/owasp/esapi/waf/actions/package.html  | 22 +++++++++----------
     1 file changed, 11 insertions(+), 11 deletions(-)
    
    diff --git a/src/main/java/org/owasp/esapi/waf/actions/package.html b/src/main/java/org/owasp/esapi/waf/actions/package.html
    index 94adcc808..89657d08c 100644
    --- a/src/main/java/org/owasp/esapi/waf/actions/package.html
    +++ b/src/main/java/org/owasp/esapi/waf/actions/package.html
    @@ -1,11 +1,11 @@
    -
    -
    -
    -
    -
    -
    -
    -This package contains the Action objects that are executed after a Rule subclass executes. 
    - 
    -
    -
    +
    +
    +
    +
    +
    +
    +
    +This package contains the Action objects that are executed after a Rule subclass executes. 
    + 
    +
    +
    
    From 04902a8957c24c1c863a00926b8ca724dccea009 Mon Sep 17 00:00:00 2001
    From: kwwall 
    Date: Tue, 19 Jan 2016 01:17:58 -0500
    Subject: [PATCH 0290/1069] Issue 356 EOL matters
    
    ---
     .../esapi/waf/configuration/package.html      | 24 +++++++++----------
     1 file changed, 12 insertions(+), 12 deletions(-)
    
    diff --git a/src/main/java/org/owasp/esapi/waf/configuration/package.html b/src/main/java/org/owasp/esapi/waf/configuration/package.html
    index ea68242bf..8c41b3a27 100644
    --- a/src/main/java/org/owasp/esapi/waf/configuration/package.html
    +++ b/src/main/java/org/owasp/esapi/waf/configuration/package.html
    @@ -1,12 +1,12 @@
    -
    -
    -
    -
    -
    -
    -
    -This package contains the both the configuration object model and the 
    -utility class to create that object model from an existing policy file. 
    - 
    -
    -
    +
    +
    +
    +
    +
    +
    +
    +This package contains the both the configuration object model and the 
    +utility class to create that object model from an existing policy file. 
    + 
    +
    +
    
    From dc7d5406c7d1f2b6a8710d14309f353f167f8e39 Mon Sep 17 00:00:00 2001
    From: kwwall 
    Date: Tue, 19 Jan 2016 01:17:58 -0500
    Subject: [PATCH 0291/1069] Issue 356 EOL matters
    
    ---
     .../org/owasp/esapi/waf/internal/package.html | 24 +++++++++----------
     1 file changed, 12 insertions(+), 12 deletions(-)
    
    diff --git a/src/main/java/org/owasp/esapi/waf/internal/package.html b/src/main/java/org/owasp/esapi/waf/internal/package.html
    index 3b535518f..a94870056 100644
    --- a/src/main/java/org/owasp/esapi/waf/internal/package.html
    +++ b/src/main/java/org/owasp/esapi/waf/internal/package.html
    @@ -1,12 +1,12 @@
    -
    -
    -
    -
    -
    -
    -
    -This package contains all HTTP-related classes used internally by the WAF for the implementation
    -of its rules. 
    - 
    -
    -
    +
    +
    +
    +
    +
    +
    +
    +This package contains all HTTP-related classes used internally by the WAF for the implementation
    +of its rules. 
    + 
    +
    +
    
    From 0c21fbaa9763fbe555e799413628caff20cc8399 Mon Sep 17 00:00:00 2001
    From: kwwall 
    Date: Tue, 19 Jan 2016 01:17:58 -0500
    Subject: [PATCH 0292/1069] Issue 356 EOL matters
    
    ---
     .../java/org/owasp/esapi/waf/package.html     | 28 +++++++++----------
     1 file changed, 14 insertions(+), 14 deletions(-)
    
    diff --git a/src/main/java/org/owasp/esapi/waf/package.html b/src/main/java/org/owasp/esapi/waf/package.html
    index bf23005ed..8f025e3ed 100644
    --- a/src/main/java/org/owasp/esapi/waf/package.html
    +++ b/src/main/java/org/owasp/esapi/waf/package.html
    @@ -1,14 +1,14 @@
    -
    -
    -
    -
    -
    -
    -
    -This package contains the ESAPI Web Application Firewall (WAF). It is an optional feature of ESAPI
    -that can be used with or without ESAPI's other security controls in place. It's purpose is to provide
    -fast virtual patching capabilities against known vulnerabilities or the enforcement of existing
    -security policies where possible.
    - 
    -
    -
    +
    +
    +
    +
    +
    +
    +
    +This package contains the ESAPI Web Application Firewall (WAF). It is an optional feature of ESAPI
    +that can be used with or without ESAPI's other security controls in place. It's purpose is to provide
    +fast virtual patching capabilities against known vulnerabilities or the enforcement of existing
    +security policies where possible.
    + 
    +
    +
    
    From ca61c6decc5c7e60d1847b32682e0f9d10d70bae Mon Sep 17 00:00:00 2001
    From: kwwall 
    Date: Tue, 19 Jan 2016 01:17:58 -0500
    Subject: [PATCH 0293/1069] Issue 356 EOL matters
    
    ---
     .../org/owasp/esapi/waf/rules/package.html    | 24 +++++++++----------
     1 file changed, 12 insertions(+), 12 deletions(-)
    
    diff --git a/src/main/java/org/owasp/esapi/waf/rules/package.html b/src/main/java/org/owasp/esapi/waf/rules/package.html
    index 133148e47..0f29d7ea3 100644
    --- a/src/main/java/org/owasp/esapi/waf/rules/package.html
    +++ b/src/main/java/org/owasp/esapi/waf/rules/package.html
    @@ -1,12 +1,12 @@
    -
    -
    -
    -
    -
    -
    -
    -This package contains all of the Rule subclasses that correspond to policy file entries. Each 
    -class contains the logic for enforcing its rule. 
    - 
    -
    -
    +
    +
    +
    +
    +
    +
    +
    +This package contains all of the Rule subclasses that correspond to policy file entries. Each 
    +class contains the logic for enforcing its rule. 
    + 
    +
    +
    
    From 99917ec05d892f6e53a8e025872c2434430ad755 Mon Sep 17 00:00:00 2001
    From: kwwall 
    Date: Tue, 19 Jan 2016 01:17:58 -0500
    Subject: [PATCH 0294/1069] Issue 356 EOL matters
    
    ---
     .../java/org/owasp/esapi/http/package.html    | 30 +++++++++----------
     1 file changed, 15 insertions(+), 15 deletions(-)
    
    diff --git a/src/test/java/org/owasp/esapi/http/package.html b/src/test/java/org/owasp/esapi/http/package.html
    index 76f720fb6..c61efd69f 100644
    --- a/src/test/java/org/owasp/esapi/http/package.html
    +++ b/src/test/java/org/owasp/esapi/http/package.html
    @@ -1,15 +1,15 @@
    -
    -
    -
    -
    -
    -
    -
    -A few simple mock classes to help test the ESAPI reference
    -implementation. These classes are not fully functional and only
    -implement the functions required to test the library. These
    -implementations are not fully accurate and may not behave like the real
    -Java EE classes.
    -
    -
    -
    +
    +
    +
    +
    +
    +
    +
    +A few simple mock classes to help test the ESAPI reference
    +implementation. These classes are not fully functional and only
    +implement the functions required to test the library. These
    +implementations are not fully accurate and may not behave like the real
    +Java EE classes.
    +
    +
    +
    
    From c2585acb10d7bb075b17f6878d84f88b87de74b5 Mon Sep 17 00:00:00 2001
    From: kwwall 
    Date: Tue, 19 Jan 2016 01:17:58 -0500
    Subject: [PATCH 0295/1069] Issue 356 EOL matters
    
    ---
     src/test/resources/esapi/waf-policies/bean-shell-rule.bsh | 8 ++++----
     1 file changed, 4 insertions(+), 4 deletions(-)
    
    diff --git a/src/test/resources/esapi/waf-policies/bean-shell-rule.bsh b/src/test/resources/esapi/waf-policies/bean-shell-rule.bsh
    index 2a1f423e7..08c8424c4 100644
    --- a/src/test/resources/esapi/waf-policies/bean-shell-rule.bsh
    +++ b/src/test/resources/esapi/waf-policies/bean-shell-rule.bsh
    @@ -1,5 +1,5 @@
    -import org.owasp.esapi.waf.actions.*;
    -
    -session.setAttribute("simple_waf_test", "true");
    -
    +import org.owasp.esapi.waf.actions.*;
    +
    +session.setAttribute("simple_waf_test", "true");
    +
     action = new RedirectAction();
    \ No newline at end of file
    
    From 86c89a20e49209f853cbd04f9361e30ca03e2e2d Mon Sep 17 00:00:00 2001
    From: kwwall 
    Date: Tue, 19 Jan 2016 21:46:51 -0500
    Subject: [PATCH 0296/1069] Close issue #354.
    
    ---
     .../java/org/owasp/esapi/codecs/Base64.java   | 27 ++++++++-
     .../owasp/esapi/reference/EncoderTest.java    | 56 +++++++++++++++++++
     2 files changed, 82 insertions(+), 1 deletion(-)
    
    diff --git a/src/main/java/org/owasp/esapi/codecs/Base64.java b/src/main/java/org/owasp/esapi/codecs/Base64.java
    index 624d23c3d..b92f35590 100644
    --- a/src/main/java/org/owasp/esapi/codecs/Base64.java
    +++ b/src/main/java/org/owasp/esapi/codecs/Base64.java
    @@ -8,7 +8,7 @@
     // I think that really depends on how much OWASP ESAPI plans on tracking changes to this
     // version vs. if the plan was just to fork from it and maintain OWASP's own version.
     // At this point, I think I prefer split from tracking Harder's original, but I'm easily
    -// persuaded otherwise. - Kevin Wall
    +// persuaded otherwise. (In fact, we have already done so w/ decodeToObject().) - Kevin Wall
     
     /**
      * 

    Encodes and decodes to and from Base64 notation.

    @@ -136,6 +136,12 @@ public class Base64 */ public final static int ORDERED = 32; + /** + * System property name that must be set to true in order to invoke {@code Base64.decodeToObject()}. + * @see https://github.com/ESAPI/esapi-java-legacy/issues/354 + * @see http://foxglovesecurity.com/2015/11/06/what-do-weblogic-websphere-jboss-jenkins-opennms-and-your-application-have-in-common-this-vulnerability/ + */ + public final static String ENABLE_UNSAFE_SERIALIZATION = "org.owasp.esapi.enableUnsafeSerialization"; // Do NOT change! /* ******** P R I V A T E F I E L D S ******** */ @@ -1091,6 +1097,15 @@ public static byte[] decode( String s, int options ) * untrusted data from a string and deserialize it into * an object can potentially result in remote command * injection vulnerabilities. Use at your own risk! + *

    IMPORTANT BACKWARD COMPATIBILITY NOTICE
    + * Because this static method can easily be used as an attack vector + * for those passing in deserialized objects, in a manner similar to the + * Apache Commons Collections InvokerTransformer + * issue, we are requiring that the system property + * {@code org.owasp.esapi.enableUnsafeSerialization} + * be set to "true" in order for this method to be successfully invoked. + * We apologize for the inconvenience this may cause in breaking anyone's + * application, but we feel that it is for the greater good. *

    * * @param encodedObject The Base64 data to decode @@ -1110,6 +1125,16 @@ public static byte[] decode( String s, int options ) @Deprecated public static Object decodeToObject( String encodedObject ) { + // We will do better when we attempt this again, allowing for a second argument + // to specify some sort of a collection of white-listed classes. Until then... + // See: http://www.ibm.com/developerworks/library/se-lookahead/ for how-to. + if ( ! "true".equalsIgnoreCase( System.getProperty( ENABLE_UNSAFE_SERIALIZATION ) ) ) { + throw new UnsupportedOperationException( + "Deserialization by Base64.decodeToObject(String) is disabled for security reasons. " + + "To re-enable it, set the system property '" + ENABLE_UNSAFE_SERIALIZATION + "' to 'true'." + + "For details, see: https://github.com/ESAPI/esapi-java-legacy/issues/354"); + } + // Decode and gunzip if necessary byte[] objBytes = decode( encodedObject ); diff --git a/src/test/java/org/owasp/esapi/reference/EncoderTest.java b/src/test/java/org/owasp/esapi/reference/EncoderTest.java index 3e8f17a6a..bc2e1afde 100644 --- a/src/test/java/org/owasp/esapi/reference/EncoderTest.java +++ b/src/test/java/org/owasp/esapi/reference/EncoderTest.java @@ -19,6 +19,8 @@ import java.io.UnsupportedEncodingException; import java.util.ArrayList; import java.util.Arrays; +import java.io.ByteArrayOutputStream; +import java.io.ObjectOutputStream; import junit.framework.Test; import junit.framework.TestCase; @@ -27,6 +29,7 @@ import org.owasp.esapi.ESAPI; import org.owasp.esapi.Encoder; import org.owasp.esapi.EncoderConstants; +import org.owasp.esapi.codecs.Base64; import org.owasp.esapi.codecs.Codec; import org.owasp.esapi.codecs.MySQLCodec; import org.owasp.esapi.codecs.OracleCodec; @@ -632,6 +635,59 @@ public void testDecodeFromBase64() { } } + /** + * Test of Base64.decodeToObject() method. Should really be put into a + * separate Base64Test.java class, but this method has been deprecated + * so hopefully, we can kill it off soon. + */ + public void testBase64decodToObject() { + try { + System.out.println("testBase64decodeToObject"); + + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + ObjectOutputStream dout = new ObjectOutputStream(baos); + + // If you don't get the joke, google John Draper. (And, BTW, you should + // be ashamed of yourself if you call yourself a hacker!) + String cerealizeThis = new String("Cap'n Crunch - every hacker's favorite cereal"); + dout.writeObject( cerealizeThis ); + byte[] serializedData = baos.toByteArray(); + dout.close(); + + String b64serialized = Base64.encodeBytes( serializedData ); + final String propName = Base64.ENABLE_UNSAFE_SERIALIZATION; + + // Make sure the property is not set. + try { System.clearProperty( propName ); } catch(Throwable t) { ; } + String capnCrunch = null; + + try { + capnCrunch = (String)Base64.decodeToObject( b64serialized ); + fail("Case 1: Did not throw UnsupportedOperationException"); + } catch(UnsupportedOperationException uoex) { + ; // Expected case + } + + try { + System.setProperty( propName, "false" ); + capnCrunch = (String)Base64.decodeToObject( b64serialized ); + fail("Case 2: Did not throw UnsupportedOperationException"); + } catch(UnsupportedOperationException uoex) { + ; // Expected case + } + + try { + // This case should work. + System.setProperty( propName, "true" ); + capnCrunch = (String)Base64.decodeToObject( b64serialized ); + assertTrue( capnCrunch.equals( cerealizeThis ) ); + } catch(Throwable t) { + fail("Case 3: Caught unexpected exception: " + t); + } + } catch(Throwable t) { + fail("Caught unexpected exception: " + t); + } + } /** * Test of WindowsCodec From 21f3992440b1894b654d93c031c705bc9e4cd52c Mon Sep 17 00:00:00 2001 From: kwwall Date: Tue, 19 Jan 2016 21:58:13 -0500 Subject: [PATCH 0297/1069] Git is complaining it's modified, but I'm committing w/out any actual change. Let's see what happens. --- LICENSE | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/LICENSE b/LICENSE index 5d0f11c51..20c1657fa 100644 --- a/LICENSE +++ b/LICENSE @@ -1,12 +1,12 @@ -The BSD License - -Copyright (c) 2007, The OWASP Foundation -All rights reserved. - -Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - -Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. -Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. -Neither the name of the OWASP Foundation nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - +The BSD License + +Copyright (c) 2007, The OWASP Foundation +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. +Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. +Neither the name of the OWASP Foundation nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + From 73995a44c0351041f6f6400a36c2c0571822602f Mon Sep 17 00:00:00 2001 From: kwwall Date: Tue, 19 Jan 2016 22:06:16 -0500 Subject: [PATCH 0298/1069] No explicit changes; just did a add, followed by commit, but that seemed to fix the others. --- LICENSE-CONTENT | 156 +++++++++--------- LICENSE-README | 12 +- documentation/esapi4java-2.0-readme.txt | 120 +++++++------- ...va-2.0rc6-override-log4jloggingfactory.txt | 142 ++++++++-------- .../esapi4java-core-2.1-release-notes.txt | 136 +++++++-------- src/main/java/META-INF/MANIFEST.MF | 6 +- 6 files changed, 286 insertions(+), 286 deletions(-) diff --git a/LICENSE-CONTENT b/LICENSE-CONTENT index 9d154ce74..b5b0f5e05 100644 --- a/LICENSE-CONTENT +++ b/LICENSE-CONTENT @@ -1,78 +1,78 @@ -Creative Commons -Creative Commons Legal Code -Attribution-ShareAlike 3.0 Unported - - CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE LEGAL SERVICES. DISTRIBUTION OF THIS LICENSE DOES NOT CREATE AN ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES REGARDING THE INFORMATION PROVIDED, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM ITS USE. - -License - -THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS CREATIVE COMMONS PUBLIC LICENSE ("CCPL" OR "LICENSE"). THE WORK IS PROTECTED BY COPYRIGHT AND/OR OTHER APPLICABLE LAW. ANY USE OF THE WORK OTHER THAN AS AUTHORIZED UNDER THIS LICENSE OR COPYRIGHT LAW IS PROHIBITED. - -BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND AGREE TO BE BOUND BY THE TERMS OF THIS LICENSE. TO THE EXTENT THIS LICENSE MAY BE CONSIDERED TO BE A CONTRACT, THE LICENSOR GRANTS YOU THE RIGHTS CONTAINED HERE IN CONSIDERATION OF YOUR ACCEPTANCE OF SUCH TERMS AND CONDITIONS. - -1. Definitions - - 1. "Adaptation" means a work based upon the Work, or upon the Work and other pre-existing works, such as a translation, adaptation, derivative work, arrangement of music or other alterations of a literary or artistic work, or phonogram or performance and includes cinematographic adaptations or any other form in which the Work may be recast, transformed, or adapted including in any form recognizably derived from the original, except that a work that constitutes a Collection will not be considered an Adaptation for the purpose of this License. For the avoidance of doubt, where the Work is a musical work, performance or phonogram, the synchronization of the Work in timed-relation with a moving image ("synching") will be considered an Adaptation for the purpose of this License. - 2. "Collection" means a collection of literary or artistic works, such as encyclopedias and anthologies, or performances, phonograms or broadcasts, or other works or subject matter other than works listed in Section 1(f) below, which, by reason of the selection and arrangement of their contents, constitute intellectual creations, in which the Work is included in its entirety in unmodified form along with one or more other contributions, each constituting separate and independent works in themselves, which together are assembled into a collective whole. A work that constitutes a Collection will not be considered an Adaptation (as defined below) for the purposes of this License. - 3. "Creative Commons Compatible License" means a license that is listed at http://creativecommons.org/compatiblelicenses that has been approved by Creative Commons as being essentially equivalent to this License, including, at a minimum, because that license: (i) contains terms that have the same purpose, meaning and effect as the License Elements of this License; and, (ii) explicitly permits the relicensing of adaptations of works made available under that license under this License or a Creative Commons jurisdiction license with the same License Elements as this License. - 4. "Distribute" means to make available to the public the original and copies of the Work or Adaptation, as appropriate, through sale or other transfer of ownership. - 5. "License Elements" means the following high-level license attributes as selected by Licensor and indicated in the title of this License: Attribution, ShareAlike. - 6. "Licensor" means the individual, individuals, entity or entities that offer(s) the Work under the terms of this License. - 7. "Original Author" means, in the case of a literary or artistic work, the individual, individuals, entity or entities who created the Work or if no individual or entity can be identified, the publisher; and in addition (i) in the case of a performance the actors, singers, musicians, dancers, and other persons who act, sing, deliver, declaim, play in, interpret or otherwise perform literary or artistic works or expressions of folklore; (ii) in the case of a phonogram the producer being the person or legal entity who first fixes the sounds of a performance or other sounds; and, (iii) in the case of broadcasts, the organization that transmits the broadcast. - 8. "Work" means the literary and/or artistic work offered under the terms of this License including without limitation any production in the literary, scientific and artistic domain, whatever may be the mode or form of its expression including digital form, such as a book, pamphlet and other writing; a lecture, address, sermon or other work of the same nature; a dramatic or dramatico-musical work; a choreographic work or entertainment in dumb show; a musical composition with or without words; a cinematographic work to which are assimilated works expressed by a process analogous to cinematography; a work of drawing, painting, architecture, sculpture, engraving or lithography; a photographic work to which are assimilated works expressed by a process analogous to photography; a work of applied art; an illustration, map, plan, sketch or three-dimensional work relative to geography, topography, architecture or science; a performance; a broadcast; a phonogram; a compilation of data to the extent it is protected as a copyrightable work; or a work performed by a variety or circus performer to the extent it is not otherwise considered a literary or artistic work. - 9. "You" means an individual or entity exercising rights under this License who has not previously violated the terms of this License with respect to the Work, or who has received express permission from the Licensor to exercise rights under this License despite a previous violation. - 10. "Publicly Perform" means to perform public recitations of the Work and to communicate to the public those public recitations, by any means or process, including by wire or wireless means or public digital performances; to make available to the public Works in such a way that members of the public may access these Works from a place and at a place individually chosen by them; to perform the Work to the public by any means or process and the communication to the public of the performances of the Work, including by public digital performance; to broadcast and rebroadcast the Work by any means including signs, sounds or images. - 11. "Reproduce" means to make copies of the Work by any means including without limitation by sound or visual recordings and the right of fixation and reproducing fixations of the Work, including storage of a protected performance or phonogram in digital form or other electronic medium. - -2. Fair Dealing Rights. Nothing in this License is intended to reduce, limit, or restrict any uses free from copyright or rights arising from limitations or exceptions that are provided for in connection with the copyright protection under copyright law or other applicable laws. - -3. License Grant. Subject to the terms and conditions of this License, Licensor hereby grants You a worldwide, royalty-free, non-exclusive, perpetual (for the duration of the applicable copyright) license to exercise the rights in the Work as stated below: - - 1. to Reproduce the Work, to incorporate the Work into one or more Collections, and to Reproduce the Work as incorporated in the Collections; - 2. to create and Reproduce Adaptations provided that any such Adaptation, including any translation in any medium, takes reasonable steps to clearly label, demarcate or otherwise identify that changes were made to the original Work. For example, a translation could be marked "The original work was translated from English to Spanish," or a modification could indicate "The original work has been modified."; - 3. to Distribute and Publicly Perform the Work including as incorporated in Collections; and, - 4. to Distribute and Publicly Perform Adaptations. - 5. - - For the avoidance of doubt: - 1. Non-waivable Compulsory License Schemes. In those jurisdictions in which the right to collect royalties through any statutory or compulsory licensing scheme cannot be waived, the Licensor reserves the exclusive right to collect such royalties for any exercise by You of the rights granted under this License; - 2. Waivable Compulsory License Schemes. In those jurisdictions in which the right to collect royalties through any statutory or compulsory licensing scheme can be waived, the Licensor waives the exclusive right to collect such royalties for any exercise by You of the rights granted under this License; and, - 3. Voluntary License Schemes. The Licensor waives the right to collect royalties, whether individually or, in the event that the Licensor is a member of a collecting society that administers voluntary licensing schemes, via that society, from any exercise by You of the rights granted under this License. - -The above rights may be exercised in all media and formats whether now known or hereafter devised. The above rights include the right to make such modifications as are technically necessary to exercise the rights in other media and formats. Subject to Section 8(f), all rights not expressly granted by Licensor are hereby reserved. - -4. Restrictions. The license granted in Section 3 above is expressly made subject to and limited by the following restrictions: - - 1. You may Distribute or Publicly Perform the Work only under the terms of this License. You must include a copy of, or the Uniform Resource Identifier (URI) for, this License with every copy of the Work You Distribute or Publicly Perform. You may not offer or impose any terms on the Work that restrict the terms of this License or the ability of the recipient of the Work to exercise the rights granted to that recipient under the terms of the License. You may not sublicense the Work. You must keep intact all notices that refer to this License and to the disclaimer of warranties with every copy of the Work You Distribute or Publicly Perform. When You Distribute or Publicly Perform the Work, You may not impose any effective technological measures on the Work that restrict the ability of a recipient of the Work from You to exercise the rights granted to that recipient under the terms of the License. This Section 4(a) applies to the Work as incorporated in a Collection, but this does not require the Collection apart from the Work itself to be made subject to the terms of this License. If You create a Collection, upon notice from any Licensor You must, to the extent practicable, remove from the Collection any credit as required by Section 4(c), as requested. If You create an Adaptation, upon notice from any Licensor You must, to the extent practicable, remove from the Adaptation any credit as required by Section 4(c), as requested. - 2. You may Distribute or Publicly Perform an Adaptation only under the terms of: (i) this License; (ii) a later version of this License with the same License Elements as this License; (iii) a Creative Commons jurisdiction license (either this or a later license version) that contains the same License Elements as this License (e.g., Attribution-ShareAlike 3.0 US)); (iv) a Creative Commons Compatible License. If you license the Adaptation under one of the licenses mentioned in (iv), you must comply with the terms of that license. If you license the Adaptation under the terms of any of the licenses mentioned in (i), (ii) or (iii) (the "Applicable License"), you must comply with the terms of the Applicable License generally and the following provisions: (I) You must include a copy of, or the URI for, the Applicable License with every copy of each Adaptation You Distribute or Publicly Perform; (II) You may not offer or impose any terms on the Adaptation that restrict the terms of the Applicable License or the ability of the recipient of the Adaptation to exercise the rights granted to that recipient under the terms of the Applicable License; (III) You must keep intact all notices that refer to the Applicable License and to the disclaimer of warranties with every copy of the Work as included in the Adaptation You Distribute or Publicly Perform; (IV) when You Distribute or Publicly Perform the Adaptation, You may not impose any effective technological measures on the Adaptation that restrict the ability of a recipient of the Adaptation from You to exercise the rights granted to that recipient under the terms of the Applicable License. This Section 4(b) applies to the Adaptation as incorporated in a Collection, but this does not require the Collection apart from the Adaptation itself to be made subject to the terms of the Applicable License. - 3. If You Distribute, or Publicly Perform the Work or any Adaptations or Collections, You must, unless a request has been made pursuant to Section 4(a), keep intact all copyright notices for the Work and provide, reasonable to the medium or means You are utilizing: (i) the name of the Original Author (or pseudonym, if applicable) if supplied, and/or if the Original Author and/or Licensor designate another party or parties (e.g., a sponsor institute, publishing entity, journal) for attribution ("Attribution Parties") in Licensor's copyright notice, terms of service or by other reasonable means, the name of such party or parties; (ii) the title of the Work if supplied; (iii) to the extent reasonably practicable, the URI, if any, that Licensor specifies to be associated with the Work, unless such URI does not refer to the copyright notice or licensing information for the Work; and (iv) , consistent with Ssection 3(b), in the case of an Adaptation, a credit identifying the use of the Work in the Adaptation (e.g., "French translation of the Work by Original Author," or "Screenplay based on original Work by Original Author"). The credit required by this Section 4(c) may be implemented in any reasonable manner; provided, however, that in the case of a Adaptation or Collection, at a minimum such credit will appear, if a credit for all contributing authors of the Adaptation or Collection appears, then as part of these credits and in a manner at least as prominent as the credits for the other contributing authors. For the avoidance of doubt, You may only use the credit required by this Section for the purpose of attribution in the manner set out above and, by exercising Your rights under this License, You may not implicitly or explicitly assert or imply any connection with, sponsorship or endorsement by the Original Author, Licensor and/or Attribution Parties, as appropriate, of You or Your use of the Work, without the separate, express prior written permission of the Original Author, Licensor and/or Attribution Parties. - 4. Except as otherwise agreed in writing by the Licensor or as may be otherwise permitted by applicable law, if You Reproduce, Distribute or Publicly Perform the Work either by itself or as part of any Adaptations or Collections, You must not distort, mutilate, modify or take other derogatory action in relation to the Work which would be prejudicial to the Original Author's honor or reputation. Licensor agrees that in those jurisdictions (e.g. Japan), in which any exercise of the right granted in Section 3(b) of this License (the right to make Adaptations) would be deemed to be a distortion, mutilation, modification or other derogatory action prejudicial to the Original Author's honor and reputation, the Licensor will waive or not assert, as appropriate, this Section, to the fullest extent permitted by the applicable national law, to enable You to reasonably exercise Your right under Section 3(b) of this License (right to make Adaptations) but not otherwise. - -5. Representations, Warranties and Disclaimer - -UNLESS OTHERWISE MUTUALLY AGREED TO BY THE PARTIES IN WRITING, LICENSOR OFFERS THE WORK AS-IS AND MAKES NO REPRESENTATIONS OR WARRANTIES OF ANY KIND CONCERNING THE WORK, EXPRESS, IMPLIED, STATUTORY OR OTHERWISE, INCLUDING, WITHOUT LIMITATION, WARRANTIES OF TITLE, MERCHANTIBILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT OR OTHER DEFECTS, ACCURACY, OR THE PRESENCE OF ABSENCE OF ERRORS, WHETHER OR NOT DISCOVERABLE. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OF IMPLIED WARRANTIES, SO SUCH EXCLUSION MAY NOT APPLY TO YOU. - -6. Limitation on Liability. EXCEPT TO THE EXTENT REQUIRED BY APPLICABLE LAW, IN NO EVENT WILL LICENSOR BE LIABLE TO YOU ON ANY LEGAL THEORY FOR ANY SPECIAL, INCIDENTAL, CONSEQUENTIAL, PUNITIVE OR EXEMPLARY DAMAGES ARISING OUT OF THIS LICENSE OR THE USE OF THE WORK, EVEN IF LICENSOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - -7. Termination - - 1. This License and the rights granted hereunder will terminate automatically upon any breach by You of the terms of this License. Individuals or entities who have received Adaptations or Collections from You under this License, however, will not have their licenses terminated provided such individuals or entities remain in full compliance with those licenses. Sections 1, 2, 5, 6, 7, and 8 will survive any termination of this License. - 2. Subject to the above terms and conditions, the license granted here is perpetual (for the duration of the applicable copyright in the Work). Notwithstanding the above, Licensor reserves the right to release the Work under different license terms or to stop distributing the Work at any time; provided, however that any such election will not serve to withdraw this License (or any other license that has been, or is required to be, granted under the terms of this License), and this License will continue in full force and effect unless terminated as stated above. - -8. Miscellaneous - - 1. Each time You Distribute or Publicly Perform the Work or a Collection, the Licensor offers to the recipient a license to the Work on the same terms and conditions as the license granted to You under this License. - 2. Each time You Distribute or Publicly Perform an Adaptation, Licensor offers to the recipient a license to the original Work on the same terms and conditions as the license granted to You under this License. - 3. If any provision of this License is invalid or unenforceable under applicable law, it shall not affect the validity or enforceability of the remainder of the terms of this License, and without further action by the parties to this agreement, such provision shall be reformed to the minimum extent necessary to make such provision valid and enforceable. - 4. No term or provision of this License shall be deemed waived and no breach consented to unless such waiver or consent shall be in writing and signed by the party to be charged with such waiver or consent. - 5. This License constitutes the entire agreement between the parties with respect to the Work licensed here. There are no understandings, agreements or representations with respect to the Work not specified here. Licensor shall not be bound by any additional provisions that may appear in any communication from You. This License may not be modified without the mutual written agreement of the Licensor and You. - 6. The rights granted under, and the subject matter referenced, in this License were drafted utilizing the terminology of the Berne Convention for the Protection of Literary and Artistic Works (as amended on September 28, 1979), the Rome Convention of 1961, the WIPO Copyright Treaty of 1996, the WIPO Performances and Phonograms Treaty of 1996 and the Universal Copyright Convention (as revised on July 24, 1971). These rights and subject matter take effect in the relevant jurisdiction in which the License terms are sought to be enforced according to the corresponding provisions of the implementation of those treaty provisions in the applicable national law. If the standard suite of rights granted under applicable copyright law includes additional rights not granted under this License, such additional rights are deemed to be included in the License; this License is not intended to restrict the license of any rights under applicable law. - - Creative Commons Notice - - Creative Commons is not a party to this License, and makes no warranty whatsoever in connection with the Work. Creative Commons will not be liable to You or any party on any legal theory for any damages whatsoever, including without limitation any general, special, incidental or consequential damages arising in connection to this license. Notwithstanding the foregoing two (2) sentences, if Creative Commons has expressly identified itself as the Licensor hereunder, it shall have all rights and obligations of Licensor. - - Except for the limited purpose of indicating to the public that the Work is licensed under the CCPL, Creative Commons does not authorize the use by either party of the trademark "Creative Commons" or any related trademark or logo of Creative Commons without the prior written consent of Creative Commons. Any permitted use will be in compliance with Creative Commons' then-current trademark usage guidelines, as may be published on its website or otherwise made available upon request from time to time. For the avoidance of doubt, this trademark restriction does not form part of the License. - - Creative Commons may be contacted at http://creativecommons.org/. - +Creative Commons +Creative Commons Legal Code +Attribution-ShareAlike 3.0 Unported + + CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE LEGAL SERVICES. DISTRIBUTION OF THIS LICENSE DOES NOT CREATE AN ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES REGARDING THE INFORMATION PROVIDED, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM ITS USE. + +License + +THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS CREATIVE COMMONS PUBLIC LICENSE ("CCPL" OR "LICENSE"). THE WORK IS PROTECTED BY COPYRIGHT AND/OR OTHER APPLICABLE LAW. ANY USE OF THE WORK OTHER THAN AS AUTHORIZED UNDER THIS LICENSE OR COPYRIGHT LAW IS PROHIBITED. + +BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND AGREE TO BE BOUND BY THE TERMS OF THIS LICENSE. TO THE EXTENT THIS LICENSE MAY BE CONSIDERED TO BE A CONTRACT, THE LICENSOR GRANTS YOU THE RIGHTS CONTAINED HERE IN CONSIDERATION OF YOUR ACCEPTANCE OF SUCH TERMS AND CONDITIONS. + +1. Definitions + + 1. "Adaptation" means a work based upon the Work, or upon the Work and other pre-existing works, such as a translation, adaptation, derivative work, arrangement of music or other alterations of a literary or artistic work, or phonogram or performance and includes cinematographic adaptations or any other form in which the Work may be recast, transformed, or adapted including in any form recognizably derived from the original, except that a work that constitutes a Collection will not be considered an Adaptation for the purpose of this License. For the avoidance of doubt, where the Work is a musical work, performance or phonogram, the synchronization of the Work in timed-relation with a moving image ("synching") will be considered an Adaptation for the purpose of this License. + 2. "Collection" means a collection of literary or artistic works, such as encyclopedias and anthologies, or performances, phonograms or broadcasts, or other works or subject matter other than works listed in Section 1(f) below, which, by reason of the selection and arrangement of their contents, constitute intellectual creations, in which the Work is included in its entirety in unmodified form along with one or more other contributions, each constituting separate and independent works in themselves, which together are assembled into a collective whole. A work that constitutes a Collection will not be considered an Adaptation (as defined below) for the purposes of this License. + 3. "Creative Commons Compatible License" means a license that is listed at http://creativecommons.org/compatiblelicenses that has been approved by Creative Commons as being essentially equivalent to this License, including, at a minimum, because that license: (i) contains terms that have the same purpose, meaning and effect as the License Elements of this License; and, (ii) explicitly permits the relicensing of adaptations of works made available under that license under this License or a Creative Commons jurisdiction license with the same License Elements as this License. + 4. "Distribute" means to make available to the public the original and copies of the Work or Adaptation, as appropriate, through sale or other transfer of ownership. + 5. "License Elements" means the following high-level license attributes as selected by Licensor and indicated in the title of this License: Attribution, ShareAlike. + 6. "Licensor" means the individual, individuals, entity or entities that offer(s) the Work under the terms of this License. + 7. "Original Author" means, in the case of a literary or artistic work, the individual, individuals, entity or entities who created the Work or if no individual or entity can be identified, the publisher; and in addition (i) in the case of a performance the actors, singers, musicians, dancers, and other persons who act, sing, deliver, declaim, play in, interpret or otherwise perform literary or artistic works or expressions of folklore; (ii) in the case of a phonogram the producer being the person or legal entity who first fixes the sounds of a performance or other sounds; and, (iii) in the case of broadcasts, the organization that transmits the broadcast. + 8. "Work" means the literary and/or artistic work offered under the terms of this License including without limitation any production in the literary, scientific and artistic domain, whatever may be the mode or form of its expression including digital form, such as a book, pamphlet and other writing; a lecture, address, sermon or other work of the same nature; a dramatic or dramatico-musical work; a choreographic work or entertainment in dumb show; a musical composition with or without words; a cinematographic work to which are assimilated works expressed by a process analogous to cinematography; a work of drawing, painting, architecture, sculpture, engraving or lithography; a photographic work to which are assimilated works expressed by a process analogous to photography; a work of applied art; an illustration, map, plan, sketch or three-dimensional work relative to geography, topography, architecture or science; a performance; a broadcast; a phonogram; a compilation of data to the extent it is protected as a copyrightable work; or a work performed by a variety or circus performer to the extent it is not otherwise considered a literary or artistic work. + 9. "You" means an individual or entity exercising rights under this License who has not previously violated the terms of this License with respect to the Work, or who has received express permission from the Licensor to exercise rights under this License despite a previous violation. + 10. "Publicly Perform" means to perform public recitations of the Work and to communicate to the public those public recitations, by any means or process, including by wire or wireless means or public digital performances; to make available to the public Works in such a way that members of the public may access these Works from a place and at a place individually chosen by them; to perform the Work to the public by any means or process and the communication to the public of the performances of the Work, including by public digital performance; to broadcast and rebroadcast the Work by any means including signs, sounds or images. + 11. "Reproduce" means to make copies of the Work by any means including without limitation by sound or visual recordings and the right of fixation and reproducing fixations of the Work, including storage of a protected performance or phonogram in digital form or other electronic medium. + +2. Fair Dealing Rights. Nothing in this License is intended to reduce, limit, or restrict any uses free from copyright or rights arising from limitations or exceptions that are provided for in connection with the copyright protection under copyright law or other applicable laws. + +3. License Grant. Subject to the terms and conditions of this License, Licensor hereby grants You a worldwide, royalty-free, non-exclusive, perpetual (for the duration of the applicable copyright) license to exercise the rights in the Work as stated below: + + 1. to Reproduce the Work, to incorporate the Work into one or more Collections, and to Reproduce the Work as incorporated in the Collections; + 2. to create and Reproduce Adaptations provided that any such Adaptation, including any translation in any medium, takes reasonable steps to clearly label, demarcate or otherwise identify that changes were made to the original Work. For example, a translation could be marked "The original work was translated from English to Spanish," or a modification could indicate "The original work has been modified."; + 3. to Distribute and Publicly Perform the Work including as incorporated in Collections; and, + 4. to Distribute and Publicly Perform Adaptations. + 5. + + For the avoidance of doubt: + 1. Non-waivable Compulsory License Schemes. In those jurisdictions in which the right to collect royalties through any statutory or compulsory licensing scheme cannot be waived, the Licensor reserves the exclusive right to collect such royalties for any exercise by You of the rights granted under this License; + 2. Waivable Compulsory License Schemes. In those jurisdictions in which the right to collect royalties through any statutory or compulsory licensing scheme can be waived, the Licensor waives the exclusive right to collect such royalties for any exercise by You of the rights granted under this License; and, + 3. Voluntary License Schemes. The Licensor waives the right to collect royalties, whether individually or, in the event that the Licensor is a member of a collecting society that administers voluntary licensing schemes, via that society, from any exercise by You of the rights granted under this License. + +The above rights may be exercised in all media and formats whether now known or hereafter devised. The above rights include the right to make such modifications as are technically necessary to exercise the rights in other media and formats. Subject to Section 8(f), all rights not expressly granted by Licensor are hereby reserved. + +4. Restrictions. The license granted in Section 3 above is expressly made subject to and limited by the following restrictions: + + 1. You may Distribute or Publicly Perform the Work only under the terms of this License. You must include a copy of, or the Uniform Resource Identifier (URI) for, this License with every copy of the Work You Distribute or Publicly Perform. You may not offer or impose any terms on the Work that restrict the terms of this License or the ability of the recipient of the Work to exercise the rights granted to that recipient under the terms of the License. You may not sublicense the Work. You must keep intact all notices that refer to this License and to the disclaimer of warranties with every copy of the Work You Distribute or Publicly Perform. When You Distribute or Publicly Perform the Work, You may not impose any effective technological measures on the Work that restrict the ability of a recipient of the Work from You to exercise the rights granted to that recipient under the terms of the License. This Section 4(a) applies to the Work as incorporated in a Collection, but this does not require the Collection apart from the Work itself to be made subject to the terms of this License. If You create a Collection, upon notice from any Licensor You must, to the extent practicable, remove from the Collection any credit as required by Section 4(c), as requested. If You create an Adaptation, upon notice from any Licensor You must, to the extent practicable, remove from the Adaptation any credit as required by Section 4(c), as requested. + 2. You may Distribute or Publicly Perform an Adaptation only under the terms of: (i) this License; (ii) a later version of this License with the same License Elements as this License; (iii) a Creative Commons jurisdiction license (either this or a later license version) that contains the same License Elements as this License (e.g., Attribution-ShareAlike 3.0 US)); (iv) a Creative Commons Compatible License. If you license the Adaptation under one of the licenses mentioned in (iv), you must comply with the terms of that license. If you license the Adaptation under the terms of any of the licenses mentioned in (i), (ii) or (iii) (the "Applicable License"), you must comply with the terms of the Applicable License generally and the following provisions: (I) You must include a copy of, or the URI for, the Applicable License with every copy of each Adaptation You Distribute or Publicly Perform; (II) You may not offer or impose any terms on the Adaptation that restrict the terms of the Applicable License or the ability of the recipient of the Adaptation to exercise the rights granted to that recipient under the terms of the Applicable License; (III) You must keep intact all notices that refer to the Applicable License and to the disclaimer of warranties with every copy of the Work as included in the Adaptation You Distribute or Publicly Perform; (IV) when You Distribute or Publicly Perform the Adaptation, You may not impose any effective technological measures on the Adaptation that restrict the ability of a recipient of the Adaptation from You to exercise the rights granted to that recipient under the terms of the Applicable License. This Section 4(b) applies to the Adaptation as incorporated in a Collection, but this does not require the Collection apart from the Adaptation itself to be made subject to the terms of the Applicable License. + 3. If You Distribute, or Publicly Perform the Work or any Adaptations or Collections, You must, unless a request has been made pursuant to Section 4(a), keep intact all copyright notices for the Work and provide, reasonable to the medium or means You are utilizing: (i) the name of the Original Author (or pseudonym, if applicable) if supplied, and/or if the Original Author and/or Licensor designate another party or parties (e.g., a sponsor institute, publishing entity, journal) for attribution ("Attribution Parties") in Licensor's copyright notice, terms of service or by other reasonable means, the name of such party or parties; (ii) the title of the Work if supplied; (iii) to the extent reasonably practicable, the URI, if any, that Licensor specifies to be associated with the Work, unless such URI does not refer to the copyright notice or licensing information for the Work; and (iv) , consistent with Ssection 3(b), in the case of an Adaptation, a credit identifying the use of the Work in the Adaptation (e.g., "French translation of the Work by Original Author," or "Screenplay based on original Work by Original Author"). The credit required by this Section 4(c) may be implemented in any reasonable manner; provided, however, that in the case of a Adaptation or Collection, at a minimum such credit will appear, if a credit for all contributing authors of the Adaptation or Collection appears, then as part of these credits and in a manner at least as prominent as the credits for the other contributing authors. For the avoidance of doubt, You may only use the credit required by this Section for the purpose of attribution in the manner set out above and, by exercising Your rights under this License, You may not implicitly or explicitly assert or imply any connection with, sponsorship or endorsement by the Original Author, Licensor and/or Attribution Parties, as appropriate, of You or Your use of the Work, without the separate, express prior written permission of the Original Author, Licensor and/or Attribution Parties. + 4. Except as otherwise agreed in writing by the Licensor or as may be otherwise permitted by applicable law, if You Reproduce, Distribute or Publicly Perform the Work either by itself or as part of any Adaptations or Collections, You must not distort, mutilate, modify or take other derogatory action in relation to the Work which would be prejudicial to the Original Author's honor or reputation. Licensor agrees that in those jurisdictions (e.g. Japan), in which any exercise of the right granted in Section 3(b) of this License (the right to make Adaptations) would be deemed to be a distortion, mutilation, modification or other derogatory action prejudicial to the Original Author's honor and reputation, the Licensor will waive or not assert, as appropriate, this Section, to the fullest extent permitted by the applicable national law, to enable You to reasonably exercise Your right under Section 3(b) of this License (right to make Adaptations) but not otherwise. + +5. Representations, Warranties and Disclaimer + +UNLESS OTHERWISE MUTUALLY AGREED TO BY THE PARTIES IN WRITING, LICENSOR OFFERS THE WORK AS-IS AND MAKES NO REPRESENTATIONS OR WARRANTIES OF ANY KIND CONCERNING THE WORK, EXPRESS, IMPLIED, STATUTORY OR OTHERWISE, INCLUDING, WITHOUT LIMITATION, WARRANTIES OF TITLE, MERCHANTIBILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT OR OTHER DEFECTS, ACCURACY, OR THE PRESENCE OF ABSENCE OF ERRORS, WHETHER OR NOT DISCOVERABLE. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OF IMPLIED WARRANTIES, SO SUCH EXCLUSION MAY NOT APPLY TO YOU. + +6. Limitation on Liability. EXCEPT TO THE EXTENT REQUIRED BY APPLICABLE LAW, IN NO EVENT WILL LICENSOR BE LIABLE TO YOU ON ANY LEGAL THEORY FOR ANY SPECIAL, INCIDENTAL, CONSEQUENTIAL, PUNITIVE OR EXEMPLARY DAMAGES ARISING OUT OF THIS LICENSE OR THE USE OF THE WORK, EVEN IF LICENSOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + +7. Termination + + 1. This License and the rights granted hereunder will terminate automatically upon any breach by You of the terms of this License. Individuals or entities who have received Adaptations or Collections from You under this License, however, will not have their licenses terminated provided such individuals or entities remain in full compliance with those licenses. Sections 1, 2, 5, 6, 7, and 8 will survive any termination of this License. + 2. Subject to the above terms and conditions, the license granted here is perpetual (for the duration of the applicable copyright in the Work). Notwithstanding the above, Licensor reserves the right to release the Work under different license terms or to stop distributing the Work at any time; provided, however that any such election will not serve to withdraw this License (or any other license that has been, or is required to be, granted under the terms of this License), and this License will continue in full force and effect unless terminated as stated above. + +8. Miscellaneous + + 1. Each time You Distribute or Publicly Perform the Work or a Collection, the Licensor offers to the recipient a license to the Work on the same terms and conditions as the license granted to You under this License. + 2. Each time You Distribute or Publicly Perform an Adaptation, Licensor offers to the recipient a license to the original Work on the same terms and conditions as the license granted to You under this License. + 3. If any provision of this License is invalid or unenforceable under applicable law, it shall not affect the validity or enforceability of the remainder of the terms of this License, and without further action by the parties to this agreement, such provision shall be reformed to the minimum extent necessary to make such provision valid and enforceable. + 4. No term or provision of this License shall be deemed waived and no breach consented to unless such waiver or consent shall be in writing and signed by the party to be charged with such waiver or consent. + 5. This License constitutes the entire agreement between the parties with respect to the Work licensed here. There are no understandings, agreements or representations with respect to the Work not specified here. Licensor shall not be bound by any additional provisions that may appear in any communication from You. This License may not be modified without the mutual written agreement of the Licensor and You. + 6. The rights granted under, and the subject matter referenced, in this License were drafted utilizing the terminology of the Berne Convention for the Protection of Literary and Artistic Works (as amended on September 28, 1979), the Rome Convention of 1961, the WIPO Copyright Treaty of 1996, the WIPO Performances and Phonograms Treaty of 1996 and the Universal Copyright Convention (as revised on July 24, 1971). These rights and subject matter take effect in the relevant jurisdiction in which the License terms are sought to be enforced according to the corresponding provisions of the implementation of those treaty provisions in the applicable national law. If the standard suite of rights granted under applicable copyright law includes additional rights not granted under this License, such additional rights are deemed to be included in the License; this License is not intended to restrict the license of any rights under applicable law. + + Creative Commons Notice + + Creative Commons is not a party to this License, and makes no warranty whatsoever in connection with the Work. Creative Commons will not be liable to You or any party on any legal theory for any damages whatsoever, including without limitation any general, special, incidental or consequential damages arising in connection to this license. Notwithstanding the foregoing two (2) sentences, if Creative Commons has expressly identified itself as the Licensor hereunder, it shall have all rights and obligations of Licensor. + + Except for the limited purpose of indicating to the public that the Work is licensed under the CCPL, Creative Commons does not authorize the use by either party of the trademark "Creative Commons" or any related trademark or logo of Creative Commons without the prior written consent of Creative Commons. Any permitted use will be in compliance with Creative Commons' then-current trademark usage guidelines, as may be published on its website or otherwise made available upon request from time to time. For the avoidance of doubt, this trademark restriction does not form part of the License. + + Creative Commons may be contacted at http://creativecommons.org/. + diff --git a/LICENSE-README b/LICENSE-README index e7295e13e..6968b1478 100644 --- a/LICENSE-README +++ b/LICENSE-README @@ -1,7 +1,7 @@ -Please note that: - -1) The LICENSE file only refers to the licensing of the source and binary code of ESAPI. - For example, the actual ESAPI JAR file is only licensed under "The BSD License". - -2) The LICENSE-CONTENT file only refers to the licensing of the content and documentation of ESAPI. +Please note that: + +1) The LICENSE file only refers to the licensing of the source and binary code of ESAPI. + For example, the actual ESAPI JAR file is only licensed under "The BSD License". + +2) The LICENSE-CONTENT file only refers to the licensing of the content and documentation of ESAPI. For example, the documentation directory is only licensed under the Creative Commons/ShareAlike 3.0 Unported license. \ No newline at end of file diff --git a/documentation/esapi4java-2.0-readme.txt b/documentation/esapi4java-2.0-readme.txt index de7c40dda..f2b3e61b6 100644 --- a/documentation/esapi4java-2.0-readme.txt +++ b/documentation/esapi4java-2.0-readme.txt @@ -1,60 +1,60 @@ - - Welcome to ESAPI for Java! - -(This file best viewed full screen.) - -Here are the most significant directories and files included the zip file for this release: - -File / Directory Description -========================================================================================= -/ -| -+---configuration/ Directory of ESAPI configuration files -| | -| |---.esapi/ -| | |---waf-policies/ Directory containing Web Application Firewall policies -| | |---ESAPI.properties The main ESAPI configuration file -| | `---validation.properties Regular expressions used by the ESAPI validator -| | -| `---properties/ Examples of how to internationalize error messages??? -| |---ESAPI_en_US.properties in US/English -| |---ESAPI_fr_FR.properties in French -| `---ESAPI_zhs_CN.properties in Chinese -| -|---documentation/ ESAPI documentation -| | -| |---esapi4java-2.0-readme.txt The file you are now reading -| |---esapi4java-core-2.0-release-notes.pdf ESAPI 2.0 release notes (draft) -| |---esapi4java-core-2.0-install-guide.doc ESAPI 2.0 installation guide (draft) -| |---esapi4java-2.0rc6-override-log4jloggingfactory.txt How to use log4j to override User logging -| |---esapi4java-core-2.0-ciphertext-serialization.pdf Describes serialization layout of ESAPI 2.0 ciphertext representation -| |---esapi4java-core-2.0-crypto-design-goals.doc (draft) Describes ESAPI 2.0 crypto design goals & design decisions -| |---esapi4java-core-2.0-readme-crypto-changes.html Describes why crypto was changed from what was in ESAPI 1.4 -| |---esapi4java-core-2.0-symmetric-crypto-user-guide.html User guide for using symmetric encryption in ESAPI 2.0 -| |---esapi4java-core-2.1-release-notes.txt ESAPI 2.1 release notes -| `---esapi4java-waf-2.0-policy-file-spec.pdf Describes how to configure ESAPI 2.0's Web Application Firewall -| -|---libs/ ESAPI dependencies -| -|---site/ -| |---apidocs ESAPI Javadoc -| |---cobertura -| `---testapidocs ESAPI Javadoc for its JUnit test cases -| -|---src/ ESAPI source code -| -|---esapi-.jar The ESAPI jar for version (e.g., == 2.0_rc10) -| -|---LICENSE.txt ESAPI license for source code and documentation -| -`---pom.xml Maven's pom.xml for building ESAPI from source via mvn. - -=========================================================== - -Where to go from here -- please see the installation guide and the release -notes. - -Please address comments and questions concerning the API and this document to -the ESAPI Users mailing list, . - -Copyright (C) 2009-2010 The OWASP Foundation. + + Welcome to ESAPI for Java! + +(This file best viewed full screen.) + +Here are the most significant directories and files included the zip file for this release: + +File / Directory Description +========================================================================================= +/ +| ++---configuration/ Directory of ESAPI configuration files +| | +| |---.esapi/ +| | |---waf-policies/ Directory containing Web Application Firewall policies +| | |---ESAPI.properties The main ESAPI configuration file +| | `---validation.properties Regular expressions used by the ESAPI validator +| | +| `---properties/ Examples of how to internationalize error messages??? +| |---ESAPI_en_US.properties in US/English +| |---ESAPI_fr_FR.properties in French +| `---ESAPI_zhs_CN.properties in Chinese +| +|---documentation/ ESAPI documentation +| | +| |---esapi4java-2.0-readme.txt The file you are now reading +| |---esapi4java-core-2.0-release-notes.pdf ESAPI 2.0 release notes (draft) +| |---esapi4java-core-2.0-install-guide.doc ESAPI 2.0 installation guide (draft) +| |---esapi4java-2.0rc6-override-log4jloggingfactory.txt How to use log4j to override User logging +| |---esapi4java-core-2.0-ciphertext-serialization.pdf Describes serialization layout of ESAPI 2.0 ciphertext representation +| |---esapi4java-core-2.0-crypto-design-goals.doc (draft) Describes ESAPI 2.0 crypto design goals & design decisions +| |---esapi4java-core-2.0-readme-crypto-changes.html Describes why crypto was changed from what was in ESAPI 1.4 +| |---esapi4java-core-2.0-symmetric-crypto-user-guide.html User guide for using symmetric encryption in ESAPI 2.0 +| |---esapi4java-core-2.1-release-notes.txt ESAPI 2.1 release notes +| `---esapi4java-waf-2.0-policy-file-spec.pdf Describes how to configure ESAPI 2.0's Web Application Firewall +| +|---libs/ ESAPI dependencies +| +|---site/ +| |---apidocs ESAPI Javadoc +| |---cobertura +| `---testapidocs ESAPI Javadoc for its JUnit test cases +| +|---src/ ESAPI source code +| +|---esapi-.jar The ESAPI jar for version (e.g., == 2.0_rc10) +| +|---LICENSE.txt ESAPI license for source code and documentation +| +`---pom.xml Maven's pom.xml for building ESAPI from source via mvn. + +=========================================================== + +Where to go from here -- please see the installation guide and the release +notes. + +Please address comments and questions concerning the API and this document to +the ESAPI Users mailing list, . + +Copyright (C) 2009-2010 The OWASP Foundation. diff --git a/documentation/esapi4java-2.0rc6-override-log4jloggingfactory.txt b/documentation/esapi4java-2.0rc6-override-log4jloggingfactory.txt index 891f76935..49accdbfc 100644 --- a/documentation/esapi4java-2.0rc6-override-log4jloggingfactory.txt +++ b/documentation/esapi4java-2.0rc6-override-log4jloggingfactory.txt @@ -1,71 +1,71 @@ -This release includes critical changes to the ESAPI Log4JLogger that will now allow you to over-ride the user specific -message using your own User or java.security.Principal implementation. - -There are a three critical steps that need to be taken to over-ride the ESAPI Log4JLogger: - -1) Please make a copy of http://owasp-esapi-java.googlecode.com/svn/trunk/src/main/java/org/owasp/esapi/reference/ExampleExtendedLog4JLogFactory.java and change the package and the class name (something like com.yourcompany.logging.ExtendedLog4JFactory). This class (not very big at all) gives you the exact “shell†that you will need to over-ride the user message of the ESAPI Log4JLogger. - -2) In your new class, please change the following function to use your user object: - - public String getUserInfo() { - return "-EXTENDEDUSERINFO-"; - } - -3) Change your copy of ESAPI.properties to use your new logging class - -The ESAPI.properties entry looks like this now: - -ESAPI.Logger=org.owasp.esapi.reference.Log4JLogFactory - -Please change it to the following, based on how you renamed your new logging class - -ESAPI.Logger=com.yourcompany.logging.ExtendedLog4JFactory - -And you should be all set! - -PS: The original ESAPI Log4JLogging class used a secure random number as a replacement to logging the session ID. This allowed -us to tie log messages from the same session together, without exposing the actual session id in the log file. The code looks -like this, and you may wish to use it in your over-ridden version of getUserInfo. - -HttpServletRequest request = ESAPI.httpUtilities().getCurrentRequest(); -if ( request != null ) { - HttpSession session = request.getSession( false ); - if ( session != null ) { - sid = (String)session.getAttribute("ESAPI_SESSION"); - // if there is no session ID for the user yet, we create one and store it in the user's session - if ( sid == null ) { - sid = ""+ ESAPI.randomizer().getRandomInteger(0, 1000000); - session.setAttribute("ESAPI_SESSION", sid); - } - } -} - -In fact, here is the entire original getUserInfo() implementation (that was tied to the ESAPI request and user object) – -you may wish to emulate some of this. - -public String getUserInfo() { - // create a random session number for the user to represent the user's 'session', if it doesn't exist already - String sid = null; - HttpServletRequest request = ESAPI.httpUtilities().getCurrentRequest(); - if ( request != null ) { - HttpSession session = request.getSession( false ); - if ( session != null ) { - sid = (String)session.getAttribute("ESAPI_SESSION"); - // if there is no session ID for the user yet, we create one and store it in the user's session - if ( sid == null ) { - sid = ""+ ESAPI.randomizer().getRandomInteger(0, 1000000); - session.setAttribute("ESAPI_SESSION", sid); - } - } - } - - // log user information - username:session@ipaddr - User user = ESAPI.authenticator().getCurrentUser(); - String userInfo = ""; - //TODO - make type logging configurable - if ( user != null) { - userInfo += user.getAccountName()+ ":" + sid + "@"+ user.getLastHostAddress(); - } - - return userInfo; -} +This release includes critical changes to the ESAPI Log4JLogger that will now allow you to over-ride the user specific +message using your own User or java.security.Principal implementation. + +There are a three critical steps that need to be taken to over-ride the ESAPI Log4JLogger: + +1) Please make a copy of http://owasp-esapi-java.googlecode.com/svn/trunk/src/main/java/org/owasp/esapi/reference/ExampleExtendedLog4JLogFactory.java and change the package and the class name (something like com.yourcompany.logging.ExtendedLog4JFactory). This class (not very big at all) gives you the exact “shell†that you will need to over-ride the user message of the ESAPI Log4JLogger. + +2) In your new class, please change the following function to use your user object: + + public String getUserInfo() { + return "-EXTENDEDUSERINFO-"; + } + +3) Change your copy of ESAPI.properties to use your new logging class + +The ESAPI.properties entry looks like this now: + +ESAPI.Logger=org.owasp.esapi.reference.Log4JLogFactory + +Please change it to the following, based on how you renamed your new logging class + +ESAPI.Logger=com.yourcompany.logging.ExtendedLog4JFactory + +And you should be all set! + +PS: The original ESAPI Log4JLogging class used a secure random number as a replacement to logging the session ID. This allowed +us to tie log messages from the same session together, without exposing the actual session id in the log file. The code looks +like this, and you may wish to use it in your over-ridden version of getUserInfo. + +HttpServletRequest request = ESAPI.httpUtilities().getCurrentRequest(); +if ( request != null ) { + HttpSession session = request.getSession( false ); + if ( session != null ) { + sid = (String)session.getAttribute("ESAPI_SESSION"); + // if there is no session ID for the user yet, we create one and store it in the user's session + if ( sid == null ) { + sid = ""+ ESAPI.randomizer().getRandomInteger(0, 1000000); + session.setAttribute("ESAPI_SESSION", sid); + } + } +} + +In fact, here is the entire original getUserInfo() implementation (that was tied to the ESAPI request and user object) – +you may wish to emulate some of this. + +public String getUserInfo() { + // create a random session number for the user to represent the user's 'session', if it doesn't exist already + String sid = null; + HttpServletRequest request = ESAPI.httpUtilities().getCurrentRequest(); + if ( request != null ) { + HttpSession session = request.getSession( false ); + if ( session != null ) { + sid = (String)session.getAttribute("ESAPI_SESSION"); + // if there is no session ID for the user yet, we create one and store it in the user's session + if ( sid == null ) { + sid = ""+ ESAPI.randomizer().getRandomInteger(0, 1000000); + session.setAttribute("ESAPI_SESSION", sid); + } + } + } + + // log user information - username:session@ipaddr + User user = ESAPI.authenticator().getCurrentUser(); + String userInfo = ""; + //TODO - make type logging configurable + if ( user != null) { + userInfo += user.getAccountName()+ ":" + sid + "@"+ user.getLastHostAddress(); + } + + return userInfo; +} diff --git a/documentation/esapi4java-core-2.1-release-notes.txt b/documentation/esapi4java-core-2.1-release-notes.txt index 3570d0f88..d84097d80 100644 --- a/documentation/esapi4java-core-2.1-release-notes.txt +++ b/documentation/esapi4java-core-2.1-release-notes.txt @@ -1,68 +1,68 @@ -ESAPI for Java - 2.1.0 Release Notes - -1) Fixed security issue #306, a vulnerability discovered by Phillipe Arteau. - This fix necessitated removing the deprecated encrypt() and decrupt() methods - that were intended to provide backward compatibility with ESAPI 1.4. - As it turns out, there was no way to fix this bug without a major rewrite - unless these methods were removed. However, as these two methods have been - deprecated more than 2 years ago and they are known to be insecure - (they are vulnerable to padding oracle attacks), the ESAPI team has - decided to remove them in accordance to their support policy. - - See comments for issue #306 for further details, as well as additional - safety precautions that you may wish to take in the unlikely, but possible - event that this vulnerability resulted in an actual security breach. - - Finally, since the removal of these methods constitute an interface change - (to the Encryptor interface), this is considered a minor release (2.1) - rather than simply a patch release (2.0.2). - - Please note that there are further updates planned to further strengthen - the MAC that ESAPI crypt uses. However, because they will require some - design changes, they may not be out for another month. Note that these - fixes do not correct any *known* vulnerabilities, but will address - some potential weaknesses in what is not included in the MAC (such as - the crypto version). - -2) Other Google Issues fixed: 257, 271, and 292 are all fixed in this release. - -3) Fixed Javadoc for Encoder.encryptForJavaScript(). [Revision r1879] - -4) DefaultEncryptedProperties - made minor Javadoc changes. - -5) The ESAPI 2.0 Encryptor.encrypt() methods now all throw an appropriate - IllegalArgumentException if any of the arguments are null. Previously, - if any of the arguments were null you would either get an AssertionError - (if you had assertions enabled) or a default NullPointerException when - assertions were disabled. While IllegalArgumentException is still an - unchecked RuntimeException, note that if you were previously catching - NullPointerExceptions for these cases, you may need to change your code. - -6) The public constructor, CiphertextSerializer(CipherText ct), was changed - to explicitly check that the parameter is not null. Previously it had - checked with assertions which might later result in a NullPointerException - being thrown if assertions were disabled. Now if the parameter is null, - an appropriate IllegalArgumentException is thrown. This should not really - affect existing code (unless you are experimenting implementing your own - crypto) since user code should not really be using CiperTextSerializer - directly. - -7) Some of the setter methods in KeyDerivationFunction were changed to explicitly - check for invalid arguments and throw an IllegalArgumentException rather than - checking these parameters via assertions. This should not affect general - user code as most would not be calling the KeyDerivationFunction class - directly. - -8) Other miscellaneous minor code clean-up, mostly to remove compiler warnings. - -NOTE: A follow-up patch release is scheduled within the next few months to - address some questionable design decisions regarding what data in - the serialized ciphertext should be authenticated via the MAC. For - instance, presently only the IV+ciphertext is MAC'd (as would be the - equivalent case of when you would use an authenticated combined cipher - mode such as GCM or CCM). A deeper analysis of the design is required - based on findings in Google Issue # 306. I will periodically try - to keep the ESAPI mailing lists updated with the progress so watch - there for emerging details and anticipated schedule. - --Kevin W. Wall , 2013-08-30 +ESAPI for Java - 2.1.0 Release Notes + +1) Fixed security issue #306, a vulnerability discovered by Phillipe Arteau. + This fix necessitated removing the deprecated encrypt() and decrupt() methods + that were intended to provide backward compatibility with ESAPI 1.4. + As it turns out, there was no way to fix this bug without a major rewrite + unless these methods were removed. However, as these two methods have been + deprecated more than 2 years ago and they are known to be insecure + (they are vulnerable to padding oracle attacks), the ESAPI team has + decided to remove them in accordance to their support policy. + + See comments for issue #306 for further details, as well as additional + safety precautions that you may wish to take in the unlikely, but possible + event that this vulnerability resulted in an actual security breach. + + Finally, since the removal of these methods constitute an interface change + (to the Encryptor interface), this is considered a minor release (2.1) + rather than simply a patch release (2.0.2). + + Please note that there are further updates planned to further strengthen + the MAC that ESAPI crypt uses. However, because they will require some + design changes, they may not be out for another month. Note that these + fixes do not correct any *known* vulnerabilities, but will address + some potential weaknesses in what is not included in the MAC (such as + the crypto version). + +2) Other Google Issues fixed: 257, 271, and 292 are all fixed in this release. + +3) Fixed Javadoc for Encoder.encryptForJavaScript(). [Revision r1879] + +4) DefaultEncryptedProperties - made minor Javadoc changes. + +5) The ESAPI 2.0 Encryptor.encrypt() methods now all throw an appropriate + IllegalArgumentException if any of the arguments are null. Previously, + if any of the arguments were null you would either get an AssertionError + (if you had assertions enabled) or a default NullPointerException when + assertions were disabled. While IllegalArgumentException is still an + unchecked RuntimeException, note that if you were previously catching + NullPointerExceptions for these cases, you may need to change your code. + +6) The public constructor, CiphertextSerializer(CipherText ct), was changed + to explicitly check that the parameter is not null. Previously it had + checked with assertions which might later result in a NullPointerException + being thrown if assertions were disabled. Now if the parameter is null, + an appropriate IllegalArgumentException is thrown. This should not really + affect existing code (unless you are experimenting implementing your own + crypto) since user code should not really be using CiperTextSerializer + directly. + +7) Some of the setter methods in KeyDerivationFunction were changed to explicitly + check for invalid arguments and throw an IllegalArgumentException rather than + checking these parameters via assertions. This should not affect general + user code as most would not be calling the KeyDerivationFunction class + directly. + +8) Other miscellaneous minor code clean-up, mostly to remove compiler warnings. + +NOTE: A follow-up patch release is scheduled within the next few months to + address some questionable design decisions regarding what data in + the serialized ciphertext should be authenticated via the MAC. For + instance, presently only the IV+ciphertext is MAC'd (as would be the + equivalent case of when you would use an authenticated combined cipher + mode such as GCM or CCM). A deeper analysis of the design is required + based on findings in Google Issue # 306. I will periodically try + to keep the ESAPI mailing lists updated with the progress so watch + there for emerging details and anticipated schedule. + +-Kevin W. Wall , 2013-08-30 diff --git a/src/main/java/META-INF/MANIFEST.MF b/src/main/java/META-INF/MANIFEST.MF index 5e9495128..254272e1c 100644 --- a/src/main/java/META-INF/MANIFEST.MF +++ b/src/main/java/META-INF/MANIFEST.MF @@ -1,3 +1,3 @@ -Manifest-Version: 1.0 -Class-Path: - +Manifest-Version: 1.0 +Class-Path: + From 142307d2811d139221c27e8da98aa01324d5514c Mon Sep 17 00:00:00 2001 From: kwwall Date: Tue, 19 Jan 2016 22:11:46 -0500 Subject: [PATCH 0299/1069] Try to get git to ignore Eclipse generated files. --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index ea8c4bf7f..0f6484a23 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,4 @@ /target +/.settings/** +.classpath +.project From 65adf0518925b91d965d6d3e70e031faa9ca122b Mon Sep 17 00:00:00 2001 From: "Kevin W. Wall" Date: Tue, 19 Jan 2016 22:19:27 -0500 Subject: [PATCH 0300/1069] Delete org.eclipse.core.resources.prefs This is a file generated by Eclipse and not really part of ESAPI per se. It may be version specific to a particular release of Eclipse, thus we are deleting it and adding it to be ignored in the .gitignore file. --- .settings/org.eclipse.core.resources.prefs | 3 --- 1 file changed, 3 deletions(-) delete mode 100644 .settings/org.eclipse.core.resources.prefs diff --git a/.settings/org.eclipse.core.resources.prefs b/.settings/org.eclipse.core.resources.prefs deleted file mode 100644 index 2655b6477..000000000 --- a/.settings/org.eclipse.core.resources.prefs +++ /dev/null @@ -1,3 +0,0 @@ -#Thu Nov 26 01:50:11 HST 2009 -eclipse.preferences.version=1 -encoding/=UTF-8 From a88295a60d39d8f696461cdb9bfa9f52352c23da Mon Sep 17 00:00:00 2001 From: "Kevin W. Wall" Date: Tue, 19 Jan 2016 22:20:48 -0500 Subject: [PATCH 0301/1069] Delete org.eclipse.jdt.core.prefs This is a file generated by Eclipse and not really part of ESAPI per se. It may be version specific to a particular release of Eclipse, thus we are deleting it and adding it to be ignored in the .gitignore file. (In fact, this one is telling Eclipse to use '-target 1.5' when actually '-target 1.6' is what the pom.xml specifies.) --- .settings/org.eclipse.jdt.core.prefs | 6 ------ 1 file changed, 6 deletions(-) delete mode 100644 .settings/org.eclipse.jdt.core.prefs diff --git a/.settings/org.eclipse.jdt.core.prefs b/.settings/org.eclipse.jdt.core.prefs deleted file mode 100644 index 9e33fcc4b..000000000 --- a/.settings/org.eclipse.jdt.core.prefs +++ /dev/null @@ -1,6 +0,0 @@ -#Tue May 10 22:28:38 MDT 2011 -eclipse.preferences.version=1 -org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.5 -org.eclipse.jdt.core.compiler.compliance=1.5 -org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning -org.eclipse.jdt.core.compiler.source=1.5 From d863f355fe6e05d81b0b209077e4d4b851d6f62d Mon Sep 17 00:00:00 2001 From: "Kevin W. Wall" Date: Tue, 19 Jan 2016 22:21:30 -0500 Subject: [PATCH 0302/1069] Delete org.eclipse.wst.common.component This is a file generated by Eclipse and not really part of ESAPI per se. It may be version specific to a particular release of Eclipse, thus we are deleting it and adding it to be ignored in the .gitignore file. --- .settings/org.eclipse.wst.common.component | 10 ---------- 1 file changed, 10 deletions(-) delete mode 100644 .settings/org.eclipse.wst.common.component diff --git a/.settings/org.eclipse.wst.common.component b/.settings/org.eclipse.wst.common.component deleted file mode 100644 index b814064d1..000000000 --- a/.settings/org.eclipse.wst.common.component +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - From ce7641299919f60f8dab994fc28afdb29884e496 Mon Sep 17 00:00:00 2001 From: "Kevin W. Wall" Date: Tue, 19 Jan 2016 22:21:45 -0500 Subject: [PATCH 0303/1069] Delete org.eclipse.wst.common.project.facet.core.xml This is a file generated by Eclipse and not really part of ESAPI per se. It may be version specific to a particular release of Eclipse, thus we are deleting it and adding it to be ignored in the .gitignore file. --- .settings/org.eclipse.wst.common.project.facet.core.xml | 6 ------ 1 file changed, 6 deletions(-) delete mode 100644 .settings/org.eclipse.wst.common.project.facet.core.xml diff --git a/.settings/org.eclipse.wst.common.project.facet.core.xml b/.settings/org.eclipse.wst.common.project.facet.core.xml deleted file mode 100644 index 9bbcbd78e..000000000 --- a/.settings/org.eclipse.wst.common.project.facet.core.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file From 7fd79e7e68b8561e50a15cfc3dfd8efc351e94d8 Mon Sep 17 00:00:00 2001 From: "Kevin W. Wall" Date: Tue, 19 Jan 2016 22:22:04 -0500 Subject: [PATCH 0304/1069] Delete org.maven.ide.eclipse.prefs This is a file generated by Eclipse and not really part of ESAPI per se. It may be version specific to a particular release of Eclipse, thus we are deleting it and adding it to be ignored in the .gitignore file. --- .settings/org.maven.ide.eclipse.prefs | 9 --------- 1 file changed, 9 deletions(-) delete mode 100644 .settings/org.maven.ide.eclipse.prefs diff --git a/.settings/org.maven.ide.eclipse.prefs b/.settings/org.maven.ide.eclipse.prefs deleted file mode 100644 index 310a30006..000000000 --- a/.settings/org.maven.ide.eclipse.prefs +++ /dev/null @@ -1,9 +0,0 @@ -#Fri Jul 10 01:10:33 EDT 2009 -activeProfiles= -eclipse.preferences.version=1 -fullBuildGoals=process-test-resources -includeModules=false -resolveWorkspaceProjects=true -resourceFilterGoals=process-resources resources\:testResources -skipCompilerPlugin=true -version=1 From 467f179292243c369c1fee09427481c3f7e6a39b Mon Sep 17 00:00:00 2001 From: "Kevin W. Wall" Date: Tue, 19 Jan 2016 22:25:02 -0500 Subject: [PATCH 0305/1069] Delete .classpath This is a file generated by Eclipse and not really part of ESAPI per se. It may be version specific to a particular release of Eclipse, thus we are deleting it and adding it to be ignored in the .gitignore file. --- .classpath | 10 ---------- 1 file changed, 10 deletions(-) delete mode 100644 .classpath diff --git a/.classpath b/.classpath deleted file mode 100644 index 3c57589c2..000000000 --- a/.classpath +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - From 65d6673e58995ca08b26e97edd090acbd2a5a68c Mon Sep 17 00:00:00 2001 From: "Kevin W. Wall" Date: Tue, 19 Jan 2016 22:25:52 -0500 Subject: [PATCH 0306/1069] Delete .project This is a file generated by Eclipse and not really part of ESAPI per se. It may be version specific to a particular release of Eclipse, thus we are deleting it and adding it to be ignored in the .gitignore file. --- .project | 42 ------------------------------------------ 1 file changed, 42 deletions(-) delete mode 100644 .project diff --git a/.project b/.project deleted file mode 100644 index b20935afd..000000000 --- a/.project +++ /dev/null @@ -1,42 +0,0 @@ - - - ESAPI_2.0 - The Enterprise Security API project is an OWASP project - to create simple strong security controls for every web platform. - Security controls are not simple to build. You can read about the - hundreds of pitfalls for unwary developers on the OWASP website. By - providing developers with a set of strong controls, we aim to - eliminate some of the complexity of creating secure web applications. - This can result in significant cost savings across the SDLC. - - - - - org.eclipse.wst.common.project.facet.core.builder - - - - - org.eclipse.jdt.core.javabuilder - - - - - org.eclipse.wst.validation.validationbuilder - - - - - org.maven.ide.eclipse.maven2Builder - - - - - - org.maven.ide.eclipse.maven2Nature - org.eclipse.jem.workbench.JavaEMFNature - org.eclipse.wst.common.modulecore.ModuleCoreNature - org.eclipse.jdt.core.javanature - org.eclipse.wst.common.project.facet.core.nature - - From 520e167a1d0cffbbdd923026e456b60ad54cd2d0 Mon Sep 17 00:00:00 2001 From: kwwall Date: Tue, 19 Jan 2016 22:38:23 -0500 Subject: [PATCH 0307/1069] Add *.swp and *~ to the ignore list. The former are vim temp files, and the latter are its backups (if so-enabled). --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index 0f6484a23..cc8990967 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,5 @@ /.settings/** .classpath .project +*.swp +*~ From 4d2b101637a137285bcd39f99335ab184926e5ed Mon Sep 17 00:00:00 2001 From: Anthony Musyoki Date: Wed, 20 Jan 2016 11:47:07 +0300 Subject: [PATCH 0308/1069] Fix issue 355 by adding a call to file.deleteOnExit(). --- .../esapi/waf/internal/InterceptingServletOutputStream.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/owasp/esapi/waf/internal/InterceptingServletOutputStream.java b/src/main/java/org/owasp/esapi/waf/internal/InterceptingServletOutputStream.java index 693c11112..cad2efe92 100644 --- a/src/main/java/org/owasp/esapi/waf/internal/InterceptingServletOutputStream.java +++ b/src/main/java/org/owasp/esapi/waf/internal/InterceptingServletOutputStream.java @@ -56,7 +56,10 @@ public InterceptingServletOutputStream(ServletOutputStream os, boolean buffered) * the prefix and suffix small for less processing. The "oew" is intended * to stand for "OWASP ESAPI WAF" and the "hop" for HTTP output. */ - this.out = new RandomAccessFile ( File.createTempFile("oew", ".hop"), "rw" ); + File tempFile= File.createTempFile("oew", ".hop"); + this.out = new RandomAccessFile (tempFile, "rw" ); + tempFile.deleteOnExit(); + } public void reset() throws IOException { From 56dae769091157755d0a8c0992e2c5d17bb8e6f8 Mon Sep 17 00:00:00 2001 From: Matt Seil Date: Wed, 20 Jan 2016 17:59:15 -0600 Subject: [PATCH 0309/1069] Applied fixes to unit test and upgraded junit to 4.12. --- pom.xml | 2 +- .../esapi/reference/AuthenticatorTest.java | 208 ++++++++++-------- 2 files changed, 116 insertions(+), 94 deletions(-) diff --git a/pom.xml b/pom.xml index dea265185..4459f4b90 100644 --- a/pom.xml +++ b/pom.xml @@ -132,7 +132,7 @@ junit junit - 4.5 + 4.12 test diff --git a/src/test/java/org/owasp/esapi/reference/AuthenticatorTest.java b/src/test/java/org/owasp/esapi/reference/AuthenticatorTest.java index e6a660f4b..1603689c7 100644 --- a/src/test/java/org/owasp/esapi/reference/AuthenticatorTest.java +++ b/src/test/java/org/owasp/esapi/reference/AuthenticatorTest.java @@ -15,13 +15,32 @@ */ package org.owasp.esapi.reference; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; +import static org.junit.Assume.assumeTrue; + import java.util.Date; import java.util.Set; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.Semaphore; +import java.util.concurrent.TimeUnit; -import junit.framework.Test; -import junit.framework.TestCase; -import junit.framework.TestSuite; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import org.junit.After; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ErrorCollector; +import org.junit.rules.TestName; +import org.junit.rules.Timeout; import org.owasp.esapi.Authenticator; import org.owasp.esapi.ESAPI; import org.owasp.esapi.EncoderConstants; @@ -38,47 +57,49 @@ * * @author Jeff Williams (jeff.williams@aspectsecurity.com) */ -public class AuthenticatorTest extends TestCase { - - - /** - * Suite. - * - * @return the test - */ - public static Test suite() { - TestSuite suite = new TestSuite(AuthenticatorTest.class); +public class AuthenticatorTest { + private static Authenticator instance; + /** + * User session information is stored on a per-thread basis. So long as this has potential to run single threaded then we'll maintain a synchronous nature execution. + * This is done to prevent tests corrupting each others states since all will be executed on a limited set of Threads within the JVM. + */ + private static Semaphore threadIsolation = new Semaphore(1, true); - return suite; - } + @Rule + public ErrorCollector collector = new ErrorCollector(); + @Rule + public Timeout testTimout = new Timeout(10, TimeUnit.SECONDS); + @Rule + public TestName name = new TestName(); - /** - * Instantiates a new authenticator test. - * - * @param testName - * the test name - */ - public AuthenticatorTest(String testName) { - super(testName); - } + @BeforeClass + public static void setUpStatic() { + instance = ESAPI.authenticator(); + } - /** - * {@inheritDoc} - * - * @throws Exception - */ - protected void setUp() throws Exception { - // none - } + @Before + public void setup() throws InterruptedException { + while (!threadIsolation.tryAcquire(500, TimeUnit.MILLISECONDS)) { + //Spurious Interrupt Guard + } + } - /** - * {@inheritDoc} - * - * @throws Exception - */ - protected void tearDown() throws Exception { - // none - } + @After + public void cleanup() { + try { + instance.logout(); + instance.clearCurrent(); + HttpServletRequest request = ESAPI.httpUtilities().getCurrentRequest(); + HttpServletResponse response = ESAPI.httpUtilities().getCurrentResponse(); + if (request != null && response != null) { + //I don't know why killAllCookies doesn't nullcheck state. I'm assuming this is unique to the test environment. + ESAPI.httpUtilities().killAllCookies(); + } + ESAPI.httpUtilities().clearCurrent(); + } finally { + threadIsolation.release(); + } + } /** @@ -88,10 +109,9 @@ protected void tearDown() throws Exception { * the authentication exception * @throws EncryptionException */ - public void testCreateUser() throws AuthenticationException, EncryptionException { + @Test public void testCreateUser() throws AuthenticationException, EncryptionException { System.out.println("createUser"); String accountName = ESAPI.randomizer().getRandomString(8, EncoderConstants.CHAR_ALPHANUMERICS); - Authenticator instance = ESAPI.authenticator(); String password = instance.generateStrongPassword(); User user = instance.createUser(accountName, password, password); assertTrue(user.verifyPassword(password)); @@ -141,9 +161,8 @@ public void testCreateUser() throws AuthenticationException, EncryptionException * @throws AuthenticationException * the authentication exception */ - public void testGenerateStrongPassword() throws AuthenticationException { + @Test public void testGenerateStrongPassword() throws AuthenticationException { System.out.println("generateStrongPassword"); - Authenticator instance = ESAPI.authenticator(); String oldPassword = "iiiiiiiiii"; // i is not allowed in passwords - this prevents failures from containing pieces of old password String newPassword = null; String username = "FictionalEsapiUser"; @@ -171,9 +190,8 @@ public void testGenerateStrongPassword() throws AuthenticationException { * * @throws Exception */ - public void testGetCurrentUser() throws Exception { + @Test public void testGetCurrentUser() throws Exception { System.out.println("getCurrentUser"); - Authenticator instance = ESAPI.authenticator(); String username1 = ESAPI.randomizer().getRandomString(8, EncoderConstants.CHAR_ALPHANUMERICS); String username2 = ESAPI.randomizer().getRandomString(8, EncoderConstants.CHAR_ALPHANUMERICS); User user1 = instance.createUser(username1, "getCurrentUser", "getCurrentUser"); @@ -192,21 +210,21 @@ public void testGetCurrentUser() throws Exception { private int count = 1; private boolean result = false; public void run() { - Authenticator auth = ESAPI.authenticator(); User a = null; try { - String password = auth.generateStrongPassword(); + String password = instance.generateStrongPassword(); String accountName = "TestAccount" + count++; - a = auth.getUser(accountName); + a = instance.getUser(accountName); if ( a != null ) { - auth.removeUser(accountName); + instance.removeUser(accountName); } - a = auth.createUser(accountName, password, password); - auth.setCurrentUser(a); + a = instance.createUser(accountName, password, password); + instance.setCurrentUser(a); } catch (AuthenticationException e) { - e.printStackTrace(); + //Use ErrorCollector to fail test. + collector.addError(e); } - User b = auth.getCurrentUser(); + User b = instance.getCurrentUser(); result &= a.equals(b); } }; @@ -225,9 +243,8 @@ public void run() { * @throws AuthenticationException * the authentication exception */ - public void testGetUser() throws AuthenticationException { + @Test public void testGetUser() throws AuthenticationException { System.out.println("getUser"); - Authenticator instance = ESAPI.authenticator(); String password = instance.generateStrongPassword(); String accountName=ESAPI.randomizer().getRandomString(8, EncoderConstants.CHAR_ALPHANUMERICS); instance.createUser(accountName, password, password); @@ -239,10 +256,8 @@ public void testGetUser() throws AuthenticationException { * * @throws org.owasp.esapi.errors.AuthenticationException */ - public void testGetUserFromRememberToken() throws AuthenticationException { + @Test public void testGetUserFromRememberToken() throws AuthenticationException { System.out.println("getUserFromRememberToken"); - Authenticator instance = ESAPI.authenticator(); - instance.logout(); // in case anyone is logged in String password = instance.generateStrongPassword(); String accountName=ESAPI.randomizer().getRandomString(8, EncoderConstants.CHAR_ALPHANUMERICS); User user = instance.createUser(accountName, password, password); @@ -255,6 +270,7 @@ public void testGetUserFromRememberToken() throws AuthenticationException { request.setCookie( HTTPUtilities.REMEMBER_TOKEN_COOKIE_NAME, "ridiculous" ); try { instance.login( request, response ); // wrong cookie will fail + fail(); } catch( AuthenticationException e ) { // expected } @@ -278,10 +294,9 @@ public void testGetUserFromRememberToken() throws AuthenticationException { * @throws AuthenticationException * the authentication exception */ - public void testGetUserFromSession() throws AuthenticationException { + @Test public void testGetUserFromSession() throws AuthenticationException { System.out.println("getUserFromSession"); - FileBasedAuthenticator instance = (FileBasedAuthenticator)ESAPI.authenticator(); - instance.logout(); // in case anyone is logged in + assumeTrue(instance instanceof FileBasedAuthenticator); String accountName=ESAPI.randomizer().getRandomString(8, EncoderConstants.CHAR_ALPHANUMERICS); String password = instance.generateStrongPassword(); User user = instance.createUser(accountName, password, password); @@ -292,7 +307,7 @@ public void testGetUserFromSession() throws AuthenticationException { MockHttpServletResponse response = new MockHttpServletResponse(); ESAPI.httpUtilities().setCurrentHTTP( request, response ); instance.login( request, response); - User test = instance.getUserFromSession(); + User test = ((FileBasedAuthenticator)instance).getUserFromSession(); assertEquals( user, test ); } @@ -302,9 +317,8 @@ public void testGetUserFromSession() throws AuthenticationException { * @throws AuthenticationException * the authentication exception */ - public void testGetUserNames() throws AuthenticationException { + @Test public void testGetUserNames() throws AuthenticationException { System.out.println("getUserNames"); - Authenticator instance = ESAPI.authenticator(); String password = instance.generateStrongPassword(); String[] testnames = new String[10]; for(int i=0;i Date: Thu, 21 Jan 2016 16:03:34 -0600 Subject: [PATCH 0310/1069] Added missing call (?) to HttpUtilitiesTest to set the current request. Refactored AbstractAuthenticator to make the method call transactional. --- .../reference/AbstractAuthenticator.java | 4 +- .../esapi/reference/HTTPUtilitiesTest.java | 37 +++++++++++++------ 2 files changed, 29 insertions(+), 12 deletions(-) diff --git a/src/main/java/org/owasp/esapi/reference/AbstractAuthenticator.java b/src/main/java/org/owasp/esapi/reference/AbstractAuthenticator.java index 780181971..d7de99f04 100644 --- a/src/main/java/org/owasp/esapi/reference/AbstractAuthenticator.java +++ b/src/main/java/org/owasp/esapi/reference/AbstractAuthenticator.java @@ -96,7 +96,9 @@ public User getCurrentUser() { * @return the user from session or null if no user is found in the session */ protected User getUserFromSession() { - HttpSession session = ESAPI.httpUtilities().getCurrentRequest().getSession(false); + HTTPUtilities httpUtils = ESAPI.httpUtilities(); + HttpServletRequest req = httpUtils.getCurrentRequest(); + HttpSession session = req.getSession(false); if (session == null) return null; return ESAPI.httpUtilities().getSessionAttribute(USER); } diff --git a/src/test/java/org/owasp/esapi/reference/HTTPUtilitiesTest.java b/src/test/java/org/owasp/esapi/reference/HTTPUtilitiesTest.java index d86f98ba4..5dadd5ce3 100644 --- a/src/test/java/org/owasp/esapi/reference/HTTPUtilitiesTest.java +++ b/src/test/java/org/owasp/esapi/reference/HTTPUtilitiesTest.java @@ -15,24 +15,38 @@ */ package org.owasp.esapi.reference; -import junit.framework.Test; -import junit.framework.TestCase; -import junit.framework.TestSuite; -import org.owasp.esapi.*; +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import javax.servlet.http.Cookie; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpSession; + +import org.owasp.esapi.Authenticator; +import org.owasp.esapi.ESAPI; +import org.owasp.esapi.EncoderConstants; +import org.owasp.esapi.HTTPUtilities; +import org.owasp.esapi.User; import org.owasp.esapi.codecs.Hex; import org.owasp.esapi.crypto.CipherText; -import org.owasp.esapi.errors.*; +import org.owasp.esapi.errors.AccessControlException; +import org.owasp.esapi.errors.AuthenticationException; +import org.owasp.esapi.errors.EncryptionException; +import org.owasp.esapi.errors.EnterpriseSecurityException; +import org.owasp.esapi.errors.ValidationException; import org.owasp.esapi.http.MockHttpServletRequest; import org.owasp.esapi.http.MockHttpServletResponse; import org.owasp.esapi.http.MockHttpSession; import org.owasp.esapi.util.FileTestUtils; -import javax.servlet.http.Cookie; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpSession; -import java.io.File; -import java.io.IOException; -import java.util.*; +import junit.framework.Test; +import junit.framework.TestCase; +import junit.framework.TestSuite; /** * The Class HTTPUtilitiesTest. @@ -462,6 +476,7 @@ public void testSetRememberToken() throws AuthenticationException { request.addParameter("username", accountName); request.addParameter("password", password); MockHttpServletResponse response = new MockHttpServletResponse(); + ESAPI.httpUtilities().setCurrentHTTP(request, response); instance.login( request, response); int maxAge = ( 60 * 60 * 24 * 14 ); From 2dfd89195cb7f4c3a81ec939db93dda6a5a4086d Mon Sep 17 00:00:00 2001 From: kwwall Date: Thu, 21 Jan 2016 22:55:35 -0500 Subject: [PATCH 0311/1069] Ignore *.iml files which are config files from InteliJ IDE. --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index cc8990967..65ac7f40a 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,4 @@ .project *.swp *~ +*.iml From 7d3b4f3e83185254142068faff5bcdb7396bde74 Mon Sep 17 00:00:00 2001 From: "Kevin W. Wall" Date: Thu, 21 Jan 2016 22:58:18 -0500 Subject: [PATCH 0312/1069] Delete esapi.iml This is a config file from InteliJ IDE and does not belong with the ESAPI repo. We did the same for the Eclipse config files. --- esapi.iml | 48 ------------------------------------------------ 1 file changed, 48 deletions(-) delete mode 100644 esapi.iml diff --git a/esapi.iml b/esapi.iml deleted file mode 100644 index 5729ef276..000000000 --- a/esapi.iml +++ /dev/null @@ -1,48 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - From 49dfc9c9816d468a7b7a94c0d43e1bb2e6d4227a Mon Sep 17 00:00:00 2001 From: kwwall Date: Thu, 21 Jan 2016 23:43:18 -0500 Subject: [PATCH 0313/1069] Change version from 2.1.1-SNAPSHOT to 2.1.0.1-SNAPSHOT --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 4459f4b90..2f41f7705 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 org.owasp.esapi esapi - 2.1.1-SNAPSHOT + 2.1.0.1-SNAPSHOT jar From d5306d69bf881f44e45490285812500dd99159ca Mon Sep 17 00:00:00 2001 From: Matt Seil Date: Sun, 24 Jan 2016 11:37:31 -0600 Subject: [PATCH 0314/1069] Changed the RandomizerTest failure into a System.err report for randomness counts. JUnit has no equivalent for "WARN". --- .../java/org/owasp/esapi/reference/RandomizerTest.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/test/java/org/owasp/esapi/reference/RandomizerTest.java b/src/test/java/org/owasp/esapi/reference/RandomizerTest.java index 32ce1cb0e..b73a0241d 100644 --- a/src/test/java/org/owasp/esapi/reference/RandomizerTest.java +++ b/src/test/java/org/owasp/esapi/reference/RandomizerTest.java @@ -97,7 +97,12 @@ public void testGetRandomString() { if ( counts[i] > max ) { max = counts[i]; } if ( counts[i] > 0 && counts[i] < min ) { min = counts[i]; } if ( max - min > trials/10 ) { - fail( "getRandomString randomness counts are off" ); + System.err.println("*** WARNING: RandomizerTest.testGetRandomString(): " + + "Randomness counts are off. This may be simply from " + + "statistical variance or it could signify a flaw in " + + "Randomizer.getRandomString(). Repeat this test " + + "multiple times and if you get repeated warnings " + + "you should assume the latter and investigate further."); } } } From 6438ad007b202c563ceda080429ea77a66d5d6ed Mon Sep 17 00:00:00 2001 From: kwwall Date: Sat, 30 Jan 2016 23:38:45 -0500 Subject: [PATCH 0315/1069] Change Google Code references to GitHub. --- pom.xml | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/pom.xml b/pom.xml index 2f41f7705..f867c9b1a 100644 --- a/pom.xml +++ b/pom.xml @@ -30,7 +30,7 @@ ESAPI - http://www.esapi.org/ + https://www.owasp.org/index.php/Category:OWASP_Enterprise_Security_API The Enterprise Security API (ESAPI) project is an OWASP project to create simple strong security controls for every web platform. Security controls are not simple to build. You can read about the @@ -70,14 +70,14 @@ - scm:svn:http://owasp-esapi-java.googlecode.com/svn/trunk - scm:svn:https://owasp-esapi-java.googlecode.com/svn/trunk - http://code.google.com/p/owasp-esapi-java/source/checkout + scm:git:git://github.com/ESAPI/esapi-java-legacy.git + scm:git:git@github.com:ESAPI/esapi-java-legacy.git + https://github.com/ESAPI/esapi-java-legacy - Google Code Issue Tracking - http://code.google.com/p/owasp-esapi-java/issues/list + GitHub Issue Tracking + https://github.com/ESAPI/esapi-java-legacy/issues @@ -92,14 +92,14 @@ Chris Schmidt Aspect Security - Project Owner + Project Co-owner Kevin W. Wall Wells Fargo - Project Manager + Project Co-owner From b7cbc53f9cc967cf1a5a9463d8c6fef9ed6ef4f7 Mon Sep 17 00:00:00 2001 From: kwwall Date: Sun, 31 Jan 2016 01:56:59 -0500 Subject: [PATCH 0316/1069] Close #306. Close #359 --- .../org/owasp/esapi/codecs/PercentCodec.java | 12 +- .../owasp/esapi/codecs/CodecImmunityTest.java | 138 ++++++++++++++++++ 2 files changed, 149 insertions(+), 1 deletion(-) create mode 100644 src/test/java/org/owasp/esapi/codecs/CodecImmunityTest.java diff --git a/src/main/java/org/owasp/esapi/codecs/PercentCodec.java b/src/main/java/org/owasp/esapi/codecs/PercentCodec.java index 3d1e76a86..42e1d040e 100644 --- a/src/main/java/org/owasp/esapi/codecs/PercentCodec.java +++ b/src/main/java/org/owasp/esapi/codecs/PercentCodec.java @@ -89,7 +89,12 @@ private static StringBuilder appendTwoUpperHex(StringBuilder sb, int b) /** * Encode a character for URLs - * @param immune characters not to encode + * @param immune Additional characters not to encode. Note this could + * break URL encoding as referenced in RFC 3986. You should + * especially be wary of including '%' in this list of immune + * characters since it is used as the "escape" character for + * the hex encoding and including it may result in subsequent + * and/or dangerous results when decoding. * @param c character to encode * @return the encoded string representing c */ @@ -99,6 +104,11 @@ public String encodeCharacter( char[] immune, Character c ) byte[] bytes; StringBuilder sb; + // check for user specified immune characters + if ( immune != null && containsCharacter( c.charValue(), immune ) ) + return cStr; + + // check for standard characters (e.g., alphanumeric, etc.) if(UNENCODED_SET.contains(c)) return cStr; diff --git a/src/test/java/org/owasp/esapi/codecs/CodecImmunityTest.java b/src/test/java/org/owasp/esapi/codecs/CodecImmunityTest.java new file mode 100644 index 000000000..7d35dc168 --- /dev/null +++ b/src/test/java/org/owasp/esapi/codecs/CodecImmunityTest.java @@ -0,0 +1,138 @@ +package org.owasp.esapi.codecs; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; + +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameters; +import org.owasp.esapi.EncoderConstants; +import org.owasp.esapi.StringUtilities; +import org.owasp.esapi.codecs.*; + +/** + * Parameterized test to verify that the Immunity parameter for a codec + * encode/decode event works as expected on a series of special characters. + * + * @author jeremiah.j.stacey@gmail.com + * @since 2.1.0.1 + * + */ +@RunWith(Parameterized.class) +public class CodecImmunityTest { + /** character arrays used as immunity lists from Default Encoder.*/ + private final static char[] IMMUNE_HTML = { ',', '.', '-', '_', ' ' }; + private final static char[] IMMUNE_HTMLATTR = { ',', '.', '-', '_' }; + private final static char[] IMMUNE_CSS = {}; + private final static char[] IMMUNE_JAVASCRIPT = { ',', '.', '_' }; + private final static char[] IMMUNE_VBSCRIPT = { ',', '.', '_' }; + private final static char[] IMMUNE_XML = { ',', '.', '-', '_', ' ' }; + private final static char[] IMMUNE_SQL = { ' ' }; + private final static char[] IMMUNE_OS = { '-' }; + private final static char[] IMMUNE_XMLATTR = { ',', '.', '-', '_' }; + private final static char[] IMMUNE_XPATH = { ',', '.', '-', '_', ' ' }; + private final static char[] IMMUNE_PERCENT = { '%' }; + // These are inline in the encode methods, but same principle. + // private final static char[] IMMUNE_LDAP = { '\\', '*', '(', ')', '\0' }; + // private final static char[] IMMUNE_DN = { '\\', ',', '+', '"', '<', '>', ';' }; + + + @Parameters(name = "{0}") + public static Collection getParams() { + Collection knownCodecs = new ArrayList(); + knownCodecs.add(new CSSCodec()); + knownCodecs.add(new DB2Codec()); + knownCodecs.add(new HTMLEntityCodec()); + knownCodecs.add(new JavaScriptCodec()); + knownCodecs.add(new MySQLCodec(0)); //Standard + knownCodecs.add(new MySQLCodec(1)); //ANSI + knownCodecs.add(new OracleCodec()); + knownCodecs.add(new PercentCodec()); + knownCodecs.add(new UnixCodec()); + knownCodecs.add(new VBScriptCodec()); + knownCodecs.add(new WindowsCodec()); + knownCodecs.add(new XMLEntityCodec()); + + // TODO: Add more strings here!! + List sampleStrings = Arrays.asList("%De"); + + Collection params = new ArrayList(); + for (Codec codec : knownCodecs) { + for (String sample : sampleStrings) { + params.add(new Object[]{codec.getClass().getSimpleName() + " " + sample, codec, sample}); + } + } + + // Add Tests for codecs against the configured ImmunityLists within the Default Encoder. + params.addAll(buildImmunitiyValidation(new HTMLEntityCodec(), IMMUNE_HTML, "IMMUNE_HTML")); + params.addAll(buildImmunitiyValidation(new HTMLEntityCodec(), IMMUNE_XPATH, "IMMUNE_XPATH")); + params.addAll(buildImmunitiyValidation(new HTMLEntityCodec(), IMMUNE_HTMLATTR, "IMMUNE_HTMLATTR")); + params.addAll(buildImmunitiyValidation(new CSSCodec(), IMMUNE_CSS, "IMMUNE_CSS")); + //params.addAll(buildImmunitiyValidation(new DB2Codec(), IMMUNE_HTML, "")); + params.addAll(buildImmunitiyValidation(new JavaScriptCodec(), IMMUNE_JAVASCRIPT, "IMMUNE_JAVASCRIPT")); + params.addAll(buildImmunitiyValidation(new MySQLCodec(0), IMMUNE_SQL, "IMMUNE_SQL")); + params.addAll(buildImmunitiyValidation(new MySQLCodec(1), IMMUNE_SQL, "IMMUNE_SQL")); + params.addAll(buildImmunitiyValidation(new OracleCodec(), IMMUNE_HTML, "IMMUNE_HTML")); + // No standard Immunity char array defined for PercentEncoder, but for + // GitHub issues #306 and $350, we use '%'. + params.addAll(buildImmunitiyValidation(new PercentCodec(), IMMUNE_PERCENT, "IMMUNE_PERCENT")); + params.addAll(buildImmunitiyValidation(new UnixCodec(), IMMUNE_OS, "IMMUNE_OS")); + params.addAll(buildImmunitiyValidation(new VBScriptCodec(), IMMUNE_VBSCRIPT, "IMMUNE_VBSCRIPT")); + params.addAll(buildImmunitiyValidation(new WindowsCodec(), IMMUNE_OS, "IMMUNE_OS")); + params.addAll(buildImmunitiyValidation(new XMLEntityCodec(), IMMUNE_XML, "IMMUNE_XML")); + params.addAll(buildImmunitiyValidation(new XMLEntityCodec(), IMMUNE_XMLATTR, "IMMUNE_XMLATTR")); + + params.addAll(fullCharacterCodecValidation(knownCodecs)); + + return params; + } + + private static Collection buildImmunitiyValidation(Codec codec, char[] immunities, String descriptor) { + Collection params = new ArrayList(); + for (char c : immunities) { + params.add(new Object[]{codec.getClass().getSimpleName() + " " + descriptor + " ("+ c + ")", codec, String.valueOf(c)}); + } + return params; + } + + private static Collection fullCharacterCodecValidation(Collection codecs) { + char[] holyCowTesting = StringUtilities.union(EncoderConstants.CHAR_ALPHANUMERICS, EncoderConstants.CHAR_SPECIALS); + Collection params = new ArrayList(); + for (Codec codec: codecs) { + params.addAll(buildImmunitiyValidation(codec, holyCowTesting, "Full_ALPHA_AND_SPECIALS")); + } + + return params; + } + + private final Codec codec; + private final String string; + private final char[] immunityList; + + public CodecImmunityTest(String ignored, Codec codec, String toTest) { + this.codec = codec; + this.string = toTest; + /** + * The Immunity character array is every character in the String we're testing. + * + */ + this.immunityList = toTest.toCharArray(); + } + + @Test + public void testImmuneEncode() { + String encoded = codec.encode(immunityList, string); + Assert.assertEquals(string, encoded); + } + /* + @Test + public void testImmuneDecode() { + String decoded = codec.decode(string); + Assert.assertEquals(string, decoded); + } +*/ +} From 436549341d14f34538cbff4232d8f349803bb19e Mon Sep 17 00:00:00 2001 From: kwwall Date: Sun, 31 Jan 2016 13:38:06 -0500 Subject: [PATCH 0317/1069] Close #261. --- .../java/org/owasp/esapi/filters/SecurityWrapperResponse.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/owasp/esapi/filters/SecurityWrapperResponse.java b/src/main/java/org/owasp/esapi/filters/SecurityWrapperResponse.java index a57e743f5..9dd0632ce 100644 --- a/src/main/java/org/owasp/esapi/filters/SecurityWrapperResponse.java +++ b/src/main/java/org/owasp/esapi/filters/SecurityWrapperResponse.java @@ -172,7 +172,7 @@ public void addHeader(String name, String value) { String strippedValue = StringUtilities.stripControls(value); String safeName = ESAPI.validator().getValidInput("addHeader", strippedName, "HTTPHeaderName", 20, false); String safeValue = ESAPI.validator().getValidInput("addHeader", strippedValue, "HTTPHeaderValue", ESAPI.securityConfiguration().getMaxHttpHeaderSize(), false); - getHttpServletResponse().setHeader(safeName, safeValue); + getHttpServletResponse().addHeader(safeName, safeValue); } catch (ValidationException e) { logger.warning(Logger.SECURITY_FAILURE, "Attempt to add invalid header denied", e); } From fb711682e97da692599c6f0769dfc7cfdbc16e87 Mon Sep 17 00:00:00 2001 From: kwwall Date: Sun, 31 Jan 2016 18:47:00 -0500 Subject: [PATCH 0318/1069] Close issue #288. --- src/test/java/org/owasp/esapi/reference/UserTest.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/test/java/org/owasp/esapi/reference/UserTest.java b/src/test/java/org/owasp/esapi/reference/UserTest.java index 27e5b617c..e2cf8aa6a 100644 --- a/src/test/java/org/owasp/esapi/reference/UserTest.java +++ b/src/test/java/org/owasp/esapi/reference/UserTest.java @@ -163,6 +163,13 @@ public void testChangePassword() throws Exception { } catch( AuthenticationException e ) { // expected } + try { + // Test for GitHub issue 288 + user.changePassword(password2, oldPassword, oldPassword); + fail("Shouldn't be able to reuse original (initial) password.") + } catch( AuthenticationException e ) { + // expected + } assertTrue(user.verifyPassword(password2)); assertFalse(user.verifyPassword("badpass")); } From f6d7a61e1dda3b18bbf4f7ed748e4747412f9acb Mon Sep 17 00:00:00 2001 From: kwwall Date: Sun, 31 Jan 2016 18:51:45 -0500 Subject: [PATCH 0319/1069] Close issue #288. --- src/test/java/org/owasp/esapi/reference/UserTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/org/owasp/esapi/reference/UserTest.java b/src/test/java/org/owasp/esapi/reference/UserTest.java index e2cf8aa6a..3bad13287 100644 --- a/src/test/java/org/owasp/esapi/reference/UserTest.java +++ b/src/test/java/org/owasp/esapi/reference/UserTest.java @@ -166,7 +166,7 @@ public void testChangePassword() throws Exception { try { // Test for GitHub issue 288 user.changePassword(password2, oldPassword, oldPassword); - fail("Shouldn't be able to reuse original (initial) password.") + fail("Shouldn't be able to reuse original (initial) password."); } catch( AuthenticationException e ) { // expected } From 7943011198b980d323c436b36bf9e965c5271d6c Mon Sep 17 00:00:00 2001 From: kwwall Date: Sun, 31 Jan 2016 19:12:55 -0500 Subject: [PATCH 0320/1069] Comment out previously proposed test changes. See comments in GitHub issue history for reason. --- .../java/org/owasp/esapi/reference/UserTest.java | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/test/java/org/owasp/esapi/reference/UserTest.java b/src/test/java/org/owasp/esapi/reference/UserTest.java index 3bad13287..201383c38 100644 --- a/src/test/java/org/owasp/esapi/reference/UserTest.java +++ b/src/test/java/org/owasp/esapi/reference/UserTest.java @@ -163,13 +163,14 @@ public void testChangePassword() throws Exception { } catch( AuthenticationException e ) { // expected } - try { - // Test for GitHub issue 288 - user.changePassword(password2, oldPassword, oldPassword); - fail("Shouldn't be able to reuse original (initial) password."); - } catch( AuthenticationException e ) { - // expected - } + // Invalid test until we implement an actual password history! + // try { + // // Test for GitHub issue 288 + // user.changePassword(password2, oldPassword, oldPassword); + // fail("Shouldn't be able to reuse original (initial) password."); + // } catch( AuthenticationException e ) { + // // expected + // } assertTrue(user.verifyPassword(password2)); assertFalse(user.verifyPassword("badpass")); } From 46a625ca24216c72830e6a8bf26a0441237ca593 Mon Sep 17 00:00:00 2001 From: kwwall Date: Sun, 31 Jan 2016 21:45:48 -0500 Subject: [PATCH 0321/1069] Close #287. --- .../org/owasp/esapi/reference/FileBasedAuthenticator.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/owasp/esapi/reference/FileBasedAuthenticator.java b/src/main/java/org/owasp/esapi/reference/FileBasedAuthenticator.java index 0077e6908..cee3685ad 100644 --- a/src/main/java/org/owasp/esapi/reference/FileBasedAuthenticator.java +++ b/src/main/java/org/owasp/esapi/reference/FileBasedAuthenticator.java @@ -161,12 +161,12 @@ String getHashedPassword(User user) { * Set the specified User's old password hashes. This will not set the User's current password hash. * * @param user the User whose old password hashes will be set - * @param oldHashes a list of the User's old password hashes * + * @param oldHashes a list of the User's old password hashes */ void setOldPasswordHashes(User user, List oldHashes) { List hashes = getAllHashedPasswords(user, true); if (hashes.size() > 1) { - hashes.removeAll(hashes.subList(1, hashes.size() - 1)); + hashes.removeAll(hashes.subList(1, hashes.size())); } hashes.addAll(oldHashes); } @@ -205,7 +205,7 @@ List getAllHashedPasswords(User user, boolean create) { List getOldPasswordHashes(User user) { List hashes = getAllHashedPasswords(user, false); if (hashes.size() > 1) { - return Collections.unmodifiableList(hashes.subList(1, hashes.size() - 1)); + return Collections.unmodifiableList(hashes.subList(1, hashes.size())); } return Collections.emptyList(); } From f4ce180c9aab7103f9f1d6c90630267a3257025c Mon Sep 17 00:00:00 2001 From: kwwall Date: Sun, 31 Jan 2016 21:46:58 -0500 Subject: [PATCH 0322/1069] Close issue #288. No, really! This time I *mean* it. :) --- .../org/owasp/esapi/reference/UserTest.java | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/test/java/org/owasp/esapi/reference/UserTest.java b/src/test/java/org/owasp/esapi/reference/UserTest.java index 201383c38..e091526ea 100644 --- a/src/test/java/org/owasp/esapi/reference/UserTest.java +++ b/src/test/java/org/owasp/esapi/reference/UserTest.java @@ -163,14 +163,15 @@ public void testChangePassword() throws Exception { } catch( AuthenticationException e ) { // expected } - // Invalid test until we implement an actual password history! - // try { - // // Test for GitHub issue 288 - // user.changePassword(password2, oldPassword, oldPassword); - // fail("Shouldn't be able to reuse original (initial) password."); - // } catch( AuthenticationException e ) { - // // expected - // } + try { + // Test for GitHub issue 288; note that GitHub issue 287 + // needed fixed first, because that's what made the password + // history work! + user.changePassword(password2, oldPassword, oldPassword); + fail("Shouldn't be able to reuse original (initial) password."); + } catch( AuthenticationException e ) { + // expected + } assertTrue(user.verifyPassword(password2)); assertFalse(user.verifyPassword("badpass")); } From e9d0328a59cebd3e36972b965f7176fc3a135cf7 Mon Sep 17 00:00:00 2001 From: kwwall Date: Sun, 31 Jan 2016 22:27:40 -0500 Subject: [PATCH 0323/1069] Close issue #276. --- .../java/org/owasp/esapi/reference/DefaultExecutor.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/owasp/esapi/reference/DefaultExecutor.java b/src/main/java/org/owasp/esapi/reference/DefaultExecutor.java index f948ad8ca..eabb840cb 100644 --- a/src/main/java/org/owasp/esapi/reference/DefaultExecutor.java +++ b/src/main/java/org/owasp/esapi/reference/DefaultExecutor.java @@ -143,9 +143,9 @@ public ExecuteResult executeSystemCommand(File executable, List params, File wor pb.redirectErrorStream(redirectErrorStream); if ( logParams ) { - logger.warning(Logger.SECURITY_SUCCESS, "Initiating executable: " + executable + " " + params + " in " + workdir); + logger.debug(Logger.SECURITY_SUCCESS, "Initiating executable: " + executable + " " + params + " in " + workdir); } else { - logger.warning(Logger.SECURITY_SUCCESS, "Initiating executable: " + executable + " [sensitive parameters obscured] in " + workdir); + logger.debug(Logger.SECURITY_SUCCESS, "Initiating executable: " + executable + " [sensitive parameters obscured] in " + workdir); } final StringBuilder outputBuffer = new StringBuilder(); @@ -187,7 +187,7 @@ public ExecuteResult executeSystemCommand(File executable, List params, File wor logger.warning( Logger.EVENT_FAILURE, "System command exited with non-zero status: " + exitValue ); } - logger.warning(Logger.SECURITY_SUCCESS, "System command complete"); + logger.debug(Logger.SECURITY_SUCCESS, "System command complete"); return new ExecuteResult(exitValue, output, errors); } catch (IOException e) { throw new ExecutorException("Execution failure", "Exception thrown during execution of system command: " + e.getMessage(), e); From 82b82e952c55dcaddf6f5fe1b0df907c7673e0c1 Mon Sep 17 00:00:00 2001 From: kwwall Date: Sun, 31 Jan 2016 22:54:46 -0500 Subject: [PATCH 0324/1069] Tentative 2.1.0.1 release notes. --- .../esapi4java-core-2.1.0.1-release-notes.txt | 126 ++++++++++++++++++ 1 file changed, 126 insertions(+) create mode 100644 documentation/esapi4java-core-2.1.0.1-release-notes.txt diff --git a/documentation/esapi4java-core-2.1.0.1-release-notes.txt b/documentation/esapi4java-core-2.1.0.1-release-notes.txt new file mode 100644 index 000000000..0820404d8 --- /dev/null +++ b/documentation/esapi4java-core-2.1.0.1-release-notes.txt @@ -0,0 +1,126 @@ +Release notes for ESAPI 2.1.0.1 + Release date: 2016-Feb-?? + -Kevin W. Wall + +Previous release: ESAPI 2.1.0, Sept 2013 + + +----------------------------------------------------------------------------- + GitHub Issues fixed in this release: + 35 issues closed + +32 - URLs in doc for HTTPUtilities.setNoCacheHeaders are wrong +58 - Separate Crypto Related Properties into Separate File + Fixed as part of issue #350. Can be addressed by placing sensitive + ESAPI crypto properties into a separate properties file controlled by + the operations team and not checked into your SCM. For further details, + see documentation/ESAPI-configuration-user-guide.md and use system property + org.owasp.esapi.opsteam. +96 - Need validation configuration enhancements +103 - Make ESAPI configuration XML +200 - DefaultHttpUtilities.sendRedirect should throw AccessControlException, not IOException +205 - BaseValidationRule.assertValid(String context, String input) causes NPE if input is not valid. +221 - IntrusionException should extend EnterpriseRuntimeException +229 - printStackTrace when loading configuration file +237 - how can we use esapi in java for validation,please see files attached containing java code and for errors +261 - Could not set multiple cookies one by one at single request +275 - Log4JLogger.java doesn't output correct file & line number because FQCN isn't forwarded to Log4J +276 - Patch for /branches/2.1/src/main/java/org/owasp/esapi/reference/DefaultExecutor.java +287 - Patch for /branches/2.1/src/main/java/org/owasp/esapi/reference/FileBasedAuthenticator.java +288 - Patch for /trunk/src/test/java/org/owasp/esapi/reference/UserTest.java +289 - ClickjackFilter after doFilter +306 - Canonicalizing "%Device% changes the meaning of the input string +313 - Insecure default configuation for Executor.ApprovedExecutables in ESAPI.properties file +315 - ValidatorTest.testIsValidDate fails if default locale is not US +318 - Incorrect Equality test on floating point values +319 - Resource leak: FileInputStream is not closed on method exit +321 - Unsynchronized get method, synchronized set method +322 - RequestRateThrottleFilter may not work as expected with hits=1 or hits=2 +323 - PolicyFactory Sanitize method weird output +328 - StringUtils.union broken which has minor impact on CSRF Protection and random file name generation +330 - setHeader blocks legitimate headers due to header name size limit being too low +331 - Log4j configuration with no root level causes NPE in Log4jLogger.java +334 - Regex in ESAPI.properties is not considering few of the french characters +336 - Log4JLogger.java doesn't output correct file & line number-Similar issue as reported in Issue 268 +344 - JUnit test failure in ValidatorTest.testGetValidSafeHTML() +345 - JUnit test failure in ValidatorTest.testIsValidDate() +347 - Fixes #345 - JUnit test failure in ValidatorTest.testIsValidDate() +349 - Package correctly the esapi.tld into ESAPI jar +350 - [ESAPI Spring Code Sprint – May / June 2015] Implementation of requirements +351 - getHeader length limit error +354 - Add stern javadoc warning about Base64.decodeToObject() being unsafe and mark method as deprecated. + Note: This method no longer functions unless the system property org.owasp.esapi.enableUnsafeSerialization + is set to "true". This breaks backward compatibility in favor of taking a more secure posture. +355 - Temp files created by org.owasp.esapi.waf.internal.InterceptingServletOutputStream not removed by WAF JUnit tests +356 - Make end-of-line terminators consistent for .java, .xml, and other ESAPI source files. +359 - CodecTest unit tests never test with a populated char array. + + +----------------------------------------------------------------------------- + + Other changes in this release not tracked via GitHub issues + +* Miscellaneous minor javadoc fixes and updates. +* Fixed grammatical error in CipherTextSerializer class error message. +* Upgraded versions of several ESAPI dependencies (i.e., 3rd party jars), including several that had unpatched CVEs. +* Added the Maven plug-in for OWASP Dependency Check so 3rd party dependencies can be kept up-to-date. +* Added .gitignore file so that certain files won't get accidentally commited such as IDE files. +* Added .gitattributes file so to help resolve end-of-line issues. (Part of issue 356.) +* Added new documentation (documentation/ESAPI-configuration-user-guide.md) describing new ESAPI configuration feature. +* Changed many assertions in ESAPI crypto to explicit runtime checks that + throw IllegalArgumentException instead. + +----------------------------------------------------------------------------- + ATTENTION: Other Important Notes + +The JUnit test AuthenticatorTest.setCurrentUser() is periodically failing +due to an apparent race condition either in the test itself or in +FileBasedAuthenticator. See GitHub issue #360 for details, including +why we don't think it is worth holding up the release for. + +----------------------------------------------------------------------------- + + Contributors for ESAPI 2.1.0.1 release + +Notice: My appologies if I've missed anyone, but you did have an opportunity + to send me your names. (I solicited for contributors names to emails + to the ESAPI-Dev and ESAPI-User mailing lists sent on 1/23/2016.) + If I missed you and you contributed to THIS release, please send + me an email with your first and last name and what your SPECIFIC + contribution was and I will see you name is added to this list. + - Kevin W. Wall + +Project co-leaders + Kevin W. Wall (kwwall) + Chris Schmidt (chrisisbeef) + +Special shout-outs to: + Matt Seil (xeno6696) + Jeremiah Stacey (jeremiahjstacey) + +Special contributions: + ESAPI Hackathon participants - November 18, 2014 - January 20, 2014 + Daniel Amodio + Eric Kobrin + Eric Citaire + Eamonn Washington + John Melton + Special thanks to Samantha Groves for assisting with the ESAPI hackathon + + Professor and students involved in ESAPI Spring Code Sprint (May - June, 2015): + Marek Zachara - instructor + Patryk Bak - student + Marcin Siedlarz - student + Szymon Bobowiec - student + Karol Kapcia - student + Fabio Cerullo - OWASP board coordination for code sprint + +Other Contributors: + Karan Sanwal + Arpit Gupta + Constantino Cronemberger + Tàrin Gamberìni + Kad Dembele + Anthony Musyoki + Andrew VanLoo + Ashish Tripathy From a45a134d064aefaf17d2f82c220532ac747afc1d Mon Sep 17 00:00:00 2001 From: kwwall Date: Sun, 31 Jan 2016 23:03:13 -0500 Subject: [PATCH 0325/1069] Added contributor. --- documentation/esapi4java-core-2.1.0.1-release-notes.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/documentation/esapi4java-core-2.1.0.1-release-notes.txt b/documentation/esapi4java-core-2.1.0.1-release-notes.txt index 0820404d8..568b4e234 100644 --- a/documentation/esapi4java-core-2.1.0.1-release-notes.txt +++ b/documentation/esapi4java-core-2.1.0.1-release-notes.txt @@ -124,3 +124,4 @@ Other Contributors: Anthony Musyoki Andrew VanLoo Ashish Tripathy + Brad Schoening From 18bdb4db742d5242a34e1cd9649fa675584196fe Mon Sep 17 00:00:00 2001 From: kwwall Date: Mon, 1 Feb 2016 21:10:56 -0500 Subject: [PATCH 0326/1069] Changes for new 2.1.0.1 release and to work with Cygwin. --- src/examples/scripts/findjar.sh | 17 ++++++++++++++--- src/examples/scripts/setenv-svn.sh | 8 ++++---- src/examples/scripts/setenv-zip.sh | 2 +- 3 files changed, 19 insertions(+), 8 deletions(-) diff --git a/src/examples/scripts/findjar.sh b/src/examples/scripts/findjar.sh index 611c32909..882c12b52 100755 --- a/src/examples/scripts/findjar.sh +++ b/src/examples/scripts/findjar.sh @@ -10,8 +10,13 @@ PROG=${0##*/} # Default starting directory is Maven2 repository under $HOME ... starting_dir=$HOME/.m2/repository +# If we are on Cygwin on Windows, the Maven repo may be under $USERPROFILE. +if [[ $(uname -o) == "Cygwin" && ! -d "$starting_dir" ]] +then starting_dir="$(cygpath --unix ${USERPROFILE:-$HOME}/.m2/repository)" +fi + case "$1" in --start) shift; starting_dir="$1"; shift ;; +-start) shift; starting_dir="$1"; shift ;; # Expected to be right if provided -\?) echo "$USAGE" >&2; exit 2 ;; -*) echo "$PROG: Unknown option: $1; treating as a jar pattern." >&2 ;; esac @@ -25,5 +30,11 @@ esac # echo "Starting location: $starting_dir" # DEBUG # echo "Jar pattern: $jar_pattern" # DEBUG -find "$starting_dir" -type f -name "$jar_pattern" -print | - egrep -v 'javadoc|sources' +if [[ -d "$starting_dir" ]] +then + find "$starting_dir" -type f -name "$jar_pattern" -print | + egrep -v 'javadoc|sources' +else + echo "$PROG: Can't find starting directory for Maven repo: $starting_dir" >&2 + exit 1 +fi diff --git a/src/examples/scripts/setenv-svn.sh b/src/examples/scripts/setenv-svn.sh index 41024857f..db4846e7c 100755 --- a/src/examples/scripts/setenv-svn.sh +++ b/src/examples/scripts/setenv-svn.sh @@ -1,6 +1,6 @@ #/bin/bash # Purpose: Use to set up environment to compile and run examples if ESAPI -# downloaded from the SVN repository. +# downloaded from the Svn or Git repository. # Usage: From csh, tcsh: # $ source ./setenv-svn.sh # From most other *nix shells: @@ -18,9 +18,9 @@ esapi_classpath=".:\ ../../../target/classes:\ $(ls ../../../target/esapi-*.jar 2>&- || echo .):\ -$(./findjar.sh log4j-1.2.16.jar):\ -$(./findjar.sh commons-fileupload-1.2.jar):\ -$(./findjar.sh servlet-api-2.4.jar)" +$(./findjar.sh log4j-1.2.17.jar):\ +$(./findjar.sh commons-fileupload-1.3.1.jar):\ +$(./findjar.sh servlet-api-2.5.jar)" esapi_resources="$(\cd ../../../configuration/esapi >&- 2>&- && pwd)" esapi_resources_test="$(\cd ../../../src/test/resources/esapi >&- 2>&- && pwd)" diff --git a/src/examples/scripts/setenv-zip.sh b/src/examples/scripts/setenv-zip.sh index 2a455791c..310864ac0 100755 --- a/src/examples/scripts/setenv-zip.sh +++ b/src/examples/scripts/setenv-zip.sh @@ -13,7 +13,7 @@ # since the specific version of the library is delivered as part of the # ESAPI zip file. In this manner, we do not have to update this if these # versions change. For the record, at the time of this writing, these were -# log4j-1.2.16.jar, commons-fileupload-1.2.jar, and servlet-api-2.4.jar. +# log4j-1.2.17.jar, commons-fileupload-1.3.1.jar, and servlet-api-2.5.jar. esapi_classpath=".:\ $(ls ../../../esapi*.jar):\ $(./findjar.sh -start ../../../libs log4j-*.jar):\ From a907633752d17c6b5ae7dc3b5e9c02f45e1fd8ab Mon Sep 17 00:00:00 2001 From: kwwall Date: Mon, 1 Feb 2016 21:28:23 -0500 Subject: [PATCH 0327/1069] Added System.err.println() to comment about GitHub issue 306. --- src/test/java/org/owasp/esapi/reference/AuthenticatorTest.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/test/java/org/owasp/esapi/reference/AuthenticatorTest.java b/src/test/java/org/owasp/esapi/reference/AuthenticatorTest.java index 1603689c7..f3dd16ddb 100644 --- a/src/test/java/org/owasp/esapi/reference/AuthenticatorTest.java +++ b/src/test/java/org/owasp/esapi/reference/AuthenticatorTest.java @@ -413,6 +413,9 @@ public void run() { */ @Test public void testSetCurrentUser() throws AuthenticationException, InterruptedException { System.out.println("setCurrentUser"); + System.err.println("AuthenticatorTest.setCurrentUser(): This test " + + "occasionally fails due to some undiscovered race condition. " + + "This has been reported as GitHub issue #360. Patches to fix welcome."); String user1 = ESAPI.randomizer().getRandomString(8, EncoderConstants.CHAR_UPPERS); String user2 = ESAPI.randomizer().getRandomString(8, EncoderConstants.CHAR_UPPERS); User userOne = instance.createUser(user1, "getCurrentUser", "getCurrentUser"); From 150de3a2c5ba37523612ee07341255139bab1c87 Mon Sep 17 00:00:00 2001 From: kwwall Date: Mon, 1 Feb 2016 21:30:04 -0500 Subject: [PATCH 0328/1069] Remove '-SNAPSHOT' in prep for 2.1.0.1 release. --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index f867c9b1a..140c6bf9d 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 org.owasp.esapi esapi - 2.1.0.1-SNAPSHOT + 2.1.0.1 jar From 66f1e5a93f701957be82e73d045917ec2b1136a9 Mon Sep 17 00:00:00 2001 From: kwwall Date: Mon, 1 Feb 2016 21:34:59 -0500 Subject: [PATCH 0329/1069] Add more closed issues and contributors. --- documentation/esapi4java-core-2.1.0.1-release-notes.txt | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/documentation/esapi4java-core-2.1.0.1-release-notes.txt b/documentation/esapi4java-core-2.1.0.1-release-notes.txt index 568b4e234..306b34f30 100644 --- a/documentation/esapi4java-core-2.1.0.1-release-notes.txt +++ b/documentation/esapi4java-core-2.1.0.1-release-notes.txt @@ -1,13 +1,14 @@ Release notes for ESAPI 2.1.0.1 Release date: 2016-Feb-?? - -Kevin W. Wall + -Kevin W. Wall + -Chris Schmidt Previous release: ESAPI 2.1.0, Sept 2013 ----------------------------------------------------------------------------- GitHub Issues fixed in this release: - 35 issues closed + 36 issues closed 32 - URLs in doc for HTTPUtilities.setNoCacheHeaders are wrong 58 - Separate Crypto Related Properties into Separate File @@ -23,6 +24,7 @@ Previous release: ESAPI 2.1.0, Sept 2013 221 - IntrusionException should extend EnterpriseRuntimeException 229 - printStackTrace when loading configuration file 237 - how can we use esapi in java for validation,please see files attached containing java code and for errors +254 - Patch for /trunk/src/main/java/org/owasp/esapi/reference/crypto/JavaEncryptor.java 261 - Could not set multiple cookies one by one at single request 275 - Log4JLogger.java doesn't output correct file & line number because FQCN isn't forwarded to Log4J 276 - Patch for /branches/2.1/src/main/java/org/owasp/esapi/reference/DefaultExecutor.java From 5582e1ffb6afce1208d65cf890986fb70d8b0aac Mon Sep 17 00:00:00 2001 From: kwwall Date: Fri, 5 Feb 2016 00:56:03 -0500 Subject: [PATCH 0330/1069] Set release date to 2016-Feb-05 --- documentation/esapi4java-core-2.1.0.1-release-notes.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/documentation/esapi4java-core-2.1.0.1-release-notes.txt b/documentation/esapi4java-core-2.1.0.1-release-notes.txt index 306b34f30..4c599ff59 100644 --- a/documentation/esapi4java-core-2.1.0.1-release-notes.txt +++ b/documentation/esapi4java-core-2.1.0.1-release-notes.txt @@ -1,5 +1,5 @@ Release notes for ESAPI 2.1.0.1 - Release date: 2016-Feb-?? + Release date: 2016-Feb-05 -Kevin W. Wall -Chris Schmidt From 74f4baa1cc70be14590f849b07205ed8a97bd1a5 Mon Sep 17 00:00:00 2001 From: "Kevin W. Wall" Date: Fri, 5 Feb 2016 12:30:46 -0500 Subject: [PATCH 0331/1069] Update pom.xml Change version from 2.1.0.1 to 2.1.0.2-SNAPSHOT to get ready for next development cycle. --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 140c6bf9d..32d10efb8 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 org.owasp.esapi esapi - 2.1.0.1 + 2.1.0.2-SNAPSHOT jar From c0a8c3e92ca6d9c7c0b5820dc58232b99b976aac Mon Sep 17 00:00:00 2001 From: "Kevin W. Wall" Date: Fri, 5 Feb 2016 23:10:22 -0500 Subject: [PATCH 0332/1069] Update README.md Add note about the 'master' branch no longer being default. --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index 96ce300c8..41f968157 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,9 @@ OWASP ESAPI (The OWASP Enterprise Security API) is a free, open source, web appl What does Legacy mean?

    This is the legacy branch of ESAPI which means it is an actively maintained branch of the project, however feature development for this branch will not be done. Features that have already been scheduled for the 2.x branch will move forward, but the main focus will be working on the ESAPI 3.x branch. +IMPORTANT NOTE: +The default branch for ESAPI legacy is now the 'develop' branch (rather than the 'master' branch), where future development, bug fixes, etc. will now be done. The 'master' branch is now marked as "protected"; it reflects the latest stable ESAPI release (2.1.0.1 as of this date). Note that this change of making the 'develop' branch the default may affect any pull requests that you were intending to make. + Where can I find ESAPI 3.x
    https://github.com/ESAPI/esapi-java From 6696001960616bd99a2c9c0e94e6e8b7830a2924 Mon Sep 17 00:00:00 2001 From: "Kevin W. Wall" Date: Fri, 5 Feb 2016 23:11:13 -0500 Subject: [PATCH 0333/1069] Update README.md Fixed screwed up formatting. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 41f968157..a5bc91456 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ OWASP ESAPI (The OWASP Enterprise Security API) is a free, open source, web appl What does Legacy mean?

    This is the legacy branch of ESAPI which means it is an actively maintained branch of the project, however feature development for this branch will not be done. Features that have already been scheduled for the 2.x branch will move forward, but the main focus will be working on the ESAPI 3.x branch. -IMPORTANT NOTE: +IMPORTANT NOTE: The default branch for ESAPI legacy is now the 'develop' branch (rather than the 'master' branch), where future development, bug fixes, etc. will now be done. The 'master' branch is now marked as "protected"; it reflects the latest stable ESAPI release (2.1.0.1 as of this date). Note that this change of making the 'develop' branch the default may affect any pull requests that you were intending to make. Where can I find ESAPI 3.x
    From 4cb93e4c3d1587379b7fe00d1c586299c331d639 Mon Sep 17 00:00:00 2001 From: Anthony Musyoki Date: Mon, 8 Feb 2016 09:40:24 +0300 Subject: [PATCH 0334/1069] Updates outdated plugin versions. --- pom.xml | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/pom.xml b/pom.xml index 140c6bf9d..6aee8b282 100644 --- a/pom.xml +++ b/pom.xml @@ -201,7 +201,7 @@ maven-compiler-plugin - 3.1 + 3.3 1.6 1.6 @@ -214,7 +214,7 @@ org.apache.maven.plugins maven-eclipse-plugin - 2.9 + 2.10 true @@ -222,7 +222,7 @@ maven-jar-plugin - 2.4 + 2.6 @@ -237,7 +237,7 @@ org.codehaus.mojo versions-maven-plugin - 2.1 + 2.2 check-for-dependency-updates @@ -259,7 +259,7 @@ org.codehaus.mojo findbugs-maven-plugin - 2.5.3 + 2.5.5 true true @@ -272,7 +272,7 @@ org.codehaus.mojo cobertura-maven-plugin - 2.6 + 2.7 html @@ -287,7 +287,7 @@ maven-pmd-plugin - 3.1 + 3.6 1.5 utf-8 @@ -296,7 +296,7 @@ org.apache.maven.plugins maven-javadoc-plugin - 2.9.1 + 2.10.3 false false @@ -310,7 +310,7 @@ org.apache.maven.plugins maven-changelog-plugin - 2.2 + 2.3 [Ii]ssue[# ]*(\d)+ http://code.google.com/p/owasp-esapi-java/issues/detail?id=%ISSUE% @@ -323,7 +323,7 @@ org.codehaus.mojo versions-maven-plugin - 2.1 + 2.2 @@ -337,7 +337,7 @@ org.owasp dependency-check-maven - 1.2.11 + 1.3.3 false @@ -345,7 +345,7 @@ org.apache.maven.plugins maven-surefire-report-plugin - 2.17 + 2.19.1 @@ -384,7 +384,7 @@ org.apache.maven.plugins maven-gpg-plugin - 1.5 + 1.6 sign-artifacts @@ -402,7 +402,7 @@ org.apache.maven.plugins maven-jar-plugin - 2.4 + 2.6 + 1.9.3 junit @@ -150,13 +151,13 @@ commons-fileupload commons-fileupload - 1.3.1 + 1.3.2 compile commons-io commons-io - 2.4 + 2.5 test @@ -185,12 +186,24 @@ org.owasp.antisamy antisamy - 1.5.3 + 1.5.5 + + + + xalan + xalan + 2.7.2 org.apache.xmlgraphics batik-css - 1.8 + 1.9 org.mockito @@ -272,6 +285,21 @@ 4.1.0 + + org.owasp + dependency-check-maven + 1.4.4 + + 1 + + + + + check + + + + @@ -344,14 +372,6 @@ - - org.owasp - dependency-check-maven - 1.3.3 - - false - - org.apache.maven.plugins maven-surefire-report-plugin From bd199f6f2fc41027040f14104fe557f38d726ce4 Mon Sep 17 00:00:00 2001 From: augustd Date: Wed, 3 May 2017 14:39:43 -0700 Subject: [PATCH 0357/1069] Suppress CVE-2016-1000031 in dependency check --- pom.xml | 3 ++- suppressions.xml | 10 ++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) create mode 100644 suppressions.xml diff --git a/pom.xml b/pom.xml index 857d7491b..637fe8fb0 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ jar - 3.0 + 3.1 @@ -291,6 +291,7 @@ 1.4.4 1 + ./suppressions.xml diff --git a/suppressions.xml b/suppressions.xml new file mode 100644 index 000000000..181b75909 --- /dev/null +++ b/suppressions.xml @@ -0,0 +1,10 @@ + + + + + .*\bcommons-fileupload-1.3.2.jar + CVE-2016-1000031 + + \ No newline at end of file From 3d72ea0fd2842687793f284bee62ba29407a46f3 Mon Sep 17 00:00:00 2001 From: augustd Date: Thu, 4 May 2017 23:57:59 -0700 Subject: [PATCH 0358/1069] Update to servlet API 3.0.1 This version compiles with dependency check only failing the build on CVSS 5.9+ --- pom.xml | 10 +- .../esapi/http/MockHttpServletRequest.java | 69 +++++++++ .../esapi/http/MockHttpServletResponse.java | 6 + .../owasp/esapi/http/MockServletContext.java | 138 ++++++++++++++++++ .../esapi/reference/HTTPUtilitiesTest.java | 17 ++- 5 files changed, 232 insertions(+), 8 deletions(-) diff --git a/pom.xml b/pom.xml index 637fe8fb0..224f1f5c7 100644 --- a/pom.xml +++ b/pom.xml @@ -138,14 +138,14 @@ javax.servlet - servlet-api - 2.5 + javax.servlet-api + 3.0.1 provided - javax.servlet + javax.servlet.jsp jsp-api - 2.0 + 2.2 provided @@ -290,7 +290,7 @@ dependency-check-maven 1.4.4 - 1 + 5.9 ./suppressions.xml diff --git a/src/test/java/org/owasp/esapi/http/MockHttpServletRequest.java b/src/test/java/org/owasp/esapi/http/MockHttpServletRequest.java index 4910499f4..c89a498ba 100644 --- a/src/test/java/org/owasp/esapi/http/MockHttpServletRequest.java +++ b/src/test/java/org/owasp/esapi/http/MockHttpServletRequest.java @@ -24,6 +24,7 @@ import java.text.SimpleDateFormat; import java.util.Arrays; import java.util.ArrayList; +import java.util.Collection; import java.util.Collections; import java.util.Date; import java.util.Enumeration; @@ -32,12 +33,20 @@ import java.util.Locale; import java.util.Map; import java.util.Vector; +import javax.servlet.AsyncContext; +import javax.servlet.DispatcherType; import javax.servlet.RequestDispatcher; +import javax.servlet.ServletContext; +import javax.servlet.ServletException; import javax.servlet.ServletInputStream; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; +import javax.servlet.http.Part; /** * The Class MockHttpServletRequest. @@ -779,4 +788,64 @@ public void dump() System.out.println( "\n" ); } + @Override + public boolean authenticate(HttpServletResponse hsr) throws IOException, ServletException { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + @Override + public void login(String string, String string1) throws ServletException { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + @Override + public void logout() throws ServletException { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + @Override + public Collection getParts() throws IOException, ServletException { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + @Override + public Part getPart(String string) throws IOException, ServletException { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + @Override + public ServletContext getServletContext() { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + @Override + public AsyncContext startAsync() throws IllegalStateException { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + @Override + public AsyncContext startAsync(ServletRequest sr, ServletResponse sr1) throws IllegalStateException { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + @Override + public boolean isAsyncStarted() { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + @Override + public boolean isAsyncSupported() { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + @Override + public AsyncContext getAsyncContext() { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + @Override + public DispatcherType getDispatcherType() { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + } diff --git a/src/test/java/org/owasp/esapi/http/MockHttpServletResponse.java b/src/test/java/org/owasp/esapi/http/MockHttpServletResponse.java index d45e3aa0b..0858f9be5 100644 --- a/src/test/java/org/owasp/esapi/http/MockHttpServletResponse.java +++ b/src/test/java/org/owasp/esapi/http/MockHttpServletResponse.java @@ -18,6 +18,7 @@ import java.io.IOException; import java.io.PrintWriter; import java.util.ArrayList; +import java.util.Collection; import java.util.Iterator; import java.util.List; import java.util.Locale; @@ -459,5 +460,10 @@ public void dump() { System.out.println( " BODY: " + this.getBody() ); System.out.println(); } + + @Override + public Collection getHeaders(String string) { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } } diff --git a/src/test/java/org/owasp/esapi/http/MockServletContext.java b/src/test/java/org/owasp/esapi/http/MockServletContext.java index dd1d01929..b4b042fc7 100644 --- a/src/test/java/org/owasp/esapi/http/MockServletContext.java +++ b/src/test/java/org/owasp/esapi/http/MockServletContext.java @@ -19,13 +19,21 @@ import java.net.MalformedURLException; import java.net.URL; import java.util.Enumeration; +import java.util.EventListener; +import java.util.Map; import java.util.Set; +import javax.servlet.Filter; +import javax.servlet.FilterRegistration; import javax.servlet.RequestDispatcher; import javax.servlet.Servlet; import javax.servlet.ServletConfig; import javax.servlet.ServletContext; import javax.servlet.ServletException; +import javax.servlet.ServletRegistration; +import javax.servlet.SessionCookieConfig; +import javax.servlet.SessionTrackingMode; +import javax.servlet.descriptor.JspConfigDescriptor; import org.owasp.esapi.ESAPI; import org.owasp.esapi.Logger; @@ -551,4 +559,134 @@ public void removeAttribute(String name) { public String getServletContextName() { return null; } + + @Override + public int getEffectiveMajorVersion() { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + @Override + public int getEffectiveMinorVersion() { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + @Override + public boolean setInitParameter(String string, String string1) { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + @Override + public ServletRegistration.Dynamic addServlet(String string, String string1) { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + @Override + public ServletRegistration.Dynamic addServlet(String string, Servlet srvlt) { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + @Override + public ServletRegistration.Dynamic addServlet(String string, Class type) { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + @Override + public T createServlet(Class type) throws ServletException { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + @Override + public ServletRegistration getServletRegistration(String string) { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + @Override + public Map getServletRegistrations() { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + @Override + public FilterRegistration.Dynamic addFilter(String string, String string1) { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + @Override + public FilterRegistration.Dynamic addFilter(String string, Filter filter) { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + @Override + public FilterRegistration.Dynamic addFilter(String string, Class type) { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + @Override + public T createFilter(Class type) throws ServletException { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + @Override + public FilterRegistration getFilterRegistration(String string) { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + @Override + public Map getFilterRegistrations() { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + @Override + public SessionCookieConfig getSessionCookieConfig() { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + @Override + public void setSessionTrackingModes(Set set) { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + @Override + public Set getDefaultSessionTrackingModes() { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + @Override + public Set getEffectiveSessionTrackingModes() { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + @Override + public void addListener(String string) { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + @Override + public void addListener(T t) { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + @Override + public void addListener(Class type) { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + @Override + public T createListener(Class type) throws ServletException { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + @Override + public JspConfigDescriptor getJspConfigDescriptor() { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + @Override + public ClassLoader getClassLoader() { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + @Override + public void declareRoles(String... strings) { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } } \ No newline at end of file diff --git a/src/test/java/org/owasp/esapi/reference/HTTPUtilitiesTest.java b/src/test/java/org/owasp/esapi/reference/HTTPUtilitiesTest.java index c58eecaa3..130534ee8 100644 --- a/src/test/java/org/owasp/esapi/reference/HTTPUtilitiesTest.java +++ b/src/test/java/org/owasp/esapi/reference/HTTPUtilitiesTest.java @@ -49,6 +49,10 @@ import junit.framework.Test; import junit.framework.TestCase; import junit.framework.TestSuite; +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertThat; +import org.junit.Rule; +import org.junit.rules.ExpectedException; /** * The Class HTTPUtilitiesTest. * @@ -334,6 +338,9 @@ public void testSendSafeRedirect() throws Exception { } } + @Rule + public ExpectedException thrown = ExpectedException.none(); + /** * Test of setCookie method, of class org.owasp.esapi.HTTPUtilities. */ @@ -349,9 +356,13 @@ public void testSetCookie() { instance.addCookie( response, new Cookie( "test2", "test2" ) ); assertTrue(response.getHeaderNames().size() == 2); - // test illegal name - instance.addCookie( response, new Cookie( "tes Date: Sat, 17 Jun 2017 14:30:31 -0700 Subject: [PATCH 0359/1069] Issue #376 -- Addressed Kevin Wall's CR comments. --- src/main/java/org/owasp/esapi/Validator.java | 2 ++ .../esapi/reference/DefaultValidator.java | 23 ++++++++++++++----- .../owasp/esapi/reference/ValidatorTest.java | 20 ++++++++++++++++ src/test/resources/urisForTest.txt | 1 + 4 files changed, 40 insertions(+), 6 deletions(-) diff --git a/src/main/java/org/owasp/esapi/Validator.java b/src/main/java/org/owasp/esapi/Validator.java index 00d950d82..7db159d94 100644 --- a/src/main/java/org/owasp/esapi/Validator.java +++ b/src/main/java/org/owasp/esapi/Validator.java @@ -694,6 +694,8 @@ public interface Validator { * the kind of regex required for subsequent validation to mitigate regex-based * DoS attacks. * + * @see https://www.ietf.org/rfc/rfc3986.txt + * * @param context * A descriptive name of the parameter that you are validating (e.g., LoginPage_UsernameField). This value is used by any logging or error handling that is done with respect to the value passed in. * @param input diff --git a/src/main/java/org/owasp/esapi/reference/DefaultValidator.java b/src/main/java/org/owasp/esapi/reference/DefaultValidator.java index 8b830d56e..02595f5d2 100644 --- a/src/main/java/org/owasp/esapi/reference/DefaultValidator.java +++ b/src/main/java/org/owasp/esapi/reference/DefaultValidator.java @@ -66,6 +66,7 @@ * * @author Jeff Williams (jeff.williams .at. aspectsecurity.com) Aspect Security * @author Jim Manico (jim@manico.net) Manico.net + * @author Matt Seil (mseil .at. acm.org) * * @since June 1, 2007 * @see org.owasp.esapi.Validator @@ -1208,14 +1209,15 @@ private final boolean isEmpty(char[] input) { */ public boolean isValidURI(String context, String input, boolean allowNull) { boolean isValid = false; - URI compliantURI = this.getRfcCompliantURI(input); + boolean inputIsNullOrEmpty = input == null || "".equals(input); try{ - if(null != compliantURI){ + URI compliantURI = null == input ? new URI("") : this.getRfcCompliantURI(input); + if(null != compliantURI && input != null){ String canonicalizedURI = getCanonicalizedURI(compliantURI); //if getCanonicalizedURI doesn't throw an IntrusionException, then the URI contains no mixed or //double-encoding attacks. - logger.info(Logger.SECURITY_SUCCESS, "We did not detect any mixed or multiple encoding in the uri:[" + input + "]"); + logger.debug(Logger.SECURITY_SUCCESS, "We did not detect any mixed or multiple encoding in the uri:[" + input + "]"); Validator v = ESAPI.validator(); //This part will use the regex from validation.properties. This regex should be super-simple, and //used mainly to restrict certain parts of a URL. @@ -1224,11 +1226,17 @@ public boolean isValidURI(String context, String input, boolean allowNull) { //and if the URI has any queries that also happen to match HTML entities, like ¶ //it will cease conforming to the regex we now specify for a URL. isValid = p.matcher(canonicalizedURI).matches(); + }else{ + if(allowNull && inputIsNullOrEmpty ){ + isValid = true; + } } }catch (IntrusionException e){ logger.error(Logger.SECURITY_FAILURE, e.getMessage()); isValid = false; + } catch (URISyntaxException e) { + logger.error(Logger.EVENT_FAILURE, e.getMessage()); } @@ -1249,11 +1257,14 @@ public URI getRfcCompliantURI(String input){ } /** - * This does alot. This will extract each piece of a URI according to parse zone, and it will construct - * a canonicalized String representing a version of the URI that is safe to run regex against to it. + * {@inheritDoc} + * + * This will extract each piece of a URI according to parse zone as specified in RFC-3986 section 3, + * and it will construct a canonicalized String representing a version of the URI that is safe to + * run regex against. * * @param dirtyUri - * @return + * @return Canonicalized URI string. * @throws IntrusionException */ public String getCanonicalizedURI(URI dirtyUri) throws IntrusionException{ diff --git a/src/test/java/org/owasp/esapi/reference/ValidatorTest.java b/src/test/java/org/owasp/esapi/reference/ValidatorTest.java index d2ce97256..614d25f1f 100644 --- a/src/test/java/org/owasp/esapi/reference/ValidatorTest.java +++ b/src/test/java/org/owasp/esapi/reference/ValidatorTest.java @@ -1156,6 +1156,12 @@ public void testGetValidUri(){ assertFalse(v.isValidURI("test", "http://core-jenkins.scansafe.cisco.com/ä½è´ºè¯ºä¼¦-^ńörén.jpg", false)); } + public void testGetValidUriNullInput(){ + Validator v = ESAPI.validator(); + boolean isValid = v.isValidURI("test", null, true); + assertTrue(isValid); + } + public void testGetCanonicalizedUri() throws Exception { Validator v = ESAPI.validator(); @@ -1169,5 +1175,19 @@ public void testGetCanonicalizedUri() throws Exception { assertEquals(expectedUri, v.getCanonicalizedURI(uri)); } + + public void testGetCanonicalizedUriWithMailto() throws Exception { + Validator v = ESAPI.validator(); + + String expectedUri = "http://palpatine@foo bar.com/path_to/resource?foo=bar#frag"; + //Please note that section 3.2.1 of RFC-3986 explicitly states not to encode + //password information as in http://palpatine:password@foo.com, and this will + //not appear in the userinfo field. + String input = "http://palpatine@foo%20bar.com/path_to/resource?foo=bar#frag"; + URI uri = new URI(input); + System.out.println(uri.toString()); + assertEquals(expectedUri, v.getCanonicalizedURI(uri)); + + } } diff --git a/src/test/resources/urisForTest.txt b/src/test/resources/urisForTest.txt index 2f28b60a1..43f795e3d 100644 --- a/src/test/resources/urisForTest.txt +++ b/src/test/resources/urisForTest.txt @@ -1,3 +1,4 @@ +#Format is URI,Expected test value https://127.0.0.1:8080/foo/bar,TRUE http://shareasale.com:8080/sem/fusce.xml?sed=sodales&tristique=scelerisque,TRUE http://shareasale.com/sem/fusce.xml?sed=sodales&tristique=scelerisque,TRUE From c126de7b24c5a72008433f87162eb1a19eb7ce70 Mon Sep 17 00:00:00 2001 From: Matt Seil Date: Sat, 17 Jun 2017 14:41:28 -0700 Subject: [PATCH 0360/1069] Issue #376 -- Addressed Kevin Wall's CR comment regarding the deprecated Security Configuration issue. --- .../java/org/owasp/esapi/reference/DefaultValidator.java | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/main/java/org/owasp/esapi/reference/DefaultValidator.java b/src/main/java/org/owasp/esapi/reference/DefaultValidator.java index 02595f5d2..0c29ba579 100644 --- a/src/main/java/org/owasp/esapi/reference/DefaultValidator.java +++ b/src/main/java/org/owasp/esapi/reference/DefaultValidator.java @@ -1307,10 +1307,8 @@ public String getCanonicalizedURI(URI dirtyUri) throws IntrusionException{ Set set = parseMap.keySet(); SecurityConfiguration sg = ESAPI.securityConfiguration(); -// boolean restrictMixed = sg.getBooleanProp("AllowMixedEncoding"); -// boolean restrictMultiple = sg.getBooleanProp("AllowMultipleEncoding"); - boolean allowMixed = sg.getAllowMixedEncoding(); - boolean allowMultiple = sg.getAllowMultipleEncoding(); + boolean allowMixed = sg.getBooleanProp("Encoder.AllowMixedEncoding"); + boolean allowMultiple = sg.getBooleanProp("Encoder.AllowMultipleEncoding"); for(UriSegment seg: set){ String value = encoder.canonicalize(parseMap.get(seg), allowMultiple, allowMixed); value = value == null ? "" : value; From 4ef0a497e1aa75c74e4e2e9476255b6f25f52181 Mon Sep 17 00:00:00 2001 From: Matt Seil Date: Sun, 18 Jun 2017 15:22:31 -0700 Subject: [PATCH 0361/1069] Issue 376 -- Added missed null check on regex pattern. --- .../org/owasp/esapi/reference/DefaultValidator.java | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/main/java/org/owasp/esapi/reference/DefaultValidator.java b/src/main/java/org/owasp/esapi/reference/DefaultValidator.java index 0c29ba579..9b5197824 100644 --- a/src/main/java/org/owasp/esapi/reference/DefaultValidator.java +++ b/src/main/java/org/owasp/esapi/reference/DefaultValidator.java @@ -1222,10 +1222,14 @@ public boolean isValidURI(String context, String input, boolean allowNull) { //This part will use the regex from validation.properties. This regex should be super-simple, and //used mainly to restrict certain parts of a URL. Pattern p = ESAPI.securityConfiguration().getValidationPattern( "URL" ); - //We're doing this instead of using the normal validator API, because it will canonicalize the input again - //and if the URI has any queries that also happen to match HTML entities, like ¶ - //it will cease conforming to the regex we now specify for a URL. - isValid = p.matcher(canonicalizedURI).matches(); + if(p != null){ + //We're doing this instead of using the normal validator API, because it will canonicalize the input again + //and if the URI has any queries that also happen to match HTML entities, like ¶ + //it will cease conforming to the regex we now specify for a URL. + isValid = p.matcher(canonicalizedURI).matches(); + }else{ + logger.error(Logger.EVENT_FAILURE, "Invalid regex pulled from configuration. Check the regex for URL and correct."); + } }else{ if(allowNull && inputIsNullOrEmpty ){ isValid = true; From 1895c821231d6cab8e9c529ae17873177a14e577 Mon Sep 17 00:00:00 2001 From: Matt Seil Date: Sun, 18 Jun 2017 15:29:03 -0700 Subject: [PATCH 0362/1069] Issue #376 -- Addressed comment about the RFC-3986 link. --- src/main/java/org/owasp/esapi/Validator.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/owasp/esapi/Validator.java b/src/main/java/org/owasp/esapi/Validator.java index 7db159d94..c01dae247 100644 --- a/src/main/java/org/owasp/esapi/Validator.java +++ b/src/main/java/org/owasp/esapi/Validator.java @@ -694,7 +694,7 @@ public interface Validator { * the kind of regex required for subsequent validation to mitigate regex-based * DoS attacks. * - * @see https://www.ietf.org/rfc/rfc3986.txt + * @see RFC-3986. * * @param context * A descriptive name of the parameter that you are validating (e.g., LoginPage_UsernameField). This value is used by any logging or error handling that is done with respect to the value passed in. From 8e3b5736b2bc05a1d11c9d0054404ff76a718db1 Mon Sep 17 00:00:00 2001 From: Matt Seil Date: Sun, 18 Jun 2017 15:49:54 -0700 Subject: [PATCH 0363/1069] Issue 316 -- updated code to account for httpOnly and Secure ccookie options. --- .../owasp/esapi/reference/DefaultHTTPUtilities.java | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/owasp/esapi/reference/DefaultHTTPUtilities.java b/src/main/java/org/owasp/esapi/reference/DefaultHTTPUtilities.java index 3c460c872..659e1cc4e 100644 --- a/src/main/java/org/owasp/esapi/reference/DefaultHTTPUtilities.java +++ b/src/main/java/org/owasp/esapi/reference/DefaultHTTPUtilities.java @@ -41,6 +41,7 @@ import org.owasp.esapi.ESAPI; import org.owasp.esapi.HTTPUtilities; import org.owasp.esapi.Logger; +import org.owasp.esapi.SecurityConfiguration; import org.owasp.esapi.StringUtilities; import org.owasp.esapi.User; import org.owasp.esapi.ValidationErrorList; @@ -929,6 +930,9 @@ public String setRememberToken( HttpServletRequest request, HttpServletResponse String clearToken = user.getAccountName() + "|" + password; long expiry = ESAPI.encryptor().getRelativeTimeStamp(maxAge * 1000); String cryptToken = ESAPI.encryptor().seal(clearToken, expiry); + SecurityConfiguration sg = ESAPI.securityConfiguration(); + boolean forceSecureCookies = sg.getBooleanProp("HttpUtilities.ForceSecureCookies"); + boolean forceHttpOnly = sg.getBooleanProp("HttpUtilities.ForceHttpOnlyCookies"); // Do NOT URLEncode cryptToken before creating cookie. See Google Issue # 144, // which was marked as "WontFix". @@ -937,6 +941,8 @@ public String setRememberToken( HttpServletRequest request, HttpServletResponse cookie.setMaxAge( maxAge ); cookie.setDomain( domain ); cookie.setPath( path ); + cookie.setHttpOnly(forceHttpOnly); + cookie.setSecure(forceSecureCookies); response.addCookie( cookie ); logger.info(Logger.SECURITY_SUCCESS, "Enabled remember me token for " + user.getAccountName() ); return cryptToken; @@ -957,7 +963,9 @@ public String setRememberToken(HttpServletRequest request, HttpServletResponse r String clearToken = user.getAccountName(); long expiry = ESAPI.encryptor().getRelativeTimeStamp(maxAge * 1000); String cryptToken = ESAPI.encryptor().seal(clearToken, expiry); - + SecurityConfiguration sg = ESAPI.securityConfiguration(); + boolean forceSecureCookies = sg.getBooleanProp("HttpUtilities.ForceSecureCookies"); + boolean forceHttpOnly = sg.getBooleanProp("HttpUtilities.ForceHttpOnlyCookies"); // Do NOT URLEncode cryptToken before creating cookie. See Google Issue # 144, // which was marked as "WontFix". @@ -965,6 +973,8 @@ public String setRememberToken(HttpServletRequest request, HttpServletResponse r cookie.setMaxAge( maxAge ); cookie.setDomain( domain ); cookie.setPath( path ); + cookie.setHttpOnly(forceHttpOnly); + cookie.setSecure(forceSecureCookies); response.addCookie( cookie ); logger.info(Logger.SECURITY_SUCCESS, "Enabled remember me token for " + user.getAccountName() ); } catch( IntegrityException e){ From 22663ed9ab5f17ddebe28e22d6ecaf6223763e21 Mon Sep 17 00:00:00 2001 From: Matt Seil Date: Sun, 18 Jun 2017 16:22:19 -0700 Subject: [PATCH 0364/1069] Issue 291 -- Closed due to Issue #376 resolving the original problem. --- src/test/resources/urisForTest.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/src/test/resources/urisForTest.txt b/src/test/resources/urisForTest.txt index 43f795e3d..df34cbc8e 100644 --- a/src/test/resources/urisForTest.txt +++ b/src/test/resources/urisForTest.txt @@ -1,4 +1,5 @@ #Format is URI,Expected test value +http://www.google.com?connectid=68470072-44c2-417b-822b-d945dc0364f4&request=GetFeature&service=wfs&version=1.1.0&typeName=DigitalGlobe%3AFinishedFeature&bbox=37.5%2C41.5%2C37.8%2C41.7&PROPERTYNAME=source%2CsourceUnit%2CproductType,FALSE https://127.0.0.1:8080/foo/bar,TRUE http://shareasale.com:8080/sem/fusce.xml?sed=sodales&tristique=scelerisque,TRUE http://shareasale.com/sem/fusce.xml?sed=sodales&tristique=scelerisque,TRUE From e5ebcab94a60ecf8a9c513ac61d8e99ac91b74f5 Mon Sep 17 00:00:00 2001 From: Matt Seil Date: Sun, 18 Jun 2017 16:42:05 -0700 Subject: [PATCH 0365/1069] Issue #394 -- Refactor the URI canonicalization into the Encoder class. --- src/main/java/org/owasp/esapi/Encoder.java | 12 ++ src/main/java/org/owasp/esapi/Validator.java | 11 -- .../owasp/esapi/reference/DefaultEncoder.java | 154 ++++++++++++++++++ .../esapi/reference/DefaultValidator.java | 150 +---------------- .../owasp/esapi/reference/EncoderTest.java | 28 ++++ .../owasp/esapi/reference/ValidatorTest.java | 28 ---- 6 files changed, 196 insertions(+), 187 deletions(-) diff --git a/src/main/java/org/owasp/esapi/Encoder.java b/src/main/java/org/owasp/esapi/Encoder.java index 2d2bfb7b9..cf83c472c 100644 --- a/src/main/java/org/owasp/esapi/Encoder.java +++ b/src/main/java/org/owasp/esapi/Encoder.java @@ -16,6 +16,7 @@ package org.owasp.esapi; import java.io.IOException; +import java.net.URI; import org.owasp.esapi.codecs.Codec; import org.owasp.esapi.errors.EncodingException; @@ -513,4 +514,15 @@ public interface Encoder { */ byte[] decodeFromBase64(String input) throws IOException; + /** + * + * Get a version of the input URI that will be safe to run regex and other validations against. + * It is not recommended to persist this value as it will transform user input. This method + * will not test to see if the URI is RFC-3986 compliant. + * + * @param input + * @return + */ + public String getCanonicalizedURI(URI dirtyUri); + } diff --git a/src/main/java/org/owasp/esapi/Validator.java b/src/main/java/org/owasp/esapi/Validator.java index c01dae247..12c87ea15 100644 --- a/src/main/java/org/owasp/esapi/Validator.java +++ b/src/main/java/org/owasp/esapi/Validator.java @@ -708,17 +708,6 @@ public interface Validator { */ boolean isValidURI(String context, String input, boolean allowNull); - /** - * - * Get a version of the input URI that will be safe to run regex and other validations against. - * It is not recommended to persist this value as it will transform user input. This method - * will not test to see if the URI is RFC-3986 compliant. - * - * @param input - * @return - */ - public String getCanonicalizedURI(URI dirtyUri); - /** * Will return a {@code URI} object that will represent a fully parsed and legal URI * as specified in RFC-3986. diff --git a/src/main/java/org/owasp/esapi/reference/DefaultEncoder.java b/src/main/java/org/owasp/esapi/reference/DefaultEncoder.java index c40c0d60b..c24fac4b5 100644 --- a/src/main/java/org/owasp/esapi/reference/DefaultEncoder.java +++ b/src/main/java/org/owasp/esapi/reference/DefaultEncoder.java @@ -17,15 +17,23 @@ import java.io.IOException; import java.io.UnsupportedEncodingException; +import java.net.URI; import java.net.URLDecoder; import java.net.URLEncoder; import java.util.ArrayList; +import java.util.EnumMap; import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.LinkedList; import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; import org.owasp.esapi.ESAPI; import org.owasp.esapi.Encoder; import org.owasp.esapi.Logger; +import org.owasp.esapi.SecurityConfiguration; import org.owasp.esapi.codecs.Base64; import org.owasp.esapi.codecs.CSSCodec; import org.owasp.esapi.codecs.Codec; @@ -445,4 +453,150 @@ public byte[] decodeFromBase64(String input) throws IOException { } return Base64.decode( input ); } + + /** + * {@inheritDoc} + * + * This will extract each piece of a URI according to parse zone as specified in RFC-3986 section 3, + * and it will construct a canonicalized String representing a version of the URI that is safe to + * run regex against. + * + * @param dirtyUri + * @return Canonicalized URI string. + * @throws IntrusionException + */ + public String getCanonicalizedURI(URI dirtyUri) throws IntrusionException{ + +// From RFC-3986 section 3 +// URI = scheme ":" hier-part [ "?" query ] [ "#" fragment ] +// +// hier-part = "//" authority path-abempty +// / path-absolute +// / path-rootless +// / path-empty + +// The following are two example URIs and their component parts: +// +// foo://example.com:8042/over/there?name=ferret#nose +// \_/ \______________/\_________/ \_________/ \__/ +// | | | | | +// scheme authority path query fragment +// | _____________________|__ +// / \ / \ +// urn:example:animal:ferret:nose + Map parseMap = new EnumMap(UriSegment.class); + parseMap.put(UriSegment.SCHEME, dirtyUri.getScheme()); + //authority = [ userinfo "@" ] host [ ":" port ] + parseMap.put(UriSegment.AUTHORITY, dirtyUri.getRawAuthority()); + parseMap.put(UriSegment.SCHEMSPECIFICPART, dirtyUri.getRawSchemeSpecificPart()); + parseMap.put(UriSegment.HOST, dirtyUri.getHost()); + //if port is undefined, it will return -1 + Integer port = new Integer(dirtyUri.getPort()); + parseMap.put(UriSegment.PORT, port == -1 ? "": port.toString()); + parseMap.put(UriSegment.PATH, dirtyUri.getRawPath()); + parseMap.put(UriSegment.QUERY, dirtyUri.getRawQuery()); + parseMap.put(UriSegment.FRAGMENT, dirtyUri.getRawFragment()); + + //Now we canonicalize each part and build our string. + StringBuilder sb = new StringBuilder(); + + //Replace all the items in the map with canonicalized versions. + + Set set = parseMap.keySet(); + + SecurityConfiguration sg = ESAPI.securityConfiguration(); + boolean allowMixed = sg.getBooleanProp("Encoder.AllowMixedEncoding"); + boolean allowMultiple = sg.getBooleanProp("Encoder.AllowMultipleEncoding"); + for(UriSegment seg: set){ + String value = canonicalize(parseMap.get(seg), allowMultiple, allowMixed); + value = value == null ? "" : value; + //In the case of a uri query, we need to break up and canonicalize the internal parts of the query. + if(seg == UriSegment.QUERY && null != parseMap.get(seg)){ + StringBuilder qBuilder = new StringBuilder(); + try { + Map> canonicalizedMap = this.splitQuery(dirtyUri); + Set>> query = canonicalizedMap.entrySet(); + Iterator>> i = query.iterator(); + while(i.hasNext()){ + Entry> e = i.next(); + String key = (String) e.getKey(); + String qVal = ""; + List list = (List) e.getValue(); + if(!list.isEmpty()){ + qVal = list.get(0); + } + qBuilder.append(key) + .append("=") + .append(qVal); + + if(i.hasNext()){ + qBuilder.append("&"); + } + } + value = qBuilder.toString(); + } catch (UnsupportedEncodingException e) { + logger.debug(Logger.EVENT_FAILURE, "decoding error when parsing [" + dirtyUri.toString() + "]"); + } + } + //Check if the port is -1, if it is, omit it from the output. + if(seg == UriSegment.PORT){ + if("-1" == parseMap.get(seg)){ + value = ""; + } + } + parseMap.put(seg, value ); + } + + return buildUrl(parseMap); + } + + /** + * All the parts should be canonicalized by this point. This is straightforward assembly. + * + * @param set + * @return + */ + protected String buildUrl(Map parseMap){ + StringBuilder sb = new StringBuilder(); + sb.append(parseMap.get(UriSegment.SCHEME)) + .append("://") + //can't use SCHEMESPECIFICPART for this, because we need to canonicalize all the parts of the query. + //USERINFO is also deprecated. So we technically have more than we need. + .append(parseMap.get(UriSegment.AUTHORITY) == null || parseMap.get(UriSegment.AUTHORITY).equals("") ? "" : parseMap.get(UriSegment.AUTHORITY)) + .append(parseMap.get(UriSegment.PATH) == null || parseMap.get(UriSegment.PATH).equals("") ? "" : parseMap.get(UriSegment.PATH)) + .append(parseMap.get(UriSegment.QUERY) == null || parseMap.get(UriSegment.QUERY).equals("") + ? "" : "?" + parseMap.get(UriSegment.QUERY)) + .append((parseMap.get(UriSegment.FRAGMENT) == null) || parseMap.get(UriSegment.FRAGMENT).equals("") + ? "": "#" + parseMap.get(UriSegment.FRAGMENT)) + ; + return sb.toString(); + } + + public enum UriSegment { + AUTHORITY, SCHEME, SCHEMSPECIFICPART, USERINFO, HOST, PORT, PATH, QUERY, FRAGMENT + } + + + /** + * The meat of this method was taken from StackOverflow: http://stackoverflow.com/a/13592567/557153 + * It has been modified to return a canonicalized key and value pairing. + * + * @param java URI + * @return a map of canonicalized query parameters. + * @throws UnsupportedEncodingException + */ + public Map> splitQuery(URI uri) throws UnsupportedEncodingException { + final Map> query_pairs = new LinkedHashMap>(); + final String[] pairs = uri.getQuery().split("&"); + for (String pair : pairs) { + final int idx = pair.indexOf("="); + final String key = idx > 0 ? canonicalize(pair.substring(0, idx)) : pair; + if (!query_pairs.containsKey(key)) { + query_pairs.put(key, new LinkedList()); + } + final String value = idx > 0 && pair.length() > idx + 1 ? URLDecoder.decode(pair.substring(idx + 1), "UTF-8") : null; + query_pairs.get(key).add(canonicalize(value)); + } + return query_pairs; + } } diff --git a/src/main/java/org/owasp/esapi/reference/DefaultValidator.java b/src/main/java/org/owasp/esapi/reference/DefaultValidator.java index 9b5197824..7b45f1d26 100644 --- a/src/main/java/org/owasp/esapi/reference/DefaultValidator.java +++ b/src/main/java/org/owasp/esapi/reference/DefaultValidator.java @@ -1210,11 +1210,11 @@ private final boolean isEmpty(char[] input) { public boolean isValidURI(String context, String input, boolean allowNull) { boolean isValid = false; boolean inputIsNullOrEmpty = input == null || "".equals(input); - + Encoder encoder = ESAPI.encoder(); try{ URI compliantURI = null == input ? new URI("") : this.getRfcCompliantURI(input); if(null != compliantURI && input != null){ - String canonicalizedURI = getCanonicalizedURI(compliantURI); + String canonicalizedURI = encoder.getCanonicalizedURI(compliantURI); //if getCanonicalizedURI doesn't throw an IntrusionException, then the URI contains no mixed or //double-encoding attacks. logger.debug(Logger.SECURITY_SUCCESS, "We did not detect any mixed or multiple encoding in the uri:[" + input + "]"); @@ -1259,150 +1259,4 @@ public URI getRfcCompliantURI(String input){ } return rval; } - - /** - * {@inheritDoc} - * - * This will extract each piece of a URI according to parse zone as specified in RFC-3986 section 3, - * and it will construct a canonicalized String representing a version of the URI that is safe to - * run regex against. - * - * @param dirtyUri - * @return Canonicalized URI string. - * @throws IntrusionException - */ - public String getCanonicalizedURI(URI dirtyUri) throws IntrusionException{ - -// From RFC-3986 section 3 -// URI = scheme ":" hier-part [ "?" query ] [ "#" fragment ] -// -// hier-part = "//" authority path-abempty -// / path-absolute -// / path-rootless -// / path-empty - -// The following are two example URIs and their component parts: -// -// foo://example.com:8042/over/there?name=ferret#nose -// \_/ \______________/\_________/ \_________/ \__/ -// | | | | | -// scheme authority path query fragment -// | _____________________|__ -// / \ / \ -// urn:example:animal:ferret:nose - Map parseMap = new EnumMap(UriSegment.class); - parseMap.put(UriSegment.SCHEME, dirtyUri.getScheme()); - //authority = [ userinfo "@" ] host [ ":" port ] - parseMap.put(UriSegment.AUTHORITY, dirtyUri.getRawAuthority()); - parseMap.put(UriSegment.SCHEMSPECIFICPART, dirtyUri.getRawSchemeSpecificPart()); - parseMap.put(UriSegment.HOST, dirtyUri.getHost()); - //if port is undefined, it will return -1 - Integer port = new Integer(dirtyUri.getPort()); - parseMap.put(UriSegment.PORT, port == -1 ? "": port.toString()); - parseMap.put(UriSegment.PATH, dirtyUri.getRawPath()); - parseMap.put(UriSegment.QUERY, dirtyUri.getRawQuery()); - parseMap.put(UriSegment.FRAGMENT, dirtyUri.getRawFragment()); - - //Now we canonicalize each part and build our string. - StringBuilder sb = new StringBuilder(); - - //Replace all the items in the map with canonicalized versions. - - Set set = parseMap.keySet(); - - SecurityConfiguration sg = ESAPI.securityConfiguration(); - boolean allowMixed = sg.getBooleanProp("Encoder.AllowMixedEncoding"); - boolean allowMultiple = sg.getBooleanProp("Encoder.AllowMultipleEncoding"); - for(UriSegment seg: set){ - String value = encoder.canonicalize(parseMap.get(seg), allowMultiple, allowMixed); - value = value == null ? "" : value; - //In the case of a uri query, we need to break up and canonicalize the internal parts of the query. - if(seg == UriSegment.QUERY && null != parseMap.get(seg)){ - StringBuilder qBuilder = new StringBuilder(); - try { - Map> canonicalizedMap = this.splitQuery(dirtyUri); - Set>> query = canonicalizedMap.entrySet(); - Iterator>> i = query.iterator(); - while(i.hasNext()){ - Entry> e = i.next(); - String key = (String) e.getKey(); - String qVal = ""; - List list = (List) e.getValue(); - if(!list.isEmpty()){ - qVal = list.get(0); - } - qBuilder.append(key) - .append("=") - .append(qVal); - - if(i.hasNext()){ - qBuilder.append("&"); - } - } - value = qBuilder.toString(); - } catch (UnsupportedEncodingException e) { - logger.debug(Logger.EVENT_FAILURE, "decoding error when parsing [" + dirtyUri.toString() + "]"); - } - } - //Check if the port is -1, if it is, omit it from the output. - if(seg == UriSegment.PORT){ - if("-1" == parseMap.get(seg)){ - value = ""; - } - } - parseMap.put(seg, value ); - } - - return buildUrl(parseMap); - } - -/** - * The meat of this method was taken from StackOverflow: http://stackoverflow.com/a/13592567/557153 - * It has been modified to return a canonicalized key and value pairing. - * - * @param java URI - * @return a map of canonicalized query parameters. - * @throws UnsupportedEncodingException - */ - public Map> splitQuery(URI uri) throws UnsupportedEncodingException { - final Map> query_pairs = new LinkedHashMap>(); - final String[] pairs = uri.getQuery().split("&"); - for (String pair : pairs) { - final int idx = pair.indexOf("="); - final String key = idx > 0 ? encoder.canonicalize(pair.substring(0, idx)) : pair; - if (!query_pairs.containsKey(key)) { - query_pairs.put(key, new LinkedList()); - } - final String value = idx > 0 && pair.length() > idx + 1 ? URLDecoder.decode(pair.substring(idx + 1), "UTF-8") : null; - query_pairs.get(key).add(encoder.canonicalize(value)); - } - return query_pairs; - } - - public enum UriSegment { - AUTHORITY, SCHEME, SCHEMSPECIFICPART, USERINFO, HOST, PORT, PATH, QUERY, FRAGMENT - } - - /** - * All the parts should be canonicalized by this point. This is straightforward assembly. - * - * @param set - * @return - */ - protected String buildUrl(Map parseMap){ - StringBuilder sb = new StringBuilder(); - sb.append(parseMap.get(UriSegment.SCHEME)) - .append("://") - //can't use SCHEMESPECIFICPART for this, because we need to canonicalize all the parts of the query. - //USERINFO is also deprecated. So we technically have more than we need. - .append(parseMap.get(UriSegment.AUTHORITY) == null || parseMap.get(UriSegment.AUTHORITY).equals("") ? "" : parseMap.get(UriSegment.AUTHORITY)) - .append(parseMap.get(UriSegment.PATH) == null || parseMap.get(UriSegment.PATH).equals("") ? "" : parseMap.get(UriSegment.PATH)) - .append(parseMap.get(UriSegment.QUERY) == null || parseMap.get(UriSegment.QUERY).equals("") - ? "" : "?" + parseMap.get(UriSegment.QUERY)) - .append((parseMap.get(UriSegment.FRAGMENT) == null) || parseMap.get(UriSegment.FRAGMENT).equals("") - ? "": "#" + parseMap.get(UriSegment.FRAGMENT)) - ; - return sb.toString(); - } - } diff --git a/src/test/java/org/owasp/esapi/reference/EncoderTest.java b/src/test/java/org/owasp/esapi/reference/EncoderTest.java index bc2e1afde..e7979af4c 100644 --- a/src/test/java/org/owasp/esapi/reference/EncoderTest.java +++ b/src/test/java/org/owasp/esapi/reference/EncoderTest.java @@ -17,6 +17,7 @@ import java.io.IOException; import java.io.UnsupportedEncodingException; +import java.net.URI; import java.util.ArrayList; import java.util.Arrays; import java.io.ByteArrayOutputStream; @@ -854,5 +855,32 @@ public String javaScriptEncode(String str) { } } + public void testGetCanonicalizedUri() throws Exception { + Encoder e = ESAPI.encoder(); + + String expectedUri = "http://palpatine@foo bar.com/path_to/resource?foo=bar#frag"; + //Please note that section 3.2.1 of RFC-3986 explicitly states not to encode + //password information as in http://palpatine:password@foo.com, and this will + //not appear in the userinfo field. + String input = "http://palpatine@foo%20bar.com/path_to/resource?foo=bar#frag"; + URI uri = new URI(input); + System.out.println(uri.toString()); + assertEquals(expectedUri, e.getCanonicalizedURI(uri)); + + } + + public void testGetCanonicalizedUriWithMailto() throws Exception { + Encoder e = ESAPI.encoder(); + + String expectedUri = "http://palpatine@foo bar.com/path_to/resource?foo=bar#frag"; + //Please note that section 3.2.1 of RFC-3986 explicitly states not to encode + //password information as in http://palpatine:password@foo.com, and this will + //not appear in the userinfo field. + String input = "http://palpatine@foo%20bar.com/path_to/resource?foo=bar#frag"; + URI uri = new URI(input); + System.out.println(uri.toString()); + assertEquals(expectedUri, e.getCanonicalizedURI(uri)); + + } } diff --git a/src/test/java/org/owasp/esapi/reference/ValidatorTest.java b/src/test/java/org/owasp/esapi/reference/ValidatorTest.java index 614d25f1f..7df1227b0 100644 --- a/src/test/java/org/owasp/esapi/reference/ValidatorTest.java +++ b/src/test/java/org/owasp/esapi/reference/ValidatorTest.java @@ -1161,33 +1161,5 @@ public void testGetValidUriNullInput(){ boolean isValid = v.isValidURI("test", null, true); assertTrue(isValid); } - - public void testGetCanonicalizedUri() throws Exception { - Validator v = ESAPI.validator(); - - String expectedUri = "http://palpatine@foo bar.com/path_to/resource?foo=bar#frag"; - //Please note that section 3.2.1 of RFC-3986 explicitly states not to encode - //password information as in http://palpatine:password@foo.com, and this will - //not appear in the userinfo field. - String input = "http://palpatine@foo%20bar.com/path_to/resource?foo=bar#frag"; - URI uri = new URI(input); - System.out.println(uri.toString()); - assertEquals(expectedUri, v.getCanonicalizedURI(uri)); - - } - - public void testGetCanonicalizedUriWithMailto() throws Exception { - Validator v = ESAPI.validator(); - - String expectedUri = "http://palpatine@foo bar.com/path_to/resource?foo=bar#frag"; - //Please note that section 3.2.1 of RFC-3986 explicitly states not to encode - //password information as in http://palpatine:password@foo.com, and this will - //not appear in the userinfo field. - String input = "http://palpatine@foo%20bar.com/path_to/resource?foo=bar#frag"; - URI uri = new URI(input); - System.out.println(uri.toString()); - assertEquals(expectedUri, v.getCanonicalizedURI(uri)); - - } } From c97f70735b30b41cf4f6147b4893acf3d61795c8 Mon Sep 17 00:00:00 2001 From: Matt Seil Date: Sat, 1 Jul 2017 12:26:02 -0700 Subject: [PATCH 0366/1069] Issue #397 -- Restore search directory to maintain legacy search behavior. --- .../DefaultSecurityConfiguration.java | 33 ++++++++++++++----- 1 file changed, 25 insertions(+), 8 deletions(-) diff --git a/src/main/java/org/owasp/esapi/reference/DefaultSecurityConfiguration.java b/src/main/java/org/owasp/esapi/reference/DefaultSecurityConfiguration.java index 22b0f2067..1d59f2b24 100644 --- a/src/main/java/org/owasp/esapi/reference/DefaultSecurityConfiguration.java +++ b/src/main/java/org/owasp/esapi/reference/DefaultSecurityConfiguration.java @@ -15,6 +15,23 @@ */ package org.owasp.esapi.reference; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Properties; +import java.util.regex.Pattern; +import java.util.regex.PatternSyntaxException; + import org.apache.commons.lang.text.StrTokenizer; import org.owasp.esapi.ESAPI; import org.owasp.esapi.Logger; @@ -22,12 +39,6 @@ import org.owasp.esapi.configuration.EsapiPropertyManager; import org.owasp.esapi.errors.ConfigurationException; -import java.io.*; -import java.net.URL; -import java.util.*; -import java.util.regex.Pattern; -import java.util.regex.PatternSyntaxException; - /** * The reference {@code SecurityConfiguration} manages all the settings used by the ESAPI in a single place. In this reference * implementation, resources can be put in several locations, which are searched in the following order: @@ -653,8 +664,14 @@ private Properties loadConfigurationFromClasspath(String fileName) throws Illega // try resources folder if (in == null) { - currentClasspathSearchLocation = "src/main/resources/"; - in = currentLoader.getResourceAsStream("src/main/resources/" + fileName); + currentClasspathSearchLocation = "resources/"; + in = currentLoader.getResourceAsStream("resources/" + fileName); + } + + // try src/main/resources folder + if (in == null) { + currentClasspathSearchLocation = "resources/"; + in = currentLoader.getResourceAsStream("resources/" + fileName); } // now load the properties From 26911a1da4577622c0f9f60f96004c5c25fc8988 Mon Sep 17 00:00:00 2001 From: JoelRabinovitch Date: Mon, 3 Jul 2017 13:14:53 -0400 Subject: [PATCH 0367/1069] Issue #389 (#390) * Issue #389 Overloaded the encodeForLDAP method to provide the option of not encoding wildcard (*) characters. This would be used when doing queries against an LDAP directory using wildcards, while at the same time, encoding other potentially dangerous characters. * Issue #389 Renamed the escapeWildcards variable to encodeWildcards to be consistent with the interface definition. --- src/main/java/org/owasp/esapi/Encoder.java | 14 +++++- .../owasp/esapi/reference/DefaultEncoder.java | 45 +++++++++++-------- .../owasp/esapi/reference/EncoderTest.java | 14 +++++- 3 files changed, 52 insertions(+), 21 deletions(-) diff --git a/src/main/java/org/owasp/esapi/Encoder.java b/src/main/java/org/owasp/esapi/Encoder.java index 2d2bfb7b9..a1a850b62 100644 --- a/src/main/java/org/owasp/esapi/Encoder.java +++ b/src/main/java/org/owasp/esapi/Encoder.java @@ -369,7 +369,7 @@ public interface Encoder { String encodeForOS(Codec codec, String input); /** - * Encode data for use in LDAP queries. + * Encode data for use in LDAP queries. Wildcard (*) characters will be encoded. * * @param input * the text to encode for LDAP @@ -378,6 +378,18 @@ public interface Encoder { */ String encodeForLDAP(String input); + /** + * Encode data for use in LDAP queries. You have the option whether or not to encode wildcard (*) characters. + * + * @param input + * the text to encode for LDAP + * @param encodeWildcards + * whether or not wildcard (*) characters will be encoded. + * + * @return input encoded for use in LDAP + */ + String encodeForLDAP(String input, boolean encodeWildcards); + /** * Encode data for use in an LDAP distinguished name. * diff --git a/src/main/java/org/owasp/esapi/reference/DefaultEncoder.java b/src/main/java/org/owasp/esapi/reference/DefaultEncoder.java index c40c0d60b..3e0ea4f50 100644 --- a/src/main/java/org/owasp/esapi/reference/DefaultEncoder.java +++ b/src/main/java/org/owasp/esapi/reference/DefaultEncoder.java @@ -281,6 +281,13 @@ public String encodeForOS(Codec codec, String input) { * {@inheritDoc} */ public String encodeForLDAP(String input) { + return encodeForLDAP(input, true); + } + + /** + * {@inheritDoc} + */ + public String encodeForLDAP(String input, boolean encodeWildcards) { if( input == null ) { return null; } @@ -288,25 +295,25 @@ public String encodeForLDAP(String input) { StringBuilder sb = new StringBuilder(); for (int i = 0; i < input.length(); i++) { char c = input.charAt(i); - switch (c) { - case '\\': - sb.append("\\5c"); - break; - case '*': - sb.append("\\2a"); - break; - case '(': - sb.append("\\28"); - break; - case ')': - sb.append("\\29"); - break; - case '\0': - sb.append("\\00"); - break; - default: - sb.append(c); - } + + if (c == '\\') { + sb.append("\\5c"); + } + else if ((c == '*') && encodeWildcards) { + sb.append("\\2a"); + } + else if (c == '(') { + sb.append("\\28"); + } + else if (c == ')') { + sb.append("\\29"); + } + else if (c == '\0') { + sb.append("\\00"); + } + else { + sb.append(c); + } } return sb.toString(); } diff --git a/src/test/java/org/owasp/esapi/reference/EncoderTest.java b/src/test/java/org/owasp/esapi/reference/EncoderTest.java index bc2e1afde..c4ebd900f 100644 --- a/src/test/java/org/owasp/esapi/reference/EncoderTest.java +++ b/src/test/java/org/owasp/esapi/reference/EncoderTest.java @@ -471,7 +471,19 @@ public void testEncodeForLDAP() { } /** - * Test of encodeForLDAP method, of class org.owasp.esapi.Encoder. + * Test of encodeForLDAP method with without encoding wildcard characters, of class org.owasp.esapi.Encoder. + */ + public void testEncodeForLDAPWithoutEncodingWildcards() { + System.out.println("encodeForLDAPWithoutEncodingWildcards"); + Encoder instance = ESAPI.encoder(); + assertEquals(null, instance.encodeForLDAP(null, false)); + assertEquals("No special characters to escape", "Hi This is a test #��", instance.encodeForLDAP("Hi This is a test #��", false)); + assertEquals("Zeros", "Hi \\00", instance.encodeForLDAP("Hi \u0000", false)); + assertEquals("LDAP Christams Tree", "Hi \\28This\\29 = is * a \\5c test # � � �", instance.encodeForLDAP("Hi (This) = is * a \\ test # � � �", false)); + } + + /** + * Test of encodeForDN method, of class org.owasp.esapi.Encoder. */ public void testEncodeForDN() { System.out.println("encodeForDN"); From 603408f73173145258e8677d0e1364d2e60c876e Mon Sep 17 00:00:00 2001 From: Matt Seil Date: Sat, 15 Jul 2017 12:07:52 -0700 Subject: [PATCH 0368/1069] Issue #399 -- Fixed mvn site so that the build no longer breaks on legacy javadoc comments. --- pom.xml | 35 ++++++++++++++++++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 224f1f5c7..fd8551117 100644 --- a/pom.xml +++ b/pom.xml @@ -309,7 +309,7 @@ org.codehaus.mojo findbugs-maven-plugin - 2.5.5 + 3.0.4 true true @@ -412,6 +412,39 @@ + + doclint-java8-disable + + [1.8,) + + + + + org.apache.maven.plugins + maven-javadoc-plugin + + -Xdoclint:none + + + + org.apache.maven.plugins + maven-site-plugin + 3.4 + + + + org.apache.maven.plugins + maven-javadoc-plugin + + -Xdoclint:none + + + + + + + + dist From a607dba2112bffcf005fdb8c9b2d4e08391e05ca Mon Sep 17 00:00:00 2001 From: Matt Seil Date: Sat, 15 Jul 2017 12:24:19 -0700 Subject: [PATCH 0369/1069] Issue #316 -- Fixed error where I got rid of src/main/resources and also updated the unit test to use non-deprecated asserts. --- .../DefaultSecurityConfiguration.java | 4 +- .../DefaultSecurityConfigurationTest.java | 193 +++++++++--------- 2 files changed, 101 insertions(+), 96 deletions(-) diff --git a/src/main/java/org/owasp/esapi/reference/DefaultSecurityConfiguration.java b/src/main/java/org/owasp/esapi/reference/DefaultSecurityConfiguration.java index 1d59f2b24..861fced2b 100644 --- a/src/main/java/org/owasp/esapi/reference/DefaultSecurityConfiguration.java +++ b/src/main/java/org/owasp/esapi/reference/DefaultSecurityConfiguration.java @@ -670,8 +670,8 @@ private Properties loadConfigurationFromClasspath(String fileName) throws Illega // try src/main/resources folder if (in == null) { - currentClasspathSearchLocation = "resources/"; - in = currentLoader.getResourceAsStream("resources/" + fileName); + currentClasspathSearchLocation = "src/main/resources/"; + in = currentLoader.getResourceAsStream("src/main/resources/" + fileName); } // now load the properties diff --git a/src/test/java/org/owasp/esapi/reference/DefaultSecurityConfigurationTest.java b/src/test/java/org/owasp/esapi/reference/DefaultSecurityConfigurationTest.java index 0a7654871..fed8647af 100644 --- a/src/test/java/org/owasp/esapi/reference/DefaultSecurityConfigurationTest.java +++ b/src/test/java/org/owasp/esapi/reference/DefaultSecurityConfigurationTest.java @@ -1,8 +1,13 @@ package org.owasp.esapi.reference; -import java.util.regex.Pattern; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; -import junit.framework.Assert; +import java.util.regex.Pattern; import org.junit.Test; import org.owasp.esapi.ESAPI; @@ -21,139 +26,139 @@ private DefaultSecurityConfiguration createWithProperty(String key, String val) public void testGetApplicationName() { final String expected = "ESAPI_UnitTests"; DefaultSecurityConfiguration secConf = this.createWithProperty(DefaultSecurityConfiguration.APPLICATION_NAME, expected); - Assert.assertEquals(expected, secConf.getApplicationName()); + assertEquals(expected, secConf.getApplicationName()); } @Test public void testGetLogImplementation() { //test the default DefaultSecurityConfiguration secConf = new DefaultSecurityConfiguration(new java.util.Properties()); - Assert.assertEquals(DefaultSecurityConfiguration.DEFAULT_LOG_IMPLEMENTATION, secConf.getLogImplementation()); + assertEquals(DefaultSecurityConfiguration.DEFAULT_LOG_IMPLEMENTATION, secConf.getLogImplementation()); final String expected = "TestLogger"; secConf = this.createWithProperty(DefaultSecurityConfiguration.LOG_IMPLEMENTATION, expected); - Assert.assertEquals(expected, secConf.getLogImplementation()); + assertEquals(expected, secConf.getLogImplementation()); } @Test public void testAuthenticationImplementation() { //test the default DefaultSecurityConfiguration secConf = new DefaultSecurityConfiguration(new java.util.Properties()); - Assert.assertEquals(DefaultSecurityConfiguration.DEFAULT_AUTHENTICATION_IMPLEMENTATION, secConf.getAuthenticationImplementation()); + assertEquals(DefaultSecurityConfiguration.DEFAULT_AUTHENTICATION_IMPLEMENTATION, secConf.getAuthenticationImplementation()); final String expected = "TestAuthentication"; secConf = this.createWithProperty(DefaultSecurityConfiguration.AUTHENTICATION_IMPLEMENTATION, expected); - Assert.assertEquals(expected, secConf.getAuthenticationImplementation()); + assertEquals(expected, secConf.getAuthenticationImplementation()); } @Test public void testEncoderImplementation() { //test the default DefaultSecurityConfiguration secConf = new DefaultSecurityConfiguration(new java.util.Properties()); - Assert.assertEquals(DefaultSecurityConfiguration.DEFAULT_ENCODER_IMPLEMENTATION, secConf.getEncoderImplementation()); + assertEquals(DefaultSecurityConfiguration.DEFAULT_ENCODER_IMPLEMENTATION, secConf.getEncoderImplementation()); final String expected = "TestEncoder"; secConf = this.createWithProperty(DefaultSecurityConfiguration.ENCODER_IMPLEMENTATION, expected); - Assert.assertEquals(expected, secConf.getEncoderImplementation()); + assertEquals(expected, secConf.getEncoderImplementation()); } @Test public void testAccessControlImplementation() { //test the default DefaultSecurityConfiguration secConf = new DefaultSecurityConfiguration(new java.util.Properties()); - Assert.assertEquals(DefaultSecurityConfiguration.DEFAULT_ACCESS_CONTROL_IMPLEMENTATION, secConf.getAccessControlImplementation()); + assertEquals(DefaultSecurityConfiguration.DEFAULT_ACCESS_CONTROL_IMPLEMENTATION, secConf.getAccessControlImplementation()); final String expected = "TestAccessControl"; secConf = this.createWithProperty(DefaultSecurityConfiguration.ACCESS_CONTROL_IMPLEMENTATION, expected); - Assert.assertEquals(expected, secConf.getAccessControlImplementation()); + assertEquals(expected, secConf.getAccessControlImplementation()); } @Test public void testEncryptionImplementation() { //test the default DefaultSecurityConfiguration secConf = new DefaultSecurityConfiguration(new java.util.Properties()); - Assert.assertEquals(DefaultSecurityConfiguration.DEFAULT_ENCRYPTION_IMPLEMENTATION, secConf.getEncryptionImplementation()); + assertEquals(DefaultSecurityConfiguration.DEFAULT_ENCRYPTION_IMPLEMENTATION, secConf.getEncryptionImplementation()); final String expected = "TestEncryption"; secConf = this.createWithProperty(DefaultSecurityConfiguration.ENCRYPTION_IMPLEMENTATION, expected); - Assert.assertEquals(expected, secConf.getEncryptionImplementation()); + assertEquals(expected, secConf.getEncryptionImplementation()); } @Test public void testIntrusionDetectionImplementation() { //test the default DefaultSecurityConfiguration secConf = new DefaultSecurityConfiguration(new java.util.Properties()); - Assert.assertEquals(DefaultSecurityConfiguration.DEFAULT_INTRUSION_DETECTION_IMPLEMENTATION, secConf.getIntrusionDetectionImplementation()); + assertEquals(DefaultSecurityConfiguration.DEFAULT_INTRUSION_DETECTION_IMPLEMENTATION, secConf.getIntrusionDetectionImplementation()); final String expected = "TestIntrusionDetection"; secConf = this.createWithProperty(DefaultSecurityConfiguration.INTRUSION_DETECTION_IMPLEMENTATION, expected); - Assert.assertEquals(expected, secConf.getIntrusionDetectionImplementation()); + assertEquals(expected, secConf.getIntrusionDetectionImplementation()); } @Test public void testRandomizerImplementation() { //test the default DefaultSecurityConfiguration secConf = new DefaultSecurityConfiguration(new java.util.Properties()); - Assert.assertEquals(DefaultSecurityConfiguration.DEFAULT_RANDOMIZER_IMPLEMENTATION, secConf.getRandomizerImplementation()); + assertEquals(DefaultSecurityConfiguration.DEFAULT_RANDOMIZER_IMPLEMENTATION, secConf.getRandomizerImplementation()); final String expected = "TestRandomizer"; secConf = this.createWithProperty(DefaultSecurityConfiguration.RANDOMIZER_IMPLEMENTATION, expected); - Assert.assertEquals(expected, secConf.getRandomizerImplementation()); + assertEquals(expected, secConf.getRandomizerImplementation()); } @Test public void testExecutorImplementation() { //test the default DefaultSecurityConfiguration secConf = new DefaultSecurityConfiguration(new java.util.Properties()); - Assert.assertEquals(DefaultSecurityConfiguration.DEFAULT_EXECUTOR_IMPLEMENTATION, secConf.getExecutorImplementation()); + assertEquals(DefaultSecurityConfiguration.DEFAULT_EXECUTOR_IMPLEMENTATION, secConf.getExecutorImplementation()); final String expected = "TestExecutor"; secConf = this.createWithProperty(DefaultSecurityConfiguration.EXECUTOR_IMPLEMENTATION, expected); - Assert.assertEquals(expected, secConf.getExecutorImplementation()); + assertEquals(expected, secConf.getExecutorImplementation()); } @Test public void testHTTPUtilitiesImplementation() { //test the default DefaultSecurityConfiguration secConf = new DefaultSecurityConfiguration(new java.util.Properties()); - Assert.assertEquals(DefaultSecurityConfiguration.DEFAULT_HTTP_UTILITIES_IMPLEMENTATION, secConf.getHTTPUtilitiesImplementation()); + assertEquals(DefaultSecurityConfiguration.DEFAULT_HTTP_UTILITIES_IMPLEMENTATION, secConf.getHTTPUtilitiesImplementation()); final String expected = "TestHTTPUtilities"; secConf = this.createWithProperty(DefaultSecurityConfiguration.HTTP_UTILITIES_IMPLEMENTATION, expected); - Assert.assertEquals(expected, secConf.getHTTPUtilitiesImplementation()); + assertEquals(expected, secConf.getHTTPUtilitiesImplementation()); } @Test public void testValidationImplementation() { //test the default DefaultSecurityConfiguration secConf = new DefaultSecurityConfiguration(new java.util.Properties()); - Assert.assertEquals(DefaultSecurityConfiguration.DEFAULT_VALIDATOR_IMPLEMENTATION, secConf.getValidationImplementation()); + assertEquals(DefaultSecurityConfiguration.DEFAULT_VALIDATOR_IMPLEMENTATION, secConf.getValidationImplementation()); final String expected = "TestValidation"; secConf = this.createWithProperty(DefaultSecurityConfiguration.VALIDATOR_IMPLEMENTATION, expected); - Assert.assertEquals(expected, secConf.getValidationImplementation()); + assertEquals(expected, secConf.getValidationImplementation()); } @Test public void testGetEncryptionKeyLength() { // test the default DefaultSecurityConfiguration secConf = new DefaultSecurityConfiguration(new java.util.Properties()); - Assert.assertEquals(128, secConf.getEncryptionKeyLength()); + assertEquals(128, secConf.getEncryptionKeyLength()); final int expected = 256; secConf = this.createWithProperty(DefaultSecurityConfiguration.KEY_LENGTH, String.valueOf(expected)); - Assert.assertEquals(expected, secConf.getEncryptionKeyLength()); + assertEquals(expected, secConf.getEncryptionKeyLength()); } @Test public void testGetKDFPseudoRandomFunction() { // test the default DefaultSecurityConfiguration secConf = new DefaultSecurityConfiguration(new java.util.Properties()); - Assert.assertEquals("HmacSHA256", secConf.getKDFPseudoRandomFunction()); + assertEquals("HmacSHA256", secConf.getKDFPseudoRandomFunction()); final String expected = "HmacSHA1"; secConf = this.createWithProperty(DefaultSecurityConfiguration.KDF_PRF_ALG, expected); - Assert.assertEquals(expected, secConf.getKDFPseudoRandomFunction()); + assertEquals(expected, secConf.getKDFPseudoRandomFunction()); } @Test @@ -161,10 +166,10 @@ public void testGetMasterSalt() { try { DefaultSecurityConfiguration secConf = new DefaultSecurityConfiguration(new java.util.Properties()); secConf.getMasterSalt(); - Assert.fail("Expected Exception not thrown"); + fail("Expected Exception not thrown"); } catch (ConfigurationException ce) { - Assert.assertNotNull(ce.getMessage()); + assertNotNull(ce.getMessage()); } final String salt = "53081"; @@ -172,7 +177,7 @@ public void testGetMasterSalt() { java.util.Properties properties = new java.util.Properties(); properties.setProperty(DefaultSecurityConfiguration.MASTER_SALT, property); DefaultSecurityConfiguration secConf = new DefaultSecurityConfiguration(properties); - Assert.assertEquals(salt, new String(secConf.getMasterSalt())); + assertEquals(salt, new String(secConf.getMasterSalt())); } @Test @@ -181,21 +186,21 @@ public void testGetAllowedExecutables() { java.util.List allowedExecutables = secConf.getAllowedExecutables(); //is this really what should be returned? what about an empty list? - Assert.assertEquals(1, allowedExecutables.size()); - Assert.assertEquals("", allowedExecutables.get(0)); + assertEquals(1, allowedExecutables.size()); + assertEquals("", allowedExecutables.get(0)); java.util.Properties properties = new java.util.Properties(); properties.setProperty(DefaultSecurityConfiguration.APPROVED_EXECUTABLES, String.valueOf("/bin/bzip2,/bin/diff, /bin/cvs")); secConf = new DefaultSecurityConfiguration(properties); allowedExecutables = secConf.getAllowedExecutables(); - Assert.assertEquals(3, allowedExecutables.size()); - Assert.assertEquals("/bin/bzip2", allowedExecutables.get(0)); - Assert.assertEquals("/bin/diff", allowedExecutables.get(1)); + assertEquals(3, allowedExecutables.size()); + assertEquals("/bin/bzip2", allowedExecutables.get(0)); + assertEquals("/bin/diff", allowedExecutables.get(1)); //this seems less than optimal, maybe each value should have a trim() done to it //at least we know that this behavior exists, the property should'nt have spaces between values - Assert.assertEquals(" /bin/cvs", allowedExecutables.get(2)); + assertEquals(" /bin/cvs", allowedExecutables.get(2)); } @Test @@ -203,189 +208,189 @@ public void testGetAllowedFileExtensions() { DefaultSecurityConfiguration secConf = new DefaultSecurityConfiguration(new java.util.Properties()); java.util.List allowedFileExtensions = secConf.getAllowedFileExtensions(); - Assert.assertFalse(allowedFileExtensions.isEmpty()); + assertFalse(allowedFileExtensions.isEmpty()); java.util.Properties properties = new java.util.Properties(); properties.setProperty(DefaultSecurityConfiguration.APPROVED_UPLOAD_EXTENSIONS, String.valueOf(".txt,.xml,.html,.png")); secConf = new DefaultSecurityConfiguration(properties); allowedFileExtensions = secConf.getAllowedFileExtensions(); - Assert.assertEquals(4, allowedFileExtensions.size()); - Assert.assertEquals(".html", allowedFileExtensions.get(2)); + assertEquals(4, allowedFileExtensions.size()); + assertEquals(".html", allowedFileExtensions.get(2)); } @Test public void testGetAllowedFileUploadSize() { DefaultSecurityConfiguration secConf = new DefaultSecurityConfiguration(new java.util.Properties()); //assert that the default is of some reasonable size - Assert.assertTrue(secConf.getAllowedFileUploadSize() > (1024 * 100)); + assertTrue(secConf.getAllowedFileUploadSize() > (1024 * 100)); final int expected = (1024 * 1000); secConf = this.createWithProperty(DefaultSecurityConfiguration.MAX_UPLOAD_FILE_BYTES, String.valueOf(expected)); - Assert.assertEquals(expected, secConf.getAllowedFileUploadSize()); + assertEquals(expected, secConf.getAllowedFileUploadSize()); } @Test public void testGetParameterNames() { //test the default DefaultSecurityConfiguration secConf = new DefaultSecurityConfiguration(new java.util.Properties()); - Assert.assertEquals("password", secConf.getPasswordParameterName()); - Assert.assertEquals("username", secConf.getUsernameParameterName()); + assertEquals("password", secConf.getPasswordParameterName()); + assertEquals("username", secConf.getUsernameParameterName()); java.util.Properties properties = new java.util.Properties(); properties.setProperty(DefaultSecurityConfiguration.PASSWORD_PARAMETER_NAME, "j_password"); properties.setProperty(DefaultSecurityConfiguration.USERNAME_PARAMETER_NAME, "j_username"); secConf = new DefaultSecurityConfiguration(properties); - Assert.assertEquals("j_password", secConf.getPasswordParameterName()); - Assert.assertEquals("j_username", secConf.getUsernameParameterName()); + assertEquals("j_password", secConf.getPasswordParameterName()); + assertEquals("j_username", secConf.getUsernameParameterName()); } @Test public void testGetEncryptionAlgorithm() { //test the default DefaultSecurityConfiguration secConf = new DefaultSecurityConfiguration(new java.util.Properties()); - Assert.assertEquals("AES", secConf.getEncryptionAlgorithm()); + assertEquals("AES", secConf.getEncryptionAlgorithm()); secConf = this.createWithProperty(DefaultSecurityConfiguration.ENCRYPTION_ALGORITHM, "3DES"); - Assert.assertEquals("3DES", secConf.getEncryptionAlgorithm()); + assertEquals("3DES", secConf.getEncryptionAlgorithm()); } @Test public void testGetCipherXProperties() { DefaultSecurityConfiguration secConf = new DefaultSecurityConfiguration(new java.util.Properties()); - Assert.assertEquals("AES/CBC/PKCS5Padding", secConf.getCipherTransformation()); - //Assert.assertEquals("AES/CBC/PKCS5Padding", secConf.getC); + assertEquals("AES/CBC/PKCS5Padding", secConf.getCipherTransformation()); + //assertEquals("AES/CBC/PKCS5Padding", secConf.getC); java.util.Properties properties = new java.util.Properties(); properties.setProperty(DefaultSecurityConfiguration.CIPHER_TRANSFORMATION_IMPLEMENTATION, "Blowfish/CFB/ISO10126Padding"); secConf = new DefaultSecurityConfiguration(properties); - Assert.assertEquals("Blowfish/CFB/ISO10126Padding", secConf.getCipherTransformation()); + assertEquals("Blowfish/CFB/ISO10126Padding", secConf.getCipherTransformation()); secConf.setCipherTransformation("DESede/PCBC/PKCS5Padding"); - Assert.assertEquals("DESede/PCBC/PKCS5Padding", secConf.getCipherTransformation()); + assertEquals("DESede/PCBC/PKCS5Padding", secConf.getCipherTransformation()); secConf.setCipherTransformation(null);//sets it back to default - Assert.assertEquals("Blowfish/CFB/ISO10126Padding", secConf.getCipherTransformation()); + assertEquals("Blowfish/CFB/ISO10126Padding", secConf.getCipherTransformation()); } @Test public void testIV() { DefaultSecurityConfiguration secConf = new DefaultSecurityConfiguration(new java.util.Properties()); - Assert.assertEquals("random", secConf.getIVType()); + assertEquals("random", secConf.getIVType()); try { secConf.getFixedIV(); - Assert.fail(); + fail(); } catch (ConfigurationException ce) { - Assert.assertNotNull(ce.getMessage()); + assertNotNull(ce.getMessage()); } java.util.Properties properties = new java.util.Properties(); properties.setProperty(DefaultSecurityConfiguration.IV_TYPE, "fixed"); properties.setProperty(DefaultSecurityConfiguration.FIXED_IV, "ivValue"); secConf = new DefaultSecurityConfiguration(properties); - Assert.assertEquals("fixed", secConf.getIVType()); - Assert.assertEquals("ivValue", secConf.getFixedIV()); + assertEquals("fixed", secConf.getIVType()); + assertEquals("ivValue", secConf.getFixedIV()); properties.setProperty(DefaultSecurityConfiguration.IV_TYPE, "illegal"); secConf = new DefaultSecurityConfiguration(properties); try { secConf.getIVType(); - Assert.fail(); + fail(); } catch (ConfigurationException ce) { - Assert.assertNotNull(ce.getMessage()); + assertNotNull(ce.getMessage()); } try { secConf.getFixedIV(); - Assert.fail(); + fail(); } catch (ConfigurationException ce) { - Assert.assertNotNull(ce.getMessage()); + assertNotNull(ce.getMessage()); } } @Test public void testGetAllowMultipleEncoding() { DefaultSecurityConfiguration secConf = new DefaultSecurityConfiguration(new java.util.Properties()); - Assert.assertFalse(secConf.getAllowMultipleEncoding()); + assertFalse(secConf.getAllowMultipleEncoding()); secConf = this.createWithProperty(DefaultSecurityConfiguration.ALLOW_MULTIPLE_ENCODING, "yes"); - Assert.assertTrue(secConf.getAllowMultipleEncoding()); + assertTrue(secConf.getAllowMultipleEncoding()); secConf = this.createWithProperty(DefaultSecurityConfiguration.ALLOW_MULTIPLE_ENCODING, "true"); - Assert.assertTrue(secConf.getAllowMultipleEncoding()); + assertTrue(secConf.getAllowMultipleEncoding()); secConf = this.createWithProperty(DefaultSecurityConfiguration.ALLOW_MULTIPLE_ENCODING, "no"); - Assert.assertFalse(secConf.getAllowMultipleEncoding()); + assertFalse(secConf.getAllowMultipleEncoding()); } @Test public void testGetDefaultCanonicalizationCodecs() { DefaultSecurityConfiguration secConf = new DefaultSecurityConfiguration(new java.util.Properties()); - Assert.assertFalse(secConf.getDefaultCanonicalizationCodecs().isEmpty()); + assertFalse(secConf.getDefaultCanonicalizationCodecs().isEmpty()); String property = "org.owasp.esapi.codecs.TestCodec1,org.owasp.esapi.codecs.TestCodec2"; secConf = this.createWithProperty(DefaultSecurityConfiguration.CANONICALIZATION_CODECS, property); - Assert.assertTrue(secConf.getDefaultCanonicalizationCodecs().contains("org.owasp.esapi.codecs.TestCodec1")); + assertTrue(secConf.getDefaultCanonicalizationCodecs().contains("org.owasp.esapi.codecs.TestCodec1")); } @Test public void testGetDisableIntrusionDetection() { DefaultSecurityConfiguration secConf = new DefaultSecurityConfiguration(new java.util.Properties()); - Assert.assertFalse(secConf.getDisableIntrusionDetection()); + assertFalse(secConf.getDisableIntrusionDetection()); secConf = this.createWithProperty(DefaultSecurityConfiguration.DISABLE_INTRUSION_DETECTION, "TRUE"); - Assert.assertTrue(secConf.getDisableIntrusionDetection()); + assertTrue(secConf.getDisableIntrusionDetection()); secConf = this.createWithProperty(DefaultSecurityConfiguration.DISABLE_INTRUSION_DETECTION, "true"); - Assert.assertTrue(secConf.getDisableIntrusionDetection()); + assertTrue(secConf.getDisableIntrusionDetection()); secConf = this.createWithProperty(DefaultSecurityConfiguration.DISABLE_INTRUSION_DETECTION, "false"); - Assert.assertFalse(secConf.getDisableIntrusionDetection()); + assertFalse(secConf.getDisableIntrusionDetection()); } @Test public void testGetLogLevel() { DefaultSecurityConfiguration secConf = new DefaultSecurityConfiguration(new java.util.Properties()); - Assert.assertEquals(Logger.WARNING, secConf.getLogLevel()); + assertEquals(Logger.WARNING, secConf.getLogLevel()); secConf = this.createWithProperty(DefaultSecurityConfiguration.LOG_LEVEL, "trace"); - Assert.assertEquals(Logger.TRACE, secConf.getLogLevel()); + assertEquals(Logger.TRACE, secConf.getLogLevel()); secConf = this.createWithProperty(DefaultSecurityConfiguration.LOG_LEVEL, "Off"); - Assert.assertEquals(Logger.OFF, secConf.getLogLevel()); + assertEquals(Logger.OFF, secConf.getLogLevel()); secConf = this.createWithProperty(DefaultSecurityConfiguration.LOG_LEVEL, "all"); - Assert.assertEquals(Logger.ALL, secConf.getLogLevel()); + assertEquals(Logger.ALL, secConf.getLogLevel()); secConf = this.createWithProperty(DefaultSecurityConfiguration.LOG_LEVEL, "DEBUG"); - Assert.assertEquals(Logger.DEBUG, secConf.getLogLevel()); + assertEquals(Logger.DEBUG, secConf.getLogLevel()); secConf = this.createWithProperty(DefaultSecurityConfiguration.LOG_LEVEL, "info"); - Assert.assertEquals(Logger.INFO, secConf.getLogLevel()); + assertEquals(Logger.INFO, secConf.getLogLevel()); secConf = this.createWithProperty(DefaultSecurityConfiguration.LOG_LEVEL, "ERROR"); - Assert.assertEquals(Logger.ERROR, secConf.getLogLevel()); + assertEquals(Logger.ERROR, secConf.getLogLevel()); } @Test public void testGetLogFileName() { DefaultSecurityConfiguration secConf = new DefaultSecurityConfiguration(new java.util.Properties()); - Assert.assertEquals("ESAPI_logging_file", secConf.getLogFileName()); + assertEquals("ESAPI_logging_file", secConf.getLogFileName()); secConf = this.createWithProperty(DefaultSecurityConfiguration.LOG_FILE_NAME, "log.txt"); - Assert.assertEquals("log.txt", secConf.getLogFileName()); + assertEquals("log.txt", secConf.getLogFileName()); } @Test public void testGetMaxLogFileSize() { DefaultSecurityConfiguration secConf = new DefaultSecurityConfiguration(new java.util.Properties()); - Assert.assertEquals(DefaultSecurityConfiguration.DEFAULT_MAX_LOG_FILE_SIZE, secConf.getMaxLogFileSize()); + assertEquals(DefaultSecurityConfiguration.DEFAULT_MAX_LOG_FILE_SIZE, secConf.getMaxLogFileSize()); int maxLogSize = (1024 * 1000); secConf = this.createWithProperty(DefaultSecurityConfiguration.MAX_LOG_FILE_SIZE, String.valueOf(maxLogSize)); - Assert.assertEquals(maxLogSize, secConf.getMaxLogFileSize()); + assertEquals(maxLogSize, secConf.getMaxLogFileSize()); } private String patternOrNull(Pattern p){ @@ -395,24 +400,24 @@ private String patternOrNull(Pattern p){ @Test public void testValidationsPropertiesFileOptions(){ DefaultSecurityConfiguration secConf = new DefaultSecurityConfiguration("ESAPI-SingleValidatorFileChecker.properties"); - Assert.assertEquals(patternOrNull(secConf.getValidationPattern("Test1")), "ValueFromFile1"); - Assert.assertNull(secConf.getValidationPattern("Test2")); - Assert.assertNull(secConf.getValidationPattern("TestC")); + assertEquals(patternOrNull(secConf.getValidationPattern("Test1")), "ValueFromFile1"); + assertNull(secConf.getValidationPattern("Test2")); + assertNull(secConf.getValidationPattern("TestC")); secConf = new DefaultSecurityConfiguration("ESAPI-DualValidatorFileChecker.properties"); - Assert.assertEquals(patternOrNull(secConf.getValidationPattern("Test1")), "ValueFromFile1"); - Assert.assertEquals(patternOrNull(secConf.getValidationPattern("Test2")), "ValueFromFile2"); - Assert.assertNull(secConf.getValidationPattern("TestC")); + assertEquals(patternOrNull(secConf.getValidationPattern("Test1")), "ValueFromFile1"); + assertEquals(patternOrNull(secConf.getValidationPattern("Test2")), "ValueFromFile2"); + assertNull(secConf.getValidationPattern("TestC")); secConf = new DefaultSecurityConfiguration("ESAPI-CommaValidatorFileChecker.properties"); - Assert.assertEquals(patternOrNull(secConf.getValidationPattern("TestC")), "ValueFromCommaFile"); - Assert.assertNull(secConf.getValidationPattern("Test1")); - Assert.assertNull(secConf.getValidationPattern("Test2")); + assertEquals(patternOrNull(secConf.getValidationPattern("TestC")), "ValueFromCommaFile"); + assertNull(secConf.getValidationPattern("Test1")); + assertNull(secConf.getValidationPattern("Test2")); secConf = new DefaultSecurityConfiguration("ESAPI-QuotedValidatorFileChecker.properties"); - Assert.assertEquals(patternOrNull(secConf.getValidationPattern("Test1")), "ValueFromFile1"); - Assert.assertEquals(patternOrNull(secConf.getValidationPattern("Test2")), "ValueFromFile2"); - Assert.assertEquals(patternOrNull(secConf.getValidationPattern("TestC")), "ValueFromCommaFile"); + assertEquals(patternOrNull(secConf.getValidationPattern("Test1")), "ValueFromFile1"); + assertEquals(patternOrNull(secConf.getValidationPattern("Test2")), "ValueFromFile2"); + assertEquals(patternOrNull(secConf.getValidationPattern("TestC")), "ValueFromCommaFile"); } } From 283753841aa927eb535ddfddb5c899fd7721d39d Mon Sep 17 00:00:00 2001 From: Matt Seil Date: Sat, 15 Jul 2017 19:21:21 -0700 Subject: [PATCH 0370/1069] Issue #316 -- Added unit test to ensure search directory changes are adequately updated in the future, and upped JVM compliance level to 1.7 since we already decided that 1.6 is more than defunct. --- .../DefaultSecurityConfiguration.java | 34 +++++++++++++++---- .../DefaultSecurityConfigurationTest.java | 17 ++++++++++ 2 files changed, 45 insertions(+), 6 deletions(-) diff --git a/src/main/java/org/owasp/esapi/reference/DefaultSecurityConfiguration.java b/src/main/java/org/owasp/esapi/reference/DefaultSecurityConfiguration.java index 861fced2b..a1f551e7d 100644 --- a/src/main/java/org/owasp/esapi/reference/DefaultSecurityConfiguration.java +++ b/src/main/java/org/owasp/esapi/reference/DefaultSecurityConfiguration.java @@ -642,36 +642,36 @@ private Properties loadConfigurationFromClasspath(String fileName) throws Illega try { // try root String currentClasspathSearchLocation = "/ (root)"; - in = loaders[i].getResourceAsStream(fileName); + in = loaders[i].getResourceAsStream(DefaultSearchPath.ROOT.toString()); // try resourceDirectory folder if (in == null) { currentClasspathSearchLocation = resourceDirectory + "/"; - in = currentLoader.getResourceAsStream(resourceDirectory + "/" + fileName); + in = currentLoader.getResourceAsStream(DefaultSearchPath.RESOURCE_DIRECTORY + fileName); } // try .esapi folder. Look here first for backward compatibility. if (in == null) { currentClasspathSearchLocation = ".esapi/"; - in = currentLoader.getResourceAsStream(".esapi/" + fileName); + in = currentLoader.getResourceAsStream(DefaultSearchPath.DOT_ESAPI + fileName); } // try esapi folder (new directory) if (in == null) { currentClasspathSearchLocation = "esapi/"; - in = currentLoader.getResourceAsStream("esapi/" + fileName); + in = currentLoader.getResourceAsStream(DefaultSearchPath.ESAPI + fileName); } // try resources folder if (in == null) { currentClasspathSearchLocation = "resources/"; - in = currentLoader.getResourceAsStream("resources/" + fileName); + in = currentLoader.getResourceAsStream(DefaultSearchPath.RESOURCES + fileName); } // try src/main/resources folder if (in == null) { currentClasspathSearchLocation = "src/main/resources/"; - in = currentLoader.getResourceAsStream("src/main/resources/" + fileName); + in = currentLoader.getResourceAsStream(DefaultSearchPath.SRC_MAIN_RESOURCES + fileName); } // now load the properties @@ -1347,4 +1347,26 @@ protected boolean shouldPrintProperties() { protected Properties getESAPIProperties() { return properties; } + + public enum DefaultSearchPath { + + RESOURCE_DIRECTORY("resourceDirectory/"), + SRC_MAIN_RESOURCES("src/main/resources/"), + ROOT("/"), + DOT_ESAPI(".esapi/"), + ESAPI("esapi/"), + RESOURCES("resources/"); + + private final String path; + + + + private DefaultSearchPath(String s){ + this.path = s; + } + + public String value(){ + return path; + } + } } diff --git a/src/test/java/org/owasp/esapi/reference/DefaultSecurityConfigurationTest.java b/src/test/java/org/owasp/esapi/reference/DefaultSecurityConfigurationTest.java index fed8647af..5171da6a5 100644 --- a/src/test/java/org/owasp/esapi/reference/DefaultSecurityConfigurationTest.java +++ b/src/test/java/org/owasp/esapi/reference/DefaultSecurityConfigurationTest.java @@ -13,6 +13,7 @@ import org.owasp.esapi.ESAPI; import org.owasp.esapi.Logger; import org.owasp.esapi.errors.ConfigurationException; +import org.owasp.esapi.reference.DefaultSecurityConfiguration.DefaultSearchPath; public class DefaultSecurityConfigurationTest { @@ -420,4 +421,20 @@ public void testValidationsPropertiesFileOptions(){ assertEquals(patternOrNull(secConf.getValidationPattern("TestC")), "ValueFromCommaFile"); } + @Test + public void DefaultSearchPathTest(){ + assertEquals("/", DefaultSearchPath.ROOT.value()); + assertEquals("resourceDirectory/", DefaultSearchPath.RESOURCE_DIRECTORY.value()); + assertEquals(".esapi/", DefaultSearchPath.DOT_ESAPI.value()); + assertEquals("esapi/", DefaultSearchPath.ESAPI.value()); + assertEquals("resources/", DefaultSearchPath.RESOURCES.value()); + assertEquals("src/main/resources/", DefaultSearchPath.SRC_MAIN_RESOURCES.value()); + } + + @Test + public void DefaultSearchPathEnumChanges(){ + int expected = 6; + int testValue = DefaultSearchPath.values().length; + assertEquals(expected, testValue); + } } From 262c342723d9c043559ada1c440dc061bdec1161 Mon Sep 17 00:00:00 2001 From: Matt Seil Date: Sun, 16 Jul 2017 09:27:46 -0700 Subject: [PATCH 0371/1069] Issue #399 -- Updated pom.xml so the base compile version is 1.7 instead of 1.6. This eliminates incompatibilities with mvn site that was preventing newer plugins from using items like java.util.Function. --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index fd8551117..b66f5ee1a 100644 --- a/pom.xml +++ b/pom.xml @@ -219,8 +219,8 @@ maven-compiler-plugin 3.3 - 1.6 - 1.6 + 1.7 + 1.7 true true false From df2c9783b3e4112519af3722e21c3d7499eab101 Mon Sep 17 00:00:00 2001 From: Matt Seil Date: Sun, 16 Jul 2017 13:13:43 -0700 Subject: [PATCH 0372/1069] Issue #398 -- Fixed hardcoded instance of HTTP header and made it configurable. --- .../org/owasp/esapi/filters/SecurityWrapperResponse.java | 6 ++++-- .../esapi/reference/DefaultSecurityConfiguration.java | 2 -- src/test/java/org/owasp/esapi/reference/ValidatorTest.java | 7 +++++++ src/test/resources/esapi/ESAPI.properties | 6 ++++-- 4 files changed, 15 insertions(+), 6 deletions(-) diff --git a/src/main/java/org/owasp/esapi/filters/SecurityWrapperResponse.java b/src/main/java/org/owasp/esapi/filters/SecurityWrapperResponse.java index 9dd0632ce..63a403879 100644 --- a/src/main/java/org/owasp/esapi/filters/SecurityWrapperResponse.java +++ b/src/main/java/org/owasp/esapi/filters/SecurityWrapperResponse.java @@ -24,6 +24,7 @@ import org.owasp.esapi.ESAPI; import org.owasp.esapi.Logger; +import org.owasp.esapi.SecurityConfiguration; import org.owasp.esapi.StringUtilities; import org.owasp.esapi.ValidationErrorList; import org.owasp.esapi.errors.IntrusionException; @@ -168,10 +169,11 @@ public void addDateHeader(String name, long date) { public void addHeader(String name, String value) { try { // TODO: make stripping a global config + SecurityConfiguration sc = ESAPI.securityConfiguration(); String strippedName = StringUtilities.stripControls(name); String strippedValue = StringUtilities.stripControls(value); - String safeName = ESAPI.validator().getValidInput("addHeader", strippedName, "HTTPHeaderName", 20, false); - String safeValue = ESAPI.validator().getValidInput("addHeader", strippedValue, "HTTPHeaderValue", ESAPI.securityConfiguration().getMaxHttpHeaderSize(), false); + String safeName = ESAPI.validator().getValidInput("addHeader", strippedName, "HTTPHeaderName", sc.getIntProp("HttpUtilities.MaxHeaderKeySize"), false); + String safeValue = ESAPI.validator().getValidInput("addHeader", strippedValue, "HTTPHeaderValue", sc.getIntProp("HttpUtilities.MaxHeaderValueSize"), false); getHttpServletResponse().addHeader(safeName, safeValue); } catch (ValidationException e) { logger.warning(Logger.SECURITY_FAILURE, "Attempt to add invalid header denied", e); diff --git a/src/main/java/org/owasp/esapi/reference/DefaultSecurityConfiguration.java b/src/main/java/org/owasp/esapi/reference/DefaultSecurityConfiguration.java index a1f551e7d..bf2a79696 100644 --- a/src/main/java/org/owasp/esapi/reference/DefaultSecurityConfiguration.java +++ b/src/main/java/org/owasp/esapi/reference/DefaultSecurityConfiguration.java @@ -1359,8 +1359,6 @@ public enum DefaultSearchPath { private final String path; - - private DefaultSearchPath(String s){ this.path = s; } diff --git a/src/test/java/org/owasp/esapi/reference/ValidatorTest.java b/src/test/java/org/owasp/esapi/reference/ValidatorTest.java index 7df1227b0..e10b47e48 100644 --- a/src/test/java/org/owasp/esapi/reference/ValidatorTest.java +++ b/src/test/java/org/owasp/esapi/reference/ValidatorTest.java @@ -1087,6 +1087,13 @@ public void testGetHeader() { assertFalse(safeRequest.getHeader("f2").equals(request.getHeader("f2"))); assertNull(safeRequest.getHeader("p3")); } + + public void testHeaderLengthChecks(){ + Validator v = ESAPI.validator(); + SecurityConfiguration sc = ESAPI.securityConfiguration(); + assertFalse(v.isValidInput("addHeader", generateStringOfLength(257), "HTTPHeaderName", sc.getIntProp("HttpUtilities.MaxHeaderKeySize"), false)); + assertFalse(v.isValidInput("addHeader", generateStringOfLength(4097), "HTTPHeaderValue", sc.getIntProp("HttpUtilities.MaxHeaderValueSize"), false)); + } public void testGetHeaderNames() { //testing Validator.HTTPHeaderName diff --git a/src/test/resources/esapi/ESAPI.properties b/src/test/resources/esapi/ESAPI.properties index 2727d4ada..becadfee7 100644 --- a/src/test/resources/esapi/ESAPI.properties +++ b/src/test/resources/esapi/ESAPI.properties @@ -331,8 +331,10 @@ HttpUtilities.ForceHttpOnlySession=false HttpUtilities.ForceSecureSession=false HttpUtilities.ForceHttpOnlyCookies=true HttpUtilities.ForceSecureCookies=true -# Maximum size of HTTP headers -HttpUtilities.MaxHeaderSize=4096 +# Maximum size of HTTP header key +HttpUtilities.MaxHeaderKeySize=256 +# Maximum size of HTTP header value +HttpUtilities.MaxHeaderValueSize=4096 # File upload configuration HttpUtilities.ApprovedUploadExtensions=.zip,.pdf,.doc,.docx,.ppt,.pptx,.tar,.gz,.tgz,.rar,.war,.jar,.ear,.xls,.rtf,.properties,.java,.class,.txt,.xml,.jsp,.jsf,.exe,.dll HttpUtilities.MaxUploadFileBytes=500000000 From 005173a2892f0a9bacd2b8edd98843751008cd19 Mon Sep 17 00:00:00 2001 From: Matt Seil Date: Fri, 21 Jul 2017 09:56:23 -0700 Subject: [PATCH 0373/1069] Added simple unit test to validate changes that made HTML entities always lower case. Associated with issues #285 and #302. --- src/test/java/org/owasp/esapi/reference/EncoderTest.java | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/test/java/org/owasp/esapi/reference/EncoderTest.java b/src/test/java/org/owasp/esapi/reference/EncoderTest.java index 6aad8c773..5bd553b25 100644 --- a/src/test/java/org/owasp/esapi/reference/EncoderTest.java +++ b/src/test/java/org/owasp/esapi/reference/EncoderTest.java @@ -469,6 +469,7 @@ public void testEncodeForLDAP() { assertEquals("No special characters to escape", "Hi This is a test #��", instance.encodeForLDAP("Hi This is a test #��")); assertEquals("Zeros", "Hi \\00", instance.encodeForLDAP("Hi \u0000")); assertEquals("LDAP Christams Tree", "Hi \\28This\\29 = is \\2a a \\5c test # � � �", instance.encodeForLDAP("Hi (This) = is * a \\ test # � � �")); + assertEquals("Hi \\28This\\29 =", instance.encodeForLDAP("Hi (This) =")); } /** @@ -499,6 +500,14 @@ public void testEncodeForDN() { assertEquals("Christmas Tree DN", "\\ Hello\\\\ \\+ \\, \\\"World\\\" \\;\\ ", instance.encodeForDN(" Hello\\ + , \"World\" ; ")); } + /** + * Longstanding issue of always lowercasing named HTML entities. This will be set right now. + */ + public void testNamedUpperCaseDecoding(){ + String input = "Ü"; + String expected = "Ü"; + assertEquals(expected, ESAPI.encoder().decodeForHTML(input)); + } public void testEncodeForXMLNull() { Encoder instance = ESAPI.encoder(); assertEquals(null, instance.encodeForXML(null)); From 4a7357d07ab46d7a659790200a22fd8085b5819e Mon Sep 17 00:00:00 2001 From: Matt Seil Date: Fri, 21 Jul 2017 15:04:53 -0700 Subject: [PATCH 0374/1069] Issue #403 -- Converted SecurityWrapperResponse.java to get rid of magic numbers and to get header lengths from ESAPI.properties. --- .../filters/SecurityWrapperResponse.java | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/src/main/java/org/owasp/esapi/filters/SecurityWrapperResponse.java b/src/main/java/org/owasp/esapi/filters/SecurityWrapperResponse.java index 3d8796649..515bf7376 100644 --- a/src/main/java/org/owasp/esapi/filters/SecurityWrapperResponse.java +++ b/src/main/java/org/owasp/esapi/filters/SecurityWrapperResponse.java @@ -151,7 +151,8 @@ private String createCookieHeader(String name, String value, int maxAge, String */ public void addDateHeader(String name, long date) { try { - String safeName = ESAPI.validator().getValidInput("safeSetDateHeader", name, "HTTPHeaderName", 20, false); + SecurityConfiguration sc = ESAPI.securityConfiguration(); + String safeName = ESAPI.validator().getValidInput("safeSetDateHeader", name, "HTTPHeaderName", sc.getIntProp("HttpUtilities.MaxHeaderKeySize"), false); getHttpServletResponse().addDateHeader(safeName, date); } catch (ValidationException e) { logger.warning(Logger.SECURITY_FAILURE, "Attempt to set invalid date header name denied", e); @@ -184,13 +185,14 @@ public void addHeader(String name, String value) { /** * Add an int header to the response after ensuring that there are no - * encoded or illegal characters in the name and name. + * encoded or illegal characters in the name and value. * @param name * @param value */ public void addIntHeader(String name, int value) { try { - String safeName = ESAPI.validator().getValidInput("safeSetDateHeader", name, "HTTPHeaderName", 20, false); + SecurityConfiguration sc = ESAPI.securityConfiguration(); + String safeName = ESAPI.validator().getValidInput("safeSetDateHeader", name, "HTTPHeaderName", sc.getIntProp("HttpUtilities.MaxHeaderKeySize"), false); getHttpServletResponse().addIntHeader(safeName, value); } catch (ValidationException e) { logger.warning(Logger.SECURITY_FAILURE, "Attempt to set invalid int header name denied", e); @@ -428,7 +430,8 @@ public void setContentType(String type) { */ public void setDateHeader(String name, long date) { try { - String safeName = ESAPI.validator().getValidInput("safeSetDateHeader", name, "HTTPHeaderName", 20, false); + SecurityConfiguration sc = ESAPI.securityConfiguration(); + String safeName = ESAPI.validator().getValidInput("safeSetDateHeader", name, "HTTPHeaderName", sc.getIntProp("HttpUtilities.MaxHeaderKeySize"), false); getHttpServletResponse().setDateHeader(safeName, date); } catch (ValidationException e) { logger.warning(Logger.SECURITY_FAILURE, "Attempt to set invalid date header name denied", e); @@ -448,8 +451,9 @@ public void setHeader(String name, String value) { try { String strippedName = StringUtilities.stripControls(name); String strippedValue = StringUtilities.stripControls(value); - String safeName = ESAPI.validator().getValidInput("setHeader", strippedName, "HTTPHeaderName", 50, false); - String safeValue = ESAPI.validator().getValidInput("setHeader", strippedValue, "HTTPHeaderValue", ESAPI.securityConfiguration().getMaxHttpHeaderSize(), false); + SecurityConfiguration sc = ESAPI.securityConfiguration(); + String safeName = ESAPI.validator().getValidInput("setHeader", strippedName, "HTTPHeaderName", sc.getIntProp("HttpUtilities.MaxHeaderKeySize"), false); + String safeValue = ESAPI.validator().getValidInput("setHeader", strippedValue, "HTTPHeaderValue", sc.getIntProp("HttpUtilities.MaxHeaderValueSize"), false); getHttpServletResponse().setHeader(safeName, safeValue); } catch (ValidationException e) { logger.warning(Logger.SECURITY_FAILURE, "Attempt to set invalid header denied", e); @@ -464,7 +468,8 @@ public void setHeader(String name, String value) { */ public void setIntHeader(String name, int value) { try { - String safeName = ESAPI.validator().getValidInput("safeSetDateHeader", name, "HTTPHeaderName", 20, false); + SecurityConfiguration sc = ESAPI.securityConfiguration(); + String safeName = ESAPI.validator().getValidInput("safeSetDateHeader", name, "HTTPHeaderName", sc.getIntProp("HttpUtilities.MaxHeaderKeySize"), false); getHttpServletResponse().setIntHeader(safeName, value); } catch (ValidationException e) { logger.warning(Logger.SECURITY_FAILURE, "Attempt to set invalid int header name denied", e); From 86ffb3d33a2846b9b0a3e34a1ca0461f6a680247 Mon Sep 17 00:00:00 2001 From: Matt Seil Date: Fri, 21 Jul 2017 15:07:14 -0700 Subject: [PATCH 0375/1069] Issue #400 -- Added mocking framework to maven test baseline and completed first minor unit tests of SecurityWrapperResponse. --- pom.xml | 38 ++++++++++++++- .../filters/SecurityWrapperResponseTest.java | 46 +++++++++++++++++++ 2 files changed, 83 insertions(+), 1 deletion(-) create mode 100644 src/test/java/org/owasp/esapi/filters/SecurityWrapperResponseTest.java diff --git a/pom.xml b/pom.xml index b66f5ee1a..7bc646277 100644 --- a/pom.xml +++ b/pom.xml @@ -205,12 +205,48 @@ batik-css 1.9 - + + + + org.powermock + powermock-api-mockito + 1.7.0 + test + + + org.powermock + powermock-module-junit4 + 1.7.0 + test + + + + + diff --git a/src/test/java/org/owasp/esapi/filters/SecurityWrapperResponseTest.java b/src/test/java/org/owasp/esapi/filters/SecurityWrapperResponseTest.java new file mode 100644 index 000000000..01eb5aca8 --- /dev/null +++ b/src/test/java/org/owasp/esapi/filters/SecurityWrapperResponseTest.java @@ -0,0 +1,46 @@ +package org.owasp.esapi.filters; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import javax.servlet.http.HttpServletResponse; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mockito; +import org.owasp.esapi.util.TestUtils; +import org.powermock.modules.junit4.PowerMockRunner; + +//@PrepareForTest({SecurityWrapperResponse.class}) +@RunWith(PowerMockRunner.class) +public class SecurityWrapperResponseTest { + + @Test + public void testAddHeader(){ + HttpServletResponse servResp = mock(HttpServletResponse.class); + SecurityWrapperResponse resp = new SecurityWrapperResponse(servResp); + resp.addHeader("Foo", "bar"); + verify(servResp, times(1)).addHeader("Foo", "bar"); + } + + @Test + public void testAddHeaderInvalidValueLength(){ + //refactor this to use a spy. + HttpServletResponse servResp = mock(HttpServletResponse.class); + SecurityWrapperResponse resp = new SecurityWrapperResponse(servResp); + SecurityWrapperResponse spyResp = spy(resp); + Mockito.doCallRealMethod().when(spyResp).addHeader("Foo", TestUtils.generateStringOfLength(4097)); + resp.addHeader("Foo", TestUtils.generateStringOfLength(4097)); + verify(servResp, times(0)).addHeader("Foo", "bar"); + } + + @Test + public void testAddHeaderInvalidKeyLength(){ + HttpServletResponse servResp = mock(HttpServletResponse.class); + SecurityWrapperResponse resp = new SecurityWrapperResponse(servResp); + resp.addHeader(TestUtils.generateStringOfLength(257), "bar"); + verify(servResp, times(0)).addHeader("Foo", "bar"); + } +} From 7c90eb797c52f0d070c2edeebcab2c9781f7f389 Mon Sep 17 00:00:00 2001 From: Matt Seil Date: Sun, 23 Jul 2017 11:24:22 -0700 Subject: [PATCH 0376/1069] Issue #400 -- added unit tests for addCookie method, implicitly tests a private method within the class as well. I also converted the class under test to use configurable header length from securityConfiguration. --- .../filters/SecurityWrapperResponse.java | 9 +- .../filters/SecurityWrapperResponseTest.java | 84 ++++++++++++++++++- 2 files changed, 88 insertions(+), 5 deletions(-) diff --git a/src/main/java/org/owasp/esapi/filters/SecurityWrapperResponse.java b/src/main/java/org/owasp/esapi/filters/SecurityWrapperResponse.java index 515bf7376..019b26c0a 100644 --- a/src/main/java/org/owasp/esapi/filters/SecurityWrapperResponse.java +++ b/src/main/java/org/owasp/esapi/filters/SecurityWrapperResponse.java @@ -82,11 +82,12 @@ public void addCookie(Cookie cookie) { String domain = cookie.getDomain(); String path = cookie.getPath(); boolean secure = cookie.getSecure(); + SecurityConfiguration sc = ESAPI.securityConfiguration(); // validate the name and value ValidationErrorList errors = new ValidationErrorList(); - String cookieName = ESAPI.validator().getValidInput("cookie name", name, "HTTPCookieName", 50, false, errors); - String cookieValue = ESAPI.validator().getValidInput("cookie value", value, "HTTPCookieValue", ESAPI.securityConfiguration().getMaxHttpHeaderSize(), false, errors); + String cookieName = ESAPI.validator().getValidInput("cookie name", name, "HTTPCookieName", sc.getIntProp("HttpUtilities.MaxHeaderKeySize"), false, errors); + String cookieValue = ESAPI.validator().getValidInput("cookie value", value, "HTTPCookieValue", sc.getIntProp("HttpUtilities.MaxHeaderValueSize"), false, errors); // if there are no errors, then just set a cookie header if (errors.size() == 0) { @@ -134,10 +135,10 @@ private String createCookieHeader(String name, String value, int maxAge, String if (path != null) { header += "; Path=" + path; } - if ( secure || ESAPI.securityConfiguration().getForceSecureCookies() ) { + if ( secure || ESAPI.securityConfiguration().getBooleanProp("HttpUtilities.ForceSecureCookies") ) { header += "; Secure"; } - if ( ESAPI.securityConfiguration().getForceHttpOnlyCookies() ) { + if ( ESAPI.securityConfiguration().getBooleanProp("HttpUtilities.ForceHttpOnlyCookies") ) { header += "; HttpOnly"; } return header; diff --git a/src/test/java/org/owasp/esapi/filters/SecurityWrapperResponseTest.java b/src/test/java/org/owasp/esapi/filters/SecurityWrapperResponseTest.java index 01eb5aca8..ecbbad138 100644 --- a/src/test/java/org/owasp/esapi/filters/SecurityWrapperResponseTest.java +++ b/src/test/java/org/owasp/esapi/filters/SecurityWrapperResponseTest.java @@ -1,15 +1,20 @@ package org.owasp.esapi.filters; +import static org.junit.Assert.assertEquals; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; + +import java.util.Collection; + +import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletResponse; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mockito; +import org.owasp.esapi.http.MockHttpServletResponse; import org.owasp.esapi.util.TestUtils; import org.powermock.modules.junit4.PowerMockRunner; @@ -25,6 +30,24 @@ public void testAddHeader(){ verify(servResp, times(1)).addHeader("Foo", "bar"); } + @Test + public void testAddDateHeader(){ + HttpServletResponse servResp = mock(HttpServletResponse.class); + SecurityWrapperResponse resp = new SecurityWrapperResponse(servResp); + long currentTime = System.currentTimeMillis(); + resp.addDateHeader("Foo", currentTime); + verify(servResp, times(1)).addDateHeader("Foo", currentTime); + } + + @Test + public void testInvalidDateHeader(){ + HttpServletResponse servResp = mock(HttpServletResponse.class); + SecurityWrapperResponse resp = new SecurityWrapperResponse(servResp); + long currentTime = System.currentTimeMillis(); + resp.addDateHeader("Foo\\r\\n", currentTime); + verify(servResp, times(0)).addDateHeader("Foo", currentTime); + } + @Test public void testAddHeaderInvalidValueLength(){ //refactor this to use a spy. @@ -43,4 +66,63 @@ public void testAddHeaderInvalidKeyLength(){ resp.addHeader(TestUtils.generateStringOfLength(257), "bar"); verify(servResp, times(0)).addHeader("Foo", "bar"); } + + @Test + public void testAddValidCookie(){ + HttpServletResponse servResp = new MockHttpServletResponse(); + servResp = spy(servResp); + SecurityWrapperResponse resp = new SecurityWrapperResponse(servResp); + SecurityWrapperResponse spyResp = spy(resp); + Cookie cookie = new Cookie("Foo", TestUtils.generateStringOfLength(10)); + Mockito.doCallRealMethod().when(spyResp).addCookie(cookie); + spyResp.addCookie(cookie); + /* + * We're indirectly testing our class. Since it ultimately + * delegates to HttpServletResponse.addHeader, we're actually + * validating that our test method constructs a header with the + * expected properties. This implicitly tests the + * createCookieHeader method as well. + */ + verify(servResp, times(1)).addHeader("Set-Cookie", "Foo=aaaaaaaaaa; Secure; HttpOnly"); + } + + @Test + public void testAddValidCookieWithDomain(){ + HttpServletResponse servResp = new MockHttpServletResponse(); + servResp = spy(servResp); + SecurityWrapperResponse resp = new SecurityWrapperResponse(servResp); + SecurityWrapperResponse spyResp = spy(resp); + Cookie cookie = new Cookie("Foo", TestUtils.generateStringOfLength(10)); + cookie.setDomain("evil.com"); + Mockito.doCallRealMethod().when(spyResp).addCookie(cookie); + spyResp.addCookie(cookie); + verify(servResp, times(1)).addHeader("Set-Cookie", "Foo=aaaaaaaaaa; Domain=evil.com; Secure; HttpOnly"); + } + + @Test + public void testAddValidCookieWithPath(){ + HttpServletResponse servResp = new MockHttpServletResponse(); + servResp = spy(servResp); + SecurityWrapperResponse resp = new SecurityWrapperResponse(servResp); + SecurityWrapperResponse spyResp = spy(resp); + Cookie cookie = new Cookie("Foo", TestUtils.generateStringOfLength(10)); + cookie.setDomain("evil.com"); + cookie.setPath("/foo/bar"); + Mockito.doCallRealMethod().when(spyResp).addCookie(cookie); + spyResp.addCookie(cookie); + verify(servResp, times(1)).addHeader("Set-Cookie", "Foo=aaaaaaaaaa; Domain=evil.com; Path=/foo/bar; Secure; HttpOnly"); + } + + @Test + public void testAddInValidCookie(){ + HttpServletResponse servResp = new MockHttpServletResponse(); + servResp = spy(servResp); + SecurityWrapperResponse resp = new SecurityWrapperResponse(servResp); + SecurityWrapperResponse spyResp = spy(resp); + Cookie cookie = new Cookie("Foo", TestUtils.generateStringOfLength(5000)); + Mockito.doCallRealMethod().when(spyResp).addCookie(cookie); + + spyResp.addCookie(cookie); + verify(servResp, times(0)).addHeader("Set-Cookie", "Foo=" + TestUtils.generateStringOfLength(5000) + "; Secure; HttpOnly"); + } } From f97a93ab7498579ce79488ee63f48e63b93ae4f2 Mon Sep 17 00:00:00 2001 From: Matt Seil Date: Mon, 24 Jul 2017 14:53:02 -0700 Subject: [PATCH 0377/1069] Issue #400 -- Added more unit tests. Also did work for #403, as well as #383. --- .../esapi/filters/SecurityWrapperRequest.java | 6 ++- .../filters/SecurityWrapperResponse.java | 42 ++++++++++++------ .../filters/SecurityWrapperResponseTest.java | 27 ++++++++++++ .../owasp/esapi/reference/EncoderTest.java | 1 + .../owasp/esapi/reference/ValidatorTest.java | 44 ++++++++----------- .../java/org/owasp/esapi/util/TestUtils.java | 14 ++++++ src/test/resources/esapi/ESAPI.properties | 9 ++-- 7 files changed, 98 insertions(+), 45 deletions(-) create mode 100644 src/test/java/org/owasp/esapi/util/TestUtils.java diff --git a/src/main/java/org/owasp/esapi/filters/SecurityWrapperRequest.java b/src/main/java/org/owasp/esapi/filters/SecurityWrapperRequest.java index c267361c2..3f6a53f3c 100644 --- a/src/main/java/org/owasp/esapi/filters/SecurityWrapperRequest.java +++ b/src/main/java/org/owasp/esapi/filters/SecurityWrapperRequest.java @@ -35,8 +35,9 @@ import org.owasp.esapi.ESAPI; import org.owasp.esapi.Logger; -import org.owasp.esapi.errors.ValidationException; +import org.owasp.esapi.SecurityConfiguration; import org.owasp.esapi.errors.AccessControlException; +import org.owasp.esapi.errors.ValidationException; // TODO: Parameterize these various lengths in calls to ESAPI.validator().getValidInput() // so that they can be placed in ESAPI.properties file (or other property file, @@ -217,10 +218,11 @@ public String getHeader(String name) { public Enumeration getHeaderNames() { Vector v = new Vector(); Enumeration en = getHttpServletRequest().getHeaderNames(); + SecurityConfiguration sc = ESAPI.securityConfiguration(); while (en.hasMoreElements()) { try { String name = (String) en.nextElement(); - String clean = ESAPI.validator().getValidInput("HTTP header name: " + name, name, "HTTPHeaderName", 150, true); + String clean = ESAPI.validator().getValidInput("HTTP header name: " + name, name, "HTTPHeaderName", sc.getIntProp("HttpUtilities.MaxHeaderNameSize"), true); v.add(clean); } catch (ValidationException e) { // already logged diff --git a/src/main/java/org/owasp/esapi/filters/SecurityWrapperResponse.java b/src/main/java/org/owasp/esapi/filters/SecurityWrapperResponse.java index 019b26c0a..82e629557 100644 --- a/src/main/java/org/owasp/esapi/filters/SecurityWrapperResponse.java +++ b/src/main/java/org/owasp/esapi/filters/SecurityWrapperResponse.java @@ -86,7 +86,7 @@ public void addCookie(Cookie cookie) { // validate the name and value ValidationErrorList errors = new ValidationErrorList(); - String cookieName = ESAPI.validator().getValidInput("cookie name", name, "HTTPCookieName", sc.getIntProp("HttpUtilities.MaxHeaderKeySize"), false, errors); + String cookieName = ESAPI.validator().getValidInput("cookie name", name, "HTTPCookieName", sc.getIntProp("HttpUtilities.MaxHeaderNameSize"), false, errors); String cookieValue = ESAPI.validator().getValidInput("cookie value", value, "HTTPCookieValue", sc.getIntProp("HttpUtilities.MaxHeaderValueSize"), false, errors); // if there are no errors, then just set a cookie header @@ -153,7 +153,7 @@ private String createCookieHeader(String name, String value, int maxAge, String public void addDateHeader(String name, long date) { try { SecurityConfiguration sc = ESAPI.securityConfiguration(); - String safeName = ESAPI.validator().getValidInput("safeSetDateHeader", name, "HTTPHeaderName", sc.getIntProp("HttpUtilities.MaxHeaderKeySize"), false); + String safeName = ESAPI.validator().getValidInput("safeSetDateHeader", name, "HTTPHeaderName", sc.getIntProp("HttpUtilities.MaxHeaderNameSize"), false); getHttpServletResponse().addDateHeader(safeName, date); } catch (ValidationException e) { logger.warning(Logger.SECURITY_FAILURE, "Attempt to set invalid date header name denied", e); @@ -176,7 +176,7 @@ public void addHeader(String name, String value) { SecurityConfiguration sc = ESAPI.securityConfiguration(); String strippedName = StringUtilities.stripControls(name); String strippedValue = StringUtilities.stripControls(value); - String safeName = ESAPI.validator().getValidInput("addHeader", strippedName, "HTTPHeaderName", sc.getIntProp("HttpUtilities.MaxHeaderKeySize"), false); + String safeName = ESAPI.validator().getValidInput("addHeader", strippedName, "HTTPHeaderName", sc.getIntProp("HttpUtilities.MaxHeaderNameSize"), false); String safeValue = ESAPI.validator().getValidInput("addHeader", strippedValue, "HTTPHeaderValue", sc.getIntProp("HttpUtilities.MaxHeaderValueSize"), false); getHttpServletResponse().addHeader(safeName, safeValue); } catch (ValidationException e) { @@ -193,7 +193,7 @@ public void addHeader(String name, String value) { public void addIntHeader(String name, int value) { try { SecurityConfiguration sc = ESAPI.securityConfiguration(); - String safeName = ESAPI.validator().getValidInput("safeSetDateHeader", name, "HTTPHeaderName", sc.getIntProp("HttpUtilities.MaxHeaderKeySize"), false); + String safeName = ESAPI.validator().getValidInput("safeSetDateHeader", name, "HTTPHeaderName", sc.getIntProp("HttpUtilities.MaxHeaderNameSize"), false); getHttpServletResponse().addIntHeader(safeName, value); } catch (ValidationException e) { logger.warning(Logger.SECURITY_FAILURE, "Attempt to set invalid int header name denied", e); @@ -353,24 +353,38 @@ public void resetBuffer() { /** * Override the error code with a 200 in order to confound attackers using - * automated scanners. - * @param sc + * automated scanners. Overwriting is controlled by {@code HttpUtilities.OverwriteStatusCodes} + * in ESAPI.properties. + * @param sc -- http status code * @throws IOException */ public void sendError(int sc) throws IOException { - getHttpServletResponse().sendError(HttpServletResponse.SC_OK, getHTTPMessage(sc)); + SecurityConfiguration config = ESAPI.securityConfiguration(); + if(config.getBooleanProp("HttpUtilities.OverwriteStatusCodes")){ + getHttpServletResponse().sendError(HttpServletResponse.SC_OK, getHTTPMessage(sc)); + }else{ + getHttpServletResponse().sendError(sc, getHTTPMessage(sc)); + } + } /** * Override the error code with a 200 in order to confound attackers using * automated scanners. The message is canonicalized and filtered for - * dangerous characters. - * @param sc - * @param msg + * dangerous characters. Overwriting is controlled by {@code HttpUtilities.OverwriteStatusCodes} + * in ESAPI.properties. + * @param sc -- http status code + * @param msg -- error message * @throws IOException */ public void sendError(int sc, String msg) throws IOException { - getHttpServletResponse().sendError(HttpServletResponse.SC_OK, ESAPI.encoder().encodeForHTML(msg)); + SecurityConfiguration config = ESAPI.securityConfiguration(); + if(config.getBooleanProp("HttpUtilities.OverwriteStatusCodes")){ + getHttpServletResponse().sendError(HttpServletResponse.SC_OK, ESAPI.encoder().encodeForHTML(msg)); + }else{ + getHttpServletResponse().sendError(sc, ESAPI.encoder().encodeForHTML(msg)); + } + } /** @@ -432,7 +446,7 @@ public void setContentType(String type) { public void setDateHeader(String name, long date) { try { SecurityConfiguration sc = ESAPI.securityConfiguration(); - String safeName = ESAPI.validator().getValidInput("safeSetDateHeader", name, "HTTPHeaderName", sc.getIntProp("HttpUtilities.MaxHeaderKeySize"), false); + String safeName = ESAPI.validator().getValidInput("safeSetDateHeader", name, "HTTPHeaderName", sc.getIntProp("HttpUtilities.MaxHeaderNameSize"), false); getHttpServletResponse().setDateHeader(safeName, date); } catch (ValidationException e) { logger.warning(Logger.SECURITY_FAILURE, "Attempt to set invalid date header name denied", e); @@ -453,7 +467,7 @@ public void setHeader(String name, String value) { String strippedName = StringUtilities.stripControls(name); String strippedValue = StringUtilities.stripControls(value); SecurityConfiguration sc = ESAPI.securityConfiguration(); - String safeName = ESAPI.validator().getValidInput("setHeader", strippedName, "HTTPHeaderName", sc.getIntProp("HttpUtilities.MaxHeaderKeySize"), false); + String safeName = ESAPI.validator().getValidInput("setHeader", strippedName, "HTTPHeaderName", sc.getIntProp("HttpUtilities.MaxHeaderNameSize"), false); String safeValue = ESAPI.validator().getValidInput("setHeader", strippedValue, "HTTPHeaderValue", sc.getIntProp("HttpUtilities.MaxHeaderValueSize"), false); getHttpServletResponse().setHeader(safeName, safeValue); } catch (ValidationException e) { @@ -470,7 +484,7 @@ public void setHeader(String name, String value) { public void setIntHeader(String name, int value) { try { SecurityConfiguration sc = ESAPI.securityConfiguration(); - String safeName = ESAPI.validator().getValidInput("safeSetDateHeader", name, "HTTPHeaderName", sc.getIntProp("HttpUtilities.MaxHeaderKeySize"), false); + String safeName = ESAPI.validator().getValidInput("safeSetDateHeader", name, "HTTPHeaderName", sc.getIntProp("HttpUtilities.MaxHeaderNameSize"), false); getHttpServletResponse().setIntHeader(safeName, value); } catch (ValidationException e) { logger.warning(Logger.SECURITY_FAILURE, "Attempt to set invalid int header name denied", e); diff --git a/src/test/java/org/owasp/esapi/filters/SecurityWrapperResponseTest.java b/src/test/java/org/owasp/esapi/filters/SecurityWrapperResponseTest.java index ecbbad138..c61a20a20 100644 --- a/src/test/java/org/owasp/esapi/filters/SecurityWrapperResponseTest.java +++ b/src/test/java/org/owasp/esapi/filters/SecurityWrapperResponseTest.java @@ -67,6 +67,33 @@ public void testAddHeaderInvalidKeyLength(){ verify(servResp, times(0)).addHeader("Foo", "bar"); } + @Test + public void testAddIntHeader(){ + HttpServletResponse servResp = mock(HttpServletResponse.class); + SecurityWrapperResponse resp = new SecurityWrapperResponse(servResp); + resp.addIntHeader("aaaa", 4); + verify(servResp, times(1)).addIntHeader("aaaa", 4); + } + + @Test + public void testAddInvalidIntHeader(){ + HttpServletResponse servResp = mock(HttpServletResponse.class); + SecurityWrapperResponse resp = new SecurityWrapperResponse(servResp); + resp.addIntHeader(TestUtils.generateStringOfLength(257), Integer.MIN_VALUE); + verify(servResp, times(0)).addIntHeader(TestUtils.generateStringOfLength(257), Integer.MIN_VALUE); + } + + @Test + public void testContainsHeader(){ + HttpServletResponse servResp = new MockHttpServletResponse(); + servResp = spy(servResp); + SecurityWrapperResponse resp = new SecurityWrapperResponse(servResp); + resp = spy(resp); + resp.addIntHeader("aaaa", Integer.MIN_VALUE); + verify(servResp, times(1)).addIntHeader("aaaa", Integer.MIN_VALUE); + assertEquals(true, servResp.containsHeader("aaaa")); + } + @Test public void testAddValidCookie(){ HttpServletResponse servResp = new MockHttpServletResponse(); diff --git a/src/test/java/org/owasp/esapi/reference/EncoderTest.java b/src/test/java/org/owasp/esapi/reference/EncoderTest.java index 5bd553b25..45702ced4 100644 --- a/src/test/java/org/owasp/esapi/reference/EncoderTest.java +++ b/src/test/java/org/owasp/esapi/reference/EncoderTest.java @@ -508,6 +508,7 @@ public void testNamedUpperCaseDecoding(){ String expected = "Ü"; assertEquals(expected, ESAPI.encoder().decodeForHTML(input)); } + public void testEncodeForXMLNull() { Encoder instance = ESAPI.encoder(); assertEquals(null, instance.encodeForXML(null)); diff --git a/src/test/java/org/owasp/esapi/reference/ValidatorTest.java b/src/test/java/org/owasp/esapi/reference/ValidatorTest.java index e10b47e48..c98620320 100644 --- a/src/test/java/org/owasp/esapi/reference/ValidatorTest.java +++ b/src/test/java/org/owasp/esapi/reference/ValidatorTest.java @@ -25,6 +25,7 @@ import org.owasp.esapi.http.MockHttpServletResponse; import org.owasp.esapi.reference.validation.HTMLValidationRule; import org.owasp.esapi.reference.validation.StringValidationRule; +import org.owasp.esapi.util.TestUtils; import javax.servlet.http.Cookie; import java.io.*; @@ -592,10 +593,11 @@ public void testisValidInput() { assertTrue(instance.isValidInput("test", "jeffWILLIAMS123", "HTTPParameterValue", 100, false)); assertTrue(instance.isValidInput("test", "jeff .-/+=@_ WILLIAMS", "HTTPParameterValue", 100, false)); // Removed per Issue 116 - The '*' character is valid as a parameter character -// assertFalse(instance.isValidInput("test", "jeff*WILLIAMS", "HTTPParameterValue", 100, false)); +// assertFalse(instance.isValidInput("test", "jeff*WILLIAMS", "HTTPParameterValue", 100, false)) + System.err.println(instance.isValidInput("test", "jeff\\WILLIAMS", "HTTPParameterValue", 100, false));; assertFalse(instance.isValidInput("test", "jeff^WILLIAMS", "HTTPParameterValue", 100, false)); assertFalse(instance.isValidInput("test", "jeff\\WILLIAMS", "HTTPParameterValue", 100, false)); - + assertTrue(instance.isValidInput("test", null, "Email", 100, true)); assertFalse(instance.isValidInput("test", null, "Email", 100, false)); @@ -1005,11 +1007,11 @@ public void testGetParameterMap() { //an example of a parameter from displaytag, should pass request.addParameter("d-49653-p", "pass"); request.addParameter("XSS"); - request.addHeader("p2", generateStringOfLength(200)); // Upper limit increased from 150 -> 200, GitHub issue #351 - request.addHeader("f2", generateStringOfLength(201)); + request.addHeader("p2", TestUtils.generateStringOfLength(200)); // Upper limit increased from 150 -> 200, GitHub issue #351 + request.addHeader("f2", TestUtils.generateStringOfLength(201)); assertEquals(safeRequest.getHeader("p1"), request.getHeader("p1")); assertEquals(safeRequest.getHeader("p2"), request.getHeader("p2")); assertFalse(safeRequest.getHeader("f1").equals(request.getHeader("f1"))); @@ -1091,8 +1093,8 @@ public void testGetHeader() { public void testHeaderLengthChecks(){ Validator v = ESAPI.validator(); SecurityConfiguration sc = ESAPI.securityConfiguration(); - assertFalse(v.isValidInput("addHeader", generateStringOfLength(257), "HTTPHeaderName", sc.getIntProp("HttpUtilities.MaxHeaderKeySize"), false)); - assertFalse(v.isValidInput("addHeader", generateStringOfLength(4097), "HTTPHeaderValue", sc.getIntProp("HttpUtilities.MaxHeaderValueSize"), false)); + assertFalse(v.isValidInput("addHeader", TestUtils.generateStringOfLength(257), "HTTPHeaderName", sc.getIntProp("HttpUtilities.MaxHeaderNameSize"), false)); + assertFalse(v.isValidInput("addHeader", TestUtils.generateStringOfLength(4097), "HTTPHeaderValue", sc.getIntProp("HttpUtilities.MaxHeaderValueSize"), false)); } public void testGetHeaderNames() { @@ -1102,11 +1104,12 @@ public void testGetHeaderNames() { request.addHeader("d-49653-p", "pass"); request.addHeader("= 0 : "length must be >= 0"; - StringBuilder longString = new StringBuilder(length); - for (int i = 0; i < length; i++) { - longString.append("a"); - } - return longString.toString(); - } - public void testGetContextPath() { // Root Context Path ("") assertTrue(ESAPI.validator().isValidInput("HTTPContextPath", "", "HTTPContextPath", 512, true)); diff --git a/src/test/java/org/owasp/esapi/util/TestUtils.java b/src/test/java/org/owasp/esapi/util/TestUtils.java new file mode 100644 index 000000000..f7f117fc5 --- /dev/null +++ b/src/test/java/org/owasp/esapi/util/TestUtils.java @@ -0,0 +1,14 @@ +package org.owasp.esapi.util; + +public class TestUtils { + + public static String generateStringOfLength(int length) { + assert length >= 0 : "length must be >= 0"; + StringBuilder longString = new StringBuilder(length); + for (int i = 0; i < length; i++) { + longString.append("a"); + } + return longString.toString(); + } + +} diff --git a/src/test/resources/esapi/ESAPI.properties b/src/test/resources/esapi/ESAPI.properties index becadfee7..1ff0c4e65 100644 --- a/src/test/resources/esapi/ESAPI.properties +++ b/src/test/resources/esapi/ESAPI.properties @@ -332,7 +332,7 @@ HttpUtilities.ForceSecureSession=false HttpUtilities.ForceHttpOnlyCookies=true HttpUtilities.ForceSecureCookies=true # Maximum size of HTTP header key -HttpUtilities.MaxHeaderKeySize=256 +HttpUtilities.MaxHeaderNameSize=256 # Maximum size of HTTP header value HttpUtilities.MaxHeaderValueSize=4096 # File upload configuration @@ -345,7 +345,8 @@ HttpUtilities.ResponseContentType=text/html; charset=UTF-8 # This is the name of the cookie used to represent the HTTP session # Typically this will be the default "JSESSIONID" HttpUtilities.HttpSessionIdName=JSESSIONID - +#Sets whether or not will will overwrite http status codes to 200. +HttpUtilities.OverwriteStatusCodes=true #=========================================================================== @@ -447,8 +448,8 @@ Validator.HTTPScheme=^(http|https)$ Validator.HTTPServerName=^[a-zA-Z0-9_.\\-]*$ Validator.HTTPCookieName=^[a-zA-Z0-9\\-_]{1,32}$ Validator.HTTPCookieValue=^[a-zA-Z0-9\\-\\/+=_ ]*$ -# Note that max header name capped at 150 in SecurityRequestWrapper! -Validator.HTTPHeaderName=^[a-zA-Z0-9\\-_]{1,50}$ +# Note that headerName and Value length is also configured in the HTTPUtilities section +Validator.HTTPHeaderName=^[a-zA-Z0-9\\-_]{1,256}$ Validator.HTTPHeaderValue=^[a-zA-Z0-9()\\-=\\*\\.\\?;,+\\/:&_ ]*$ Validator.HTTPServletPath=^[a-zA-Z0-9.\\-\\/_]*$ Validator.HTTPPath=^[a-zA-Z0-9.\\-_]*$ From 5c0e1de2ba09a2f606d26740a70a35fbe42a5090 Mon Sep 17 00:00:00 2001 From: Matt Seil Date: Mon, 24 Jul 2017 17:22:24 -0700 Subject: [PATCH 0378/1069] Issue #400 -- Added more unit tests for SecurityWrapperResponse. --- .../filters/SecurityWrapperResponse.java | 19 ++++++++-- .../filters/SecurityWrapperResponseTest.java | 37 +++++++++++++++++++ src/test/resources/esapi/ESAPI.properties | 3 +- 3 files changed, 54 insertions(+), 5 deletions(-) diff --git a/src/main/java/org/owasp/esapi/filters/SecurityWrapperResponse.java b/src/main/java/org/owasp/esapi/filters/SecurityWrapperResponse.java index 82e629557..343a44396 100644 --- a/src/main/java/org/owasp/esapi/filters/SecurityWrapperResponse.java +++ b/src/main/java/org/owasp/esapi/filters/SecurityWrapperResponse.java @@ -418,7 +418,8 @@ public void setBufferSize(int size) { * @param charset */ public void setCharacterEncoding(String charset) { - getHttpServletResponse().setCharacterEncoding(ESAPI.securityConfiguration().getCharacterEncoding()); + SecurityConfiguration sc = ESAPI.securityConfiguration(); + getHttpServletResponse().setCharacterEncoding(sc.getStringProp("HttpUtilities.CharacterEncoding")); } /** @@ -506,7 +507,13 @@ public void setLocale(Locale loc) { * @param sc */ public void setStatus(int sc) { - getHttpServletResponse().setStatus(HttpServletResponse.SC_OK); + SecurityConfiguration config = ESAPI.securityConfiguration(); + if(config.getBooleanProp("HttpUtilities.OverwriteStatusCodes")){ + getHttpServletResponse().setStatus(HttpServletResponse.SC_OK); + }else{ + getHttpServletResponse().setStatus(sc); + } + } /** @@ -520,8 +527,12 @@ public void setStatus(int sc) { @Deprecated public void setStatus(int sc, String sm) { try { - // setStatus is deprecated so use sendError instead - sendError(HttpServletResponse.SC_OK, sm); + SecurityConfiguration config = ESAPI.securityConfiguration(); + if(config.getBooleanProp("HttpUtilities.OverwriteStatusCodes")){ + sendError(HttpServletResponse.SC_OK, sm); + }else{ + sendError(sc, sm); + } } catch (IOException e) { logger.warning(Logger.SECURITY_FAILURE, "Attempt to set response status failed", e); } diff --git a/src/test/java/org/owasp/esapi/filters/SecurityWrapperResponseTest.java b/src/test/java/org/owasp/esapi/filters/SecurityWrapperResponseTest.java index c61a20a20..8811f99f5 100644 --- a/src/test/java/org/owasp/esapi/filters/SecurityWrapperResponseTest.java +++ b/src/test/java/org/owasp/esapi/filters/SecurityWrapperResponseTest.java @@ -121,6 +121,7 @@ public void testAddValidCookieWithDomain(){ SecurityWrapperResponse spyResp = spy(resp); Cookie cookie = new Cookie("Foo", TestUtils.generateStringOfLength(10)); cookie.setDomain("evil.com"); + cookie.setMaxAge(-1); Mockito.doCallRealMethod().when(spyResp).addCookie(cookie); spyResp.addCookie(cookie); verify(servResp, times(1)).addHeader("Set-Cookie", "Foo=aaaaaaaaaa; Domain=evil.com; Secure; HttpOnly"); @@ -152,4 +153,40 @@ public void testAddInValidCookie(){ spyResp.addCookie(cookie); verify(servResp, times(0)).addHeader("Set-Cookie", "Foo=" + TestUtils.generateStringOfLength(5000) + "; Secure; HttpOnly"); } + + @Test + public void testSendError() throws Exception{ + HttpServletResponse servResp = new MockHttpServletResponse(); + servResp = spy(servResp); + SecurityWrapperResponse resp = new SecurityWrapperResponse(servResp); + SecurityWrapperResponse spyResp = spy(resp); + Mockito.doCallRealMethod().when(spyResp).sendError(200); + spyResp.sendError(200); + + verify(servResp, times(1)).sendError(200, "HTTP error code: 200");; + } + + @Test + public void testSendStatus() throws Exception{ + HttpServletResponse servResp = new MockHttpServletResponse(); + servResp = spy(servResp); + SecurityWrapperResponse resp = new SecurityWrapperResponse(servResp); + SecurityWrapperResponse spyResp = spy(resp); + Mockito.doCallRealMethod().when(spyResp).setStatus(200);; + spyResp.setStatus(200); + + verify(servResp, times(1)).setStatus(200);; + } + + @Test + public void testSendStatusWithString() throws Exception{ + HttpServletResponse servResp = new MockHttpServletResponse(); + servResp = spy(servResp); + SecurityWrapperResponse resp = new SecurityWrapperResponse(servResp); + SecurityWrapperResponse spyResp = spy(resp); + Mockito.doCallRealMethod().when(spyResp).setStatus(200, "foo");; + spyResp.setStatus(200, "foo"); + + verify(servResp, times(1)).sendError(200, "foo");; + } } diff --git a/src/test/resources/esapi/ESAPI.properties b/src/test/resources/esapi/ESAPI.properties index 1ff0c4e65..6e4bc6246 100644 --- a/src/test/resources/esapi/ESAPI.properties +++ b/src/test/resources/esapi/ESAPI.properties @@ -347,7 +347,8 @@ HttpUtilities.ResponseContentType=text/html; charset=UTF-8 HttpUtilities.HttpSessionIdName=JSESSIONID #Sets whether or not will will overwrite http status codes to 200. HttpUtilities.OverwriteStatusCodes=true - +#Sets the application's base character encoding. This is forked from the Java Encryptor property. +HttpUtilities.CharacterEncoding=UTF-8 #=========================================================================== # ESAPI Executor From f73b9dbb7b1460b669f68231d8cefee9ac39881a Mon Sep 17 00:00:00 2001 From: Matt Seil Date: Mon, 24 Jul 2017 17:41:32 -0700 Subject: [PATCH 0379/1069] Issue #400 -- Got unit test coverage up to 65.5% --- .../filters/SecurityWrapperResponse.java | 1 - .../filters/SecurityWrapperResponseTest.java | 38 ++++++++++++++++++- 2 files changed, 37 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/owasp/esapi/filters/SecurityWrapperResponse.java b/src/main/java/org/owasp/esapi/filters/SecurityWrapperResponse.java index 343a44396..c1de913c4 100644 --- a/src/main/java/org/owasp/esapi/filters/SecurityWrapperResponse.java +++ b/src/main/java/org/owasp/esapi/filters/SecurityWrapperResponse.java @@ -384,7 +384,6 @@ public void sendError(int sc, String msg) throws IOException { }else{ getHttpServletResponse().sendError(sc, ESAPI.encoder().encodeForHTML(msg)); } - } /** diff --git a/src/test/java/org/owasp/esapi/filters/SecurityWrapperResponseTest.java b/src/test/java/org/owasp/esapi/filters/SecurityWrapperResponseTest.java index 8811f99f5..478f0b050 100644 --- a/src/test/java/org/owasp/esapi/filters/SecurityWrapperResponseTest.java +++ b/src/test/java/org/owasp/esapi/filters/SecurityWrapperResponseTest.java @@ -39,6 +39,40 @@ public void testAddDateHeader(){ verify(servResp, times(1)).addDateHeader("Foo", currentTime); } + @Test + public void testSetDateHeader(){ + HttpServletResponse servResp = mock(HttpServletResponse.class); + SecurityWrapperResponse resp = new SecurityWrapperResponse(servResp); + long currentTime = System.currentTimeMillis(); + resp.setDateHeader("Foo", currentTime); + verify(servResp, times(1)).setDateHeader("Foo", currentTime); + } + + @Test + public void testSetInvalidDateHeader(){ + HttpServletResponse servResp = mock(HttpServletResponse.class); + SecurityWrapperResponse resp = new SecurityWrapperResponse(servResp); + long currentTime = System.currentTimeMillis(); + resp.setDateHeader("alert"); + verify(servResp, times(0)).setHeader("foo", ""); + } + @Test public void testInvalidDateHeader(){ HttpServletResponse servResp = mock(HttpServletResponse.class); @@ -101,8 +135,10 @@ public void testAddValidCookie(){ SecurityWrapperResponse resp = new SecurityWrapperResponse(servResp); SecurityWrapperResponse spyResp = spy(resp); Cookie cookie = new Cookie("Foo", TestUtils.generateStringOfLength(10)); + cookie.setMaxAge(5000); Mockito.doCallRealMethod().when(spyResp).addCookie(cookie); spyResp.addCookie(cookie); + /* * We're indirectly testing our class. Since it ultimately * delegates to HttpServletResponse.addHeader, we're actually @@ -110,7 +146,7 @@ public void testAddValidCookie(){ * expected properties. This implicitly tests the * createCookieHeader method as well. */ - verify(servResp, times(1)).addHeader("Set-Cookie", "Foo=aaaaaaaaaa; Secure; HttpOnly"); + verify(servResp, times(1)).addHeader("Set-Cookie", "Foo=aaaaaaaaaa; Max-Age=5000; Secure; HttpOnly"); } @Test From 271284e3c407e2be47d43062680e1fba6b058128 Mon Sep 17 00:00:00 2001 From: Matt Seil Date: Mon, 24 Jul 2017 18:14:24 -0700 Subject: [PATCH 0380/1069] Issue #317 -- Fixed resource leak. Special thanks to eamonn. --- .../owasp/esapi/waf/rules/BeanShellRule.java | 71 ++++++++++--------- 1 file changed, 37 insertions(+), 34 deletions(-) diff --git a/src/main/java/org/owasp/esapi/waf/rules/BeanShellRule.java b/src/main/java/org/owasp/esapi/waf/rules/BeanShellRule.java index ea0caa786..f90da574e 100644 --- a/src/main/java/org/owasp/esapi/waf/rules/BeanShellRule.java +++ b/src/main/java/org/owasp/esapi/waf/rules/BeanShellRule.java @@ -34,6 +34,7 @@ /** * This is the Rule subclass executed for <bean-shell-script> rules. + * * @author Arshan Dabirsiaghi * */ @@ -42,43 +43,41 @@ public class BeanShellRule extends Rule { private Interpreter i; private String script; private Pattern path; - - public BeanShellRule(String fileLocation, String id, Pattern path) throws IOException, EvalError { + + public BeanShellRule(String fileLocation, String id, Pattern path) throws IOException, EvalError { i = new Interpreter(); i.set("logger", logger); - this.script = getFileContents( ESAPI.securityConfiguration().getResourceFile(fileLocation)); + this.script = getFileContents(ESAPI.securityConfiguration().getResourceFile(fileLocation)); this.id = id; this.path = path; } - - public Action check(HttpServletRequest request, - InterceptingHTTPServletResponse response, + + public Action check(HttpServletRequest request, InterceptingHTTPServletResponse response, HttpServletResponse httpResponse) { /* * Early fail: if the URL doesn't match one we're interested in. */ - - if ( path != null && ! path.matcher(request.getRequestURI()).matches() ) { + + if (path != null && !path.matcher(request.getRequestURI()).matches()) { return new DoNothingAction(); } - + /* - * Run the beanshell that we've already parsed - * and pre-compiled. Populate the "request" - * and "response" objects so the script has + * Run the beanshell that we've already parsed and pre-compiled. + * Populate the "request" and "response" objects so the script has * access to the same variables we do here. */ - + try { - + Action a = null; - + i.set("action", a); i.set("request", request); - - if ( response != null ) { - i.set("response", response); + + if (response != null) { + i.set("response", response); } else { i.set("response", httpResponse); } @@ -86,31 +85,35 @@ public Action check(HttpServletRequest request, i.set("session", request.getSession()); i.eval(script); - a = (Action)i.get("action"); - - if ( a != null ) { + a = (Action) i.get("action"); + + if (a != null) { return a; } - + } catch (EvalError e) { - log(request,"Error running custom beanshell rule (" + id + ") - " + e.getMessage()); + log(request, "Error running custom beanshell rule (" + id + ") - " + e.getMessage()); } - + return new DoNothingAction(); } - + private String getFileContents(File f) throws IOException { - - FileReader fr = new FileReader(f); StringBuffer sb = new StringBuffer(); - String line; - BufferedReader br = new BufferedReader(fr); - - while( (line=br.readLine()) != null ) { - sb.append(line + System.getProperty("line.separator")); + BufferedReader br = null; + + try { + br = new BufferedReader(new FileReader(f)); + String line; + while ((line = br.readLine()) != null) { + sb.append(line + System.getProperty("line.separator")); + } + + } finally { + if (br != null) { + br.close(); + } } - return sb.toString(); } - } From 9177ca3034b1af7fb7cd95a6433338b0b3a0ec03 Mon Sep 17 00:00:00 2001 From: Matt Seil Date: Tue, 25 Jul 2017 20:51:55 -0700 Subject: [PATCH 0381/1069] Issue #327 -- got rid of misleading HTML entity in URL regex. --- configuration/esapi/validation.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configuration/esapi/validation.properties b/configuration/esapi/validation.properties index 433fa0b6b..dd24e46c3 100644 --- a/configuration/esapi/validation.properties +++ b/configuration/esapi/validation.properties @@ -23,7 +23,7 @@ Validator.SafeString=^[.\\p{Alnum}\\p{Space}]{0,1024}$ Validator.Email=^[A-Za-z0-9._%'-]+@[A-Za-z0-9.-]+\\.[a-zA-Z]{2,4}$ Validator.IPAddress=^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$ -Validator.URL=^(ht|f)tp(s?)\\:\\/\\/[0-9a-zA-Z]([-.\\w]*[0-9a-zA-Z])*(:(0-9)*)*(\\/?)([a-zA-Z0-9\\-\\.\\?\\,\\:\\'\\/\\\\\\+=&%\\$#_]*)?$ +Validator.URL=^(ht|f)tp(s?)\\:\\/\\/[0-9a-zA-Z]([-.\\w]*[0-9a-zA-Z])*(:(0-9)*)*(\\/?)([a-zA-Z0-9\\-\\.\\?\\,\\:\\'\\/\\\\\\+=&;%\\$#_]*)?$ Validator.CreditCard=^(\\d{4}[- ]?){3}\\d{4}$ Validator.SSN=^(?!000)([0-6]\\d{2}|7([0-6]\\d|7[012]))([ -]?)(?!00)\\d\\d\\3(?!0000)\\d{4}$ From 158c78924d624e26ea8a0110a50371420ad4e2ab Mon Sep 17 00:00:00 2001 From: adetlefsen-rms Date: Thu, 27 Jul 2017 12:32:27 -0700 Subject: [PATCH 0382/1069] Update AntiSamy and XOM versions --- pom.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pom.xml b/pom.xml index 7bc646277..40386fbf6 100644 --- a/pom.xml +++ b/pom.xml @@ -174,9 +174,9 @@ jar - xom + com.io7m.xom xom - 1.2.5 + 1.2.10 org.beanshell @@ -186,7 +186,7 @@ org.owasp.antisamy antisamy - 1.5.5 + 1.5.6 + 8A2A524F From 5e4e201e8552f94785e02ecc0c33e7c8d24efee7 Mon Sep 17 00:00:00 2001 From: kwwall Date: Mon, 31 Jul 2017 22:21:05 -0400 Subject: [PATCH 0394/1069] Remove trailing tab on line 881. --- src/main/java/org/owasp/esapi/crypto/CipherText.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/owasp/esapi/crypto/CipherText.java b/src/main/java/org/owasp/esapi/crypto/CipherText.java index b327d1a2f..15a0b4675 100644 --- a/src/main/java/org/owasp/esapi/crypto/CipherText.java +++ b/src/main/java/org/owasp/esapi/crypto/CipherText.java @@ -878,7 +878,7 @@ private void received(EnumSet ctSet) { */ public int getKDFInfo() { final int unusedBit28 = 0x8000000; // 1000000000000000000000000000 - + // kdf version is bits 1-27, bit 28 (reserved) should be 0, and // bits 29-32 are the MAC algorithm indicating which PRF to use for the KDF. int kdfVers = this.getKDFVersion(); From 660054db9a8a366dee499928108fc9239894e84a Mon Sep 17 00:00:00 2001 From: Matt Seil Date: Tue, 1 Aug 2017 21:27:57 -0700 Subject: [PATCH 0395/1069] Issue #300 -- Solved the root problem, now have many unit tests to clean up. --- .../java/org/owasp/esapi/codecs/Codec.java | 43 +++++++++++++++++-- .../owasp/esapi/codecs/HTMLEntityCodec.java | 36 ++++++++++++++++ .../owasp/esapi/reference/EncoderTest.java | 23 +++++++++- 3 files changed, 98 insertions(+), 4 deletions(-) diff --git a/src/main/java/org/owasp/esapi/codecs/Codec.java b/src/main/java/org/owasp/esapi/codecs/Codec.java index ec02a806a..b9f545e52 100644 --- a/src/main/java/org/owasp/esapi/codecs/Codec.java +++ b/src/main/java/org/owasp/esapi/codecs/Codec.java @@ -64,9 +64,15 @@ public Codec() { */ public String encode(char[] immune, String input) { StringBuilder sb = new StringBuilder(); - for (int i = 0; i < input.length(); i++) { - char c = input.charAt(i); - sb.append(encodeCharacter(immune, c)); + for(int offset = 0; offset < input.length(); ){ + final int point = input.codePointAt(offset); + if(Character.isBmpCodePoint(point)){ + //We can then safely cast this to char and maintain legacy behavior. + sb.append(encodeCharacter(immune, (char) point)); + }else{ + sb.append(encodeCharacter(immune, point)); + } + offset += Character.charCount(point); } return sb.toString(); } @@ -83,6 +89,19 @@ public String encode(char[] immune, String input) { public String encodeCharacter( char[] immune, Character c ) { return ""+c; } + + /** + * Default codepoint implementation that should be overridden in specific codecs. + * + * @param immune + * @param codePoint + * the integer to encode + * @return + * the encoded Character + */ + public String encodeCharacter( char[] immune, int codePoint ) { + return new StringBuilder().appendCodePoint(codePoint).toString(); + } /** * Decode a String that was encoded using the encode method in this Class @@ -131,6 +150,19 @@ public static String getHexForNonAlphanumeric(char c) return hex[c]; return toHex(c); } + + /** + * Lookup the hex value of any character that is not alphanumeric. + * @param c The character to lookup. + * @return, return null if alphanumeric or the character code + * in hex. + */ + public static String getHexForNonAlphanumeric(int c) + { + if(c<0xFF) + return hex[c]; + return toHex(c); + } public static String toOctal(char c) { @@ -141,6 +173,11 @@ public static String toHex(char c) { return Integer.toHexString(c); } + + public static String toHex(int c) + { + return Integer.toHexString(c); + } /** * Utility to search a char[] for a specific char. diff --git a/src/main/java/org/owasp/esapi/codecs/HTMLEntityCodec.java b/src/main/java/org/owasp/esapi/codecs/HTMLEntityCodec.java index cd72dc79c..c9ad38ca1 100644 --- a/src/main/java/org/owasp/esapi/codecs/HTMLEntityCodec.java +++ b/src/main/java/org/owasp/esapi/codecs/HTMLEntityCodec.java @@ -78,6 +78,42 @@ public String encodeCharacter( char[] immune, Character c ) { return "&#x" + hex + ";"; } + /** + * {@inheritDoc} + * + * Encodes a Character for safe use in an HTML entity field. + * @param immune + */ + public String encodeCharacter( char[] immune, int codePoint ) { + + // check for immune characters +// if ( containsCharacter(codePoint, immune ) ) { +// return ""+codePoint; +// } + +// // check for alphanumeric characters + String hex = Codec.getHexForNonAlphanumeric(codePoint); +// if ( hex == null ) { +// return ""+c; +// } +// +// // check for illegal characters +// if ( ( c <= 0x1f && c != '\t' && c != '\n' && c != '\r' ) || ( c >= 0x7f && c <= 0x9f ) ) +// { +// hex = REPLACEMENT_HEX; // Let's entity encode this instead of returning it +// c = REPLACEMENT_CHAR; +// } +// +// // check if there's a defined entity +// String entityName = (String) characterToEntityMap.get(c); +// if (entityName != null) { +// return "&" + entityName + ";"; +// } + + // return the hex entity as suggested in the spec + return "&#x" + hex + ";"; + } + /** * {@inheritDoc} * diff --git a/src/test/java/org/owasp/esapi/reference/EncoderTest.java b/src/test/java/org/owasp/esapi/reference/EncoderTest.java index 45702ced4..896a93661 100644 --- a/src/test/java/org/owasp/esapi/reference/EncoderTest.java +++ b/src/test/java/org/owasp/esapi/reference/EncoderTest.java @@ -32,6 +32,7 @@ import org.owasp.esapi.EncoderConstants; import org.owasp.esapi.codecs.Base64; import org.owasp.esapi.codecs.Codec; +import org.owasp.esapi.codecs.HTMLEntityCodec; import org.owasp.esapi.codecs.MySQLCodec; import org.owasp.esapi.codecs.OracleCodec; import org.owasp.esapi.codecs.PushbackString; @@ -902,7 +903,27 @@ public void testGetCanonicalizedUriWithMailto() throws Exception { URI uri = new URI(input); System.out.println(uri.toString()); assertEquals(expectedUri, e.getCanonicalizedURI(uri)); - + } + + public void testHtmlEncodeStrSurrogatePair() + { + Encoder enc = ESAPI.encoder(); + String inStr = new String (new int[]{0x2f804}, 0, 1); + assertEquals(false, Character.isBmpCodePoint(inStr.codePointAt(0))); + assertEquals(true, Character.isBmpCodePoint(new String(new int[] {0x0a}, 0, 1).codePointAt(0))); + String expected = "你"; + String result; + + result = enc.encodeForHTML(inStr); + assertEquals(expected, result); + } + + public void testHtmlDecodeHexEntititesSurrogatePair() + { + HTMLEntityCodec htmlCodec = new HTMLEntityCodec(); + String expected = new String (new int[]{0x2f804}, 0, 1); + assertEquals( expected, htmlCodec.decode("你") ); + assertEquals( expected, htmlCodec.decode("你") ); } } From 45b3d1680bf90133f1ac6fc2addafe9c5855de98 Mon Sep 17 00:00:00 2001 From: Matt Seil Date: Tue, 1 Aug 2017 23:00:10 -0700 Subject: [PATCH 0396/1069] Issue #300 -- Fixed most unit tests. chars are NOT autoboxed to Characters, make sure we update documentation! --- .../java/org/owasp/esapi/PreparedString.java | 1 + .../org/owasp/esapi/codecs/AbstractCodec.java | 173 ++++++++++++++++++ .../java/org/owasp/esapi/codecs/CSSCodec.java | 4 +- .../java/org/owasp/esapi/codecs/Codec.java | 104 ++--------- .../java/org/owasp/esapi/codecs/DB2Codec.java | 2 +- .../owasp/esapi/codecs/HTMLEntityCodec.java | 6 +- .../owasp/esapi/codecs/JavaScriptCodec.java | 4 +- .../org/owasp/esapi/codecs/MySQLCodec.java | 4 +- .../org/owasp/esapi/codecs/OracleCodec.java | 2 +- .../org/owasp/esapi/codecs/PercentCodec.java | 2 +- .../org/owasp/esapi/codecs/UnixCodec.java | 4 +- .../org/owasp/esapi/codecs/VBScriptCodec.java | 4 +- .../org/owasp/esapi/codecs/WindowsCodec.java | 4 +- .../owasp/esapi/codecs/XMLEntityCodec.java | 2 +- .../org/owasp/esapi/PreparedStringTest.java | 1 + ...{CodecTest.java => AbstractCodecTest.java} | 38 ++-- .../owasp/esapi/codecs/CodecImmunityTest.java | 4 +- .../owasp/esapi/reference/RandomizerTest.java | 2 +- 18 files changed, 228 insertions(+), 133 deletions(-) create mode 100644 src/main/java/org/owasp/esapi/codecs/AbstractCodec.java rename src/test/java/org/owasp/esapi/codecs/{CodecTest.java => AbstractCodecTest.java} (96%) diff --git a/src/main/java/org/owasp/esapi/PreparedString.java b/src/main/java/org/owasp/esapi/PreparedString.java index 3dccff055..176421347 100644 --- a/src/main/java/org/owasp/esapi/PreparedString.java +++ b/src/main/java/org/owasp/esapi/PreparedString.java @@ -16,6 +16,7 @@ package org.owasp.esapi; import java.util.ArrayList; + import org.owasp.esapi.codecs.Codec; import org.owasp.esapi.codecs.HTMLEntityCodec; diff --git a/src/main/java/org/owasp/esapi/codecs/AbstractCodec.java b/src/main/java/org/owasp/esapi/codecs/AbstractCodec.java new file mode 100644 index 000000000..11d33f8a8 --- /dev/null +++ b/src/main/java/org/owasp/esapi/codecs/AbstractCodec.java @@ -0,0 +1,173 @@ +/** + * OWASP Enterprise Security API (ESAPI) + * + * This file is part of the Open Web Application Security Project (OWASP) + * Enterprise Security API (ESAPI) project. For details, please see + * http://www.owasp.org/index.php/ESAPI. + * + * Copyright (c) 2007 - The OWASP Foundation + * + * The ESAPI is published by OWASP under the BSD license. You should read and accept the + * LICENSE before you use, modify, and/or redistribute this software. + * + * @author Jeff Williams Aspect Security + * @created 2007 + */ +package org.owasp.esapi.codecs; + + +/** + * The Codec interface defines a set of methods for encoding and decoding application level encoding schemes, + * such as HTML entity encoding and percent encoding (aka URL encoding). Codecs are used in output encoding + * and canonicalization. The design of these codecs allows for character-by-character decoding, which is + * necessary to detect double-encoding and the use of multiple encoding schemes, both of which are techniques + * used by attackers to bypass validation and bury encoded attacks in data. + * + * @author Jeff Williams (jeff.williams .at. aspectsecurity.com) Aspect Security + * @since June 1, 2007 + * @see org.owasp.esapi.Encoder + */ +public abstract class AbstractCodec implements Codec { + + /** + * Initialize an array to mark which characters are to be encoded. Store the hex + * string for that character to save time later. If the character shouldn't be + * encoded, then store null. + */ + private final String[] hex = new String[256]; + + /** + * Default constructor + */ + public AbstractCodec() { + for ( char c = 0; c < 0xFF; c++ ) { + if ( c >= 0x30 && c <= 0x39 || c >= 0x41 && c <= 0x5A || c >= 0x61 && c <= 0x7A ) { + hex[c] = null; + } else { + hex[c] = toHex(c).intern(); + } + } + } + + /* (non-Javadoc) + * @see org.owasp.esapi.codecs.Codec#encode(char[], java.lang.String) + */ + @Override + public String encode(char[] immune, String input) { + StringBuilder sb = new StringBuilder(); + for(int offset = 0; offset < input.length(); ){ + final int point = input.codePointAt(offset); + if(Character.isBmpCodePoint(point)){ + //We can then safely cast this to char and maintain legacy behavior. + sb.append(encodeCharacter(immune, new Character((char) point))); + }else{ + sb.append(encodeCharacter(immune, point)); + } + offset += Character.charCount(point); + } + return sb.toString(); + } + + /** + * WARNING!!!! Passing a standard char to this method will resolve to the + * @{code public String encodeCharacter( char[] immune, int codePoint )} method + * instead of this one!!! YOU HAVE BEEN WARNED!!!! + * + * @{Inherit} + */ + @Override + public String encodeCharacter( char[] immune, Character c ) { + return ""+c; + } + + /* (non-Javadoc) + * @see org.owasp.esapi.codecs.Codec#encodeCharacter(char[], int) + */ + @Override + public String encodeCharacter( char[] immune, int codePoint ) { + return new StringBuilder().appendCodePoint(codePoint).toString(); + } + + /* (non-Javadoc) + * @see org.owasp.esapi.codecs.Codec#decode(java.lang.String) + */ + @Override + public String decode(String input) { + StringBuilder sb = new StringBuilder(); + PushbackString pbs = new PushbackString(input); + while (pbs.hasNext()) { + Character c = decodeCharacter(pbs); + if (c != null) { + sb.append(c); + } else { + sb.append(pbs.next()); + } + } + return sb.toString(); + } + + /* (non-Javadoc) + * @see org.owasp.esapi.codecs.Codec#decodeCharacter(org.owasp.esapi.codecs.PushbackString) + */ + @Override + public Character decodeCharacter( PushbackString input ) { + return input.next(); + } + + /** + * Lookup the hex value of any character that is not alphanumeric. + * @param c The character to lookup. + * @return, return null if alphanumeric or the character code + * in hex. + */ + public String getHexForNonAlphanumeric(char c) + { + if(c<0xFF) + return hex[c]; + return toHex(c); + } + + /** + * Lookup the hex value of any character that is not alphanumeric. + * @param c The character to lookup. + * @return, return null if alphanumeric or the character code + * in hex. + */ + public String getHexForNonAlphanumeric(int c) + { + if(c<0xFF) + return hex[c]; + return toHex(c); + } + + public String toOctal(char c) + { + return Integer.toOctalString(c); + } + + public String toHex(char c) + { + return Integer.toHexString(c); + } + + public String toHex(int c) + { + return Integer.toHexString(c); + } + + /** + * Utility to search a char[] for a specific char. + * + * @param c + * @param array + * @return + */ + public boolean containsCharacter( char c, char[] array ) { + for (char ch : array) { + if (c == ch) return true; + } + return false; + } + +} diff --git a/src/main/java/org/owasp/esapi/codecs/CSSCodec.java b/src/main/java/org/owasp/esapi/codecs/CSSCodec.java index cee77ccaf..a44cebeed 100644 --- a/src/main/java/org/owasp/esapi/codecs/CSSCodec.java +++ b/src/main/java/org/owasp/esapi/codecs/CSSCodec.java @@ -23,7 +23,7 @@ * @since June 1, 2007 * @see org.owasp.esapi.Encoder */ -public class CSSCodec extends Codec +public class CSSCodec extends AbstractCodec { private static final Character REPLACEMENT = '\ufffd'; @@ -42,7 +42,7 @@ public String encodeCharacter(char[] immune, Character c) { } // check for alphanumeric characters - String hex = Codec.getHexForNonAlphanumeric(c); + String hex = super.getHexForNonAlphanumeric(c); if ( hex == null ) { return ""+c; } diff --git a/src/main/java/org/owasp/esapi/codecs/Codec.java b/src/main/java/org/owasp/esapi/codecs/Codec.java index b9f545e52..3f7cd2554 100644 --- a/src/main/java/org/owasp/esapi/codecs/Codec.java +++ b/src/main/java/org/owasp/esapi/codecs/Codec.java @@ -28,32 +28,7 @@ * @since June 1, 2007 * @see org.owasp.esapi.Encoder */ -public abstract class Codec { - - /** - * Initialize an array to mark which characters are to be encoded. Store the hex - * string for that character to save time later. If the character shouldn't be - * encoded, then store null. - */ - private static final String[] hex = new String[256]; - - static { - for ( char c = 0; c < 0xFF; c++ ) { - if ( c >= 0x30 && c <= 0x39 || c >= 0x41 && c <= 0x5A || c >= 0x61 && c <= 0x7A ) { - hex[c] = null; - } else { - hex[c] = toHex(c).intern(); - } - } - } - - - /** - * Default constructor - */ - public Codec() { - } - +public interface Codec { /** * Encode a String so that it can be safely used in a specific context. * @@ -62,20 +37,7 @@ public Codec() { * the String to encode * @return the encoded String */ - public String encode(char[] immune, String input) { - StringBuilder sb = new StringBuilder(); - for(int offset = 0; offset < input.length(); ){ - final int point = input.codePointAt(offset); - if(Character.isBmpCodePoint(point)){ - //We can then safely cast this to char and maintain legacy behavior. - sb.append(encodeCharacter(immune, (char) point)); - }else{ - sb.append(encodeCharacter(immune, point)); - } - offset += Character.charCount(point); - } - return sb.toString(); - } + public String encode(char[] immune, String input); /** * Default implementation that should be overridden in specific codecs. @@ -86,9 +48,7 @@ public String encode(char[] immune, String input) { * @return * the encoded Character */ - public String encodeCharacter( char[] immune, Character c ) { - return ""+c; - } + public String encodeCharacter( char[] immune, Character c ); /** * Default codepoint implementation that should be overridden in specific codecs. @@ -99,9 +59,7 @@ public String encodeCharacter( char[] immune, Character c ) { * @return * the encoded Character */ - public String encodeCharacter( char[] immune, int codePoint ) { - return new StringBuilder().appendCodePoint(codePoint).toString(); - } + public String encodeCharacter( char[] immune, int codePoint ); /** * Decode a String that was encoded using the encode method in this Class @@ -111,19 +69,7 @@ public String encodeCharacter( char[] immune, int codePoint ) { * @return * the decoded String */ - public String decode(String input) { - StringBuilder sb = new StringBuilder(); - PushbackString pbs = new PushbackString(input); - while (pbs.hasNext()) { - Character c = decodeCharacter(pbs); - if (c != null) { - sb.append(c); - } else { - sb.append(pbs.next()); - } - } - return sb.toString(); - } + public String decode(String input); /** * Returns the decoded version of the next character from the input string and advances the @@ -134,9 +80,7 @@ public String decode(String input) { * * @return the decoded Character */ - public Character decodeCharacter( PushbackString input ) { - return input.next(); - } + public Character decodeCharacter( PushbackString input ); /** * Lookup the hex value of any character that is not alphanumeric. @@ -144,12 +88,7 @@ public Character decodeCharacter( PushbackString input ) { * @return, return null if alphanumeric or the character code * in hex. */ - public static String getHexForNonAlphanumeric(char c) - { - if(c<0xFF) - return hex[c]; - return toHex(c); - } + public String getHexForNonAlphanumeric(char c); /** * Lookup the hex value of any character that is not alphanumeric. @@ -157,27 +96,13 @@ public static String getHexForNonAlphanumeric(char c) * @return, return null if alphanumeric or the character code * in hex. */ - public static String getHexForNonAlphanumeric(int c) - { - if(c<0xFF) - return hex[c]; - return toHex(c); - } + public String getHexForNonAlphanumeric(int c); - public static String toOctal(char c) - { - return Integer.toOctalString(c); - } + public String toOctal(char c); - public static String toHex(char c) - { - return Integer.toHexString(c); - } + public String toHex(char c); - public static String toHex(int c) - { - return Integer.toHexString(c); - } + public String toHex(int c); /** * Utility to search a char[] for a specific char. @@ -186,11 +111,6 @@ public static String toHex(int c) * @param array * @return */ - public static boolean containsCharacter( char c, char[] array ) { - for (char ch : array) { - if (c == ch) return true; - } - return false; - } + public boolean containsCharacter( char c, char[] array ); } diff --git a/src/main/java/org/owasp/esapi/codecs/DB2Codec.java b/src/main/java/org/owasp/esapi/codecs/DB2Codec.java index 4ff63ea1a..236bed78e 100644 --- a/src/main/java/org/owasp/esapi/codecs/DB2Codec.java +++ b/src/main/java/org/owasp/esapi/codecs/DB2Codec.java @@ -20,7 +20,7 @@ * @since October 26, 2010 * @see org.owasp.esapi.Encoder */ -public class DB2Codec extends Codec { +public class DB2Codec extends AbstractCodec { public String encodeCharacter(char[] immune, Character c) { diff --git a/src/main/java/org/owasp/esapi/codecs/HTMLEntityCodec.java b/src/main/java/org/owasp/esapi/codecs/HTMLEntityCodec.java index c9ad38ca1..e3892d2f8 100644 --- a/src/main/java/org/owasp/esapi/codecs/HTMLEntityCodec.java +++ b/src/main/java/org/owasp/esapi/codecs/HTMLEntityCodec.java @@ -27,7 +27,7 @@ * @since June 1, 2007 * @see org.owasp.esapi.Encoder */ -public class HTMLEntityCodec extends Codec +public class HTMLEntityCodec extends AbstractCodec { private static final char REPLACEMENT_CHAR = '\ufffd'; private static final String REPLACEMENT_HEX = "fffd"; @@ -56,7 +56,7 @@ public String encodeCharacter( char[] immune, Character c ) { } // check for alphanumeric characters - String hex = Codec.getHexForNonAlphanumeric(c); + String hex = super.getHexForNonAlphanumeric(c); if ( hex == null ) { return ""+c; } @@ -92,7 +92,7 @@ public String encodeCharacter( char[] immune, int codePoint ) { // } // // check for alphanumeric characters - String hex = Codec.getHexForNonAlphanumeric(codePoint); + String hex = super.getHexForNonAlphanumeric(codePoint); // if ( hex == null ) { // return ""+c; // } diff --git a/src/main/java/org/owasp/esapi/codecs/JavaScriptCodec.java b/src/main/java/org/owasp/esapi/codecs/JavaScriptCodec.java index 7980155e1..98c38c7b2 100644 --- a/src/main/java/org/owasp/esapi/codecs/JavaScriptCodec.java +++ b/src/main/java/org/owasp/esapi/codecs/JavaScriptCodec.java @@ -24,7 +24,7 @@ * @since June 1, 2007 * @see org.owasp.esapi.Encoder */ -public class JavaScriptCodec extends Codec { +public class JavaScriptCodec extends AbstractCodec { /** @@ -45,7 +45,7 @@ public String encodeCharacter( char[] immune, Character c ) { } // check for alphanumeric characters - String hex = Codec.getHexForNonAlphanumeric(c); + String hex = super.getHexForNonAlphanumeric(c); if ( hex == null ) { return ""+c; } diff --git a/src/main/java/org/owasp/esapi/codecs/MySQLCodec.java b/src/main/java/org/owasp/esapi/codecs/MySQLCodec.java index 7d2aacbaa..e43f13ca2 100644 --- a/src/main/java/org/owasp/esapi/codecs/MySQLCodec.java +++ b/src/main/java/org/owasp/esapi/codecs/MySQLCodec.java @@ -26,7 +26,7 @@ * @since June 1, 2007 * @see org.owasp.esapi.Encoder */ -public class MySQLCodec extends Codec { +public class MySQLCodec extends AbstractCodec { /** * Specifies the SQL Mode the target MySQL Server is running with. For details about MySQL Server Modes * please see the Manual at {@link http://dev.mysql.com/doc/refman/5.0/en/server-sql-mode.html#sqlmode_ansi} @@ -95,7 +95,7 @@ public String encodeCharacter( char[] immune, Character c ) { } // check for alphanumeric characters - String hex = Codec.getHexForNonAlphanumeric( ch ); + String hex = super.getHexForNonAlphanumeric( ch ); if ( hex == null ) { return ""+ch; } diff --git a/src/main/java/org/owasp/esapi/codecs/OracleCodec.java b/src/main/java/org/owasp/esapi/codecs/OracleCodec.java index 540920d24..06cca0609 100644 --- a/src/main/java/org/owasp/esapi/codecs/OracleCodec.java +++ b/src/main/java/org/owasp/esapi/codecs/OracleCodec.java @@ -30,7 +30,7 @@ * @since June 1, 2007 * @see org.owasp.esapi.Encoder */ -public class OracleCodec extends Codec { +public class OracleCodec extends AbstractCodec { /** diff --git a/src/main/java/org/owasp/esapi/codecs/PercentCodec.java b/src/main/java/org/owasp/esapi/codecs/PercentCodec.java index 42e1d040e..ed7e65dd8 100644 --- a/src/main/java/org/owasp/esapi/codecs/PercentCodec.java +++ b/src/main/java/org/owasp/esapi/codecs/PercentCodec.java @@ -28,7 +28,7 @@ * @since June 1, 2007 * @see org.owasp.esapi.Encoder */ -public class PercentCodec extends Codec +public class PercentCodec extends AbstractCodec { private static final String ALPHA_NUMERIC_STR = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; @SuppressWarnings("unused") diff --git a/src/main/java/org/owasp/esapi/codecs/UnixCodec.java b/src/main/java/org/owasp/esapi/codecs/UnixCodec.java index 489cf073b..3381e82f0 100644 --- a/src/main/java/org/owasp/esapi/codecs/UnixCodec.java +++ b/src/main/java/org/owasp/esapi/codecs/UnixCodec.java @@ -24,7 +24,7 @@ * @since June 1, 2007 * @see org.owasp.esapi.Encoder */ -public class UnixCodec extends Codec { +public class UnixCodec extends AbstractCodec { /** * {@inheritDoc} @@ -42,7 +42,7 @@ public String encodeCharacter( char[] immune, Character c ) { } // check for alphanumeric characters - String hex = Codec.getHexForNonAlphanumeric( ch ); + String hex = super.getHexForNonAlphanumeric( ch ); if ( hex == null ) { return ""+ch; } diff --git a/src/main/java/org/owasp/esapi/codecs/VBScriptCodec.java b/src/main/java/org/owasp/esapi/codecs/VBScriptCodec.java index 31442127a..85ce820e9 100644 --- a/src/main/java/org/owasp/esapi/codecs/VBScriptCodec.java +++ b/src/main/java/org/owasp/esapi/codecs/VBScriptCodec.java @@ -26,7 +26,7 @@ * @since June 1, 2007 * @see org.owasp.esapi.Encoder */ -public class VBScriptCodec extends Codec { +public class VBScriptCodec extends AbstractCodec { /** * Encode a String so that it can be safely used in a specific context. @@ -78,7 +78,7 @@ public String encodeCharacter( char[] immune, Character c ) { } // check for alphanumeric characters - String hex = Codec.getHexForNonAlphanumeric( ch ); + String hex = super.getHexForNonAlphanumeric( ch ); if ( hex == null ) { return ""+ch; } diff --git a/src/main/java/org/owasp/esapi/codecs/WindowsCodec.java b/src/main/java/org/owasp/esapi/codecs/WindowsCodec.java index f7abb3a4b..bcc53f626 100644 --- a/src/main/java/org/owasp/esapi/codecs/WindowsCodec.java +++ b/src/main/java/org/owasp/esapi/codecs/WindowsCodec.java @@ -24,7 +24,7 @@ * @since June 1, 2007 * @see org.owasp.esapi.Encoder */ -public class WindowsCodec extends Codec { +public class WindowsCodec extends AbstractCodec { /** @@ -43,7 +43,7 @@ public String encodeCharacter( char[] immune, Character c ) { } // check for alphanumeric characters - String hex = Codec.getHexForNonAlphanumeric( ch ); + String hex = super.getHexForNonAlphanumeric( ch ); if ( hex == null ) { return ""+ch; } diff --git a/src/main/java/org/owasp/esapi/codecs/XMLEntityCodec.java b/src/main/java/org/owasp/esapi/codecs/XMLEntityCodec.java index e248392ac..25b4ffbc4 100644 --- a/src/main/java/org/owasp/esapi/codecs/XMLEntityCodec.java +++ b/src/main/java/org/owasp/esapi/codecs/XMLEntityCodec.java @@ -41,7 +41,7 @@ * of knowing about. Decoding is included for completeness but it's use * is not recommended. Use a XML parser instead! */ -public class XMLEntityCodec extends Codec +public class XMLEntityCodec extends AbstractCodec { private static final String ALPHA_NUMERIC_STR = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; private static final String UNENCODED_STR = ALPHA_NUMERIC_STR + " \t"; diff --git a/src/test/java/org/owasp/esapi/PreparedStringTest.java b/src/test/java/org/owasp/esapi/PreparedStringTest.java index b288c1325..c3ddf8ece 100644 --- a/src/test/java/org/owasp/esapi/PreparedStringTest.java +++ b/src/test/java/org/owasp/esapi/PreparedStringTest.java @@ -17,6 +17,7 @@ package org.owasp.esapi; import junit.framework.TestCase; + import org.owasp.esapi.codecs.Codec; import org.owasp.esapi.codecs.HTMLEntityCodec; diff --git a/src/test/java/org/owasp/esapi/codecs/CodecTest.java b/src/test/java/org/owasp/esapi/codecs/AbstractCodecTest.java similarity index 96% rename from src/test/java/org/owasp/esapi/codecs/CodecTest.java rename to src/test/java/org/owasp/esapi/codecs/AbstractCodecTest.java index 8a7a225ba..1472e5dfb 100644 --- a/src/test/java/org/owasp/esapi/codecs/CodecTest.java +++ b/src/test/java/org/owasp/esapi/codecs/AbstractCodecTest.java @@ -26,7 +26,7 @@ * href="http://www.aspectsecurity.com">Aspect Security * @since June 1, 2007 */ -public class CodecTest extends TestCase { +public class AbstractCodecTest extends TestCase { private static final char[] EMPTY_CHAR_ARRAY = new char[0]; private static final Character LESS_THAN = Character.valueOf('<'); @@ -48,7 +48,7 @@ public class CodecTest extends TestCase { * @param testName * the test name */ - public CodecTest(String testName) { + public AbstractCodecTest(String testName) { super(testName); } @@ -74,7 +74,7 @@ protected void tearDown() throws Exception { * @return the test */ public static Test suite() { - TestSuite suite = new TestSuite(CodecTest.class); + TestSuite suite = new TestSuite(AbstractCodecTest.class); return suite; } @@ -142,7 +142,7 @@ public void testHtmlEncodeChar() public void testHtmlEncodeChar0x100() { - char in = 0x100; + Character in = 0x100; String inStr = Character.toString(in); String expected = "Ā"; String result; @@ -156,7 +156,7 @@ public void testHtmlEncodeChar0x100() public void testHtmlEncodeStr0x100() { - char in = 0x100; + Character in = 0x100; String inStr = Character.toString(in); String expected = "Ā"; String result; @@ -175,7 +175,7 @@ public void testPercentEncodeChar() public void testPercentEncodeChar0x100() { - char in = 0x100; + Character in = 0x100; String inStr = Character.toString(in); String expected = "%C4%80"; String result; @@ -189,7 +189,7 @@ public void testPercentEncodeChar0x100() public void testPercentEncodeStr0x100() { - char in = 0x100; + Character in = 0x100; String inStr = Character.toString(in); String expected = "%C4%80"; String result; @@ -208,7 +208,7 @@ public void testJavaScriptEncodeChar() public void testJavaScriptEncodeChar0x100() { - char in = 0x100; + Character in = 0x100; String inStr = Character.toString(in); String expected = "\\u0100"; String result; @@ -221,7 +221,7 @@ public void testJavaScriptEncodeChar0x100() public void testJavaScriptEncodeStr0x100() { - char in = 0x100; + Character in = 0x100; String inStr = Character.toString(in); String expected = "\\u0100"; String result; @@ -239,7 +239,7 @@ public void testVBScriptEncodeChar() public void testVBScriptEncodeChar0x100() { - char in = 0x100; + Character in = 0x100; String inStr = Character.toString(in); // FIXME I don't know vb... // String expected = "\\u0100"; @@ -253,7 +253,7 @@ public void testVBScriptEncodeChar0x100() public void testVBScriptEncodeStr0x100() { - char in = 0x100; + Character in = 0x100; String inStr = Character.toString(in); // FIXME I don't know vb... // String expected = "chrw(0x100)"; @@ -272,7 +272,7 @@ public void testCSSEncodeChar() public void testCSSEncodeChar0x100() { - char in = 0x100; + Character in = 0x100; String inStr = Character.toString(in); String expected = "\\100 "; String result; @@ -285,7 +285,7 @@ public void testCSSEncodeChar0x100() public void testCSSEncodeStr0x100() { - char in = 0x100; + Character in = 0x100; String inStr = Character.toString(in); String expected = "\\100 "; String result; @@ -303,7 +303,7 @@ public void testMySQLANSIEncodeChar() public void testMySQLStandardEncodeChar0x100() { - char in = 0x100; + Character in = 0x100; String inStr = Character.toString(in); String expected = "\\" + in; String result; @@ -316,7 +316,7 @@ public void testMySQLStandardEncodeChar0x100() public void testMySQLStandardEncodeStr0x100() { - char in = 0x100; + Character in = 0x100; String inStr = Character.toString(in); String expected = "\\" + in; String result; @@ -344,7 +344,7 @@ public void testUnixEncodeChar() public void testUnixEncodeChar0x100() { - char in = 0x100; + Character in = 0x100; String inStr = Character.toString(in); String expected = "\\" + in; String result; @@ -357,7 +357,7 @@ public void testUnixEncodeChar0x100() public void testUnixEncodeStr0x100() { - char in = 0x100; + Character in = 0x100; String inStr = Character.toString(in); String expected = "\\" + in; String result; @@ -375,7 +375,7 @@ public void testWindowsEncodeChar() public void testWindowsEncodeChar0x100() { - char in = 0x100; + Character in = 0x100; String inStr = Character.toString(in); String expected = "^" + in; String result; @@ -388,7 +388,7 @@ public void testWindowsEncodeChar0x100() public void testWindowsEncodeStr0x100() { - char in = 0x100; + Character in = 0x100; String inStr = Character.toString(in); String expected = "^" + in; String result; diff --git a/src/test/java/org/owasp/esapi/codecs/CodecImmunityTest.java b/src/test/java/org/owasp/esapi/codecs/CodecImmunityTest.java index 7d35dc168..9a3b8806f 100644 --- a/src/test/java/org/owasp/esapi/codecs/CodecImmunityTest.java +++ b/src/test/java/org/owasp/esapi/codecs/CodecImmunityTest.java @@ -43,7 +43,7 @@ public class CodecImmunityTest { @Parameters(name = "{0}") public static Collection getParams() { - Collection knownCodecs = new ArrayList(); + Collection knownCodecs = new ArrayList(); knownCodecs.add(new CSSCodec()); knownCodecs.add(new DB2Codec()); knownCodecs.add(new HTMLEntityCodec()); @@ -99,7 +99,7 @@ private static Collection buildImmunitiyValidation(Codec codec, char[] return params; } - private static Collection fullCharacterCodecValidation(Collection codecs) { + private static Collection fullCharacterCodecValidation(Collection codecs) { char[] holyCowTesting = StringUtilities.union(EncoderConstants.CHAR_ALPHANUMERICS, EncoderConstants.CHAR_SPECIALS); Collection params = new ArrayList(); for (Codec codec: codecs) { diff --git a/src/test/java/org/owasp/esapi/reference/RandomizerTest.java b/src/test/java/org/owasp/esapi/reference/RandomizerTest.java index b73a0241d..32dc9bd1f 100644 --- a/src/test/java/org/owasp/esapi/reference/RandomizerTest.java +++ b/src/test/java/org/owasp/esapi/reference/RandomizerTest.java @@ -26,7 +26,7 @@ import org.owasp.esapi.ESAPI; import org.owasp.esapi.EncoderConstants; import org.owasp.esapi.Randomizer; -import org.owasp.esapi.codecs.Codec; +import org.owasp.esapi.codecs.AbstractCodec; import org.owasp.esapi.errors.EncryptionException; /** From ca044492e3678f38323cd1c89c39dbfeed920a74 Mon Sep 17 00:00:00 2001 From: Matt Seil Date: Fri, 4 Aug 2017 15:40:50 -0700 Subject: [PATCH 0397/1069] Issue #300 -- Refactored PushbackString into a more generic class, and created a new Integer based impl to support codePoints. Refactored PusbhbackString to also use the same interface. --- .../codecs/AbstractPushbackSequence.java | 47 +++ .../esapi/codecs/PushBackSequenceImpl.java | 137 +++++++++ .../owasp/esapi/codecs/PushbackSequence.java | 64 +++++ .../owasp/esapi/codecs/PushbackString.java | 272 ++++++++++-------- .../esapi/codecs/PushBackStringTest.java | 41 +++ 5 files changed, 434 insertions(+), 127 deletions(-) create mode 100644 src/main/java/org/owasp/esapi/codecs/AbstractPushbackSequence.java create mode 100644 src/main/java/org/owasp/esapi/codecs/PushBackSequenceImpl.java create mode 100644 src/main/java/org/owasp/esapi/codecs/PushbackSequence.java create mode 100644 src/test/java/org/owasp/esapi/codecs/PushBackStringTest.java diff --git a/src/main/java/org/owasp/esapi/codecs/AbstractPushbackSequence.java b/src/main/java/org/owasp/esapi/codecs/AbstractPushbackSequence.java new file mode 100644 index 000000000..ee35478c3 --- /dev/null +++ b/src/main/java/org/owasp/esapi/codecs/AbstractPushbackSequence.java @@ -0,0 +1,47 @@ +package org.owasp.esapi.codecs; + +public abstract class AbstractPushbackSequence implements PushbackSequence { + protected String input; + protected T pushback; + protected T temp; + protected int index = 0; + protected int mark = 0; + + public AbstractPushbackSequence(String input) { + this.input = input; + } + + /** + * + * @param c + */ + public void pushback(T c) { + pushback = c; + } + + /** + * Get the current index of the PushbackString. Typically used in error + * messages. + * + * @return The current index of the PushbackString. + */ + public int index() { + return index; + } + + /** + * + * @return + */ + public boolean hasNext() { + if (pushback != null) + return true; + if (input == null) + return false; + if (input.length() == 0) + return false; + if (index >= input.length()) + return false; + return true; + } +} diff --git a/src/main/java/org/owasp/esapi/codecs/PushBackSequenceImpl.java b/src/main/java/org/owasp/esapi/codecs/PushBackSequenceImpl.java new file mode 100644 index 000000000..ea31e88d6 --- /dev/null +++ b/src/main/java/org/owasp/esapi/codecs/PushBackSequenceImpl.java @@ -0,0 +1,137 @@ +package org.owasp.esapi.codecs; + + +/** + * The pushback string is used by Codecs to allow them to push decoded characters back onto a string + * for further decoding. This is necessary to detect double-encoding. + * + * @author Jeff Williams (jeff.williams .at. aspectsecurity.com) Aspect Security + * @since June 1, 2007 + * @see org.owasp.esapi.Encoder + */ +public class PushBackSequenceImpl extends AbstractPushbackSequence{ + /** + * + * @param input + */ + public PushBackSequenceImpl( String input ) { + super(input); + } + + /** + * + * @return + */ + public Integer next() { + if ( pushback != null ) { + Integer save = pushback; + pushback = null; + return save; + } + if ( input == null ) return null; + if ( input.length() == 0 ) return null; + if ( index >= input.length() ) return null; + final Integer point = input.codePointAt(index); + index += Character.charCount(point); + return point; + } + + /** + * + * @return + */ + public Integer nextHex() { + Integer c = next(); + if ( c == null ) return null; + if ( isHexDigit( c ) ) return c; + return null; + } + + /** + * + * @return + */ + public Integer nextOctal() { + Integer c = next(); + if ( c == null ) return null; + if ( isOctalDigit( c ) ) return c; + return null; + } + + /** + * Returns true if the parameter character is a hexidecimal digit 0 through 9, a through f, or A through F. + * @param c + * @return + */ + public static boolean isHexDigit( Integer c ) { + if ( c == null ) return false; + Integer ch = Integer.valueOf(c); + return (ch >= '0' && ch <= '9' ) || (ch >= 'a' && ch <= 'f' ) || (ch >= 'A' && ch <= 'F' ); + } + + /** + * Returns true if the parameter character is an octal digit 0 through 7. + * @param c + * @return + */ + public static boolean isOctalDigit( Integer c ) { + if ( c == null ) return false; + Integer ch = Integer.valueOf(c); + return ch >= '0' && ch <= '7'; + } + + /** + * Return the next codePoint without affecting the current index. + * @return + */ + public Integer peek() { + if ( pushback != null ) return pushback; + if ( input == null ) return null; + if ( input.length() == 0 ) return null; + if ( index >= input.length() ) return null; + return input.codePointAt(index); + } + + /** + * Test to see if the next codePoint is a particular value without affecting the current index. + * @param c + * @return + */ + public boolean peek( Integer c ) { + if ( pushback != null && pushback.intValue() == c ) return true; + if ( input == null ) return false; + if ( input.length() == 0 ) return false; + if ( index >= input.length() ) return false; + return input.codePointAt(index) == c; + } + + /** + * + */ + public void mark() { + temp = pushback; + mark = index; + } + + /** + * + */ + public void reset() { + pushback = temp; + index = mark; + } + + /** + * + * @return + */ + protected String remainder() { + String output = input.substring( index ); + if ( pushback != null ) { + output = pushback + output; + } + return output; + } + +} diff --git a/src/main/java/org/owasp/esapi/codecs/PushbackSequence.java b/src/main/java/org/owasp/esapi/codecs/PushbackSequence.java new file mode 100644 index 000000000..58fe4a930 --- /dev/null +++ b/src/main/java/org/owasp/esapi/codecs/PushbackSequence.java @@ -0,0 +1,64 @@ +package org.owasp.esapi.codecs; + +public interface PushbackSequence { + + /** + * + * @param c + */ + void pushback(T c); + + /** + * Get the current index of the PushbackString. Typically used in error messages. + * @return The current index of the PushbackString. + */ + int index(); + + /** + * + * @return + */ + boolean hasNext(); + + /** + * + * @return + */ + T next(); + + /** + * + * @return + */ + T nextHex(); + + /** + * + * @return + */ + T nextOctal(); + + /** + * Return the next character without affecting the current index. + * @return + */ + T peek(); + + /** + * Test to see if the next character is a particular value without affecting the current index. + * @param c + * @return + */ + boolean peek(T c); + + /** + * + */ + void mark(); + + /** + * + */ + void reset(); + +} \ No newline at end of file diff --git a/src/main/java/org/owasp/esapi/codecs/PushbackString.java b/src/main/java/org/owasp/esapi/codecs/PushbackString.java index 1119b223c..9982d48e2 100644 --- a/src/main/java/org/owasp/esapi/codecs/PushbackString.java +++ b/src/main/java/org/owasp/esapi/codecs/PushbackString.java @@ -15,169 +15,187 @@ */ package org.owasp.esapi.codecs; - /** - * The pushback string is used by Codecs to allow them to push decoded characters back onto a string - * for further decoding. This is necessary to detect double-encoding. + * The pushback string is used by Codecs to allow them to push decoded + * characters back onto a string for further decoding. This is necessary to + * detect double-encoding. * - * @author Jeff Williams (jeff.williams .at. aspectsecurity.com) Aspect Security + * @author Jeff Williams (jeff.williams .at. aspectsecurity.com) + * Aspect Security * @since June 1, 2007 * @see org.owasp.esapi.Encoder */ -public class PushbackString { - - private String input; - private Character pushback; - private Character temp; - private int index = 0; - private int mark = 0; - - /** - * - * @param input - */ - public PushbackString( String input ) { - this.input = input; - } - - /** - * - * @param c - */ - public void pushback( Character c ) { - pushback = c; +public class PushbackString extends AbstractPushbackSequence{ + /** + * + * @param input + */ + public PushbackString(String input) { + super(input); } - - /** - * Get the current index of the PushbackString. Typically used in error messages. - * @return The current index of the PushbackString. - */ - public int index() { + /* + * (non-Javadoc) + * + * @see org.owasp.esapi.codecs.PushbackSequence#index() + */ + public int index() { return index; } - - /** - * - * @return - */ - public boolean hasNext() { - if ( pushback != null ) return true; - if ( input == null ) return false; - if ( input.length() == 0 ) return false; - if ( index >= input.length() ) return false; - return true; + + /* + * (non-Javadoc) + * + * @see org.owasp.esapi.codecs.PushbackSequence#hasNext() + */ + public boolean hasNext() { + if (pushback != null) + return true; + if (input == null) + return false; + if (input.length() == 0) + return false; + if (index >= input.length()) + return false; + return true; } - - /** - * - * @return - */ - public Character next() { - if ( pushback != null ) { + + /* + * (non-Javadoc) + * + * @see org.owasp.esapi.codecs.PushbackSequence#next() + */ + public Character next() { + if (pushback != null) { Character save = pushback; pushback = null; return save; } - if ( input == null ) return null; - if ( input.length() == 0 ) return null; - if ( index >= input.length() ) return null; - return Character.valueOf( input.charAt(index++) ); + if (input == null) + return null; + if (input.length() == 0) + return null; + if (index >= input.length()) + return null; + return Character.valueOf(input.charAt(index++)); } - - /** - * - * @return - */ - public Character nextHex() { + + /* + * (non-Javadoc) + * + * @see org.owasp.esapi.codecs.PushbackSequence#nextHex() + */ + public Character nextHex() { Character c = next(); - if ( c == null ) return null; - if ( isHexDigit( c ) ) return c; + if (c == null) + return null; + if (isHexDigit(c)) + return c; return null; } - /** - * - * @return - */ - public Character nextOctal() { + /* + * (non-Javadoc) + * + * @see org.owasp.esapi.codecs.PushbackSequence#nextOctal() + */ + public Character nextOctal() { Character c = next(); - if ( c == null ) return null; - if ( isOctalDigit( c ) ) return c; + if (c == null) + return null; + if (isOctalDigit(c)) + return c; return null; } - /** - * Returns true if the parameter character is a hexidecimal digit 0 through 9, a through f, or A through F. - * @param c - * @return - */ - public static boolean isHexDigit( Character c ) { - if ( c == null ) return false; + /** + * Returns true if the parameter character is a hexidecimal digit 0 through + * 9, a through f, or A through F. + * + * @param c + * @return + */ + public static boolean isHexDigit(Character c) { + if (c == null) + return false; char ch = c.charValue(); - return (ch >= '0' && ch <= '9' ) || (ch >= 'a' && ch <= 'f' ) || (ch >= 'A' && ch <= 'F' ); + return (ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'f') || (ch >= 'A' && ch <= 'F'); } - /** - * Returns true if the parameter character is an octal digit 0 through 7. - * @param c - * @return - */ -public static boolean isOctalDigit( Character c ) { - if ( c == null ) return false; - char ch = c.charValue(); - return ch >= '0' && ch <= '7'; -} + /** + * Returns true if the parameter character is an octal digit 0 through 7. + * + * @param c + * @return + */ + public static boolean isOctalDigit(Character c) { + if (c == null) + return false; + char ch = c.charValue(); + return ch >= '0' && ch <= '7'; + } - /** - * Return the next character without affecting the current index. - * @return - */ - public Character peek() { - if ( pushback != null ) return pushback; - if ( input == null ) return null; - if ( input.length() == 0 ) return null; - if ( index >= input.length() ) return null; - return Character.valueOf( input.charAt(index) ); + /* + * (non-Javadoc) + * + * @see org.owasp.esapi.codecs.PushbackSequence#peek() + */ + public Character peek() { + if (pushback != null) + return pushback; + if (input == null) + return null; + if (input.length() == 0) + return null; + if (index >= input.length()) + return null; + return Character.valueOf(input.charAt(index)); } - - /** - * Test to see if the next character is a particular value without affecting the current index. - * @param c - * @return - */ - public boolean peek( char c ) { - if ( pushback != null && pushback.charValue() == c ) return true; - if ( input == null ) return false; - if ( input.length() == 0 ) return false; - if ( index >= input.length() ) return false; + + /* + * (non-Javadoc) + * + * @see org.owasp.esapi.codecs.PushbackSequence#peek(char) + */ + public boolean peek(Character c) { + if (pushback != null && pushback.charValue() == c) + return true; + if (input == null) + return false; + if (input.length() == 0) + return false; + if (index >= input.length()) + return false; return input.charAt(index) == c; - } - - /** - * - */ - public void mark() { + } + + /* + * (non-Javadoc) + * + * @see org.owasp.esapi.codecs.PushbackSequence#mark() + */ + public void mark() { temp = pushback; mark = index; } - /** - * - */ - public void reset() { + /* + * (non-Javadoc) + * + * @see org.owasp.esapi.codecs.PushbackSequence#reset() + */ + public void reset() { pushback = temp; index = mark; } - - /** - * - * @return - */ - protected String remainder() { - String output = input.substring( index ); - if ( pushback != null ) { + + /** + * + * @return + */ + protected String remainder() { + String output = input.substring(index); + if (pushback != null) { output = pushback + output; } return output; diff --git a/src/test/java/org/owasp/esapi/codecs/PushBackStringTest.java b/src/test/java/org/owasp/esapi/codecs/PushBackStringTest.java new file mode 100644 index 000000000..2a4ed84e2 --- /dev/null +++ b/src/test/java/org/owasp/esapi/codecs/PushBackStringTest.java @@ -0,0 +1,41 @@ +package org.owasp.esapi.codecs; + +import static org.junit.Assert.assertEquals; + +import org.junit.Test; + +public class PushBackStringTest { + + @Test + public void testPushbackString() { + PushbackSequence pbs = new PushbackString("012345"); + + pbs.mark(); + assertEquals(0, pbs.index()); + Character first = pbs.next(); + + System.out.println("0x" + Integer.toHexString(first)); + + assertEquals("0", new StringBuilder().appendCodePoint(first).toString()); + } + + @Test + public void testPushbackSequence() { + AbstractPushbackSequence pbs = new PushBackSequenceImpl("12345"); + + pbs.mark(); + assertEquals(0, pbs.index()); + Integer first = pbs.next(); + + System.out.println("0x" + Integer.toHexString(first)); + + assertEquals("&", new StringBuilder().appendCodePoint(first).toString()); + + Integer second = pbs.next(); + + if(second == '#'){ + System.out.printf("[%d]:[%d]\n", second, (int) '#'); + + } + } +} From 65646f174e42be2bd1a88706f53d6be56d94d4a7 Mon Sep 17 00:00:00 2001 From: Matt Seil Date: Sat, 5 Aug 2017 15:34:50 -0700 Subject: [PATCH 0398/1069] Issue #300 -- Finally got the core issue whacked! Now we have to clean up the rest of the unit tests. --- .../esapi/codecs/AbstractCharacterCodec.java | 21 + .../org/owasp/esapi/codecs/AbstractCodec.java | 23 +- .../esapi/codecs/AbstractIntegerCodec.java | 22 + .../java/org/owasp/esapi/codecs/CSSCodec.java | 4 +- .../java/org/owasp/esapi/codecs/Codec.java | 6 +- .../java/org/owasp/esapi/codecs/DB2Codec.java | 2 +- .../owasp/esapi/codecs/HTMLEntityCodec.java | 565 +++++++++--------- .../owasp/esapi/codecs/JavaScriptCodec.java | 2 +- .../org/owasp/esapi/codecs/MySQLCodec.java | 2 +- .../org/owasp/esapi/codecs/OracleCodec.java | 2 +- .../org/owasp/esapi/codecs/PercentCodec.java | 2 +- .../esapi/codecs/PushBackSequenceImpl.java | 2 +- .../owasp/esapi/codecs/PushbackSequence.java | 8 + .../owasp/esapi/codecs/PushbackString.java | 2 +- .../org/owasp/esapi/codecs/UnixCodec.java | 2 +- .../org/owasp/esapi/codecs/VBScriptCodec.java | 2 +- .../org/owasp/esapi/codecs/WindowsCodec.java | 2 +- .../owasp/esapi/codecs/XMLEntityCodec.java | 2 +- .../owasp/esapi/codecs/AbstractCodecTest.java | 2 +- .../owasp/esapi/reference/EncoderTest.java | 19 +- 20 files changed, 366 insertions(+), 326 deletions(-) create mode 100644 src/main/java/org/owasp/esapi/codecs/AbstractCharacterCodec.java create mode 100644 src/main/java/org/owasp/esapi/codecs/AbstractIntegerCodec.java diff --git a/src/main/java/org/owasp/esapi/codecs/AbstractCharacterCodec.java b/src/main/java/org/owasp/esapi/codecs/AbstractCharacterCodec.java new file mode 100644 index 000000000..5868d2bc0 --- /dev/null +++ b/src/main/java/org/owasp/esapi/codecs/AbstractCharacterCodec.java @@ -0,0 +1,21 @@ +package org.owasp.esapi.codecs; + +public abstract class AbstractCharacterCodec extends AbstractCodec { + /* (non-Javadoc) + * @see org.owasp.esapi.codecs.Codec#decode(java.lang.String) + */ + @Override + public String decode(String input) { + StringBuilder sb = new StringBuilder(); + PushbackSequence pbs = new PushbackString(input); + while (pbs.hasNext()) { + Character c = decodeCharacter(pbs); + if (c != null) { + sb.append(c); + } else { + sb.append(pbs.next()); + } + } + return sb.toString(); + } +} diff --git a/src/main/java/org/owasp/esapi/codecs/AbstractCodec.java b/src/main/java/org/owasp/esapi/codecs/AbstractCodec.java index 11d33f8a8..236125497 100644 --- a/src/main/java/org/owasp/esapi/codecs/AbstractCodec.java +++ b/src/main/java/org/owasp/esapi/codecs/AbstractCodec.java @@ -25,10 +25,11 @@ * * @author Jeff Williams (jeff.williams .at. aspectsecurity.com) Aspect Security + * @param * @since June 1, 2007 * @see org.owasp.esapi.Encoder */ -public abstract class AbstractCodec implements Codec { +public abstract class AbstractCodec implements Codec { /** * Initialize an array to mark which characters are to be encoded. Store the hex @@ -89,29 +90,13 @@ public String encodeCharacter( char[] immune, int codePoint ) { return new StringBuilder().appendCodePoint(codePoint).toString(); } - /* (non-Javadoc) - * @see org.owasp.esapi.codecs.Codec#decode(java.lang.String) - */ - @Override - public String decode(String input) { - StringBuilder sb = new StringBuilder(); - PushbackString pbs = new PushbackString(input); - while (pbs.hasNext()) { - Character c = decodeCharacter(pbs); - if (c != null) { - sb.append(c); - } else { - sb.append(pbs.next()); - } - } - return sb.toString(); - } + /* (non-Javadoc) * @see org.owasp.esapi.codecs.Codec#decodeCharacter(org.owasp.esapi.codecs.PushbackString) */ @Override - public Character decodeCharacter( PushbackString input ) { + public T decodeCharacter( PushbackSequence input ) { return input.next(); } diff --git a/src/main/java/org/owasp/esapi/codecs/AbstractIntegerCodec.java b/src/main/java/org/owasp/esapi/codecs/AbstractIntegerCodec.java new file mode 100644 index 000000000..f43891481 --- /dev/null +++ b/src/main/java/org/owasp/esapi/codecs/AbstractIntegerCodec.java @@ -0,0 +1,22 @@ +package org.owasp.esapi.codecs; + +public class AbstractIntegerCodec extends AbstractCodec { + + /** + * {@inheritDoc} + */ + @Override + public String decode(String input) { + StringBuilder sb = new StringBuilder(); + PushbackSequence pbs = new PushBackSequenceImpl(input); + while (pbs.hasNext()) { + Integer c = decodeCharacter(pbs); + if (c != null) { + sb.appendCodePoint(c); + } else { + sb.appendCodePoint(pbs.next()); + } + } + return sb.toString(); + } +} diff --git a/src/main/java/org/owasp/esapi/codecs/CSSCodec.java b/src/main/java/org/owasp/esapi/codecs/CSSCodec.java index a44cebeed..088463889 100644 --- a/src/main/java/org/owasp/esapi/codecs/CSSCodec.java +++ b/src/main/java/org/owasp/esapi/codecs/CSSCodec.java @@ -23,7 +23,7 @@ * @since June 1, 2007 * @see org.owasp.esapi.Encoder */ -public class CSSCodec extends AbstractCodec +public class CSSCodec extends AbstractCharacterCodec { private static final Character REPLACEMENT = '\ufffd'; @@ -58,7 +58,7 @@ public String encodeCharacter(char[] immune, Character c) { * Returns the decoded version of the character starting at index, * or null if no decoding is possible. */ - public Character decodeCharacter(PushbackString input) + public Character decodeCharacter(PushbackSequence input) { input.mark(); Character first = input.next(); diff --git a/src/main/java/org/owasp/esapi/codecs/Codec.java b/src/main/java/org/owasp/esapi/codecs/Codec.java index 3f7cd2554..c205d1b3f 100644 --- a/src/main/java/org/owasp/esapi/codecs/Codec.java +++ b/src/main/java/org/owasp/esapi/codecs/Codec.java @@ -28,7 +28,7 @@ * @since June 1, 2007 * @see org.owasp.esapi.Encoder */ -public interface Codec { +public interface Codec { /** * Encode a String so that it can be safely used in a specific context. * @@ -73,14 +73,14 @@ public interface Codec { /** * Returns the decoded version of the next character from the input string and advances the - * current character in the PushbackString. If the current character is not encoded, this + * current character in the PushbackSequence. If the current character is not encoded, this * method MUST reset the PushbackString. * * @param input the Character to decode * * @return the decoded Character */ - public Character decodeCharacter( PushbackString input ); + public T decodeCharacter( PushbackSequence input ); /** * Lookup the hex value of any character that is not alphanumeric. diff --git a/src/main/java/org/owasp/esapi/codecs/DB2Codec.java b/src/main/java/org/owasp/esapi/codecs/DB2Codec.java index 236bed78e..850d9a6aa 100644 --- a/src/main/java/org/owasp/esapi/codecs/DB2Codec.java +++ b/src/main/java/org/owasp/esapi/codecs/DB2Codec.java @@ -20,7 +20,7 @@ * @since October 26, 2010 * @see org.owasp.esapi.Encoder */ -public class DB2Codec extends AbstractCodec { +public class DB2Codec extends AbstractCharacterCodec { public String encodeCharacter(char[] immune, Character c) { diff --git a/src/main/java/org/owasp/esapi/codecs/HTMLEntityCodec.java b/src/main/java/org/owasp/esapi/codecs/HTMLEntityCodec.java index e3892d2f8..4adfdf84c 100644 --- a/src/main/java/org/owasp/esapi/codecs/HTMLEntityCodec.java +++ b/src/main/java/org/owasp/esapi/codecs/HTMLEntityCodec.java @@ -18,6 +18,7 @@ import java.util.HashMap; import java.util.Collections; import java.util.Map; +import java.util.Map.Entry; /** * Implementation of the Codec interface for HTML entity encoding. @@ -27,14 +28,14 @@ * @since June 1, 2007 * @see org.owasp.esapi.Encoder */ -public class HTMLEntityCodec extends AbstractCodec +public class HTMLEntityCodec extends AbstractIntegerCodec { private static final char REPLACEMENT_CHAR = '\ufffd'; private static final String REPLACEMENT_HEX = "fffd"; private static final String REPLACEMENT_STR = "" + REPLACEMENT_CHAR; - private static final Map characterToEntityMap = mkCharacterToEntityMap(); + private static final Map characterToEntityMap = mkCharacterToEntityMap(); - private static final Trie entityToCharacterTrie = mkEntityToCharacterTrie(); + private static final Trie entityToCharacterTrie = mkEntityToCharacterTrie(); /** * @@ -69,7 +70,7 @@ public String encodeCharacter( char[] immune, Character c ) { } // check if there's a defined entity - String entityName = (String) characterToEntityMap.get(c); + String entityName = (String) characterToEntityMap.get(Integer.valueOf(c)); if (entityName != null) { return "&" + entityName + ";"; } @@ -125,9 +126,9 @@ public String encodeCharacter( char[] immune, int codePoint ) { * &#xhhhh; * &name; */ - public Character decodeCharacter( PushbackString input ) { + public Integer decodeCharacter( PushbackSequence input ) { input.mark(); - Character first = input.next(); + Integer first = input.next(); if ( first == null ) { input.reset(); return null; @@ -140,7 +141,7 @@ public Character decodeCharacter( PushbackString input ) { } // test for numeric encodings - Character second = input.next(); + Integer second = input.next(); if ( second == null ) { input.reset(); return null; @@ -148,12 +149,12 @@ public Character decodeCharacter( PushbackString input ) { if (second == '#' ) { // handle numbers - Character c = getNumericEntity( input ); + Integer c = getNumericEntity( input ); if ( c != null ) return c; - } else if ( Character.isLetter( second.charValue() ) ) { + } else if ( Character.isLetter( second ) ) { // handle entities input.pushback( second ); - Character c = getNamedEntity( input ); + Integer c = getNamedEntity( input ); if ( c != null ) return c; } input.reset(); @@ -169,8 +170,8 @@ public Character decodeCharacter( PushbackString input ) { * @return * null if input is null, the character of input after decoding */ - private Character getNumericEntity( PushbackString input ) { - Character first = input.peek(); + private Integer getNumericEntity( PushbackSequence input ) { + Integer first = input.peek(); if ( first == null ) return null; if (first == 'x' || first == 'X' ) { @@ -189,14 +190,14 @@ private Character getNumericEntity( PushbackString input ) { * character representation of this decimal value, e.g. A * @throws NumberFormatException */ - private Character parseNumber( PushbackString input ) { + private Integer parseNumber( PushbackSequence input ) { StringBuilder sb = new StringBuilder(); while( input.hasNext() ) { - Character c = input.peek(); + Integer c = input.peek(); // if character is a digit then add it on and keep going - if ( Character.isDigit( c.charValue() ) ) { - sb.append( c ); + if ( Character.isDigit( c ) ) { + sb.appendCodePoint( c ); input.next(); // if character is a semi-colon, eat it and quit @@ -212,7 +213,7 @@ private Character parseNumber( PushbackString input ) { try { int i = Integer.parseInt(sb.toString()); if (Character.isValidCodePoint(i)) { - return (char) i; + return i; } } catch( NumberFormatException e ) { // throw an exception for malformed entity? @@ -229,14 +230,14 @@ private Character parseNumber( PushbackString input ) { * A single character from the string * @throws NumberFormatException */ - private Character parseHex( PushbackString input ) { + private Integer parseHex( PushbackSequence input ) { StringBuilder sb = new StringBuilder(); while( input.hasNext() ) { - Character c = input.peek(); + Integer c = input.peek(); // if character is a hex digit then add it on and keep going if ( "0123456789ABCDEFabcdef".indexOf(c) != -1 ) { - sb.append( c ); + sb.appendCodePoint( c ); input.next(); // if character is a semi-colon, eat it and quit @@ -252,7 +253,7 @@ private Character parseHex( PushbackString input ) { try { int i = Integer.parseInt(sb.toString(), 16); if (Character.isValidCodePoint(i)) { - return (char) i; + return i; } } catch( NumberFormatException e ) { // throw an exception for malformed entity? @@ -278,9 +279,9 @@ private Character parseHex( PushbackString input ) { * @return * Returns the decoded version of the character starting at index, or null if no decoding is possible. */ - private Character getNamedEntity( PushbackString input ) { + private Integer getNamedEntity( PushbackSequence input ) { StringBuilder possible = new StringBuilder(); - Map.Entry entry; + Entry entry; int len; // kludge around PushbackString.... @@ -296,7 +297,7 @@ private Character getNamedEntity( PushbackString input ) { String possibleString = possible.toString(); String possibleStringLowerCase = possibleString.toLowerCase(); if(!possibleString.equals(possibleStringLowerCase)) { - Map.Entry exactEntry = entityToCharacterTrie.getLongestMatch(possibleStringLowerCase); + Map.Entry exactEntry = entityToCharacterTrie.getLongestMatch(possibleStringLowerCase); if(exactEntry != null) entry = exactEntry; } if(entry == null) return null; // no match, caller will reset input @@ -310,7 +311,7 @@ private Character getNamedEntity( PushbackString input ) { input.next(); // check for a trailing semicolen - if(input.peek(';')) + if(input.peek(Integer.valueOf(';'))) input.next(); return entry.getValue(); @@ -320,262 +321,262 @@ private Character getNamedEntity( PushbackString input ) { * Build a unmodifiable Map from entity Character to Name. * @return Unmodifiable map. */ - private static synchronized Map mkCharacterToEntityMap() + private static synchronized Map mkCharacterToEntityMap() { - Map map = new HashMap(252); + Map map = new HashMap(252); - map.put((char)34, "quot"); /* quotation mark */ - map.put((char)38, "amp"); /* ampersand */ - map.put((char)60, "lt"); /* less-than sign */ - map.put((char)62, "gt"); /* greater-than sign */ - map.put((char)160, "nbsp"); /* no-break space */ - map.put((char)161, "iexcl"); /* inverted exclamation mark */ - map.put((char)162, "cent"); /* cent sign */ - map.put((char)163, "pound"); /* pound sign */ - map.put((char)164, "curren"); /* currency sign */ - map.put((char)165, "yen"); /* yen sign */ - map.put((char)166, "brvbar"); /* broken bar */ - map.put((char)167, "sect"); /* section sign */ - map.put((char)168, "uml"); /* diaeresis */ - map.put((char)169, "copy"); /* copyright sign */ - map.put((char)170, "ordf"); /* feminine ordinal indicator */ - map.put((char)171, "laquo"); /* left-pointing double angle quotation mark */ - map.put((char)172, "not"); /* not sign */ - map.put((char)173, "shy"); /* soft hyphen */ - map.put((char)174, "reg"); /* registered sign */ - map.put((char)175, "macr"); /* macron */ - map.put((char)176, "deg"); /* degree sign */ - map.put((char)177, "plusmn"); /* plus-minus sign */ - map.put((char)178, "sup2"); /* superscript two */ - map.put((char)179, "sup3"); /* superscript three */ - map.put((char)180, "acute"); /* acute accent */ - map.put((char)181, "micro"); /* micro sign */ - map.put((char)182, "para"); /* pilcrow sign */ - map.put((char)183, "middot"); /* middle dot */ - map.put((char)184, "cedil"); /* cedilla */ - map.put((char)185, "sup1"); /* superscript one */ - map.put((char)186, "ordm"); /* masculine ordinal indicator */ - map.put((char)187, "raquo"); /* right-pointing double angle quotation mark */ - map.put((char)188, "frac14"); /* vulgar fraction one quarter */ - map.put((char)189, "frac12"); /* vulgar fraction one half */ - map.put((char)190, "frac34"); /* vulgar fraction three quarters */ - map.put((char)191, "iquest"); /* inverted question mark */ - map.put((char)192, "Agrave"); /* Latin capital letter a with grave */ - map.put((char)193, "Aacute"); /* Latin capital letter a with acute */ - map.put((char)194, "Acirc"); /* Latin capital letter a with circumflex */ - map.put((char)195, "Atilde"); /* Latin capital letter a with tilde */ - map.put((char)196, "Auml"); /* Latin capital letter a with diaeresis */ - map.put((char)197, "Aring"); /* Latin capital letter a with ring above */ - map.put((char)198, "AElig"); /* Latin capital letter ae */ - map.put((char)199, "Ccedil"); /* Latin capital letter c with cedilla */ - map.put((char)200, "Egrave"); /* Latin capital letter e with grave */ - map.put((char)201, "Eacute"); /* Latin capital letter e with acute */ - map.put((char)202, "Ecirc"); /* Latin capital letter e with circumflex */ - map.put((char)203, "Euml"); /* Latin capital letter e with diaeresis */ - map.put((char)204, "Igrave"); /* Latin capital letter i with grave */ - map.put((char)205, "Iacute"); /* Latin capital letter i with acute */ - map.put((char)206, "Icirc"); /* Latin capital letter i with circumflex */ - map.put((char)207, "Iuml"); /* Latin capital letter i with diaeresis */ - map.put((char)208, "ETH"); /* Latin capital letter eth */ - map.put((char)209, "Ntilde"); /* Latin capital letter n with tilde */ - map.put((char)210, "Ograve"); /* Latin capital letter o with grave */ - map.put((char)211, "Oacute"); /* Latin capital letter o with acute */ - map.put((char)212, "Ocirc"); /* Latin capital letter o with circumflex */ - map.put((char)213, "Otilde"); /* Latin capital letter o with tilde */ - map.put((char)214, "Ouml"); /* Latin capital letter o with diaeresis */ - map.put((char)215, "times"); /* multiplication sign */ - map.put((char)216, "Oslash"); /* Latin capital letter o with stroke */ - map.put((char)217, "Ugrave"); /* Latin capital letter u with grave */ - map.put((char)218, "Uacute"); /* Latin capital letter u with acute */ - map.put((char)219, "Ucirc"); /* Latin capital letter u with circumflex */ - map.put((char)220, "Uuml"); /* Latin capital letter u with diaeresis */ - map.put((char)221, "Yacute"); /* Latin capital letter y with acute */ - map.put((char)222, "THORN"); /* Latin capital letter thorn */ - map.put((char)223, "szlig"); /* Latin small letter sharp sXCOMMAX German Eszett */ - map.put((char)224, "agrave"); /* Latin small letter a with grave */ - map.put((char)225, "aacute"); /* Latin small letter a with acute */ - map.put((char)226, "acirc"); /* Latin small letter a with circumflex */ - map.put((char)227, "atilde"); /* Latin small letter a with tilde */ - map.put((char)228, "auml"); /* Latin small letter a with diaeresis */ - map.put((char)229, "aring"); /* Latin small letter a with ring above */ - map.put((char)230, "aelig"); /* Latin lowercase ligature ae */ - map.put((char)231, "ccedil"); /* Latin small letter c with cedilla */ - map.put((char)232, "egrave"); /* Latin small letter e with grave */ - map.put((char)233, "eacute"); /* Latin small letter e with acute */ - map.put((char)234, "ecirc"); /* Latin small letter e with circumflex */ - map.put((char)235, "euml"); /* Latin small letter e with diaeresis */ - map.put((char)236, "igrave"); /* Latin small letter i with grave */ - map.put((char)237, "iacute"); /* Latin small letter i with acute */ - map.put((char)238, "icirc"); /* Latin small letter i with circumflex */ - map.put((char)239, "iuml"); /* Latin small letter i with diaeresis */ - map.put((char)240, "eth"); /* Latin small letter eth */ - map.put((char)241, "ntilde"); /* Latin small letter n with tilde */ - map.put((char)242, "ograve"); /* Latin small letter o with grave */ - map.put((char)243, "oacute"); /* Latin small letter o with acute */ - map.put((char)244, "ocirc"); /* Latin small letter o with circumflex */ - map.put((char)245, "otilde"); /* Latin small letter o with tilde */ - map.put((char)246, "ouml"); /* Latin small letter o with diaeresis */ - map.put((char)247, "divide"); /* division sign */ - map.put((char)248, "oslash"); /* Latin small letter o with stroke */ - map.put((char)249, "ugrave"); /* Latin small letter u with grave */ - map.put((char)250, "uacute"); /* Latin small letter u with acute */ - map.put((char)251, "ucirc"); /* Latin small letter u with circumflex */ - map.put((char)252, "uuml"); /* Latin small letter u with diaeresis */ - map.put((char)253, "yacute"); /* Latin small letter y with acute */ - map.put((char)254, "thorn"); /* Latin small letter thorn */ - map.put((char)255, "yuml"); /* Latin small letter y with diaeresis */ - map.put((char)338, "OElig"); /* Latin capital ligature oe */ - map.put((char)339, "oelig"); /* Latin small ligature oe */ - map.put((char)352, "Scaron"); /* Latin capital letter s with caron */ - map.put((char)353, "scaron"); /* Latin small letter s with caron */ - map.put((char)376, "Yuml"); /* Latin capital letter y with diaeresis */ - map.put((char)402, "fnof"); /* Latin small letter f with hook */ - map.put((char)710, "circ"); /* modifier letter circumflex accent */ - map.put((char)732, "tilde"); /* small tilde */ - map.put((char)913, "Alpha"); /* Greek capital letter alpha */ - map.put((char)914, "Beta"); /* Greek capital letter beta */ - map.put((char)915, "Gamma"); /* Greek capital letter gamma */ - map.put((char)916, "Delta"); /* Greek capital letter delta */ - map.put((char)917, "Epsilon"); /* Greek capital letter epsilon */ - map.put((char)918, "Zeta"); /* Greek capital letter zeta */ - map.put((char)919, "Eta"); /* Greek capital letter eta */ - map.put((char)920, "Theta"); /* Greek capital letter theta */ - map.put((char)921, "Iota"); /* Greek capital letter iota */ - map.put((char)922, "Kappa"); /* Greek capital letter kappa */ - map.put((char)923, "Lambda"); /* Greek capital letter lambda */ - map.put((char)924, "Mu"); /* Greek capital letter mu */ - map.put((char)925, "Nu"); /* Greek capital letter nu */ - map.put((char)926, "Xi"); /* Greek capital letter xi */ - map.put((char)927, "Omicron"); /* Greek capital letter omicron */ - map.put((char)928, "Pi"); /* Greek capital letter pi */ - map.put((char)929, "Rho"); /* Greek capital letter rho */ - map.put((char)931, "Sigma"); /* Greek capital letter sigma */ - map.put((char)932, "Tau"); /* Greek capital letter tau */ - map.put((char)933, "Upsilon"); /* Greek capital letter upsilon */ - map.put((char)934, "Phi"); /* Greek capital letter phi */ - map.put((char)935, "Chi"); /* Greek capital letter chi */ - map.put((char)936, "Psi"); /* Greek capital letter psi */ - map.put((char)937, "Omega"); /* Greek capital letter omega */ - map.put((char)945, "alpha"); /* Greek small letter alpha */ - map.put((char)946, "beta"); /* Greek small letter beta */ - map.put((char)947, "gamma"); /* Greek small letter gamma */ - map.put((char)948, "delta"); /* Greek small letter delta */ - map.put((char)949, "epsilon"); /* Greek small letter epsilon */ - map.put((char)950, "zeta"); /* Greek small letter zeta */ - map.put((char)951, "eta"); /* Greek small letter eta */ - map.put((char)952, "theta"); /* Greek small letter theta */ - map.put((char)953, "iota"); /* Greek small letter iota */ - map.put((char)954, "kappa"); /* Greek small letter kappa */ - map.put((char)955, "lambda"); /* Greek small letter lambda */ - map.put((char)956, "mu"); /* Greek small letter mu */ - map.put((char)957, "nu"); /* Greek small letter nu */ - map.put((char)958, "xi"); /* Greek small letter xi */ - map.put((char)959, "omicron"); /* Greek small letter omicron */ - map.put((char)960, "pi"); /* Greek small letter pi */ - map.put((char)961, "rho"); /* Greek small letter rho */ - map.put((char)962, "sigmaf"); /* Greek small letter final sigma */ - map.put((char)963, "sigma"); /* Greek small letter sigma */ - map.put((char)964, "tau"); /* Greek small letter tau */ - map.put((char)965, "upsilon"); /* Greek small letter upsilon */ - map.put((char)966, "phi"); /* Greek small letter phi */ - map.put((char)967, "chi"); /* Greek small letter chi */ - map.put((char)968, "psi"); /* Greek small letter psi */ - map.put((char)969, "omega"); /* Greek small letter omega */ - map.put((char)977, "thetasym"); /* Greek theta symbol */ - map.put((char)978, "upsih"); /* Greek upsilon with hook symbol */ - map.put((char)982, "piv"); /* Greek pi symbol */ - map.put((char)8194, "ensp"); /* en space */ - map.put((char)8195, "emsp"); /* em space */ - map.put((char)8201, "thinsp"); /* thin space */ - map.put((char)8204, "zwnj"); /* zero width non-joiner */ - map.put((char)8205, "zwj"); /* zero width joiner */ - map.put((char)8206, "lrm"); /* left-to-right mark */ - map.put((char)8207, "rlm"); /* right-to-left mark */ - map.put((char)8211, "ndash"); /* en dash */ - map.put((char)8212, "mdash"); /* em dash */ - map.put((char)8216, "lsquo"); /* left single quotation mark */ - map.put((char)8217, "rsquo"); /* right single quotation mark */ - map.put((char)8218, "sbquo"); /* single low-9 quotation mark */ - map.put((char)8220, "ldquo"); /* left double quotation mark */ - map.put((char)8221, "rdquo"); /* right double quotation mark */ - map.put((char)8222, "bdquo"); /* double low-9 quotation mark */ - map.put((char)8224, "dagger"); /* dagger */ - map.put((char)8225, "Dagger"); /* double dagger */ - map.put((char)8226, "bull"); /* bullet */ - map.put((char)8230, "hellip"); /* horizontal ellipsis */ - map.put((char)8240, "permil"); /* per mille sign */ - map.put((char)8242, "prime"); /* prime */ - map.put((char)8243, "Prime"); /* double prime */ - map.put((char)8249, "lsaquo"); /* single left-pointing angle quotation mark */ - map.put((char)8250, "rsaquo"); /* single right-pointing angle quotation mark */ - map.put((char)8254, "oline"); /* overline */ - map.put((char)8260, "frasl"); /* fraction slash */ - map.put((char)8364, "euro"); /* euro sign */ - map.put((char)8465, "image"); /* black-letter capital i */ - map.put((char)8472, "weierp"); /* script capital pXCOMMAX Weierstrass p */ - map.put((char)8476, "real"); /* black-letter capital r */ - map.put((char)8482, "trade"); /* trademark sign */ - map.put((char)8501, "alefsym"); /* alef symbol */ - map.put((char)8592, "larr"); /* leftwards arrow */ - map.put((char)8593, "uarr"); /* upwards arrow */ - map.put((char)8594, "rarr"); /* rightwards arrow */ - map.put((char)8595, "darr"); /* downwards arrow */ - map.put((char)8596, "harr"); /* left right arrow */ - map.put((char)8629, "crarr"); /* downwards arrow with corner leftwards */ - map.put((char)8656, "lArr"); /* leftwards double arrow */ - map.put((char)8657, "uArr"); /* upwards double arrow */ - map.put((char)8658, "rArr"); /* rightwards double arrow */ - map.put((char)8659, "dArr"); /* downwards double arrow */ - map.put((char)8660, "hArr"); /* left right double arrow */ - map.put((char)8704, "forall"); /* for all */ - map.put((char)8706, "part"); /* partial differential */ - map.put((char)8707, "exist"); /* there exists */ - map.put((char)8709, "empty"); /* empty set */ - map.put((char)8711, "nabla"); /* nabla */ - map.put((char)8712, "isin"); /* element of */ - map.put((char)8713, "notin"); /* not an element of */ - map.put((char)8715, "ni"); /* contains as member */ - map.put((char)8719, "prod"); /* n-ary product */ - map.put((char)8721, "sum"); /* n-ary summation */ - map.put((char)8722, "minus"); /* minus sign */ - map.put((char)8727, "lowast"); /* asterisk operator */ - map.put((char)8730, "radic"); /* square root */ - map.put((char)8733, "prop"); /* proportional to */ - map.put((char)8734, "infin"); /* infinity */ - map.put((char)8736, "ang"); /* angle */ - map.put((char)8743, "and"); /* logical and */ - map.put((char)8744, "or"); /* logical or */ - map.put((char)8745, "cap"); /* intersection */ - map.put((char)8746, "cup"); /* union */ - map.put((char)8747, "int"); /* integral */ - map.put((char)8756, "there4"); /* therefore */ - map.put((char)8764, "sim"); /* tilde operator */ - map.put((char)8773, "cong"); /* congruent to */ - map.put((char)8776, "asymp"); /* almost equal to */ - map.put((char)8800, "ne"); /* not equal to */ - map.put((char)8801, "equiv"); /* identical toXCOMMAX equivalent to */ - map.put((char)8804, "le"); /* less-than or equal to */ - map.put((char)8805, "ge"); /* greater-than or equal to */ - map.put((char)8834, "sub"); /* subset of */ - map.put((char)8835, "sup"); /* superset of */ - map.put((char)8836, "nsub"); /* not a subset of */ - map.put((char)8838, "sube"); /* subset of or equal to */ - map.put((char)8839, "supe"); /* superset of or equal to */ - map.put((char)8853, "oplus"); /* circled plus */ - map.put((char)8855, "otimes"); /* circled times */ - map.put((char)8869, "perp"); /* up tack */ - map.put((char)8901, "sdot"); /* dot operator */ - map.put((char)8968, "lceil"); /* left ceiling */ - map.put((char)8969, "rceil"); /* right ceiling */ - map.put((char)8970, "lfloor"); /* left floor */ - map.put((char)8971, "rfloor"); /* right floor */ - map.put((char)9001, "lang"); /* left-pointing angle bracket */ - map.put((char)9002, "rang"); /* right-pointing angle bracket */ - map.put((char)9674, "loz"); /* lozenge */ - map.put((char)9824, "spades"); /* black spade suit */ - map.put((char)9827, "clubs"); /* black club suit */ - map.put((char)9829, "hearts"); /* black heart suit */ - map.put((char)9830, "diams"); /* black diamond suit */ + map.put(34, "quot"); /* quotation mark */ + map.put(38, "amp"); /* ampersand */ + map.put(60, "lt"); /* less-than sign */ + map.put(62, "gt"); /* greater-than sign */ + map.put(160, "nbsp"); /* no-break space */ + map.put(161, "iexcl"); /* inverted exclamation mark */ + map.put(162, "cent"); /* cent sign */ + map.put(163, "pound"); /* pound sign */ + map.put(164, "curren"); /* currency sign */ + map.put(165, "yen"); /* yen sign */ + map.put(166, "brvbar"); /* broken bar */ + map.put(167, "sect"); /* section sign */ + map.put(168, "uml"); /* diaeresis */ + map.put(169, "copy"); /* copyright sign */ + map.put(170, "ordf"); /* feminine ordinal indicator */ + map.put(171, "laquo"); /* left-pointing double angle quotation mark */ + map.put(172, "not"); /* not sign */ + map.put(173, "shy"); /* soft hyphen */ + map.put(174, "reg"); /* registered sign */ + map.put(175, "macr"); /* macron */ + map.put(176, "deg"); /* degree sign */ + map.put(177, "plusmn"); /* plus-minus sign */ + map.put(178, "sup2"); /* superscript two */ + map.put(179, "sup3"); /* superscript three */ + map.put(180, "acute"); /* acute accent */ + map.put(181, "micro"); /* micro sign */ + map.put(182, "para"); /* pilcrow sign */ + map.put(183, "middot"); /* middle dot */ + map.put(184, "cedil"); /* cedilla */ + map.put(185, "sup1"); /* superscript one */ + map.put(186, "ordm"); /* masculine ordinal indicator */ + map.put(187, "raquo"); /* right-pointing double angle quotation mark */ + map.put(188, "frac14"); /* vulgar fraction one quarter */ + map.put(189, "frac12"); /* vulgar fraction one half */ + map.put(190, "frac34"); /* vulgar fraction three quarters */ + map.put(191, "iquest"); /* inverted question mark */ + map.put(192, "Agrave"); /* Latin capital letter a with grave */ + map.put(193, "Aacute"); /* Latin capital letter a with acute */ + map.put(194, "Acirc"); /* Latin capital letter a with circumflex */ + map.put(195, "Atilde"); /* Latin capital letter a with tilde */ + map.put(196, "Auml"); /* Latin capital letter a with diaeresis */ + map.put(197, "Aring"); /* Latin capital letter a with ring above */ + map.put(198, "AElig"); /* Latin capital letter ae */ + map.put(199, "Ccedil"); /* Latin capital letter c with cedilla */ + map.put(200, "Egrave"); /* Latin capital letter e with grave */ + map.put(201, "Eacute"); /* Latin capital letter e with acute */ + map.put(202, "Ecirc"); /* Latin capital letter e with circumflex */ + map.put(203, "Euml"); /* Latin capital letter e with diaeresis */ + map.put(204, "Igrave"); /* Latin capital letter i with grave */ + map.put(205, "Iacute"); /* Latin capital letter i with acute */ + map.put(206, "Icirc"); /* Latin capital letter i with circumflex */ + map.put(207, "Iuml"); /* Latin capital letter i with diaeresis */ + map.put(208, "ETH"); /* Latin capital letter eth */ + map.put(209, "Ntilde"); /* Latin capital letter n with tilde */ + map.put(210, "Ograve"); /* Latin capital letter o with grave */ + map.put(211, "Oacute"); /* Latin capital letter o with acute */ + map.put(212, "Ocirc"); /* Latin capital letter o with circumflex */ + map.put(213, "Otilde"); /* Latin capital letter o with tilde */ + map.put(214, "Ouml"); /* Latin capital letter o with diaeresis */ + map.put(215, "times"); /* multiplication sign */ + map.put(216, "Oslash"); /* Latin capital letter o with stroke */ + map.put(217, "Ugrave"); /* Latin capital letter u with grave */ + map.put(218, "Uacute"); /* Latin capital letter u with acute */ + map.put(219, "Ucirc"); /* Latin capital letter u with circumflex */ + map.put(220, "Uuml"); /* Latin capital letter u with diaeresis */ + map.put(221, "Yacute"); /* Latin capital letter y with acute */ + map.put(222, "THORN"); /* Latin capital letter thorn */ + map.put(223, "szlig"); /* Latin small letter sharp sXCOMMAX German Eszett */ + map.put(224, "agrave"); /* Latin small letter a with grave */ + map.put(225, "aacute"); /* Latin small letter a with acute */ + map.put(226, "acirc"); /* Latin small letter a with circumflex */ + map.put(227, "atilde"); /* Latin small letter a with tilde */ + map.put(228, "auml"); /* Latin small letter a with diaeresis */ + map.put(229, "aring"); /* Latin small letter a with ring above */ + map.put(230, "aelig"); /* Latin lowercase ligature ae */ + map.put(231, "ccedil"); /* Latin small letter c with cedilla */ + map.put(232, "egrave"); /* Latin small letter e with grave */ + map.put(233, "eacute"); /* Latin small letter e with acute */ + map.put(234, "ecirc"); /* Latin small letter e with circumflex */ + map.put(235, "euml"); /* Latin small letter e with diaeresis */ + map.put(236, "igrave"); /* Latin small letter i with grave */ + map.put(237, "iacute"); /* Latin small letter i with acute */ + map.put(238, "icirc"); /* Latin small letter i with circumflex */ + map.put(239, "iuml"); /* Latin small letter i with diaeresis */ + map.put(240, "eth"); /* Latin small letter eth */ + map.put(241, "ntilde"); /* Latin small letter n with tilde */ + map.put(242, "ograve"); /* Latin small letter o with grave */ + map.put(243, "oacute"); /* Latin small letter o with acute */ + map.put(244, "ocirc"); /* Latin small letter o with circumflex */ + map.put(245, "otilde"); /* Latin small letter o with tilde */ + map.put(246, "ouml"); /* Latin small letter o with diaeresis */ + map.put(247, "divide"); /* division sign */ + map.put(248, "oslash"); /* Latin small letter o with stroke */ + map.put(249, "ugrave"); /* Latin small letter u with grave */ + map.put(250, "uacute"); /* Latin small letter u with acute */ + map.put(251, "ucirc"); /* Latin small letter u with circumflex */ + map.put(252, "uuml"); /* Latin small letter u with diaeresis */ + map.put(253, "yacute"); /* Latin small letter y with acute */ + map.put(254, "thorn"); /* Latin small letter thorn */ + map.put(255, "yuml"); /* Latin small letter y with diaeresis */ + map.put(338, "OElig"); /* Latin capital ligature oe */ + map.put(339, "oelig"); /* Latin small ligature oe */ + map.put(352, "Scaron"); /* Latin capital letter s with caron */ + map.put(353, "scaron"); /* Latin small letter s with caron */ + map.put(376, "Yuml"); /* Latin capital letter y with diaeresis */ + map.put(402, "fnof"); /* Latin small letter f with hook */ + map.put(710, "circ"); /* modifier letter circumflex accent */ + map.put(732, "tilde"); /* small tilde */ + map.put(913, "Alpha"); /* Greek capital letter alpha */ + map.put(914, "Beta"); /* Greek capital letter beta */ + map.put(915, "Gamma"); /* Greek capital letter gamma */ + map.put(916, "Delta"); /* Greek capital letter delta */ + map.put(917, "Epsilon"); /* Greek capital letter epsilon */ + map.put(918, "Zeta"); /* Greek capital letter zeta */ + map.put(919, "Eta"); /* Greek capital letter eta */ + map.put(920, "Theta"); /* Greek capital letter theta */ + map.put(921, "Iota"); /* Greek capital letter iota */ + map.put(922, "Kappa"); /* Greek capital letter kappa */ + map.put(923, "Lambda"); /* Greek capital letter lambda */ + map.put(924, "Mu"); /* Greek capital letter mu */ + map.put(925, "Nu"); /* Greek capital letter nu */ + map.put(926, "Xi"); /* Greek capital letter xi */ + map.put(927, "Omicron"); /* Greek capital letter omicron */ + map.put(928, "Pi"); /* Greek capital letter pi */ + map.put(929, "Rho"); /* Greek capital letter rho */ + map.put(931, "Sigma"); /* Greek capital letter sigma */ + map.put(932, "Tau"); /* Greek capital letter tau */ + map.put(933, "Upsilon"); /* Greek capital letter upsilon */ + map.put(934, "Phi"); /* Greek capital letter phi */ + map.put(935, "Chi"); /* Greek capital letter chi */ + map.put(936, "Psi"); /* Greek capital letter psi */ + map.put(937, "Omega"); /* Greek capital letter omega */ + map.put(945, "alpha"); /* Greek small letter alpha */ + map.put(946, "beta"); /* Greek small letter beta */ + map.put(947, "gamma"); /* Greek small letter gamma */ + map.put(948, "delta"); /* Greek small letter delta */ + map.put(949, "epsilon"); /* Greek small letter epsilon */ + map.put(950, "zeta"); /* Greek small letter zeta */ + map.put(951, "eta"); /* Greek small letter eta */ + map.put(952, "theta"); /* Greek small letter theta */ + map.put(953, "iota"); /* Greek small letter iota */ + map.put(954, "kappa"); /* Greek small letter kappa */ + map.put(955, "lambda"); /* Greek small letter lambda */ + map.put(956, "mu"); /* Greek small letter mu */ + map.put(957, "nu"); /* Greek small letter nu */ + map.put(958, "xi"); /* Greek small letter xi */ + map.put(959, "omicron"); /* Greek small letter omicron */ + map.put(960, "pi"); /* Greek small letter pi */ + map.put(961, "rho"); /* Greek small letter rho */ + map.put(962, "sigmaf"); /* Greek small letter final sigma */ + map.put(963, "sigma"); /* Greek small letter sigma */ + map.put(964, "tau"); /* Greek small letter tau */ + map.put(965, "upsilon"); /* Greek small letter upsilon */ + map.put(966, "phi"); /* Greek small letter phi */ + map.put(967, "chi"); /* Greek small letter chi */ + map.put(968, "psi"); /* Greek small letter psi */ + map.put(969, "omega"); /* Greek small letter omega */ + map.put(977, "thetasym"); /* Greek theta symbol */ + map.put(978, "upsih"); /* Greek upsilon with hook symbol */ + map.put(982, "piv"); /* Greek pi symbol */ + map.put(8194, "ensp"); /* en space */ + map.put(8195, "emsp"); /* em space */ + map.put(8201, "thinsp"); /* thin space */ + map.put(8204, "zwnj"); /* zero width non-joiner */ + map.put(8205, "zwj"); /* zero width joiner */ + map.put(8206, "lrm"); /* left-to-right mark */ + map.put(8207, "rlm"); /* right-to-left mark */ + map.put(8211, "ndash"); /* en dash */ + map.put(8212, "mdash"); /* em dash */ + map.put(8216, "lsquo"); /* left single quotation mark */ + map.put(8217, "rsquo"); /* right single quotation mark */ + map.put(8218, "sbquo"); /* single low-9 quotation mark */ + map.put(8220, "ldquo"); /* left double quotation mark */ + map.put(8221, "rdquo"); /* right double quotation mark */ + map.put(8222, "bdquo"); /* double low-9 quotation mark */ + map.put(8224, "dagger"); /* dagger */ + map.put(8225, "Dagger"); /* double dagger */ + map.put(8226, "bull"); /* bullet */ + map.put(8230, "hellip"); /* horizontal ellipsis */ + map.put(8240, "permil"); /* per mille sign */ + map.put(8242, "prime"); /* prime */ + map.put(8243, "Prime"); /* double prime */ + map.put(8249, "lsaquo"); /* single left-pointing angle quotation mark */ + map.put(8250, "rsaquo"); /* single right-pointing angle quotation mark */ + map.put(8254, "oline"); /* overline */ + map.put(8260, "frasl"); /* fraction slash */ + map.put(8364, "euro"); /* euro sign */ + map.put(8465, "image"); /* black-letter capital i */ + map.put(8472, "weierp"); /* script capital pXCOMMAX Weierstrass p */ + map.put(8476, "real"); /* black-letter capital r */ + map.put(8482, "trade"); /* trademark sign */ + map.put(8501, "alefsym"); /* alef symbol */ + map.put(8592, "larr"); /* leftwards arrow */ + map.put(8593, "uarr"); /* upwards arrow */ + map.put(8594, "rarr"); /* rightwards arrow */ + map.put(8595, "darr"); /* downwards arrow */ + map.put(8596, "harr"); /* left right arrow */ + map.put(8629, "crarr"); /* downwards arrow with corner leftwards */ + map.put(8656, "lArr"); /* leftwards double arrow */ + map.put(8657, "uArr"); /* upwards double arrow */ + map.put(8658, "rArr"); /* rightwards double arrow */ + map.put(8659, "dArr"); /* downwards double arrow */ + map.put(8660, "hArr"); /* left right double arrow */ + map.put(8704, "forall"); /* for all */ + map.put(8706, "part"); /* partial differential */ + map.put(8707, "exist"); /* there exists */ + map.put(8709, "empty"); /* empty set */ + map.put(8711, "nabla"); /* nabla */ + map.put(8712, "isin"); /* element of */ + map.put(8713, "notin"); /* not an element of */ + map.put(8715, "ni"); /* contains as member */ + map.put(8719, "prod"); /* n-ary product */ + map.put(8721, "sum"); /* n-ary summation */ + map.put(8722, "minus"); /* minus sign */ + map.put(8727, "lowast"); /* asterisk operator */ + map.put(8730, "radic"); /* square root */ + map.put(8733, "prop"); /* proportional to */ + map.put(8734, "infin"); /* infinity */ + map.put(8736, "ang"); /* angle */ + map.put(8743, "and"); /* logical and */ + map.put(8744, "or"); /* logical or */ + map.put(8745, "cap"); /* intersection */ + map.put(8746, "cup"); /* union */ + map.put(8747, "int"); /* integral */ + map.put(8756, "there4"); /* therefore */ + map.put(8764, "sim"); /* tilde operator */ + map.put(8773, "cong"); /* congruent to */ + map.put(8776, "asymp"); /* almost equal to */ + map.put(8800, "ne"); /* not equal to */ + map.put(8801, "equiv"); /* identical toXCOMMAX equivalent to */ + map.put(8804, "le"); /* less-than or equal to */ + map.put(8805, "ge"); /* greater-than or equal to */ + map.put(8834, "sub"); /* subset of */ + map.put(8835, "sup"); /* superset of */ + map.put(8836, "nsub"); /* not a subset of */ + map.put(8838, "sube"); /* subset of or equal to */ + map.put(8839, "supe"); /* superset of or equal to */ + map.put(8853, "oplus"); /* circled plus */ + map.put(8855, "otimes"); /* circled times */ + map.put(8869, "perp"); /* up tack */ + map.put(8901, "sdot"); /* dot operator */ + map.put(8968, "lceil"); /* left ceiling */ + map.put(8969, "rceil"); /* right ceiling */ + map.put(8970, "lfloor"); /* left floor */ + map.put(8971, "rfloor"); /* right floor */ + map.put(9001, "lang"); /* left-pointing angle bracket */ + map.put(9002, "rang"); /* right-pointing angle bracket */ + map.put(9674, "loz"); /* lozenge */ + map.put(9824, "spades"); /* black spade suit */ + map.put(9827, "clubs"); /* black club suit */ + map.put(9829, "hearts"); /* black heart suit */ + map.put(9830, "diams"); /* black diamond suit */ return Collections.unmodifiableMap(map); } @@ -584,11 +585,11 @@ private static synchronized Map mkCharacterToEntityMap() * Build a unmodifiable Trie from entitiy Name to Character * @return Unmodifiable trie. */ - private static synchronized Trie mkEntityToCharacterTrie() + private static synchronized Trie mkEntityToCharacterTrie() { - Trie trie = new HashTrie(); + Trie trie = new HashTrie(); - for(Map.Entry entry : characterToEntityMap.entrySet()) + for(Map.Entry entry : characterToEntityMap.entrySet()) trie.put(entry.getValue(),entry.getKey()); return Trie.Util.unmodifiable(trie); } diff --git a/src/main/java/org/owasp/esapi/codecs/JavaScriptCodec.java b/src/main/java/org/owasp/esapi/codecs/JavaScriptCodec.java index 98c38c7b2..a10d9b588 100644 --- a/src/main/java/org/owasp/esapi/codecs/JavaScriptCodec.java +++ b/src/main/java/org/owasp/esapi/codecs/JavaScriptCodec.java @@ -24,7 +24,7 @@ * @since June 1, 2007 * @see org.owasp.esapi.Encoder */ -public class JavaScriptCodec extends AbstractCodec { +public class JavaScriptCodec extends AbstractCharacterCodec { /** diff --git a/src/main/java/org/owasp/esapi/codecs/MySQLCodec.java b/src/main/java/org/owasp/esapi/codecs/MySQLCodec.java index e43f13ca2..6552d089f 100644 --- a/src/main/java/org/owasp/esapi/codecs/MySQLCodec.java +++ b/src/main/java/org/owasp/esapi/codecs/MySQLCodec.java @@ -26,7 +26,7 @@ * @since June 1, 2007 * @see org.owasp.esapi.Encoder */ -public class MySQLCodec extends AbstractCodec { +public class MySQLCodec extends AbstractCharacterCodec { /** * Specifies the SQL Mode the target MySQL Server is running with. For details about MySQL Server Modes * please see the Manual at {@link http://dev.mysql.com/doc/refman/5.0/en/server-sql-mode.html#sqlmode_ansi} diff --git a/src/main/java/org/owasp/esapi/codecs/OracleCodec.java b/src/main/java/org/owasp/esapi/codecs/OracleCodec.java index 06cca0609..ff87ea8c0 100644 --- a/src/main/java/org/owasp/esapi/codecs/OracleCodec.java +++ b/src/main/java/org/owasp/esapi/codecs/OracleCodec.java @@ -30,7 +30,7 @@ * @since June 1, 2007 * @see org.owasp.esapi.Encoder */ -public class OracleCodec extends AbstractCodec { +public class OracleCodec extends AbstractCharacterCodec { /** diff --git a/src/main/java/org/owasp/esapi/codecs/PercentCodec.java b/src/main/java/org/owasp/esapi/codecs/PercentCodec.java index ed7e65dd8..6b3d5d0a5 100644 --- a/src/main/java/org/owasp/esapi/codecs/PercentCodec.java +++ b/src/main/java/org/owasp/esapi/codecs/PercentCodec.java @@ -28,7 +28,7 @@ * @since June 1, 2007 * @see org.owasp.esapi.Encoder */ -public class PercentCodec extends AbstractCodec +public class PercentCodec extends AbstractCharacterCodec { private static final String ALPHA_NUMERIC_STR = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; @SuppressWarnings("unused") diff --git a/src/main/java/org/owasp/esapi/codecs/PushBackSequenceImpl.java b/src/main/java/org/owasp/esapi/codecs/PushBackSequenceImpl.java index ea31e88d6..c10f4de22 100644 --- a/src/main/java/org/owasp/esapi/codecs/PushBackSequenceImpl.java +++ b/src/main/java/org/owasp/esapi/codecs/PushBackSequenceImpl.java @@ -126,7 +126,7 @@ public void reset() { * * @return */ - protected String remainder() { + public String remainder() { String output = input.substring( index ); if ( pushback != null ) { output = pushback + output; diff --git a/src/main/java/org/owasp/esapi/codecs/PushbackSequence.java b/src/main/java/org/owasp/esapi/codecs/PushbackSequence.java index 58fe4a930..5588ca91f 100644 --- a/src/main/java/org/owasp/esapi/codecs/PushbackSequence.java +++ b/src/main/java/org/owasp/esapi/codecs/PushbackSequence.java @@ -61,4 +61,12 @@ public interface PushbackSequence { */ void reset(); + /** + * Not at all sure what this method is intended to do. There + * is a line in HTMLEntityCodec that said calling this method + * is a "kludge around PushbackString..." + * @return + */ + String remainder(); + } \ No newline at end of file diff --git a/src/main/java/org/owasp/esapi/codecs/PushbackString.java b/src/main/java/org/owasp/esapi/codecs/PushbackString.java index 9982d48e2..4255045f9 100644 --- a/src/main/java/org/owasp/esapi/codecs/PushbackString.java +++ b/src/main/java/org/owasp/esapi/codecs/PushbackString.java @@ -193,7 +193,7 @@ public void reset() { * * @return */ - protected String remainder() { + public String remainder() { String output = input.substring(index); if (pushback != null) { output = pushback + output; diff --git a/src/main/java/org/owasp/esapi/codecs/UnixCodec.java b/src/main/java/org/owasp/esapi/codecs/UnixCodec.java index 3381e82f0..da2dddc0b 100644 --- a/src/main/java/org/owasp/esapi/codecs/UnixCodec.java +++ b/src/main/java/org/owasp/esapi/codecs/UnixCodec.java @@ -24,7 +24,7 @@ * @since June 1, 2007 * @see org.owasp.esapi.Encoder */ -public class UnixCodec extends AbstractCodec { +public class UnixCodec extends AbstractCharacterCodec { /** * {@inheritDoc} diff --git a/src/main/java/org/owasp/esapi/codecs/VBScriptCodec.java b/src/main/java/org/owasp/esapi/codecs/VBScriptCodec.java index 85ce820e9..ae7f1f6ac 100644 --- a/src/main/java/org/owasp/esapi/codecs/VBScriptCodec.java +++ b/src/main/java/org/owasp/esapi/codecs/VBScriptCodec.java @@ -26,7 +26,7 @@ * @since June 1, 2007 * @see org.owasp.esapi.Encoder */ -public class VBScriptCodec extends AbstractCodec { +public class VBScriptCodec extends AbstractCharacterCodec { /** * Encode a String so that it can be safely used in a specific context. diff --git a/src/main/java/org/owasp/esapi/codecs/WindowsCodec.java b/src/main/java/org/owasp/esapi/codecs/WindowsCodec.java index bcc53f626..c0a5540ea 100644 --- a/src/main/java/org/owasp/esapi/codecs/WindowsCodec.java +++ b/src/main/java/org/owasp/esapi/codecs/WindowsCodec.java @@ -24,7 +24,7 @@ * @since June 1, 2007 * @see org.owasp.esapi.Encoder */ -public class WindowsCodec extends AbstractCodec { +public class WindowsCodec extends AbstractCharacterCodec { /** diff --git a/src/main/java/org/owasp/esapi/codecs/XMLEntityCodec.java b/src/main/java/org/owasp/esapi/codecs/XMLEntityCodec.java index 25b4ffbc4..45482adae 100644 --- a/src/main/java/org/owasp/esapi/codecs/XMLEntityCodec.java +++ b/src/main/java/org/owasp/esapi/codecs/XMLEntityCodec.java @@ -41,7 +41,7 @@ * of knowing about. Decoding is included for completeness but it's use * is not recommended. Use a XML parser instead! */ -public class XMLEntityCodec extends AbstractCodec +public class XMLEntityCodec extends AbstractCharacterCodec { private static final String ALPHA_NUMERIC_STR = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; private static final String UNENCODED_STR = ALPHA_NUMERIC_STR + " \t"; diff --git a/src/test/java/org/owasp/esapi/codecs/AbstractCodecTest.java b/src/test/java/org/owasp/esapi/codecs/AbstractCodecTest.java index 1472e5dfb..9e0a5477b 100644 --- a/src/test/java/org/owasp/esapi/codecs/AbstractCodecTest.java +++ b/src/test/java/org/owasp/esapi/codecs/AbstractCodecTest.java @@ -574,7 +574,7 @@ public void testWindowsDecode() public void testHtmlDecodeCharLessThan() { - assertEquals( LESS_THAN, htmlCodec.decodeCharacter(new PushbackString("<")) ); + assertEquals( LESS_THAN, htmlCodec.decodeCharacter(new PushBackSequenceImpl("<")) ); } public void testPercentDecodeChar() diff --git a/src/test/java/org/owasp/esapi/reference/EncoderTest.java b/src/test/java/org/owasp/esapi/reference/EncoderTest.java index 896a93661..125e8ed48 100644 --- a/src/test/java/org/owasp/esapi/reference/EncoderTest.java +++ b/src/test/java/org/owasp/esapi/reference/EncoderTest.java @@ -15,18 +15,15 @@ */ package org.owasp.esapi.reference; +import java.io.ByteArrayOutputStream; import java.io.IOException; +import java.io.ObjectOutputStream; import java.io.UnsupportedEncodingException; import java.net.URI; import java.util.ArrayList; import java.util.Arrays; -import java.io.ByteArrayOutputStream; -import java.io.ObjectOutputStream; - -import junit.framework.Test; -import junit.framework.TestCase; -import junit.framework.TestSuite; +import org.junit.Ignore; import org.owasp.esapi.ESAPI; import org.owasp.esapi.Encoder; import org.owasp.esapi.EncoderConstants; @@ -41,6 +38,10 @@ import org.owasp.esapi.errors.EncodingException; import org.owasp.esapi.errors.IntrusionException; +import junit.framework.Test; +import junit.framework.TestCase; +import junit.framework.TestSuite; + /** * The Class EncoderTest. * @@ -91,6 +92,8 @@ public static Test suite() { * * @throws EncodingException */ + //FIXME: Remove @Ignore + @Ignore public void testCanonicalize() throws EncodingException { System.out.println("canonicalize"); @@ -720,7 +723,7 @@ public void testWindowsCodec() { System.out.println("WindowsCodec"); Encoder instance = ESAPI.encoder(); - Codec win = new WindowsCodec(); + Codec win = new WindowsCodec(); char[] immune = new char[0]; assertEquals(null, instance.encodeForOS(win, null)); @@ -754,7 +757,7 @@ public void testUnixCodec() { System.out.println("UnixCodec"); Encoder instance = ESAPI.encoder(); - Codec unix = new UnixCodec(); + Codec unix = new UnixCodec(); char[] immune = new char[0]; assertEquals(null, instance.encodeForOS(unix, null)); From 914edd7584fa9bfa1e5a307787e64d82318a2aee Mon Sep 17 00:00:00 2001 From: Matt Seil Date: Sun, 6 Aug 2017 09:55:22 -0700 Subject: [PATCH 0399/1069] Issue #300 -- Fixed a tricky polymorphism bug with Jeremiah Stacey's help. Added a unit test for PercentCodec. --- .../org/owasp/esapi/codecs/JavaScriptCodec.java | 2 +- .../java/org/owasp/esapi/codecs/MySQLCodec.java | 2 +- .../java/org/owasp/esapi/codecs/OracleCodec.java | 2 +- .../org/owasp/esapi/codecs/PercentCodec.java | 2 +- .../java/org/owasp/esapi/codecs/UnixCodec.java | 2 +- .../org/owasp/esapi/codecs/VBScriptCodec.java | 2 +- .../org/owasp/esapi/codecs/WindowsCodec.java | 2 +- .../org/owasp/esapi/codecs/PercentCodecTest.java | 16 ++++++++++++++++ 8 files changed, 23 insertions(+), 7 deletions(-) create mode 100644 src/test/java/org/owasp/esapi/codecs/PercentCodecTest.java diff --git a/src/main/java/org/owasp/esapi/codecs/JavaScriptCodec.java b/src/main/java/org/owasp/esapi/codecs/JavaScriptCodec.java index a10d9b588..a43cbd004 100644 --- a/src/main/java/org/owasp/esapi/codecs/JavaScriptCodec.java +++ b/src/main/java/org/owasp/esapi/codecs/JavaScriptCodec.java @@ -87,7 +87,7 @@ public String encodeCharacter( char[] immune, Character c ) { * \\uHHHH * \\OOO (1, 2, or 3 digits) */ - public Character decodeCharacter( PushbackString input ) { + public Character decodeCharacter( PushbackSequence input ) { input.mark(); Character first = input.next(); if ( first == null ) { diff --git a/src/main/java/org/owasp/esapi/codecs/MySQLCodec.java b/src/main/java/org/owasp/esapi/codecs/MySQLCodec.java index 6552d089f..1bc902342 100644 --- a/src/main/java/org/owasp/esapi/codecs/MySQLCodec.java +++ b/src/main/java/org/owasp/esapi/codecs/MySQLCodec.java @@ -162,7 +162,7 @@ private String encodeCharacterMySQL( Character c ) { * In ANSI_MODE '' decodes to ' * In MYSQL_MODE \x decodes to x (or a small list of specials) */ - public Character decodeCharacter( PushbackString input ) { + public Character decodeCharacter( PushbackSequence input ) { switch( mode ) { case ANSI: return decodeCharacterANSI( input ); case STANDARD: return decodeCharacterMySQL( input ); diff --git a/src/main/java/org/owasp/esapi/codecs/OracleCodec.java b/src/main/java/org/owasp/esapi/codecs/OracleCodec.java index ff87ea8c0..0021a8613 100644 --- a/src/main/java/org/owasp/esapi/codecs/OracleCodec.java +++ b/src/main/java/org/owasp/esapi/codecs/OracleCodec.java @@ -59,7 +59,7 @@ public String encodeCharacter( char[] immune, Character c ) { * Formats all are legal * '' decodes to ' */ - public Character decodeCharacter( PushbackString input ) { + public Character decodeCharacter( PushbackSequence input ) { input.mark(); Character first = input.next(); if ( first == null ) { diff --git a/src/main/java/org/owasp/esapi/codecs/PercentCodec.java b/src/main/java/org/owasp/esapi/codecs/PercentCodec.java index 6b3d5d0a5..ac9e9a59e 100644 --- a/src/main/java/org/owasp/esapi/codecs/PercentCodec.java +++ b/src/main/java/org/owasp/esapi/codecs/PercentCodec.java @@ -128,7 +128,7 @@ public String encodeCharacter( char[] immune, Character c ) * @param input * encoded character using percent characters (such as URL encoding) */ - public Character decodeCharacter( PushbackString input ) { + public Character decodeCharacter( PushbackSequence input ) { input.mark(); Character first = input.next(); if ( first == null ) { diff --git a/src/main/java/org/owasp/esapi/codecs/UnixCodec.java b/src/main/java/org/owasp/esapi/codecs/UnixCodec.java index da2dddc0b..faeebad65 100644 --- a/src/main/java/org/owasp/esapi/codecs/UnixCodec.java +++ b/src/main/java/org/owasp/esapi/codecs/UnixCodec.java @@ -61,7 +61,7 @@ public String encodeCharacter( char[] immune, Character c ) { * \x - all special characters * */ - public Character decodeCharacter( PushbackString input ) { + public Character decodeCharacter( PushbackSequence input ) { input.mark(); Character first = input.next(); if ( first == null ) { diff --git a/src/main/java/org/owasp/esapi/codecs/VBScriptCodec.java b/src/main/java/org/owasp/esapi/codecs/VBScriptCodec.java index ae7f1f6ac..122e90aad 100644 --- a/src/main/java/org/owasp/esapi/codecs/VBScriptCodec.java +++ b/src/main/java/org/owasp/esapi/codecs/VBScriptCodec.java @@ -96,7 +96,7 @@ public String encodeCharacter( char[] immune, Character c ) { * "x - all special characters * " + chr(x) + " - not supported yet */ - public Character decodeCharacter( PushbackString input ) { + public Character decodeCharacter( PushbackSequence input ) { input.mark(); Character first = input.next(); if ( first == null ) { diff --git a/src/main/java/org/owasp/esapi/codecs/WindowsCodec.java b/src/main/java/org/owasp/esapi/codecs/WindowsCodec.java index c0a5540ea..be22640ad 100644 --- a/src/main/java/org/owasp/esapi/codecs/WindowsCodec.java +++ b/src/main/java/org/owasp/esapi/codecs/WindowsCodec.java @@ -61,7 +61,7 @@ public String encodeCharacter( char[] immune, Character c ) { * Formats all are legal both upper/lower case: * ^x - all special characters */ - public Character decodeCharacter( PushbackString input ) { + public Character decodeCharacter( PushbackSequence input ) { input.mark(); Character first = input.next(); if ( first == null ) { diff --git a/src/test/java/org/owasp/esapi/codecs/PercentCodecTest.java b/src/test/java/org/owasp/esapi/codecs/PercentCodecTest.java new file mode 100644 index 000000000..c1b2b7ad8 --- /dev/null +++ b/src/test/java/org/owasp/esapi/codecs/PercentCodecTest.java @@ -0,0 +1,16 @@ +package org.owasp.esapi.codecs; + +import static org.junit.Assert.assertEquals; + +import org.junit.Test; + +public class PercentCodecTest { + + @Test + public void testPercentDecode(){ + Codec codec = new PercentCodec(); + + String expected = " "; + assertEquals(expected, codec.decode("%20")); + } +} From 946158b501d5d17c52cc5bbf88dedc7888fe59f2 Mon Sep 17 00:00:00 2001 From: Matt Seil Date: Sun, 6 Aug 2017 11:04:45 -0700 Subject: [PATCH 0400/1069] Issue #300 -- We need to get more data driven unit tests, but this issue is now completely whacked! --- .../org/owasp/esapi/codecs/HTMLEntityCodec.java | 9 ++++++--- .../java/org/owasp/esapi/codecs/MySQLCodec.java | 4 ++-- .../org/owasp/esapi/codecs/XMLEntityCodec.java | 10 +++++----- .../owasp/esapi/codecs/AbstractCodecTest.java | 5 ++++- .../owasp/esapi/codecs/HTMLEntityCodecTest.java | 17 +++++++++++++++++ .../org/owasp/esapi/reference/EncoderTest.java | 2 -- 6 files changed, 34 insertions(+), 13 deletions(-) create mode 100644 src/test/java/org/owasp/esapi/codecs/HTMLEntityCodecTest.java diff --git a/src/main/java/org/owasp/esapi/codecs/HTMLEntityCodec.java b/src/main/java/org/owasp/esapi/codecs/HTMLEntityCodec.java index 4adfdf84c..14564372f 100644 --- a/src/main/java/org/owasp/esapi/codecs/HTMLEntityCodec.java +++ b/src/main/java/org/owasp/esapi/codecs/HTMLEntityCodec.java @@ -286,9 +286,12 @@ private Integer getNamedEntity( PushbackSequence input ) { // kludge around PushbackString.... len = Math.min(input.remainder().length(), entityToCharacterTrie.getMaxKeyLength()); - for(int i=0;i input ) { * @return * A single character, decoded */ - private Character decodeCharacterANSI( PushbackString input ) { + private Character decodeCharacterANSI( PushbackSequence input ) { input.mark(); Character first = input.next(); if ( first == null ) { @@ -214,7 +214,7 @@ private Character decodeCharacterANSI( PushbackString input ) { * @return * A single character from that string, decoded. */ - private Character decodeCharacterMySQL( PushbackString input ) { + private Character decodeCharacterMySQL( PushbackSequence input ) { input.mark(); Character first = input.next(); if ( first == null ) { diff --git a/src/main/java/org/owasp/esapi/codecs/XMLEntityCodec.java b/src/main/java/org/owasp/esapi/codecs/XMLEntityCodec.java index 45482adae..418f4c5a7 100644 --- a/src/main/java/org/owasp/esapi/codecs/XMLEntityCodec.java +++ b/src/main/java/org/owasp/esapi/codecs/XMLEntityCodec.java @@ -91,7 +91,7 @@ public String encodeCharacter(char[] immune, Character c) *

  • &name;
  • *
*/ - public Character decodeCharacter(PushbackString input) + public Character decodeCharacter(PushbackSequence input) { Character ret = null; Character first; @@ -137,7 +137,7 @@ else if(Character.isLetter(second.charValue())) * is positioned at the character after the &# * @return The character decoded or null on failure. */ - private static Character getNumericEntity(PushbackString input) + private static Character getNumericEntity(PushbackSequence input) { Character first = input.peek(); @@ -174,7 +174,7 @@ private static Character int2char(int i) * the next char is not a 'x' or 'X'. * @return The character decoded or null on failutre. */ - private static Character parseNumber(PushbackString input) + private static Character parseNumber(PushbackSequence input) { StringBuilder sb = new StringBuilder(); Character c; @@ -209,7 +209,7 @@ private static Character parseNumber(PushbackString input) * is positioned at the character after the &#[xX] * @return The character decoded or null on failutre. */ - private static Character parseHex(PushbackString input) + private static Character parseHex(PushbackSequence input) { Character c; StringBuilder sb = new StringBuilder(); @@ -268,7 +268,7 @@ private static Character parseHex(PushbackString input) * is positioned at the character after the &. * @return The character decoded or null on failutre. */ - private Character getNamedEntity(PushbackString input) + private Character getNamedEntity(PushbackSequence input) { StringBuilder possible = new StringBuilder(); Map.Entry entry; diff --git a/src/test/java/org/owasp/esapi/codecs/AbstractCodecTest.java b/src/test/java/org/owasp/esapi/codecs/AbstractCodecTest.java index 9e0a5477b..1456058bd 100644 --- a/src/test/java/org/owasp/esapi/codecs/AbstractCodecTest.java +++ b/src/test/java/org/owasp/esapi/codecs/AbstractCodecTest.java @@ -574,7 +574,10 @@ public void testWindowsDecode() public void testHtmlDecodeCharLessThan() { - assertEquals( LESS_THAN, htmlCodec.decodeCharacter(new PushBackSequenceImpl("<")) ); + Integer value = htmlCodec.decodeCharacter(new PushBackSequenceImpl("<")); + assertEquals(new Integer(60), value); + StringBuilder sb = new StringBuilder().appendCodePoint(value); + assertEquals( LESS_THAN.toString(), sb.toString()); } public void testPercentDecodeChar() diff --git a/src/test/java/org/owasp/esapi/codecs/HTMLEntityCodecTest.java b/src/test/java/org/owasp/esapi/codecs/HTMLEntityCodecTest.java new file mode 100644 index 000000000..a12f287cd --- /dev/null +++ b/src/test/java/org/owasp/esapi/codecs/HTMLEntityCodecTest.java @@ -0,0 +1,17 @@ +package org.owasp.esapi.codecs; + +import static org.junit.Assert.assertEquals; + +import org.junit.Test; + +public class HTMLEntityCodecTest { + Codec codec = new HTMLEntityCodec(); + + @Test + public void testEntityDecoding(){ + assertEquals("<", codec.decode("<")); + assertEquals( "<", codec.decode("<")); + assertEquals( "<", codec.decode("<")); + assertEquals( "<", codec.decode("<")); + } +} diff --git a/src/test/java/org/owasp/esapi/reference/EncoderTest.java b/src/test/java/org/owasp/esapi/reference/EncoderTest.java index 125e8ed48..64bb88ac9 100644 --- a/src/test/java/org/owasp/esapi/reference/EncoderTest.java +++ b/src/test/java/org/owasp/esapi/reference/EncoderTest.java @@ -92,8 +92,6 @@ public static Test suite() { * * @throws EncodingException */ - //FIXME: Remove @Ignore - @Ignore public void testCanonicalize() throws EncodingException { System.out.println("canonicalize"); From 62c5100f4654eab3df7191c90e458120fff71133 Mon Sep 17 00:00:00 2001 From: Matt Seil Date: Sun, 6 Aug 2017 11:29:49 -0700 Subject: [PATCH 0401/1069] Issue #303 -- Added unit test to prove that this issue is resolved. --- .../java/org/owasp/esapi/codecs/HTMLEntityCodecTest.java | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/test/java/org/owasp/esapi/codecs/HTMLEntityCodecTest.java b/src/test/java/org/owasp/esapi/codecs/HTMLEntityCodecTest.java index a12f287cd..bae3732dc 100644 --- a/src/test/java/org/owasp/esapi/codecs/HTMLEntityCodecTest.java +++ b/src/test/java/org/owasp/esapi/codecs/HTMLEntityCodecTest.java @@ -14,4 +14,13 @@ public void testEntityDecoding(){ assertEquals( "<", codec.decode("<")); assertEquals( "<", codec.decode("<")); } + + @Test + public void test32BitCJK(){ + String s = "𡘾𦴩𥻂"; + String expected = "𡘾𦴩𥻂"; + String bad = "������"; + assertEquals(false, expected.equals(bad)); + assertEquals(expected, codec.encode(new char[0], s)); + } } From 957a9c79f290b9d67dfa5cbdbfee78f6c2fbca4a Mon Sep 17 00:00:00 2001 From: Matt Seil Date: Mon, 7 Aug 2017 19:50:44 -0700 Subject: [PATCH 0402/1069] Issue #300 -- Brought back a slightly modified version of the old HTMLEntityCodec for backwards compatability purposes. --- .../esapi/codecs/LegacyHTMLEntityCodec.java | 552 ++++++++++++++++++ 1 file changed, 552 insertions(+) create mode 100644 src/main/java/org/owasp/esapi/codecs/LegacyHTMLEntityCodec.java diff --git a/src/main/java/org/owasp/esapi/codecs/LegacyHTMLEntityCodec.java b/src/main/java/org/owasp/esapi/codecs/LegacyHTMLEntityCodec.java new file mode 100644 index 000000000..e35296b62 --- /dev/null +++ b/src/main/java/org/owasp/esapi/codecs/LegacyHTMLEntityCodec.java @@ -0,0 +1,552 @@ +/** + * OWASP Enterprise Security API (ESAPI) + * + * This file is part of the Open Web Application Security Project (OWASP) + * Enterprise Security API (ESAPI) project. For details, please see + * http://www.owasp.org/index.php/ESAPI. + * + * Copyright (c) 2007 - The OWASP Foundation + * + * The ESAPI is published by OWASP under the BSD license. You should read and accept the + * LICENSE before you use, modify, and/or redistribute this software. + * + * @author Jeff Williams Aspect Security + * @created 2007 + */ +package org.owasp.esapi.codecs; + +import java.util.HashMap; +import java.util.Collections; +import java.util.Map; + +/** + * Implementation of the Codec interface for HTML entity encoding. + * + * @author Jeff Williams (jeff.williams .at. aspectsecurity.com) Aspect Security + * @since June 1, 2007 + * @see org.owasp.esapi.Encoder + */ +public class LegacyHTMLEntityCodec extends AbstractCharacterCodec { + + private static final char REPLACEMENT_CHAR = '\ufffd'; + private static final String REPLACEMENT_HEX = "fffd"; + private static final String REPLACEMENT_STR = "" + REPLACEMENT_CHAR; + private static final Map characterToEntityMap = mkCharacterToEntityMap(); + private static final Trie entityToCharacterTrie = mkEntityToCharacterTrie(); + + /** + * {@inheritDoc} + * + * Encodes a Character for safe use in an HTML entity field. + * @param immune + */ + public String encodeCharacter( char[] immune, Character c ) { + + // check for immune characters + if ( containsCharacter(c, immune ) ) { + return ""+c; + } + + // check for alphanumeric characters + String hex = super.getHexForNonAlphanumeric(c); + if ( hex == null ) { + return ""+c; + } + + // check for illegal characters + if ( ( c <= 0x1f && c != '\t' && c != '\n' && c != '\r' ) || ( c >= 0x7f && c <= 0x9f ) ) + { + hex = REPLACEMENT_HEX; // Let's entity encode this instead of returning it + c = REPLACEMENT_CHAR; + } + + // check if there's a defined entity + String entityName = (String) characterToEntityMap.get(c); + if (entityName != null) { + return "&" + entityName + ";"; + } + + // return the hex entity as suggested in the spec + return "&#x" + hex + ";"; + } + + /** + * {@inheritDoc} + * + * Returns the decoded version of the character starting at index, or + * null if no decoding is possible. + * + * Formats all are legal both with and without semi-colon, upper/lower case: + * &#dddd; + * &#xhhhh; + * &name; + */ + public Character decodeCharacter( PushbackString input ) { + input.mark(); + Character first = input.next(); + if ( first == null ) { + input.reset(); + return null; + } + + // if this is not an encoded character, return null + if (first != '&' ) { + input.reset(); + return null; + } + + // test for numeric encodings + Character second = input.next(); + if ( second == null ) { + input.reset(); + return null; + } + + if (second == '#' ) { + // handle numbers + Character c = getNumericEntity( input ); + if ( c != null ) return c; + } else if ( Character.isLetter( second.charValue() ) ) { + // handle entities + input.pushback( second ); + Character c = getNamedEntity( input ); + if ( c != null ) return c; + } + input.reset(); + return null; + } + + /** + * getNumericEntry checks input to see if it is a numeric entity + * + * @param input + * The input to test for being a numeric entity + * + * @return + * null if input is null, the character of input after decoding + */ + private Character getNumericEntity( PushbackString input ) { + Character first = input.peek(); + if ( first == null ) return null; + + if (first == 'x' || first == 'X' ) { + input.next(); + return parseHex( input ); + } + return parseNumber( input ); + } + + /** + * Parse a decimal number, such as those from JavaScript's String.fromCharCode(value) + * + * @param input + * decimal encoded string, such as 65 + * @return + * character representation of this decimal value, e.g. A + * @throws NumberFormatException + */ + private Character parseNumber( PushbackString input ) { + StringBuilder sb = new StringBuilder(); + while( input.hasNext() ) { + Character c = input.peek(); + + // if character is a digit then add it on and keep going + if ( Character.isDigit( c.charValue() ) ) { + sb.append( c ); + input.next(); + + // if character is a semi-colon, eat it and quit + } else if (c == ';' ) { + input.next(); + break; + + // otherwise just quit + } else { + break; + } + } + try { + int i = Integer.parseInt(sb.toString()); + if (Character.isValidCodePoint(i)) { + return (char) i; + } + } catch( NumberFormatException e ) { + // throw an exception for malformed entity? + } + return null; + } + + /** + * Parse a hex encoded entity + * + * @param input + * Hex encoded input (such as 437ae;) + * @return + * A single character from the string + * @throws NumberFormatException + */ + private Character parseHex( PushbackString input ) { + StringBuilder sb = new StringBuilder(); + while( input.hasNext() ) { + Character c = input.peek(); + + // if character is a hex digit then add it on and keep going + if ( "0123456789ABCDEFabcdef".indexOf(c) != -1 ) { + sb.append( c ); + input.next(); + + // if character is a semi-colon, eat it and quit + } else if (c == ';' ) { + input.next(); + break; + + // otherwise just quit + } else { + break; + } + } + try { + int i = Integer.parseInt(sb.toString(), 16); + if (Character.isValidCodePoint(i)) { + return (char) i; + } + } catch( NumberFormatException e ) { + // throw an exception for malformed entity? + } + return null; + } + + /** + * + * Returns the decoded version of the character starting at index, or + * null if no decoding is possible. + * + * Formats all are legal both with and without semi-colon, upper/lower case: + * &aa; + * &aaa; + * &aaaa; + * &aaaaa; + * &aaaaaa; + * &aaaaaaa; + * + * @param input + * A string containing a named entity like " + * @return + * Returns the decoded version of the character starting at index, or null if no decoding is possible. + */ + private Character getNamedEntity( PushbackString input ) { + StringBuilder possible = new StringBuilder(); + Map.Entry entry; + int len; + + // kludge around PushbackString.... + len = Math.min(input.remainder().length(), entityToCharacterTrie.getMaxKeyLength()); + for(int i=0;i exactEntry = entityToCharacterTrie.getLongestMatch(possibleStringLowerCase); + if(exactEntry != null) entry = exactEntry; + } + if(entry == null) return null; // no match, caller will reset input + } + + // fixup input + input.reset(); + input.next(); // read & + len = entry.getKey().length(); // what matched's length + for(int i=0;i mkCharacterToEntityMap() + { + Map map = new HashMap(252); + + map.put((char)34, "quot"); /* quotation mark */ + map.put((char)38, "amp"); /* ampersand */ + map.put((char)60, "lt"); /* less-than sign */ + map.put((char)62, "gt"); /* greater-than sign */ + map.put((char)160, "nbsp"); /* no-break space */ + map.put((char)161, "iexcl"); /* inverted exclamation mark */ + map.put((char)162, "cent"); /* cent sign */ + map.put((char)163, "pound"); /* pound sign */ + map.put((char)164, "curren"); /* currency sign */ + map.put((char)165, "yen"); /* yen sign */ + map.put((char)166, "brvbar"); /* broken bar */ + map.put((char)167, "sect"); /* section sign */ + map.put((char)168, "uml"); /* diaeresis */ + map.put((char)169, "copy"); /* copyright sign */ + map.put((char)170, "ordf"); /* feminine ordinal indicator */ + map.put((char)171, "laquo"); /* left-pointing double angle quotation mark */ + map.put((char)172, "not"); /* not sign */ + map.put((char)173, "shy"); /* soft hyphen */ + map.put((char)174, "reg"); /* registered sign */ + map.put((char)175, "macr"); /* macron */ + map.put((char)176, "deg"); /* degree sign */ + map.put((char)177, "plusmn"); /* plus-minus sign */ + map.put((char)178, "sup2"); /* superscript two */ + map.put((char)179, "sup3"); /* superscript three */ + map.put((char)180, "acute"); /* acute accent */ + map.put((char)181, "micro"); /* micro sign */ + map.put((char)182, "para"); /* pilcrow sign */ + map.put((char)183, "middot"); /* middle dot */ + map.put((char)184, "cedil"); /* cedilla */ + map.put((char)185, "sup1"); /* superscript one */ + map.put((char)186, "ordm"); /* masculine ordinal indicator */ + map.put((char)187, "raquo"); /* right-pointing double angle quotation mark */ + map.put((char)188, "frac14"); /* vulgar fraction one quarter */ + map.put((char)189, "frac12"); /* vulgar fraction one half */ + map.put((char)190, "frac34"); /* vulgar fraction three quarters */ + map.put((char)191, "iquest"); /* inverted question mark */ + map.put((char)192, "Agrave"); /* Latin capital letter a with grave */ + map.put((char)193, "Aacute"); /* Latin capital letter a with acute */ + map.put((char)194, "Acirc"); /* Latin capital letter a with circumflex */ + map.put((char)195, "Atilde"); /* Latin capital letter a with tilde */ + map.put((char)196, "Auml"); /* Latin capital letter a with diaeresis */ + map.put((char)197, "Aring"); /* Latin capital letter a with ring above */ + map.put((char)198, "AElig"); /* Latin capital letter ae */ + map.put((char)199, "Ccedil"); /* Latin capital letter c with cedilla */ + map.put((char)200, "Egrave"); /* Latin capital letter e with grave */ + map.put((char)201, "Eacute"); /* Latin capital letter e with acute */ + map.put((char)202, "Ecirc"); /* Latin capital letter e with circumflex */ + map.put((char)203, "Euml"); /* Latin capital letter e with diaeresis */ + map.put((char)204, "Igrave"); /* Latin capital letter i with grave */ + map.put((char)205, "Iacute"); /* Latin capital letter i with acute */ + map.put((char)206, "Icirc"); /* Latin capital letter i with circumflex */ + map.put((char)207, "Iuml"); /* Latin capital letter i with diaeresis */ + map.put((char)208, "ETH"); /* Latin capital letter eth */ + map.put((char)209, "Ntilde"); /* Latin capital letter n with tilde */ + map.put((char)210, "Ograve"); /* Latin capital letter o with grave */ + map.put((char)211, "Oacute"); /* Latin capital letter o with acute */ + map.put((char)212, "Ocirc"); /* Latin capital letter o with circumflex */ + map.put((char)213, "Otilde"); /* Latin capital letter o with tilde */ + map.put((char)214, "Ouml"); /* Latin capital letter o with diaeresis */ + map.put((char)215, "times"); /* multiplication sign */ + map.put((char)216, "Oslash"); /* Latin capital letter o with stroke */ + map.put((char)217, "Ugrave"); /* Latin capital letter u with grave */ + map.put((char)218, "Uacute"); /* Latin capital letter u with acute */ + map.put((char)219, "Ucirc"); /* Latin capital letter u with circumflex */ + map.put((char)220, "Uuml"); /* Latin capital letter u with diaeresis */ + map.put((char)221, "Yacute"); /* Latin capital letter y with acute */ + map.put((char)222, "THORN"); /* Latin capital letter thorn */ + map.put((char)223, "szlig"); /* Latin small letter sharp sXCOMMAX German Eszett */ + map.put((char)224, "agrave"); /* Latin small letter a with grave */ + map.put((char)225, "aacute"); /* Latin small letter a with acute */ + map.put((char)226, "acirc"); /* Latin small letter a with circumflex */ + map.put((char)227, "atilde"); /* Latin small letter a with tilde */ + map.put((char)228, "auml"); /* Latin small letter a with diaeresis */ + map.put((char)229, "aring"); /* Latin small letter a with ring above */ + map.put((char)230, "aelig"); /* Latin lowercase ligature ae */ + map.put((char)231, "ccedil"); /* Latin small letter c with cedilla */ + map.put((char)232, "egrave"); /* Latin small letter e with grave */ + map.put((char)233, "eacute"); /* Latin small letter e with acute */ + map.put((char)234, "ecirc"); /* Latin small letter e with circumflex */ + map.put((char)235, "euml"); /* Latin small letter e with diaeresis */ + map.put((char)236, "igrave"); /* Latin small letter i with grave */ + map.put((char)237, "iacute"); /* Latin small letter i with acute */ + map.put((char)238, "icirc"); /* Latin small letter i with circumflex */ + map.put((char)239, "iuml"); /* Latin small letter i with diaeresis */ + map.put((char)240, "eth"); /* Latin small letter eth */ + map.put((char)241, "ntilde"); /* Latin small letter n with tilde */ + map.put((char)242, "ograve"); /* Latin small letter o with grave */ + map.put((char)243, "oacute"); /* Latin small letter o with acute */ + map.put((char)244, "ocirc"); /* Latin small letter o with circumflex */ + map.put((char)245, "otilde"); /* Latin small letter o with tilde */ + map.put((char)246, "ouml"); /* Latin small letter o with diaeresis */ + map.put((char)247, "divide"); /* division sign */ + map.put((char)248, "oslash"); /* Latin small letter o with stroke */ + map.put((char)249, "ugrave"); /* Latin small letter u with grave */ + map.put((char)250, "uacute"); /* Latin small letter u with acute */ + map.put((char)251, "ucirc"); /* Latin small letter u with circumflex */ + map.put((char)252, "uuml"); /* Latin small letter u with diaeresis */ + map.put((char)253, "yacute"); /* Latin small letter y with acute */ + map.put((char)254, "thorn"); /* Latin small letter thorn */ + map.put((char)255, "yuml"); /* Latin small letter y with diaeresis */ + map.put((char)338, "OElig"); /* Latin capital ligature oe */ + map.put((char)339, "oelig"); /* Latin small ligature oe */ + map.put((char)352, "Scaron"); /* Latin capital letter s with caron */ + map.put((char)353, "scaron"); /* Latin small letter s with caron */ + map.put((char)376, "Yuml"); /* Latin capital letter y with diaeresis */ + map.put((char)402, "fnof"); /* Latin small letter f with hook */ + map.put((char)710, "circ"); /* modifier letter circumflex accent */ + map.put((char)732, "tilde"); /* small tilde */ + map.put((char)913, "Alpha"); /* Greek capital letter alpha */ + map.put((char)914, "Beta"); /* Greek capital letter beta */ + map.put((char)915, "Gamma"); /* Greek capital letter gamma */ + map.put((char)916, "Delta"); /* Greek capital letter delta */ + map.put((char)917, "Epsilon"); /* Greek capital letter epsilon */ + map.put((char)918, "Zeta"); /* Greek capital letter zeta */ + map.put((char)919, "Eta"); /* Greek capital letter eta */ + map.put((char)920, "Theta"); /* Greek capital letter theta */ + map.put((char)921, "Iota"); /* Greek capital letter iota */ + map.put((char)922, "Kappa"); /* Greek capital letter kappa */ + map.put((char)923, "Lambda"); /* Greek capital letter lambda */ + map.put((char)924, "Mu"); /* Greek capital letter mu */ + map.put((char)925, "Nu"); /* Greek capital letter nu */ + map.put((char)926, "Xi"); /* Greek capital letter xi */ + map.put((char)927, "Omicron"); /* Greek capital letter omicron */ + map.put((char)928, "Pi"); /* Greek capital letter pi */ + map.put((char)929, "Rho"); /* Greek capital letter rho */ + map.put((char)931, "Sigma"); /* Greek capital letter sigma */ + map.put((char)932, "Tau"); /* Greek capital letter tau */ + map.put((char)933, "Upsilon"); /* Greek capital letter upsilon */ + map.put((char)934, "Phi"); /* Greek capital letter phi */ + map.put((char)935, "Chi"); /* Greek capital letter chi */ + map.put((char)936, "Psi"); /* Greek capital letter psi */ + map.put((char)937, "Omega"); /* Greek capital letter omega */ + map.put((char)945, "alpha"); /* Greek small letter alpha */ + map.put((char)946, "beta"); /* Greek small letter beta */ + map.put((char)947, "gamma"); /* Greek small letter gamma */ + map.put((char)948, "delta"); /* Greek small letter delta */ + map.put((char)949, "epsilon"); /* Greek small letter epsilon */ + map.put((char)950, "zeta"); /* Greek small letter zeta */ + map.put((char)951, "eta"); /* Greek small letter eta */ + map.put((char)952, "theta"); /* Greek small letter theta */ + map.put((char)953, "iota"); /* Greek small letter iota */ + map.put((char)954, "kappa"); /* Greek small letter kappa */ + map.put((char)955, "lambda"); /* Greek small letter lambda */ + map.put((char)956, "mu"); /* Greek small letter mu */ + map.put((char)957, "nu"); /* Greek small letter nu */ + map.put((char)958, "xi"); /* Greek small letter xi */ + map.put((char)959, "omicron"); /* Greek small letter omicron */ + map.put((char)960, "pi"); /* Greek small letter pi */ + map.put((char)961, "rho"); /* Greek small letter rho */ + map.put((char)962, "sigmaf"); /* Greek small letter final sigma */ + map.put((char)963, "sigma"); /* Greek small letter sigma */ + map.put((char)964, "tau"); /* Greek small letter tau */ + map.put((char)965, "upsilon"); /* Greek small letter upsilon */ + map.put((char)966, "phi"); /* Greek small letter phi */ + map.put((char)967, "chi"); /* Greek small letter chi */ + map.put((char)968, "psi"); /* Greek small letter psi */ + map.put((char)969, "omega"); /* Greek small letter omega */ + map.put((char)977, "thetasym"); /* Greek theta symbol */ + map.put((char)978, "upsih"); /* Greek upsilon with hook symbol */ + map.put((char)982, "piv"); /* Greek pi symbol */ + map.put((char)8194, "ensp"); /* en space */ + map.put((char)8195, "emsp"); /* em space */ + map.put((char)8201, "thinsp"); /* thin space */ + map.put((char)8204, "zwnj"); /* zero width non-joiner */ + map.put((char)8205, "zwj"); /* zero width joiner */ + map.put((char)8206, "lrm"); /* left-to-right mark */ + map.put((char)8207, "rlm"); /* right-to-left mark */ + map.put((char)8211, "ndash"); /* en dash */ + map.put((char)8212, "mdash"); /* em dash */ + map.put((char)8216, "lsquo"); /* left single quotation mark */ + map.put((char)8217, "rsquo"); /* right single quotation mark */ + map.put((char)8218, "sbquo"); /* single low-9 quotation mark */ + map.put((char)8220, "ldquo"); /* left double quotation mark */ + map.put((char)8221, "rdquo"); /* right double quotation mark */ + map.put((char)8222, "bdquo"); /* double low-9 quotation mark */ + map.put((char)8224, "dagger"); /* dagger */ + map.put((char)8225, "Dagger"); /* double dagger */ + map.put((char)8226, "bull"); /* bullet */ + map.put((char)8230, "hellip"); /* horizontal ellipsis */ + map.put((char)8240, "permil"); /* per mille sign */ + map.put((char)8242, "prime"); /* prime */ + map.put((char)8243, "Prime"); /* double prime */ + map.put((char)8249, "lsaquo"); /* single left-pointing angle quotation mark */ + map.put((char)8250, "rsaquo"); /* single right-pointing angle quotation mark */ + map.put((char)8254, "oline"); /* overline */ + map.put((char)8260, "frasl"); /* fraction slash */ + map.put((char)8364, "euro"); /* euro sign */ + map.put((char)8465, "image"); /* black-letter capital i */ + map.put((char)8472, "weierp"); /* script capital pXCOMMAX Weierstrass p */ + map.put((char)8476, "real"); /* black-letter capital r */ + map.put((char)8482, "trade"); /* trademark sign */ + map.put((char)8501, "alefsym"); /* alef symbol */ + map.put((char)8592, "larr"); /* leftwards arrow */ + map.put((char)8593, "uarr"); /* upwards arrow */ + map.put((char)8594, "rarr"); /* rightwards arrow */ + map.put((char)8595, "darr"); /* downwards arrow */ + map.put((char)8596, "harr"); /* left right arrow */ + map.put((char)8629, "crarr"); /* downwards arrow with corner leftwards */ + map.put((char)8656, "lArr"); /* leftwards double arrow */ + map.put((char)8657, "uArr"); /* upwards double arrow */ + map.put((char)8658, "rArr"); /* rightwards double arrow */ + map.put((char)8659, "dArr"); /* downwards double arrow */ + map.put((char)8660, "hArr"); /* left right double arrow */ + map.put((char)8704, "forall"); /* for all */ + map.put((char)8706, "part"); /* partial differential */ + map.put((char)8707, "exist"); /* there exists */ + map.put((char)8709, "empty"); /* empty set */ + map.put((char)8711, "nabla"); /* nabla */ + map.put((char)8712, "isin"); /* element of */ + map.put((char)8713, "notin"); /* not an element of */ + map.put((char)8715, "ni"); /* contains as member */ + map.put((char)8719, "prod"); /* n-ary product */ + map.put((char)8721, "sum"); /* n-ary summation */ + map.put((char)8722, "minus"); /* minus sign */ + map.put((char)8727, "lowast"); /* asterisk operator */ + map.put((char)8730, "radic"); /* square root */ + map.put((char)8733, "prop"); /* proportional to */ + map.put((char)8734, "infin"); /* infinity */ + map.put((char)8736, "ang"); /* angle */ + map.put((char)8743, "and"); /* logical and */ + map.put((char)8744, "or"); /* logical or */ + map.put((char)8745, "cap"); /* intersection */ + map.put((char)8746, "cup"); /* union */ + map.put((char)8747, "int"); /* integral */ + map.put((char)8756, "there4"); /* therefore */ + map.put((char)8764, "sim"); /* tilde operator */ + map.put((char)8773, "cong"); /* congruent to */ + map.put((char)8776, "asymp"); /* almost equal to */ + map.put((char)8800, "ne"); /* not equal to */ + map.put((char)8801, "equiv"); /* identical toXCOMMAX equivalent to */ + map.put((char)8804, "le"); /* less-than or equal to */ + map.put((char)8805, "ge"); /* greater-than or equal to */ + map.put((char)8834, "sub"); /* subset of */ + map.put((char)8835, "sup"); /* superset of */ + map.put((char)8836, "nsub"); /* not a subset of */ + map.put((char)8838, "sube"); /* subset of or equal to */ + map.put((char)8839, "supe"); /* superset of or equal to */ + map.put((char)8853, "oplus"); /* circled plus */ + map.put((char)8855, "otimes"); /* circled times */ + map.put((char)8869, "perp"); /* up tack */ + map.put((char)8901, "sdot"); /* dot operator */ + map.put((char)8968, "lceil"); /* left ceiling */ + map.put((char)8969, "rceil"); /* right ceiling */ + map.put((char)8970, "lfloor"); /* left floor */ + map.put((char)8971, "rfloor"); /* right floor */ + map.put((char)9001, "lang"); /* left-pointing angle bracket */ + map.put((char)9002, "rang"); /* right-pointing angle bracket */ + map.put((char)9674, "loz"); /* lozenge */ + map.put((char)9824, "spades"); /* black spade suit */ + map.put((char)9827, "clubs"); /* black club suit */ + map.put((char)9829, "hearts"); /* black heart suit */ + map.put((char)9830, "diams"); /* black diamond suit */ + + return Collections.unmodifiableMap(map); + } + + /** + * Build a unmodifiable Trie from entitiy Name to Character + * @return Unmodifiable trie. + */ + private static synchronized Trie mkEntityToCharacterTrie() + { + Trie trie = new HashTrie(); + + for(Map.Entry entry : characterToEntityMap.entrySet()) + trie.put(entry.getValue(),entry.getKey()); + return Trie.Util.unmodifiable(trie); + } +} From 524950f2eaa65dbadd1583c2ed84e14b921893e7 Mon Sep 17 00:00:00 2001 From: Matt Seil Date: Mon, 7 Aug 2017 19:51:26 -0700 Subject: [PATCH 0403/1069] Issue #300 -- Brought back a slightly modified version of the old HTMLEntityCodec for backwards compatability purposes. --- .../java/org/owasp/esapi/codecs/LegacyHTMLEntityCodec.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/main/java/org/owasp/esapi/codecs/LegacyHTMLEntityCodec.java b/src/main/java/org/owasp/esapi/codecs/LegacyHTMLEntityCodec.java index e35296b62..57e5cb6b8 100644 --- a/src/main/java/org/owasp/esapi/codecs/LegacyHTMLEntityCodec.java +++ b/src/main/java/org/owasp/esapi/codecs/LegacyHTMLEntityCodec.java @@ -20,6 +20,12 @@ import java.util.Map; /** + * + * This class is DEPRECATED. It did not correctly handle encoding of non-BMP + * unicode code points. This class is provided solely for any fatal bugs + * not accounted for in the new version and will be removed entirely in + * a future release. + * * Implementation of the Codec interface for HTML entity encoding. * * @author Jeff Williams (jeff.williams .at. aspectsecurity.com) Date: Mon, 7 Aug 2017 23:22:53 -0700 Subject: [PATCH 0404/1069] Issue #300 -- Addressed a review comment. --- src/main/java/org/owasp/esapi/codecs/AbstractCodec.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/owasp/esapi/codecs/AbstractCodec.java b/src/main/java/org/owasp/esapi/codecs/AbstractCodec.java index 236125497..99188bc1b 100644 --- a/src/main/java/org/owasp/esapi/codecs/AbstractCodec.java +++ b/src/main/java/org/owasp/esapi/codecs/AbstractCodec.java @@ -121,9 +121,11 @@ public String getHexForNonAlphanumeric(char c) */ public String getHexForNonAlphanumeric(int c) { - if(c<0xFF) + if(c<0xFF){ return hex[c]; - return toHex(c); + }else{ + return toHex(c); + } } public String toOctal(char c) From 4c92b64ca3d64ca7b8801f52203689315ec803a6 Mon Sep 17 00:00:00 2001 From: Matt Seil Date: Tue, 8 Aug 2017 22:50:15 -0700 Subject: [PATCH 0405/1069] Issue #300 -- Added OWASP file headers, and did some lexical cleanup on if statements. --- .../esapi/codecs/AbstractCharacterCodec.java | 26 +++++++ .../org/owasp/esapi/codecs/AbstractCodec.java | 6 +- .../esapi/codecs/AbstractIntegerCodec.java | 28 +++++++ .../codecs/AbstractPushbackSequence.java | 29 ++++++++ .../java/org/owasp/esapi/codecs/Codec.java | 3 + .../owasp/esapi/codecs/HTMLEntityCodec.java | 11 ++- .../owasp/esapi/codecs/PushbackSequence.java | 18 ++++- .../owasp/esapi/codecs/PushbackString.java | 73 +++++++++++++------ 8 files changed, 164 insertions(+), 30 deletions(-) diff --git a/src/main/java/org/owasp/esapi/codecs/AbstractCharacterCodec.java b/src/main/java/org/owasp/esapi/codecs/AbstractCharacterCodec.java index 5868d2bc0..ed73e4bd9 100644 --- a/src/main/java/org/owasp/esapi/codecs/AbstractCharacterCodec.java +++ b/src/main/java/org/owasp/esapi/codecs/AbstractCharacterCodec.java @@ -1,5 +1,31 @@ +/** + * OWASP Enterprise Security API (ESAPI) + * + * This file is part of the Open Web Application Security Project (OWASP) + * Enterprise Security API (ESAPI) project. For details, please see + * http://www.owasp.org/index.php/ESAPI. + * + * Copyright (c) 2017 - The OWASP Foundation + * + * The ESAPI is published by OWASP under the BSD license. You should read and accept the + * LICENSE before you use, modify, and/or redistribute this software. + * + * @author Matt Seil (mseil .at. owasp.org) + * @created 2017 + * + * @author Jeff Williams (jeff.williams .at. aspectsecurity.com) Aspect Security + * @created 2007 + */ + package org.owasp.esapi.codecs; +/** + * + * This abstract Impl is broken off from the original {@code Codec} class and + * provides the {@code Character} parsing logic that has been with ESAPI from the beginning. + * + */ public abstract class AbstractCharacterCodec extends AbstractCodec { /* (non-Javadoc) * @see org.owasp.esapi.codecs.Codec#decode(java.lang.String) diff --git a/src/main/java/org/owasp/esapi/codecs/AbstractCodec.java b/src/main/java/org/owasp/esapi/codecs/AbstractCodec.java index 99188bc1b..d6ee5ffcf 100644 --- a/src/main/java/org/owasp/esapi/codecs/AbstractCodec.java +++ b/src/main/java/org/owasp/esapi/codecs/AbstractCodec.java @@ -5,13 +5,13 @@ * Enterprise Security API (ESAPI) project. For details, please see * http://www.owasp.org/index.php/ESAPI. * - * Copyright (c) 2007 - The OWASP Foundation + * Copyright (c) 2017 - The OWASP Foundation * * The ESAPI is published by OWASP under the BSD license. You should read and accept the * LICENSE before you use, modify, and/or redistribute this software. * - * @author Jeff Williams Aspect Security - * @created 2007 + * @author Matt Seil (mseil .at. owasp.org) + * @created 2017 */ package org.owasp.esapi.codecs; diff --git a/src/main/java/org/owasp/esapi/codecs/AbstractIntegerCodec.java b/src/main/java/org/owasp/esapi/codecs/AbstractIntegerCodec.java index f43891481..6e6a9c1a9 100644 --- a/src/main/java/org/owasp/esapi/codecs/AbstractIntegerCodec.java +++ b/src/main/java/org/owasp/esapi/codecs/AbstractIntegerCodec.java @@ -1,5 +1,33 @@ +/** + * OWASP Enterprise Security API (ESAPI) + * + * This file is part of the Open Web Application Security Project (OWASP) + * Enterprise Security API (ESAPI) project. For details, please see + * http://www.owasp.org/index.php/ESAPI. + * + * Copyright (c) 2017 - The OWASP Foundation + * + * The ESAPI is published by OWASP under the BSD license. You should read and accept the + * LICENSE before you use, modify, and/or redistribute this software. + * + * @author Matt Seil (mseil .at. owasp.org) + * @created 2017 + * + * @author Jeff Williams (jeff.williams .at. aspectsecurity.com) Aspect Security + * @created 2007 + */ package org.owasp.esapi.codecs; +/** + * This class is intended to be an alternative Abstract Implementation for parsing encoding + * data by focusing on {@code int} as opposed to {@code Character}. Because non-BMP code + * points cannot be represented by a {@code char}, this class remedies that by parsing string + * data as codePoints as opposed to a stream of {@code char}s. + * + * @author Matt Seil (mseil .at. owasp.org) + * @Created 2017 -- Adapted from Jeff Williams' original {@code Codec} class. + */ public class AbstractIntegerCodec extends AbstractCodec { /** diff --git a/src/main/java/org/owasp/esapi/codecs/AbstractPushbackSequence.java b/src/main/java/org/owasp/esapi/codecs/AbstractPushbackSequence.java index ee35478c3..bf9f78a8e 100644 --- a/src/main/java/org/owasp/esapi/codecs/AbstractPushbackSequence.java +++ b/src/main/java/org/owasp/esapi/codecs/AbstractPushbackSequence.java @@ -1,5 +1,34 @@ +/** + * OWASP Enterprise Security API (ESAPI) + * + * This file is part of the Open Web Application Security Project (OWASP) + * Enterprise Security API (ESAPI) project. For details, please see + * http://www.owasp.org/index.php/ESAPI. + * + * Copyright (c) 2017 - The OWASP Foundation + * + * The ESAPI is published by OWASP under the BSD license. You should read and accept the + * LICENSE before you use, modify, and/or redistribute this software. + * + * @author Matt Seil (mseil .at. owasp.org) + * @created 2017 + * + */ + package org.owasp.esapi.codecs; +/** + * + * This Abstract class provides the generic logic for using a {@code PushbackSequence} + * in regards to iterating strings. The final Impl is intended for the user to supply + * a type {@code T} such that the pushback interface can be utilized for sequences + * of type {@code T}. Presently this generic class is limited by the fact that + * @{code input} is a {@code String}. + * + * @author Matt Seil + * + * @param + */ public abstract class AbstractPushbackSequence implements PushbackSequence { protected String input; protected T pushback; diff --git a/src/main/java/org/owasp/esapi/codecs/Codec.java b/src/main/java/org/owasp/esapi/codecs/Codec.java index c205d1b3f..5e914a4a0 100644 --- a/src/main/java/org/owasp/esapi/codecs/Codec.java +++ b/src/main/java/org/owasp/esapi/codecs/Codec.java @@ -26,6 +26,9 @@ * @author Jeff Williams (jeff.williams .at. aspectsecurity.com) Aspect Security * @since June 1, 2007 + * + * @author Matt Seil (mseil .at. owasp.org) + * @since June 1, 2017 * @see org.owasp.esapi.Encoder */ public interface Codec { diff --git a/src/main/java/org/owasp/esapi/codecs/HTMLEntityCodec.java b/src/main/java/org/owasp/esapi/codecs/HTMLEntityCodec.java index 14564372f..fd2c866cc 100644 --- a/src/main/java/org/owasp/esapi/codecs/HTMLEntityCodec.java +++ b/src/main/java/org/owasp/esapi/codecs/HTMLEntityCodec.java @@ -5,12 +5,16 @@ * Enterprise Security API (ESAPI) project. For details, please see * http://www.owasp.org/index.php/ESAPI. * - * Copyright (c) 2007 - The OWASP Foundation + * Copyright (c) 2017 - The OWASP Foundation * * The ESAPI is published by OWASP under the BSD license. You should read and accept the * LICENSE before you use, modify, and/or redistribute this software. * - * @author Jeff Williams Aspect Security + * @author Matt Seil (mseil .at. owasp.org) + * @created 2017 + * + * @author Jeff Williams (jeff.williams .at. aspectsecurity.com) Aspect Security * @created 2007 */ package org.owasp.esapi.codecs; @@ -26,6 +30,9 @@ * @author Jeff Williams (jeff.williams .at. aspectsecurity.com) Aspect Security * @since June 1, 2007 + * + * @author Matt Seil (mseil .at. owasp.org) (mseil .at. owasp.org) + * * @see org.owasp.esapi.Encoder */ public class HTMLEntityCodec extends AbstractIntegerCodec diff --git a/src/main/java/org/owasp/esapi/codecs/PushbackSequence.java b/src/main/java/org/owasp/esapi/codecs/PushbackSequence.java index 5588ca91f..bcdbff635 100644 --- a/src/main/java/org/owasp/esapi/codecs/PushbackSequence.java +++ b/src/main/java/org/owasp/esapi/codecs/PushbackSequence.java @@ -1,3 +1,19 @@ +/** + * OWASP Enterprise Security API (ESAPI) + * + * This file is part of the Open Web Application Security Project (OWASP) + * Enterprise Security API (ESAPI) project. For details, please see + * http://www.owasp.org/index.php/ESAPI. + * + * Copyright (c) 2017 - The OWASP Foundation + * + * The ESAPI is published by OWASP under the BSD license. You should read and accept the + * LICENSE before you use, modify, and/or redistribute this software. + * + * @author Matt Seil (mseil .at. owasp.org) + * @created 2017 + * + */ package org.owasp.esapi.codecs; public interface PushbackSequence { @@ -10,7 +26,7 @@ public interface PushbackSequence { /** * Get the current index of the PushbackString. Typically used in error messages. - * @return The current index of the PushbackString. + * @return The current index of the PushbackSequence. */ int index(); diff --git a/src/main/java/org/owasp/esapi/codecs/PushbackString.java b/src/main/java/org/owasp/esapi/codecs/PushbackString.java index 4255045f9..103993c05 100644 --- a/src/main/java/org/owasp/esapi/codecs/PushbackString.java +++ b/src/main/java/org/owasp/esapi/codecs/PushbackString.java @@ -5,12 +5,16 @@ * Enterprise Security API (ESAPI) project. For details, please see * http://www.owasp.org/index.php/ESAPI. * - * Copyright (c) 2007 - The OWASP Foundation + * Copyright (c) 2017 - The OWASP Foundation * * The ESAPI is published by OWASP under the BSD license. You should read and accept the * LICENSE before you use, modify, and/or redistribute this software. * - * @author Jeff Williams Aspect Security + * @author Matt Seil (mseil .at. owasp.org) + * @updated 2017 + * + * @author Jeff Williams (jeff.williams .at. aspectsecurity.com) Aspect Security * @created 2007 */ package org.owasp.esapi.codecs; @@ -25,7 +29,7 @@ * @since June 1, 2007 * @see org.owasp.esapi.Encoder */ -public class PushbackString extends AbstractPushbackSequence{ +public class PushbackString extends AbstractPushbackSequence { /** * * @param input @@ -49,14 +53,18 @@ public int index() { * @see org.owasp.esapi.codecs.PushbackSequence#hasNext() */ public boolean hasNext() { - if (pushback != null) + if (pushback != null){ return true; - if (input == null) + } + if (input == null){ return false; - if (input.length() == 0) + } + if (input.length() == 0){ return false; - if (index >= input.length()) + } + if (index >= input.length()){ return false; + } return true; } @@ -71,12 +79,15 @@ public Character next() { pushback = null; return save; } - if (input == null) + if (input == null){ return null; - if (input.length() == 0) + } + if (input.length() == 0){ return null; - if (index >= input.length()) + } + if (index >= input.length()){ return null; + } return Character.valueOf(input.charAt(index++)); } @@ -87,10 +98,12 @@ public Character next() { */ public Character nextHex() { Character c = next(); - if (c == null) + if (c == null){ return null; - if (isHexDigit(c)) + } + if (isHexDigit(c)){ return c; + } return null; } @@ -101,10 +114,12 @@ public Character nextHex() { */ public Character nextOctal() { Character c = next(); - if (c == null) + if (c == null){ return null; - if (isOctalDigit(c)) + } + if (isOctalDigit(c)){ return c; + } return null; } @@ -116,8 +131,9 @@ public Character nextOctal() { * @return */ public static boolean isHexDigit(Character c) { - if (c == null) + if (c == null){ return false; + } char ch = c.charValue(); return (ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'f') || (ch >= 'A' && ch <= 'F'); } @@ -129,8 +145,9 @@ public static boolean isHexDigit(Character c) { * @return */ public static boolean isOctalDigit(Character c) { - if (c == null) + if (c == null){ return false; + } char ch = c.charValue(); return ch >= '0' && ch <= '7'; } @@ -141,14 +158,18 @@ public static boolean isOctalDigit(Character c) { * @see org.owasp.esapi.codecs.PushbackSequence#peek() */ public Character peek() { - if (pushback != null) + if (pushback != null){ return pushback; - if (input == null) + } + if (input == null){ return null; - if (input.length() == 0) + } + if (input.length() == 0){ return null; - if (index >= input.length()) + } + if (index >= input.length()){ return null; + } return Character.valueOf(input.charAt(index)); } @@ -158,14 +179,18 @@ public Character peek() { * @see org.owasp.esapi.codecs.PushbackSequence#peek(char) */ public boolean peek(Character c) { - if (pushback != null && pushback.charValue() == c) + if (pushback != null && pushback.charValue() == c){ return true; - if (input == null) + } + if (input == null){ return false; - if (input.length() == 0) + } + if (input.length() == 0){ return false; - if (index >= input.length()) + } + if (index >= input.length()){ return false; + } return input.charAt(index) == c; } From 039040686faeeed649b1ed544417731dddc08aaa Mon Sep 17 00:00:00 2001 From: Matt Seil Date: Thu, 10 Aug 2017 00:09:18 -0700 Subject: [PATCH 0406/1069] Issue #300 -- implemented HTMLEntityCodec and affected unit tests. --- src/main/java/org/owasp/esapi/codecs/AbstractCodec.java | 6 +++++- .../java/org/owasp/esapi/codecs/AbstractIntegerCodec.java | 5 ++--- src/main/java/org/owasp/esapi/codecs/HTMLEntityCodec.java | 5 +++-- 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/src/main/java/org/owasp/esapi/codecs/AbstractCodec.java b/src/main/java/org/owasp/esapi/codecs/AbstractCodec.java index d6ee5ffcf..2a71c1b1c 100644 --- a/src/main/java/org/owasp/esapi/codecs/AbstractCodec.java +++ b/src/main/java/org/owasp/esapi/codecs/AbstractCodec.java @@ -87,7 +87,11 @@ public String encodeCharacter( char[] immune, Character c ) { */ @Override public String encodeCharacter( char[] immune, int codePoint ) { - return new StringBuilder().appendCodePoint(codePoint).toString(); + String rval = ""; + if(Character.isValidCodePoint(codePoint)){ + rval = new StringBuilder().appendCodePoint(codePoint).toString(); + } + return rval; } diff --git a/src/main/java/org/owasp/esapi/codecs/AbstractIntegerCodec.java b/src/main/java/org/owasp/esapi/codecs/AbstractIntegerCodec.java index 6e6a9c1a9..61dadbc76 100644 --- a/src/main/java/org/owasp/esapi/codecs/AbstractIntegerCodec.java +++ b/src/main/java/org/owasp/esapi/codecs/AbstractIntegerCodec.java @@ -39,10 +39,9 @@ public String decode(String input) { PushbackSequence pbs = new PushBackSequenceImpl(input); while (pbs.hasNext()) { Integer c = decodeCharacter(pbs); - if (c != null) { + boolean isValid = Character.isValidCodePoint(c); + if (c != null && isValid) { sb.appendCodePoint(c); - } else { - sb.appendCodePoint(pbs.next()); } } return sb.toString(); diff --git a/src/main/java/org/owasp/esapi/codecs/HTMLEntityCodec.java b/src/main/java/org/owasp/esapi/codecs/HTMLEntityCodec.java index fd2c866cc..3976d9e14 100644 --- a/src/main/java/org/owasp/esapi/codecs/HTMLEntityCodec.java +++ b/src/main/java/org/owasp/esapi/codecs/HTMLEntityCodec.java @@ -203,7 +203,7 @@ private Integer parseNumber( PushbackSequence input ) { Integer c = input.peek(); // if character is a digit then add it on and keep going - if ( Character.isDigit( c ) ) { + if ( Character.isDigit( c ) && Character.isValidCodePoint(c) ) { sb.appendCodePoint( c ); input.next(); @@ -243,6 +243,7 @@ private Integer parseHex( PushbackSequence input ) { Integer c = input.peek(); // if character is a hex digit then add it on and keep going + //This statement implicitly tests for Character.isValidCodePoint(int) if ( "0123456789ABCDEFabcdef".indexOf(c) != -1 ) { sb.appendCodePoint( c ); input.next(); @@ -295,7 +296,7 @@ private Integer getNamedEntity( PushbackSequence input ) { len = Math.min(input.remainder().length(), entityToCharacterTrie.getMaxKeyLength()); for(int i=0;i Date: Thu, 10 Aug 2017 19:57:35 -0700 Subject: [PATCH 0407/1069] Issue #300 -- Added test cases with mixed BMP and non BMP data. --- .../org/owasp/esapi/codecs/AbstractCodec.java | 4 + .../esapi/codecs/AbstractIntegerCodec.java | 4 +- .../owasp/esapi/codecs/HTMLEntityCodec.java | 80 ++++++++----------- .../owasp/esapi/codecs/AbstractCodecTest.java | 16 ++-- .../esapi/codecs/HTMLEntityCodecTest.java | 25 ++++++ .../owasp/esapi/reference/EncoderTest.java | 3 +- 6 files changed, 76 insertions(+), 56 deletions(-) diff --git a/src/main/java/org/owasp/esapi/codecs/AbstractCodec.java b/src/main/java/org/owasp/esapi/codecs/AbstractCodec.java index 2a71c1b1c..b9c0ac6c7 100644 --- a/src/main/java/org/owasp/esapi/codecs/AbstractCodec.java +++ b/src/main/java/org/owasp/esapi/codecs/AbstractCodec.java @@ -82,6 +82,10 @@ public String encodeCharacter( char[] immune, Character c ) { return ""+c; } + public String encodeCharacter(char[] immune, char c){ + throw new IllegalArgumentException("You tried to call encodeCharacter with a char. Nope. Use Character instead!"); + } + /* (non-Javadoc) * @see org.owasp.esapi.codecs.Codec#encodeCharacter(char[], int) */ diff --git a/src/main/java/org/owasp/esapi/codecs/AbstractIntegerCodec.java b/src/main/java/org/owasp/esapi/codecs/AbstractIntegerCodec.java index 61dadbc76..635f2f1e0 100644 --- a/src/main/java/org/owasp/esapi/codecs/AbstractIntegerCodec.java +++ b/src/main/java/org/owasp/esapi/codecs/AbstractIntegerCodec.java @@ -39,9 +39,11 @@ public String decode(String input) { PushbackSequence pbs = new PushBackSequenceImpl(input); while (pbs.hasNext()) { Integer c = decodeCharacter(pbs); - boolean isValid = Character.isValidCodePoint(c); + boolean isValid = null == c ? false:Character.isValidCodePoint(c); if (c != null && isValid) { sb.appendCodePoint(c); + }else{ + sb.appendCodePoint(pbs.next()); } } return sb.toString(); diff --git a/src/main/java/org/owasp/esapi/codecs/HTMLEntityCodec.java b/src/main/java/org/owasp/esapi/codecs/HTMLEntityCodec.java index 3976d9e14..ee7139450 100644 --- a/src/main/java/org/owasp/esapi/codecs/HTMLEntityCodec.java +++ b/src/main/java/org/owasp/esapi/codecs/HTMLEntityCodec.java @@ -50,34 +50,56 @@ public class HTMLEntityCodec extends AbstractIntegerCodec public HTMLEntityCodec() { } + /** + * Overrides the AbstractImpl to keep this code performing entirely at the {@code int} + * level. + */ + @Override + public String encode(char[] immune, String input) { + StringBuilder sb = new StringBuilder(); + for(int offset = 0; offset < input.length(); ){ + final int point = input.codePointAt(offset); + if(Character.isValidCodePoint(point)){ + sb.append(encodeCharacter(immune, point)); + } + offset += Character.charCount(point); + } + return sb.toString(); + } + /** * {@inheritDoc} * - * Encodes a Character for safe use in an HTML entity field. + * Encodes a codePoint for safe use in an HTML entity field. * @param immune */ - public String encodeCharacter( char[] immune, Character c ) { + @Override + public String encodeCharacter( char[] immune, int codePoint ) { // check for immune characters - if ( containsCharacter(c, immune ) ) { - return ""+c; + // Cast the codePoint to a char because we want to limit immunity to the BMP field only. + if ( containsCharacter( (char) codePoint, immune ) && Character.isValidCodePoint(codePoint)) { + return new StringBuilder().appendCodePoint(codePoint).toString(); } // check for alphanumeric characters - String hex = super.getHexForNonAlphanumeric(c); - if ( hex == null ) { - return ""+c; + String hex = super.getHexForNonAlphanumeric(codePoint); + if ( hex == null && Character.isValidCodePoint(codePoint)) { + return new StringBuilder().appendCodePoint(codePoint).toString(); } - // check for illegal characters - if ( ( c <= 0x1f && c != '\t' && c != '\n' && c != '\r' ) || ( c >= 0x7f && c <= 0x9f ) ) + if ( ( codePoint <= 0x1f + && codePoint != '\t' + && codePoint != '\n' + && codePoint != '\r' ) + || ( codePoint >= 0x7f && codePoint <= 0x9f ) ) { hex = REPLACEMENT_HEX; // Let's entity encode this instead of returning it - c = REPLACEMENT_CHAR; + codePoint = REPLACEMENT_CHAR; } // check if there's a defined entity - String entityName = (String) characterToEntityMap.get(Integer.valueOf(c)); + String entityName = (String) characterToEntityMap.get(codePoint); if (entityName != null) { return "&" + entityName + ";"; } @@ -86,42 +108,6 @@ public String encodeCharacter( char[] immune, Character c ) { return "&#x" + hex + ";"; } - /** - * {@inheritDoc} - * - * Encodes a Character for safe use in an HTML entity field. - * @param immune - */ - public String encodeCharacter( char[] immune, int codePoint ) { - - // check for immune characters -// if ( containsCharacter(codePoint, immune ) ) { -// return ""+codePoint; -// } - -// // check for alphanumeric characters - String hex = super.getHexForNonAlphanumeric(codePoint); -// if ( hex == null ) { -// return ""+c; -// } -// -// // check for illegal characters -// if ( ( c <= 0x1f && c != '\t' && c != '\n' && c != '\r' ) || ( c >= 0x7f && c <= 0x9f ) ) -// { -// hex = REPLACEMENT_HEX; // Let's entity encode this instead of returning it -// c = REPLACEMENT_CHAR; -// } -// -// // check if there's a defined entity -// String entityName = (String) characterToEntityMap.get(c); -// if (entityName != null) { -// return "&" + entityName + ";"; -// } - - // return the hex entity as suggested in the spec - return "&#x" + hex + ";"; - } - /** * {@inheritDoc} * diff --git a/src/test/java/org/owasp/esapi/codecs/AbstractCodecTest.java b/src/test/java/org/owasp/esapi/codecs/AbstractCodecTest.java index 1456058bd..813330ed6 100644 --- a/src/test/java/org/owasp/esapi/codecs/AbstractCodecTest.java +++ b/src/test/java/org/owasp/esapi/codecs/AbstractCodecTest.java @@ -137,7 +137,8 @@ public void testWindowsEncode() public void testHtmlEncodeChar() { - assertEquals( "<", htmlCodec.encodeCharacter(EMPTY_CHAR_ARRAY, LESS_THAN) ); + + assertEquals( "<", htmlCodec.encodeCharacter(EMPTY_CHAR_ARRAY, (int) LESS_THAN) ); } public void testHtmlEncodeChar0x100() @@ -146,12 +147,13 @@ public void testHtmlEncodeChar0x100() String inStr = Character.toString(in); String expected = "Ā"; String result; - - result = htmlCodec.encodeCharacter(EMPTY_CHAR_ARRAY, in); - // this should be escaped - assertFalse(inStr.equals(result)); - // UTF-8 encoded and then percent escaped - assertEquals(expected, result); + //The new default for HTMLEntityCodec is ints/Integers. Use Character/char at your own risk! + //Characters destroy non-BMP codepoints. This Codec is now supposed surpass that. + result = htmlCodec.encodeCharacter(EMPTY_CHAR_ARRAY, (int) in); + // this should be escaped + assertFalse(inStr.equals(result)); + // UTF-8 encoded and then percent escaped + assertEquals(expected, result); } public void testHtmlEncodeStr0x100() diff --git a/src/test/java/org/owasp/esapi/codecs/HTMLEntityCodecTest.java b/src/test/java/org/owasp/esapi/codecs/HTMLEntityCodecTest.java index bae3732dc..c81031692 100644 --- a/src/test/java/org/owasp/esapi/codecs/HTMLEntityCodecTest.java +++ b/src/test/java/org/owasp/esapi/codecs/HTMLEntityCodecTest.java @@ -23,4 +23,29 @@ public void test32BitCJK(){ assertEquals(false, expected.equals(bad)); assertEquals(expected, codec.encode(new char[0], s)); } + + @Test + public void test32BitCJKMixedWithBmp(){ + String s = "𡘾𦴩<𥻂"; + String expected = "𡘾𦴩<𥻂"; + String bad = "������"; + assertEquals(false, expected.equals(bad)); + assertEquals(expected, codec.encode(new char[0], s)); + } + + @Test + public void testDecodeforChars(){ + String s = "!@$%()=+{}[]"; + String expected = "!@$%()=+{}[]"; + assertEquals(expected, codec.decode(s)); + } + + @Test + public void testMixedBmpAndNonBmp(){ + String nonBMP = new String(new int[]{0x2f804}, 0, 1); + String bmp = "")); assertEquals("&lt;script&gt;", instance.encodeForHTML("<script>")); assertEquals("!@$%()=+{}[]", instance.encodeForHTML("!@$%()=+{}[]")); - assertEquals("!@$%()=+{}[]", instance.encodeForHTML(instance.canonicalize("!@$%()=+{}[]") ) ); + String canonicalized = instance.canonicalize("!@$%()=+{}[]"); + assertEquals("!@$%()=+{}[]", instance.encodeForHTML( canonicalized ) ); assertEquals(",.-_ ", instance.encodeForHTML(",.-_ ")); assertEquals("dir&", instance.encodeForHTML("dir&")); assertEquals("one&two", instance.encodeForHTML("one&two")); From 3f9d208d8fe4ff5b6a03f78112dec5a6a0a909eb Mon Sep 17 00:00:00 2001 From: Matt Seil Date: Thu, 10 Aug 2017 21:49:16 -0700 Subject: [PATCH 0408/1069] Issue #300 -- Added documentation warnings in regards to the destructive nature of the codecs on non-UTF data inserted into a string. --- .../java/org/owasp/esapi/codecs/AbstractCodec.java | 12 ++++++++++-- src/main/java/org/owasp/esapi/codecs/Codec.java | 3 ++- .../java/org/owasp/esapi/codecs/HTMLEntityCodec.java | 9 +++++++-- 3 files changed, 19 insertions(+), 5 deletions(-) diff --git a/src/main/java/org/owasp/esapi/codecs/AbstractCodec.java b/src/main/java/org/owasp/esapi/codecs/AbstractCodec.java index b9c0ac6c7..8660fb7a7 100644 --- a/src/main/java/org/owasp/esapi/codecs/AbstractCodec.java +++ b/src/main/java/org/owasp/esapi/codecs/AbstractCodec.java @@ -51,8 +51,16 @@ public AbstractCodec() { } } - /* (non-Javadoc) - * @see org.owasp.esapi.codecs.Codec#encode(char[], java.lang.String) + /** + * WARNING!! {@code Character} based Codecs will silently transform code points that are not + * legal UTF code points into garbage data as they will cast them to {@code char}s. + *

+ * If you are implementing an {@code Integer} based codec, these will be silently discarded + * based on the return from {@code Character.isValidCodePoint( int )}. This is the preferred + * behavior moving forward. + * + * + * {@inheritDoc} */ @Override public String encode(char[] immune, String input) { diff --git a/src/main/java/org/owasp/esapi/codecs/Codec.java b/src/main/java/org/owasp/esapi/codecs/Codec.java index 5e914a4a0..cd0b66f03 100644 --- a/src/main/java/org/owasp/esapi/codecs/Codec.java +++ b/src/main/java/org/owasp/esapi/codecs/Codec.java @@ -45,7 +45,8 @@ public interface Codec { /** * Default implementation that should be overridden in specific codecs. * - * @param immune + * @param immune + * array of chars to NOT encode. Use with caution. * @param c * the Character to encode * @return diff --git a/src/main/java/org/owasp/esapi/codecs/HTMLEntityCodec.java b/src/main/java/org/owasp/esapi/codecs/HTMLEntityCodec.java index ee7139450..984a02105 100644 --- a/src/main/java/org/owasp/esapi/codecs/HTMLEntityCodec.java +++ b/src/main/java/org/owasp/esapi/codecs/HTMLEntityCodec.java @@ -51,8 +51,13 @@ public HTMLEntityCodec() { } /** - * Overrides the AbstractImpl to keep this code performing entirely at the {@code int} - * level. + * Given an array of {@code char}, scan the input {@code String} and encode unsafe + * codePoints, except for codePoints passed into the {@code char} array. + *

+ * WARNING: This method will silently discard any code point per the + * call to {@code Character.isValidCodePoint( int )} method. + * + * {@inheritDoc} */ @Override public String encode(char[] immune, String input) { From 3ed0a2fa4dfe43c14bbb282c0662ed6d8bbf35bd Mon Sep 17 00:00:00 2001 From: Matt Seil Date: Thu, 10 Aug 2017 22:13:33 -0700 Subject: [PATCH 0409/1069] Issue #281 -- Updated unit tests with missing assertions. --- .../org/owasp/esapi/reference/SafeFileTest.java | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/src/test/java/org/owasp/esapi/reference/SafeFileTest.java b/src/test/java/org/owasp/esapi/reference/SafeFileTest.java index a946bdc7c..f2acd589c 100644 --- a/src/test/java/org/owasp/esapi/reference/SafeFileTest.java +++ b/src/test/java/org/owasp/esapi/reference/SafeFileTest.java @@ -80,21 +80,17 @@ public void testEscapeCharactersInFilename() { } File sf = new File(testDir, "test^.file" ); - if ( sf.exists() ) { - System.out.println( " Injection allowed "+ sf.getAbsolutePath() ); - } else { - System.out.println( " Injection didn't work "+ sf.getAbsolutePath() ); - } + assertFalse("Injection didn't work " + sf.getAbsolutePath(), + sf.exists()); + assertTrue(" Injection allowed " + sf.getAbsolutePath(), sf.exists()); } public void testEscapeCharacterInDirectoryInjection() { System.out.println("testEscapeCharacterInDirectoryInjection"); File sf = new File(testDir, "test\\^.^.\\file"); - if ( sf.exists() ) { - System.out.println( " Injection allowed "+ sf.getAbsolutePath() ); - } else { - System.out.println( " Injection didn't work "+ sf.getAbsolutePath() ); - } + assertFalse(" Injection didn't work " + sf.getAbsolutePath(), + sf.exists()); + assertTrue(" Injection allowed " + sf.getAbsolutePath(), sf.exists()); } public void testJavaFileInjectionGood() throws ValidationException @@ -275,5 +271,4 @@ public void testCreateParentPercentNull() // expected } } - } From 98aaaa06cb7ba37f932c7db26cb967d92695d23f Mon Sep 17 00:00:00 2001 From: Matt Seil Date: Thu, 10 Aug 2017 22:20:00 -0700 Subject: [PATCH 0410/1069] Issue #278 -- Added default local to date test to avoid non-us unit test failures. --- src/test/java/org/owasp/esapi/reference/ValidatorTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/org/owasp/esapi/reference/ValidatorTest.java b/src/test/java/org/owasp/esapi/reference/ValidatorTest.java index f5a8fb330..8fb0a2e8a 100644 --- a/src/test/java/org/owasp/esapi/reference/ValidatorTest.java +++ b/src/test/java/org/owasp/esapi/reference/ValidatorTest.java @@ -142,7 +142,7 @@ public void testGetValidDate() throws Exception { // TODO: This test case fails due to an apparent bug in SimpleDateFormat // Note: This seems to be fixed in JDK 6. Will leave it commented out since // we only require JDK 5. -kww - instance.getValidDate("test", "June 32, 2008", DateFormat.getDateInstance(), false, errors); + instance.getValidDate("test", "June 32, 2008", DateFormat.getDateInstance(DateFormat.DEFAULT, Locale.US), false, errors); // assertEquals( 2, errors.size() ); } From c27d5e06cd11034cf2f5529da023b4d2ca43c72a Mon Sep 17 00:00:00 2001 From: Matt Seil Date: Fri, 11 Aug 2017 18:34:04 -0700 Subject: [PATCH 0411/1069] Issue #281 -- added windows escape char to blacklist in SafeFile to adhere to intended contract. --- src/main/java/org/owasp/esapi/SafeFile.java | 8 ++-- .../owasp/esapi/reference/SafeFileTest.java | 44 ++++++++++++------- 2 files changed, 33 insertions(+), 19 deletions(-) diff --git a/src/main/java/org/owasp/esapi/SafeFile.java b/src/main/java/org/owasp/esapi/SafeFile.java index 6a4f239c5..7df86bb38 100644 --- a/src/main/java/org/owasp/esapi/SafeFile.java +++ b/src/main/java/org/owasp/esapi/SafeFile.java @@ -32,8 +32,8 @@ public class SafeFile extends File { private static final long serialVersionUID = 1L; private static final Pattern PERCENTS_PAT = Pattern.compile("(%)([0-9a-fA-F])([0-9a-fA-F])"); - private static final Pattern FILE_BLACKLIST_PAT = Pattern.compile("([\\\\/:*?<>|])"); - private static final Pattern DIR_BLACKLIST_PAT = Pattern.compile("([*?<>|])"); + private static final Pattern FILE_BLACKLIST_PAT = Pattern.compile("([\\\\/:*?<>|^])"); + private static final Pattern DIR_BLACKLIST_PAT = Pattern.compile("([*?<>|^])"); public SafeFile(String path) throws ValidationException { super(path); @@ -62,12 +62,12 @@ public SafeFile(URI uri) throws ValidationException { private void doDirCheck(String path) throws ValidationException { Matcher m1 = DIR_BLACKLIST_PAT.matcher( path ); - if ( m1.find() ) { + if ( null != m1 && m1.find() ) { throw new ValidationException( "Invalid directory", "Directory path (" + path + ") contains illegal character: " + m1.group() ); } Matcher m2 = PERCENTS_PAT.matcher( path ); - if ( m2.find() ) { + if (null != m2 && m2.find() ) { throw new ValidationException( "Invalid directory", "Directory path (" + path + ") contains encoded characters: " + m2.group() ); } diff --git a/src/test/java/org/owasp/esapi/reference/SafeFileTest.java b/src/test/java/org/owasp/esapi/reference/SafeFileTest.java index f2acd589c..923b75ec0 100644 --- a/src/test/java/org/owasp/esapi/reference/SafeFileTest.java +++ b/src/test/java/org/owasp/esapi/reference/SafeFileTest.java @@ -16,19 +16,17 @@ package org.owasp.esapi.reference; import java.io.File; -import java.net.URI; -import java.net.URLDecoder; import java.util.Iterator; import java.util.Set; -import junit.framework.Test; -import junit.framework.TestCase; -import junit.framework.TestSuite; - import org.owasp.esapi.SafeFile; import org.owasp.esapi.errors.ValidationException; -import org.owasp.esapi.util.FileTestUtils; import org.owasp.esapi.util.CollectionsUtil; +import org.owasp.esapi.util.FileTestUtils; + +import junit.framework.Test; +import junit.framework.TestCase; +import junit.framework.TestSuite; /** * @author Jeff Williams (jeff.williams@aspectsecurity.com) @@ -79,18 +77,21 @@ public void testEscapeCharactersInFilename() { System.out.println( "File is there: " + tf ); } - File sf = new File(testDir, "test^.file" ); - assertFalse("Injection didn't work " + sf.getAbsolutePath(), - sf.exists()); - assertTrue(" Injection allowed " + sf.getAbsolutePath(), sf.exists()); + try { + File sf = new SafeFile(testDir, "test^.file" ); + } catch (ValidationException e) { + assertEquals("Invalid directory", e.getMessage()); + } + } public void testEscapeCharacterInDirectoryInjection() { System.out.println("testEscapeCharacterInDirectoryInjection"); - File sf = new File(testDir, "test\\^.^.\\file"); - assertFalse(" Injection didn't work " + sf.getAbsolutePath(), - sf.exists()); - assertTrue(" Injection allowed " + sf.getAbsolutePath(), sf.exists()); + try { + File sf = new SafeFile(testDir, "test\\^.^.\\file"); + } catch (ValidationException e) { + assertEquals("Invalid directory", e.getMessage()); + } } public void testJavaFileInjectionGood() throws ValidationException @@ -271,4 +272,17 @@ public void testCreateParentPercentNull() // expected } } + + public final void testSafeFileShouldAcceptEmptyPath() throws ValidationException + { + String filename = "hello.txt"; + //API dictates that NPE should be thrown. + try{ + SafeFile file = new SafeFile(filename); + }catch(NullPointerException npe){ + assertNotNull(npe); + } + + + } } From a663a4be2ff75b05e283e1365c8812c71d0570af Mon Sep 17 00:00:00 2001 From: Matt Seil Date: Thu, 17 Aug 2017 17:03:45 -0700 Subject: [PATCH 0412/1069] Catching up ESAPI.properties prod and test version. --- configuration/esapi/ESAPI.properties | 51 ++++++++++++++++++++++------ 1 file changed, 41 insertions(+), 10 deletions(-) diff --git a/configuration/esapi/ESAPI.properties b/configuration/esapi/ESAPI.properties index f98cc5a49..82d7fad92 100644 --- a/configuration/esapi/ESAPI.properties +++ b/configuration/esapi/ESAPI.properties @@ -319,8 +319,30 @@ HttpUtilities.ForceHttpOnlySession=false HttpUtilities.ForceSecureSession=false HttpUtilities.ForceHttpOnlyCookies=true HttpUtilities.ForceSecureCookies=true -# Maximum size of HTTP headers -HttpUtilities.MaxHeaderSize=4096 +# Maximum size of HTTP header key--the validator regex may have additional values. +HttpUtilities.MaxHeaderNameSize=256 +# Maximum size of HTTP header value--the validator regex may have additional values. +HttpUtilities.MaxHeaderValueSize=4096 +# Maximum size of JSESSIONID for the application--the validator regex may have additional values. +HttpUtilities.HTTPJSESSIONIDLENGTH=50 +# Maximum length of a URL (see https://stackoverflow.com/questions/417142/what-is-the-maximum-length-of-a-url-in-different-browsers) +HttpUtilities.URILENGTH=2000 +# Maximum length of a redirect +HttpUtilities.maxRedirectLength=512 +# Maximum length for an http scheme +HttpUtilities.HTTPSCHEMELENGTH=10 +# Maximum length for an http host +HttpUtilities.HTTPHOSTLENGTH=100 +# Maximum length for an http path +HttpUtilities.HTTPPATHLENGTH=150 +#Maximum length for a context path +HttpUtilities.contextPathLength=150 +#Maximum length for an httpServletPath +HttpUtilities.HTTPSERVLETPATHLENGTH=100 +#Maximum length for an http query parameter name +HttpUtilities.httpQueryParamNameLength=100 +#Maximum length for an http query parameter -- old default was 2000, but that's the max length for a URL... +HttpUtilities.httpQueryParamValueLength=500 # File upload configuration HttpUtilities.ApprovedUploadExtensions=.zip,.pdf,.doc,.docx,.ppt,.pptx,.tar,.gz,.tgz,.rar,.war,.jar,.ear,.xls,.rtf,.properties,.java,.class,.txt,.xml,.jsp,.jsf,.exe,.dll HttpUtilities.MaxUploadFileBytes=500000000 @@ -331,8 +353,10 @@ HttpUtilities.ResponseContentType=text/html; charset=UTF-8 # This is the name of the cookie used to represent the HTTP session # Typically this will be the default "JSESSIONID" HttpUtilities.HttpSessionIdName=JSESSIONID - - +#Sets whether or not will will overwrite http status codes to 200. +HttpUtilities.OverwriteStatusCodes=true +#Sets the application's base character encoding. This is forked from the Java Encryptor property. +HttpUtilities.CharacterEncoding=UTF-8 #=========================================================================== # ESAPI Executor @@ -441,20 +465,27 @@ Validator.Redirect=^\\/test.*$ # Values with Base64 encoded data (e.g. encrypted state) will need at least [a-zA-Z0-9\/+=] Validator.HTTPScheme=^(http|https)$ Validator.HTTPServerName=^[a-zA-Z0-9_.\\-]*$ -Validator.HTTPParameterName=^[a-zA-Z0-9_]{1,32}$ -Validator.HTTPParameterValue=^[a-zA-Z0-9.\\-\\/+=@_ ]*$ Validator.HTTPCookieName=^[a-zA-Z0-9\\-_]{1,32}$ Validator.HTTPCookieValue=^[a-zA-Z0-9\\-\\/+=_ ]*$ -# Note that max header name capped at 150 in SecurityRequestWrapper! -Validator.HTTPHeaderName=^[a-zA-Z0-9\\-_]{1,50}$ +# Note that headerName and Value length is also configured in the HTTPUtilities section +Validator.HTTPHeaderName=^[a-zA-Z0-9\\-_]{1,256}$ Validator.HTTPHeaderValue=^[a-zA-Z0-9()\\-=\\*\\.\\?;,+\\/:&_ ]*$ -Validator.HTTPContextPath=^\\/?[a-zA-Z0-9.\\-\\/_]*$ Validator.HTTPServletPath=^[a-zA-Z0-9.\\-\\/_]*$ Validator.HTTPPath=^[a-zA-Z0-9.\\-_]*$ Validator.HTTPQueryString=^[a-zA-Z0-9()\\-=\\*\\.\\?;,+\\/:&_ %]*$ Validator.HTTPURI=^[a-zA-Z0-9()\\-=\\*\\.\\?;,+\\/:&_ ]*$ Validator.HTTPURL=^.*$ -Validator.HTTPJSESSIONID=^[A-Z0-9]{10,30}$ +Validator.HTTPJSESSIONID=^[A-Z0-9]{10,32}$ + + +# Contributed by Fraenku@gmx.ch +# Googlecode Issue 116 (http://code.google.com/p/owasp-esapi-java/issues/detail?id=116) +Validator.HTTPParameterName=^[a-zA-Z0-9_\\-]{1,32}$ +Validator.HTTPParameterValue=^[\\p{L}\\p{N}.\\-/+=_ !$*?@]{0,1000}$ +Validator.HTTPContextPath=^/[a-zA-Z0-9.\\-_]*$ +Validator.HTTPQueryString=^([a-zA-Z0-9_\\-]{1,32}=[\\p{L}\\p{N}.\\-/+=_ !$*?@%]*&?)*$ +Validator.HTTPURI=^/([a-zA-Z0-9.\\-_]*/?)*$ + # Validation of file related input Validator.FileName=^[a-zA-Z0-9!@#$%^&{}\\[\\]()_+\\-=,.~'` ]{1,255}$ From 97366649c5caf81c9671599c400bd70382bbe250 Mon Sep 17 00:00:00 2001 From: Matt Seil Date: Sat, 19 Aug 2017 15:44:48 -0700 Subject: [PATCH 0413/1069] Fixing some minor issues of duplication and grammar. --- configuration/esapi/ESAPI.properties | 6 ++---- src/test/resources/esapi/ESAPI.properties | 2 +- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/configuration/esapi/ESAPI.properties b/configuration/esapi/ESAPI.properties index 82d7fad92..6a7ef055c 100644 --- a/configuration/esapi/ESAPI.properties +++ b/configuration/esapi/ESAPI.properties @@ -353,7 +353,7 @@ HttpUtilities.ResponseContentType=text/html; charset=UTF-8 # This is the name of the cookie used to represent the HTTP session # Typically this will be the default "JSESSIONID" HttpUtilities.HttpSessionIdName=JSESSIONID -#Sets whether or not will will overwrite http status codes to 200. +#Sets whether or not we will overwrite http status codes to 200. HttpUtilities.OverwriteStatusCodes=true #Sets the application's base character encoding. This is forked from the Java Encryptor property. HttpUtilities.CharacterEncoding=UTF-8 @@ -472,14 +472,12 @@ Validator.HTTPHeaderName=^[a-zA-Z0-9\\-_]{1,256}$ Validator.HTTPHeaderValue=^[a-zA-Z0-9()\\-=\\*\\.\\?;,+\\/:&_ ]*$ Validator.HTTPServletPath=^[a-zA-Z0-9.\\-\\/_]*$ Validator.HTTPPath=^[a-zA-Z0-9.\\-_]*$ -Validator.HTTPQueryString=^[a-zA-Z0-9()\\-=\\*\\.\\?;,+\\/:&_ %]*$ -Validator.HTTPURI=^[a-zA-Z0-9()\\-=\\*\\.\\?;,+\\/:&_ ]*$ Validator.HTTPURL=^.*$ Validator.HTTPJSESSIONID=^[A-Z0-9]{10,32}$ # Contributed by Fraenku@gmx.ch -# Googlecode Issue 116 (http://code.google.com/p/owasp-esapi-java/issues/detail?id=116) +# Github Issue 126 https://github.com/ESAPI/esapi-java-legacy/issues/126 Validator.HTTPParameterName=^[a-zA-Z0-9_\\-]{1,32}$ Validator.HTTPParameterValue=^[\\p{L}\\p{N}.\\-/+=_ !$*?@]{0,1000}$ Validator.HTTPContextPath=^/[a-zA-Z0-9.\\-_]*$ diff --git a/src/test/resources/esapi/ESAPI.properties b/src/test/resources/esapi/ESAPI.properties index 9003ff97a..6e184a46e 100644 --- a/src/test/resources/esapi/ESAPI.properties +++ b/src/test/resources/esapi/ESAPI.properties @@ -365,7 +365,7 @@ HttpUtilities.ResponseContentType=text/html; charset=UTF-8 # This is the name of the cookie used to represent the HTTP session # Typically this will be the default "JSESSIONID" HttpUtilities.HttpSessionIdName=JSESSIONID -#Sets whether or not will will overwrite http status codes to 200. +#Sets whether or not we will overwrite http status codes to 200. HttpUtilities.OverwriteStatusCodes=true #Sets the application's base character encoding. This is forked from the Java Encryptor property. HttpUtilities.CharacterEncoding=UTF-8 From 8f531164201d05c3a2acd7f3e1eb43c1f36d7551 Mon Sep 17 00:00:00 2001 From: Matt Seil Date: Sat, 19 Aug 2017 15:53:55 -0700 Subject: [PATCH 0414/1069] Cleaned up imports. --- .../java/org/owasp/esapi/reference/DefaultValidator.java | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/main/java/org/owasp/esapi/reference/DefaultValidator.java b/src/main/java/org/owasp/esapi/reference/DefaultValidator.java index 074b44dd1..d2ac1996a 100644 --- a/src/main/java/org/owasp/esapi/reference/DefaultValidator.java +++ b/src/main/java/org/owasp/esapi/reference/DefaultValidator.java @@ -20,22 +20,16 @@ import java.io.File; import java.io.IOException; import java.io.InputStream; -import java.io.UnsupportedEncodingException; import java.net.URI; import java.net.URISyntaxException; -import java.net.URLDecoder; import java.text.DateFormat; import java.util.ArrayList; import java.util.Date; -import java.util.EnumMap; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; -import java.util.LinkedHashMap; -import java.util.LinkedList; import java.util.List; import java.util.Map; -import java.util.Map.Entry; import java.util.Set; import java.util.regex.Pattern; From a9849f1421617b5087685daeb7239015bd753930 Mon Sep 17 00:00:00 2001 From: Matt Seil Date: Sat, 26 Aug 2017 16:13:21 -0700 Subject: [PATCH 0415/1069] Issue #284 -- Restored original canonicalization behavior due to issue #54 being an invalid fix for a legacy URL issue where query params would get flagged as HTML Entities. --- .../esapi/reference/DefaultValidator.java | 2 - .../validation/StringValidationRule.java | 50 +++---------------- .../owasp/esapi/reference/ValidatorTest.java | 2 +- 3 files changed, 8 insertions(+), 46 deletions(-) diff --git a/src/main/java/org/owasp/esapi/reference/DefaultValidator.java b/src/main/java/org/owasp/esapi/reference/DefaultValidator.java index d2ac1996a..92bef36c6 100644 --- a/src/main/java/org/owasp/esapi/reference/DefaultValidator.java +++ b/src/main/java/org/owasp/esapi/reference/DefaultValidator.java @@ -216,7 +216,6 @@ public String getValidInput(String context, String input, String type, int maxLe } rvr.setMaximumLength(maxLength); rvr.setAllowNull(allowNull); - rvr.setValidateInputAndCanonical(canonicalize); return rvr.getValid(context, input); } @@ -343,7 +342,6 @@ public String getValidSafeHTML( String context, String input, int maxLength, boo HTMLValidationRule hvr = new HTMLValidationRule( "safehtml", encoder ); hvr.setMaximumLength(maxLength); hvr.setAllowNull(allowNull); - hvr.setValidateInputAndCanonical(false); return hvr.getValid(context, input); } diff --git a/src/main/java/org/owasp/esapi/reference/validation/StringValidationRule.java b/src/main/java/org/owasp/esapi/reference/validation/StringValidationRule.java index c48286cf5..3163c532d 100644 --- a/src/main/java/org/owasp/esapi/reference/validation/StringValidationRule.java +++ b/src/main/java/org/owasp/esapi/reference/validation/StringValidationRule.java @@ -44,7 +44,6 @@ public class StringValidationRule extends BaseValidationRule { protected List blacklistPatterns = new ArrayList(); protected int minLength = 0; protected int maxLength = Integer.MAX_VALUE; - protected boolean validateInputAndCanonical = true; public StringValidationRule( String typeName ) { super( typeName ); @@ -116,16 +115,6 @@ public void setMaximumLength( int length ) { maxLength = length; } - /** - * Set the flag which determines whether the in input itself is - * checked as well as the canonical form of the input. - * @param flag The value to set - */ - public void setValidateInputAndCanonical(boolean flag) - { - validateInputAndCanonical = flag; - } - /** * checks input against whitelists. * @param context The context to include in exception messages @@ -267,47 +256,22 @@ public String getValid( String context, String input ) throws ValidationExceptio { String data = null; - // checks on input itself - // check for empty/null if(checkEmpty(context, input) == null) return null; - if (validateInputAndCanonical) - { - //first validate pre-canonicalized data - - // check length - checkLength(context, input); - - // check whitelist patterns - checkWhitelist(context, input); - - // check blacklist patterns - checkBlacklist(context, input); - - // canonicalize - data = encoder.canonicalize( input ); - - } else { - - //skip canonicalization - data = input; - } - - // check for empty/null - if(checkEmpty(context, data, input) == null) - return null; - // check length - checkLength(context, data, input); + checkLength(context, input); + + // canonicalize + data = encoder.canonicalize( input ); // check whitelist patterns - checkWhitelist(context, data, input); + checkWhitelist(context, input); // check blacklist patterns - checkBlacklist(context, data, input); - + checkBlacklist(context, input); + // validation passed return data; } diff --git a/src/test/java/org/owasp/esapi/reference/ValidatorTest.java b/src/test/java/org/owasp/esapi/reference/ValidatorTest.java index 8fb0a2e8a..bcef424b4 100644 --- a/src/test/java/org/owasp/esapi/reference/ValidatorTest.java +++ b/src/test/java/org/owasp/esapi/reference/ValidatorTest.java @@ -232,7 +232,7 @@ public void testGetValidFileName() throws Exception { assertEquals("Percent encoding is not changed", testName, instance.getValidFileName("test", testName, ESAPI.securityConfiguration().getAllowedFileExtensions(), false, errors)); } - public void testGetValidInput() { + public void testGetValidInput() throws Exception { System.out.println("getValidInput"); Validator instance = ESAPI.validator(); ValidationErrorList errors = new ValidationErrorList(); From 66745aef119a661f35eb8ef9cbae2aa514926f59 Mon Sep 17 00:00:00 2001 From: Matt Seil Date: Sat, 26 Aug 2017 16:15:42 -0700 Subject: [PATCH 0416/1069] Issue #284 -- Fixed code no longer needed for testing. --- src/test/java/org/owasp/esapi/reference/ValidatorTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/org/owasp/esapi/reference/ValidatorTest.java b/src/test/java/org/owasp/esapi/reference/ValidatorTest.java index bcef424b4..6222ebeb2 100644 --- a/src/test/java/org/owasp/esapi/reference/ValidatorTest.java +++ b/src/test/java/org/owasp/esapi/reference/ValidatorTest.java @@ -232,7 +232,7 @@ public void testGetValidFileName() throws Exception { assertEquals("Percent encoding is not changed", testName, instance.getValidFileName("test", testName, ESAPI.securityConfiguration().getAllowedFileExtensions(), false, errors)); } - public void testGetValidInput() throws Exception { + public void testGetValidInput(){ System.out.println("getValidInput"); Validator instance = ESAPI.validator(); ValidationErrorList errors = new ValidationErrorList(); From f01719ad13ad19e7f6bb7e88e2d384f44dd32ef3 Mon Sep 17 00:00:00 2001 From: Matt Seil Date: Sat, 26 Aug 2017 18:10:32 -0700 Subject: [PATCH 0417/1069] Adding mocking frameworks for testing. --- pom.xml | 34 ++++++++++++++++++++-------------- 1 file changed, 20 insertions(+), 14 deletions(-) diff --git a/pom.xml b/pom.xml index 25b4cebff..ddf78c307 100644 --- a/pom.xml +++ b/pom.xml @@ -88,13 +88,6 @@ Project Inventor - - Chris Schmidt - Aspect Security - - Project Co-owner - - Kevin W. Wall Wells Fargo @@ -102,6 +95,13 @@ Project Co-owner + + Matt Seil + OWASP + + Project Co-owner + + @@ -119,6 +119,12 @@ + + + joda-time + joda-time + 2.9.9 + commons-configuration commons-configuration @@ -224,29 +230,29 @@ 1.7.0 test - - - - - + @@ -529,7 +535,7 @@
- + org.apache.maven.plugins maven-jar-plugin @@ -561,7 +567,7 @@ - + From a47d8e70ec57e362ed58225fd97b12cb5b7cf2da Mon Sep 17 00:00:00 2001 From: NiklasMehner Date: Tue, 7 Nov 2017 03:52:51 +0100 Subject: [PATCH 0418/1069] Fix configuration loading: Use value instead of constants also update vulnerable dependencies (#426) * Fix configuration loading: Use value instead of constants * Update dependencies with vulnerabilities Close #426 --- pom.xml | 10 +++++----- .../esapi/reference/DefaultSecurityConfiguration.java | 10 +++++----- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/pom.xml b/pom.xml index ddf78c307..44db96788 100644 --- a/pom.xml +++ b/pom.xml @@ -157,7 +157,7 @@ commons-fileupload commons-fileupload - 1.3.2 + 1.3.3 compile @@ -185,14 +185,14 @@ 1.2.10 - org.beanshell - bsh-core - 2.0b4 + org.apache-extras.beanshell + bsh + 2.0b6 org.owasp.antisamy antisamy - 1.5.6 + 1.5.7 + -Xmaxwarns + 2000 + + + -Xlint:all,-deprecation,-rawtypes,-unchecked + + @@ -330,7 +362,7 @@ org.owasp dependency-check-maven - 1.4.4 + 2.1.0 5.9 ./suppressions.xml @@ -370,7 +402,7 @@ maven-pmd-plugin 3.6 - 1.5 + 1.7 utf-8 @@ -464,6 +496,7 @@ org.apache.maven.plugins maven-javadoc-plugin + 3.0.0-M1 -Xdoclint:none @@ -568,7 +601,7 @@ - org.apache.maven.plugins @@ -583,10 +616,10 @@ - 1.6 + 1.7 - ESAPI 2.1 uses the JDK1.6 for it's baseline. Please make sure that your - JAVA_HOME environment variable is pointed to a JDK1.6 distribution. + ESAPI 2.x now uses the JDK1.7 for it's baseline. Please make sure that your + JAVA_HOME environment variable is pointed to a JDK1.7 distribution. From f3cdc694e56f5bb5c248dc6294ec174df5bd6e20 Mon Sep 17 00:00:00 2001 From: "Kevin W. Wall" Date: Tue, 26 Dec 2017 11:37:14 -0500 Subject: [PATCH 0422/1069] Update README.md file to fix minor typo --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 3c5ccd2c0..f6f3e4721 100644 --- a/README.md +++ b/README.md @@ -35,7 +35,7 @@ In mid-2014 ESAPI Migrated all code to GitHub. This migration was completed in N ### What about the issues still located on Google Code? All issues from Google Code have been migrated to GitHub issues. We have a JIRA/Confluence instance allocated to us, but it has not be configured to synchronize with the GitHub issues, and thus is should not be used. JIRA is fine, but if we can't have it synchronized with GitHub issues (which is where the majority of our users report issues), it is not usuable. As developers, we do not want to spent time having to close issues from multiple bug-tracking sites. Therefore, until this synchronization happens (see GitHub issue #371), please ONLY use GitHub for reporting bugs. -When reporting an issue, please be clear and try to ensure that the ESAPI development team has sufficient information to be able to reproduce your results. If you have not already done son, this might be a good time to read Eric S. Raymond's classic "How to Ask Questions the Smart Way", at http://www.catb.org/esr/faqs/smart-questions.html before posting your issue. +When reporting an issue, please be clear and try to ensure that the ESAPI development team has sufficient information to be able to reproduce your results. If you have not already done so, this might be a good time to read Eric S. Raymond's classic "How to Ask Questions the Smart Way", at http://www.catb.org/esr/faqs/smart-questions.html before posting your issue. ### Find an Issue? If you have found a bug, then create an issue on the esapi-legacy-java repo: https://github.com/ESAPI/esapi-java-legacy/issues From 15b5b767f16cb590c5a6aba273a33da15bbb9730 Mon Sep 17 00:00:00 2001 From: "Kevin W. Wall" Date: Sat, 30 Dec 2017 15:05:04 -0500 Subject: [PATCH 0423/1069] Update README.md to refer to GitHub's suggested begiiner label for issues. Previously we were using the label 'FirstBug' to reflect issues appropriate for beginners to work on. That label was created prior to GitHub suggesting the use of 'help wanted' or 'good first issue' for that label. I have created the 'good first use' label (with the same color as suggested by GitHub) and replaced all the (now obsolete) 'FirstBug' labels with the label 'good first use' and then deleted the custom 'FirstBug' label. Updating this README.md file is the file step. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index f6f3e4721..27168a156 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,7 @@ https://github.com/ESAPI/esapi-java ## How can I contribute or help with fix bugs? Fork and submit a pull request! Simple as pi! We generally only accept bug fixes, not new features because as a legacy project, we don't intend on adding new features, although we may make exceptions. If you wish to propose a new feature, the best place to discuss it is via the ESAPI-DEV mailing list mentioned below. Note that we vet all pull requests, including coding style of any contributions; use the same coding style found in the files you are already editing. -If you are new to ESAPI, a good place to start is to look for GitHub issues labled as 'FirstBug'. (E.g., https://github.com/ESAPI/esapi-java-legacy/labels/FirstBug) +If you are new to ESAPI, a good place to start is to look for GitHub issues labled as 'good first issue'. (E.g., to find all open issues with that label, use https://github.com/ESAPI/esapi-java-legacy/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22.) ### What happened to Google code? In mid-2014 ESAPI Migrated all code to GitHub. This migration was completed in November 2014. From b712af5a543e8428a44a363c96a4b0ee8d2e300b Mon Sep 17 00:00:00 2001 From: Jeremiah Stacey Date: Fri, 5 Jan 2018 13:31:05 -0600 Subject: [PATCH 0424/1069] GITHUB #135 Updating SecurityWrapperRequest.getQueryString to return the original encoded string to the caller IFF that string can be converted into a 'safe' string by the ESAPI Validator instance/configuration. --- .../org/owasp/esapi/filters/SecurityWrapperRequest.java | 7 ++++++- .../java/org/owasp/esapi/filters/SafeRequestTest.java | 8 +++----- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/src/main/java/org/owasp/esapi/filters/SecurityWrapperRequest.java b/src/main/java/org/owasp/esapi/filters/SecurityWrapperRequest.java index 095069918..72f9f9bdc 100644 --- a/src/main/java/org/owasp/esapi/filters/SecurityWrapperRequest.java +++ b/src/main/java/org/owasp/esapi/filters/SecurityWrapperRequest.java @@ -513,7 +513,12 @@ public String getQueryString() { } catch (ValidationException e) { // already logged } - return clean; + /* GITHUB #135 + * as long as the original query can be cleaned then we assume it's safe. + * Returning the decoded 'clean' value changes how the string is interpreted, + * so we need to return the original query value. + */ + return clean == null || clean.isEmpty() ? clean : query; } /** diff --git a/src/test/java/org/owasp/esapi/filters/SafeRequestTest.java b/src/test/java/org/owasp/esapi/filters/SafeRequestTest.java index e4d55950d..e758fe02f 100644 --- a/src/test/java/org/owasp/esapi/filters/SafeRequestTest.java +++ b/src/test/java/org/owasp/esapi/filters/SafeRequestTest.java @@ -121,7 +121,7 @@ public void testGetQueryStringPercent() req.setQueryString("a=%62"); wrappedReq = new SecurityWrapperRequest(req); - assertEquals("a=b",wrappedReq.getQueryString()); + assertEquals("a=%62",wrappedReq.getQueryString()); } public void testGetQueryStringPercentNUL() @@ -131,11 +131,9 @@ public void testGetQueryStringPercentNUL() req.setQueryString("a=%00"); wrappedReq = new SecurityWrapperRequest(req); - assertEquals("",wrappedReq.getQueryString()); + assertEquals("a=%00", wrappedReq.getQueryString()); } - /* these tests need to be enabled&changed based on the decisions - * made regarding issue 125. Currently they fail. public void testGetQueryStringPercentEquals() { MockHttpServletRequest req = new MockHttpServletRequest(); @@ -155,7 +153,7 @@ public void testGetQueryStringPercentAmpersand() wrappedReq = new SecurityWrapperRequest(req); assertEquals("a=%26b",wrappedReq.getQueryString()); } - */ + // Test to ensure null-value contract defined by ServletRequest.getParameterNames(String) is met. public void testGetParameterValuesReturnsNullWhenParameterDoesNotExistInRequest() { From 2c199e5e84ebee32e1147440c8ef9b72ecf8e198 Mon Sep 17 00:00:00 2001 From: Jeremiah Stacey Date: Sat, 20 Jan 2018 09:19:24 -0600 Subject: [PATCH 0425/1069] Canonicalize SecurityWrapperRequest.getQueryString Reverts last change which removed canonicalization from the return value. Tests being updated to reflect the expected behavior, which includes both canonicalization as well as test-scoped whitelist functionality from validation. --- .../org/owasp/esapi/filters/SecurityWrapperRequest.java | 7 +------ .../java/org/owasp/esapi/filters/SafeRequestTest.java | 8 ++++---- 2 files changed, 5 insertions(+), 10 deletions(-) diff --git a/src/main/java/org/owasp/esapi/filters/SecurityWrapperRequest.java b/src/main/java/org/owasp/esapi/filters/SecurityWrapperRequest.java index 72f9f9bdc..095069918 100644 --- a/src/main/java/org/owasp/esapi/filters/SecurityWrapperRequest.java +++ b/src/main/java/org/owasp/esapi/filters/SecurityWrapperRequest.java @@ -513,12 +513,7 @@ public String getQueryString() { } catch (ValidationException e) { // already logged } - /* GITHUB #135 - * as long as the original query can be cleaned then we assume it's safe. - * Returning the decoded 'clean' value changes how the string is interpreted, - * so we need to return the original query value. - */ - return clean == null || clean.isEmpty() ? clean : query; + return clean; } /** diff --git a/src/test/java/org/owasp/esapi/filters/SafeRequestTest.java b/src/test/java/org/owasp/esapi/filters/SafeRequestTest.java index e758fe02f..e404f8c46 100644 --- a/src/test/java/org/owasp/esapi/filters/SafeRequestTest.java +++ b/src/test/java/org/owasp/esapi/filters/SafeRequestTest.java @@ -121,7 +121,7 @@ public void testGetQueryStringPercent() req.setQueryString("a=%62"); wrappedReq = new SecurityWrapperRequest(req); - assertEquals("a=%62",wrappedReq.getQueryString()); + assertEquals("a=b",wrappedReq.getQueryString()); } public void testGetQueryStringPercentNUL() @@ -131,7 +131,7 @@ public void testGetQueryStringPercentNUL() req.setQueryString("a=%00"); wrappedReq = new SecurityWrapperRequest(req); - assertEquals("a=%00", wrappedReq.getQueryString()); + assertEquals("a="+Character.MIN_VALUE, wrappedReq.getQueryString()); } public void testGetQueryStringPercentEquals() @@ -141,7 +141,7 @@ public void testGetQueryStringPercentEquals() req.setQueryString("a=%3d"); wrappedReq = new SecurityWrapperRequest(req); - assertEquals("a=%3d",wrappedReq.getQueryString()); + assertEquals("a==",wrappedReq.getQueryString()); } public void testGetQueryStringPercentAmpersand() @@ -151,7 +151,7 @@ public void testGetQueryStringPercentAmpersand() req.setQueryString("a=%26b"); wrappedReq = new SecurityWrapperRequest(req); - assertEquals("a=%26b",wrappedReq.getQueryString()); + assertEquals("a=&b",wrappedReq.getQueryString()); } From f006b5308eb54e2ba316a4b55e24cc6430fdcfed Mon Sep 17 00:00:00 2001 From: Jeremiah Stacey Date: Sat, 20 Jan 2018 09:26:35 -0600 Subject: [PATCH 0426/1069] SecurityWrapperRequest Workflow Test Using PowerMock to assert the happy-path and exception cases when requesting a QueryString reference. This approach removes the test environment from the unit and focuses on the behavior of the class in isolation. --- .../filters/SecurityWrapperRequestTest.java | 108 ++++++++++++++++++ 1 file changed, 108 insertions(+) create mode 100644 src/test/java/org/owasp/esapi/filters/SecurityWrapperRequestTest.java diff --git a/src/test/java/org/owasp/esapi/filters/SecurityWrapperRequestTest.java b/src/test/java/org/owasp/esapi/filters/SecurityWrapperRequestTest.java new file mode 100644 index 000000000..3200700c1 --- /dev/null +++ b/src/test/java/org/owasp/esapi/filters/SecurityWrapperRequestTest.java @@ -0,0 +1,108 @@ +package org.owasp.esapi.filters; + +import javax.servlet.http.HttpServletRequest; + +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Matchers; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.owasp.esapi.ESAPI; +import org.owasp.esapi.SecurityConfiguration; +import org.owasp.esapi.Validator; +import org.owasp.esapi.errors.IntrusionException; +import org.owasp.esapi.errors.ValidationException; +import org.owasp.esapi.filters.SecurityWrapperRequest; +import org.powermock.api.mockito.PowerMockito; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.PowerMockRunner; + +/** + * FIXME: Document intent of class. General Function, purpose of creation, intended feature, etc. + * Why do people care this exists? + * + * @author Jeremiah + * @since Jan 3, 2018 + */ +@RunWith(PowerMockRunner.class) +@PrepareForTest(ESAPI.class) +public class SecurityWrapperRequestTest { + + @Mock + private HttpServletRequest mockRequest; + @Mock + private Validator mockValidator; + @Mock + private SecurityConfiguration mockSecConfig; + + @Before + public void setup() throws Exception { + PowerMockito.mockStatic(ESAPI.class); + PowerMockito.when(ESAPI.class, "validator").thenReturn(mockValidator); + PowerMockito.when(ESAPI.class, "securityConfiguration").thenReturn(mockSecConfig); + } + + @Test + public void testGetQueryString() throws IntrusionException, ValidationException { + String queryString = "queryString"; + int maxLength = 255; + + ArgumentCaptor inputCapture = ArgumentCaptor.forClass(String.class); + ArgumentCaptor typeCapture = ArgumentCaptor.forClass(String.class); + ArgumentCaptor lenghtCapture = ArgumentCaptor.forClass(Integer.class); + ArgumentCaptor allowNullCapture = ArgumentCaptor.forClass(Boolean.class); + + PowerMockito.when(mockValidator.getValidInput(Matchers.anyString(), inputCapture.capture(), typeCapture + .capture(), lenghtCapture.capture(), allowNullCapture.capture())).thenReturn("canonicalized"); + PowerMockito.when(mockSecConfig.getIntProp("HttpUtilities.URILENGTH")).thenReturn(maxLength); + PowerMockito.when(mockRequest.getQueryString()).thenReturn(queryString); + + SecurityWrapperRequest request = new SecurityWrapperRequest(mockRequest); + String rval = request.getQueryString(); + Assert.assertEquals("canonicalized", rval); + + Assert.assertEquals(queryString, inputCapture.getValue()); + Assert.assertEquals("HTTPQueryString", typeCapture.getValue()); + Assert.assertTrue(maxLength == lenghtCapture.getValue().intValue()); + Assert.assertEquals(true, allowNullCapture.getValue()); + + Mockito.verify(mockValidator, Mockito.times(1)).getValidInput(Matchers.anyString(), Matchers.anyString(), + Matchers.anyString(), Matchers.anyInt(), Matchers.anyBoolean()); + Mockito.verify(mockSecConfig, Mockito.times(1)).getIntProp("HttpUtilities.URILENGTH"); + Mockito.verify(mockRequest, Mockito.times(1)).getQueryString(); + } + + @SuppressWarnings("unchecked") + @Test + public void testGetQueryStringCanonicalizeException() throws IntrusionException, ValidationException { + String queryString = "queryString"; + int maxLength = 255; + + ArgumentCaptor inputCapture = ArgumentCaptor.forClass(String.class); + ArgumentCaptor typeCapture = ArgumentCaptor.forClass(String.class); + ArgumentCaptor lenghtCapture = ArgumentCaptor.forClass(Integer.class); + ArgumentCaptor allowNullCapture = ArgumentCaptor.forClass(Boolean.class); + + PowerMockito.when(mockValidator.getValidInput(Matchers.anyString(), inputCapture.capture(), typeCapture + .capture(), lenghtCapture.capture(), allowNullCapture.capture())).thenThrow(ValidationException.class); + PowerMockito.when(mockSecConfig.getIntProp("HttpUtilities.URILENGTH")).thenReturn(maxLength); + PowerMockito.when(mockRequest.getQueryString()).thenReturn(queryString); + + SecurityWrapperRequest request = new SecurityWrapperRequest(mockRequest); + String rval = request.getQueryString(); + Assert.assertEquals("", rval); + + Assert.assertEquals(queryString, inputCapture.getValue()); + Assert.assertEquals("HTTPQueryString", typeCapture.getValue()); + Assert.assertTrue(maxLength == lenghtCapture.getValue().intValue()); + Assert.assertEquals(true, allowNullCapture.getValue()); + + Mockito.verify(mockValidator, Mockito.times(1)).getValidInput(Matchers.anyString(), Matchers.anyString(), + Matchers.anyString(), Matchers.anyInt(), Matchers.anyBoolean()); + Mockito.verify(mockSecConfig, Mockito.times(1)).getIntProp("HttpUtilities.URILENGTH"); + Mockito.verify(mockRequest, Mockito.times(1)).getQueryString(); + } +} From 5f91df0a6a454bc5df04e2e64918ebba59f4512a Mon Sep 17 00:00:00 2001 From: Jeremiah Stacey Date: Sat, 20 Jan 2018 09:29:03 -0600 Subject: [PATCH 0427/1069] Whitelist Regex Validation Tests Providing a structure to assert that the enviroment configurations will provide expected responses when passed controlled values. Performing a preliminary whitelist test set on HttpQueryString regex from ESAPI.properties. The test implementation asserts that the regex being tested matches the test environment's regex for a given property. If the regex strings differ, the tests associated with that property will be skipped to prevent false-positives from being provided to a client. --- .../esapi/reference/AbstractPatternTest.java | 49 ++++++++++ ...EsapiWhitelistValidationPatternTester.java | 91 +++++++++++++++++++ 2 files changed, 140 insertions(+) create mode 100644 src/test/java/org/owasp/esapi/reference/AbstractPatternTest.java create mode 100644 src/test/java/org/owasp/esapi/reference/EsapiWhitelistValidationPatternTester.java diff --git a/src/test/java/org/owasp/esapi/reference/AbstractPatternTest.java b/src/test/java/org/owasp/esapi/reference/AbstractPatternTest.java new file mode 100644 index 000000000..3fd72ebaf --- /dev/null +++ b/src/test/java/org/owasp/esapi/reference/AbstractPatternTest.java @@ -0,0 +1,49 @@ +package org.owasp.esapi.reference; + +import java.util.regex.Pattern; + +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + + +/** + * FIXME: Document intent of class. General Function, purpose of creation, intended feature, etc. + * Why do people care this exists? + * @author Jeremiah + * @since Jan 20, 2018 + * + */ +@RunWith (Parameterized.class) +public abstract class AbstractPatternTest { + + protected static class PatternTestTuple { + String input; + String regex; + boolean shouldMatch; + String description; + /** {@inheritDoc}*/ + @Override + public String toString() { + return description != null ? description : regex; + } + } + + private String input; + private Pattern pattern; + private boolean shouldMatch; + + + public AbstractPatternTest(PatternTestTuple tuple) { + this.input = tuple.input; + this.pattern = Pattern.compile(tuple.regex); + this.shouldMatch = tuple.shouldMatch; + } + + @Test + public void checkPatternMatches() { + Assert.assertEquals(shouldMatch, pattern.matcher(input).matches()); + } + +} diff --git a/src/test/java/org/owasp/esapi/reference/EsapiWhitelistValidationPatternTester.java b/src/test/java/org/owasp/esapi/reference/EsapiWhitelistValidationPatternTester.java new file mode 100644 index 000000000..4a1bd5ffb --- /dev/null +++ b/src/test/java/org/owasp/esapi/reference/EsapiWhitelistValidationPatternTester.java @@ -0,0 +1,91 @@ +package org.owasp.esapi.reference; + +import java.util.ArrayList; +import java.util.Collection; + +import org.junit.Assume; +import org.junit.runners.Parameterized.Parameters; +import org.owasp.esapi.reference.DefaultSecurityConfiguration; + +/** + * Extension of the AbstractPatternTest which focuses on asserting that the default whitelist regex values applied in + * the validation process are performing the intended function in the environment. + * + *
+ * + * If the regex values in this test are found to not match the running environment configurations, then the tests will + * be skipped. + * + * @author Jeremiah + * @since Jan 20, 2018 + */ +public class EsapiWhitelistValidationPatternTester extends AbstractPatternTest { + //See ESAPI.properties + private static final String HTTP_QUERY_STRING_PROP_NAME="HTTPQueryString"; + private static final String HTTP_QUERY_STRING_REGEX="^([a-zA-Z0-9_\\-]{1,32}=[\\p{L}\\p{N}.\\-/+=_ !$*?@%]*&?)*$"; + + @Parameters(name = "{0}-{1}") + public static Collection createDefaultPatternTests() { + Collection parameters = new ArrayList<>(); + + for(PatternTestTuple tuple : buildHttpQueryStringTests()) { + parameters.add(new Object[] { HTTP_QUERY_STRING_PROP_NAME, tuple }); + } + + + return parameters; + } + + private static Collection buildHttpQueryStringTests() { + Collection httpQueryStringTests = new ArrayList<>(); + + //MATCHING CASES + PatternTestTuple tuple = newHttpQueryStringTuple("Default Case", "b", true); + httpQueryStringTests.add(tuple); + tuple = newHttpQueryStringTuple("Percent Encoded Value", "%62", true); + httpQueryStringTests.add(tuple); + tuple = newHttpQueryStringTuple("Percent Encoded Null Character", "%00", true); + httpQueryStringTests.add(tuple); + tuple = newHttpQueryStringTuple("Double Equals", "=", true); + httpQueryStringTests.add(tuple); + + //NON-MATCHING CASES + tuple = newHttpQueryStringTuple("Ampersand In Value", "&b", false); + httpQueryStringTests.add(tuple); + tuple = newHttpQueryStringTuple("Null Character", ""+Character.MIN_VALUE, false); + httpQueryStringTests.add(tuple); + tuple = newHttpQueryStringTuple("Encoded Null Character", "\u0000", false); + httpQueryStringTests.add(tuple); + + return httpQueryStringTests; + } + + private static PatternTestTuple newHttpQueryStringTuple(String description, String value, boolean shouldPass) { + PatternTestTuple tuple = new PatternTestTuple(); + tuple.input = "a="+value; + tuple.shouldMatch = shouldPass; + tuple.regex = HTTP_QUERY_STRING_REGEX; + tuple.description = description; + return tuple; + } + + public EsapiWhitelistValidationPatternTester(String property, PatternTestTuple tuple) { + super(tuple); + /* + * This next block causes the case to be skipped programatically if the regex being tested + * is different than the one being loaded at runtime. + * This is being done to prevent a false sense of security. + * If the configurations are changed to meet additional environmental concerns, the intent of this test should + * be copied into that environment and tested there to assert the additional expectations or changes in desired + * behavior. + */ + DefaultSecurityConfiguration configuration = new DefaultSecurityConfiguration(); + Assume.assumeTrue( + "The regular expression specified does not match the configuration settings.\n" + + "If the value was changed from the ESAPI default, it is recommended to copy " + + "this class into your project, update the regex being tested, and update all " + + "associated input expectations for your unique environment.", + configuration.getValidationPattern(property).toString().equals(tuple.regex)); + } + +} From f5a190ff8a709bf712783cac12280c4f786c2624 Mon Sep 17 00:00:00 2001 From: Jeremiah Stacey Date: Sat, 20 Jan 2018 09:34:08 -0600 Subject: [PATCH 0428/1069] PercentCodec String/character encode/decode Set of tests for PercentCodec which tests the encode and decode features using both String and Character api calls. Initial test data sets are derived from 'AbstractCodecTest', 'SafeRequestTest', as well as the interal immune character set from PercentCodec implementation. --- .../codecs/AbstractCodecCharacterTest.java | 72 ++++++++++++ .../esapi/codecs/AbstractCodecStringTest.java | 61 ++++++++++ .../codecs/PercentCodecCharacterTest.java | 108 ++++++++++++++++++ .../esapi/codecs/PercentCodecStringTest.java | 85 ++++++++++++++ 4 files changed, 326 insertions(+) create mode 100644 src/test/java/org/owasp/esapi/codecs/AbstractCodecCharacterTest.java create mode 100644 src/test/java/org/owasp/esapi/codecs/AbstractCodecStringTest.java create mode 100644 src/test/java/org/owasp/esapi/codecs/PercentCodecCharacterTest.java create mode 100644 src/test/java/org/owasp/esapi/codecs/PercentCodecStringTest.java diff --git a/src/test/java/org/owasp/esapi/codecs/AbstractCodecCharacterTest.java b/src/test/java/org/owasp/esapi/codecs/AbstractCodecCharacterTest.java new file mode 100644 index 000000000..f6b708a67 --- /dev/null +++ b/src/test/java/org/owasp/esapi/codecs/AbstractCodecCharacterTest.java @@ -0,0 +1,72 @@ +package org.owasp.esapi.codecs; + +import java.util.Arrays; + +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.owasp.esapi.codecs.Codec; +import org.owasp.esapi.codecs.PushbackString; + + +/** + * FIXME: Document intent of class. General Function, purpose of creation, intended feature, etc. + * Why do people care this exists? + * @author Jeremiah + * @since Jan 20, 2018 + * + */ +@RunWith(Parameterized.class) +public abstract class AbstractCodecCharacterTest { + + protected static class CodecCharacterTestTuple { + Codec codec; + char[] encodeImmune; + String input; + Character decodedValue; + String description; + /** {@inheritDoc}*/ + + @Override + public String toString() { + return description != null ? description : codec.getClass().getSimpleName() + " "+input; + } + } + + + protected final Codec codec; + protected final String input; + protected final char[] encodeImmune; + protected final Character decodedValue; + + public AbstractCodecCharacterTest(CodecCharacterTestTuple tuple) { + this.codec = tuple.codec; + this.input = tuple.input; + this.decodedValue = tuple.decodedValue; + this.encodeImmune = tuple.encodeImmune; + } + + @Test + public void testEncodeCharacter() { + Assert.assertEquals(input, codec.encodeCharacter(encodeImmune, decodedValue)); + } + + @Test + public void testEncode() { + String expected = Arrays.asList(encodeImmune).contains(decodedValue) ? decodedValue.toString() : input; + Assert.assertEquals(expected, codec.encode(encodeImmune, decodedValue.toString())); + } + + @Test + public void testDecode() { + Assert.assertEquals(decodedValue.toString(), codec.decode(input)); + } + + + @Test + public void testDecodePushbackSequence() { + Assert.assertEquals(decodedValue, codec.decodeCharacter(new PushbackString(input))); + } + +} diff --git a/src/test/java/org/owasp/esapi/codecs/AbstractCodecStringTest.java b/src/test/java/org/owasp/esapi/codecs/AbstractCodecStringTest.java new file mode 100644 index 000000000..27d0ea12e --- /dev/null +++ b/src/test/java/org/owasp/esapi/codecs/AbstractCodecStringTest.java @@ -0,0 +1,61 @@ +package org.owasp.esapi.codecs; + +import java.util.Arrays; + +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.owasp.esapi.codecs.Codec; + + +/** + * FIXME: Document intent of class. General Function, purpose of creation, intended feature, etc. + * Why do people care this exists? + * @author Jeremiah + * @since Jan 20, 2018 + * + */ +@RunWith(Parameterized.class) +public abstract class AbstractCodecStringTest { + + protected static class CodecStringTestTuple { + Codec codec; + char[] encodeImmune; + String input; + String decodedValue; + String description; + /** {@inheritDoc}*/ + + @Override + public String toString() { + return description != null ? description : codec.getClass().getSimpleName() + " "+input; + } + } + + + private final Codec codec; + private final String input; + private final char[] encodeImmune; + private final String decodedValue; + + public AbstractCodecStringTest(CodecStringTestTuple tuple) { + this.codec = tuple.codec; + this.input = tuple.input; + this.decodedValue = tuple.decodedValue; + this.encodeImmune = tuple.encodeImmune; + } + + @Test + public void testDecode() { + Assert.assertEquals(decodedValue, codec.decode(input)); + } + + + @Test + public void testEncode() { + String expected = Arrays.asList(encodeImmune).contains(decodedValue) ? decodedValue : input; + Assert.assertEquals(expected, codec.encode(encodeImmune, decodedValue)); + } + +} diff --git a/src/test/java/org/owasp/esapi/codecs/PercentCodecCharacterTest.java b/src/test/java/org/owasp/esapi/codecs/PercentCodecCharacterTest.java new file mode 100644 index 000000000..9617ccb9c --- /dev/null +++ b/src/test/java/org/owasp/esapi/codecs/PercentCodecCharacterTest.java @@ -0,0 +1,108 @@ +package org.owasp.esapi.codecs; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import org.junit.Assert; +import org.junit.Test; +import org.junit.runners.Parameterized.Parameters; +import org.owasp.esapi.codecs.PercentCodec; +import org.owasp.esapi.codecs.PushbackString; + +/** + * FIXME: Document intent of class. General Function, purpose of creation, intended feature, etc. + * Why do people care this exists? + * @author Jeremiah + * @since Jan 20, 2018 + * + */ +public class PercentCodecCharacterTest extends AbstractCodecCharacterTest { + private static final char[] PERCENT_CODEC_IMMUNE; + + static { + /* + * The percent codec contains a unique immune character set which include letters and numbers that will not be transformed. + * + * It is being replicated here to allow the test to reasonably expect the correct state back. + */ + List immune = new ArrayList<>(); + // 65 - 90 (capital letters) 97 - 122 lower case 48 - 57 digits + //numbers + for (int index = 48 ; index < 58; index ++) { + immune.add((char)index); + } + //letters + for (int index = 65 ; index < 91; index ++) { + Character capsChar = (char)index; + immune.add(capsChar); + immune.add(Character.toLowerCase(capsChar)); + } + + PERCENT_CODEC_IMMUNE = new char[immune.size()]; + for (int index = 0; index < immune.size(); index++) { + PERCENT_CODEC_IMMUNE[index] = immune.get(index).charValue(); + } + } + + @Parameters(name="{0}") + public static Collection buildTests() { + Collection tests = new ArrayList<>(); + + Collection tuples = new ArrayList<>(); + tuples.add(newTuple("%3C",Character.valueOf('<'))); + + tuples.add(newTuple("%C4%80",Character.valueOf((char)0x100))); + tuples.add(newTuple("%00",Character.MIN_VALUE)); + tuples.add(newTuple("%3D",'=')); + tuples.add(newTuple("%26",'&')); + + for (char c : PERCENT_CODEC_IMMUNE) { + tuples.add(newTuple(Character.toString(c), c)); + } + + for (CodecCharacterTestTuple tuple : tuples) { + tests.add(new Object[]{tuple}); + } + + return tests; + } + + + + private static CodecCharacterTestTuple newTuple(String encodedInput, Character decoded) { + CodecCharacterTestTuple tuple = new CodecCharacterTestTuple(); + tuple.codec = new PercentCodec(); + tuple.encodeImmune = PERCENT_CODEC_IMMUNE; + tuple.decodedValue = decoded; + tuple.input = encodedInput; + + return tuple; + } + /** + * @param tuple + */ + public PercentCodecCharacterTest(CodecCharacterTestTuple tuple) { + super(tuple); + } + + + @Override + @Test + public void testDecodePushbackSequence() { + //If the input is not decoded, then null should be returned and the pushback string index should be unchanged. + //If the input is decode then the decoded value should be returned, and the pushback string index should have progressed forward. + + PushbackString pbs = new PushbackString(input); + int startIndex = pbs.index(); + boolean shouldDecode = input.startsWith("%"); + if (shouldDecode) { + Assert.assertEquals(decodedValue, codec.decodeCharacter(pbs)); + Assert.assertTrue(startIndex < pbs.index()); + } else { + Assert.assertEquals(null, codec.decodeCharacter(pbs)); + Assert.assertEquals(startIndex, pbs.index()); + } + } + +} diff --git a/src/test/java/org/owasp/esapi/codecs/PercentCodecStringTest.java b/src/test/java/org/owasp/esapi/codecs/PercentCodecStringTest.java new file mode 100644 index 000000000..91ca18b87 --- /dev/null +++ b/src/test/java/org/owasp/esapi/codecs/PercentCodecStringTest.java @@ -0,0 +1,85 @@ +package org.owasp.esapi.codecs; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import org.junit.runners.Parameterized.Parameters; +import org.owasp.esapi.codecs.PercentCodec; + +/** + * FIXME: Document intent of class. General Function, purpose of creation, intended feature, etc. + * Why do people care this exists? + * + * @author Jeremiah + * @since Jan 20, 2018 + */ +public class PercentCodecStringTest extends AbstractCodecStringTest { + private static final char[] PERCENT_CODEC_IMMUNE; + + static { + /* + * The percent codec contains a unique immune character set which include letters and numbers that will not be transformed. + * + * It is being replicated here to allow the test to reasonably expect the correct state back. + */ + List immune = new ArrayList(); + // 65 - 90 (capital letters) 97 - 122 lower case 48 - 57 digits + //numbers + for (int index = 48 ; index < 58; index ++) { + immune.add((char)index); + } + //letters + for (int index = 65 ; index < 91; index ++) { + Character capsChar = (char)index; + immune.add(capsChar); + immune.add(Character.toLowerCase(capsChar)); + } + + PERCENT_CODEC_IMMUNE = new char[immune.size()]; + for (int index = 0; index < immune.size(); index++) { + PERCENT_CODEC_IMMUNE[index] = immune.get(index).charValue(); + } + } + + @Parameters(name = "{0}") + public static Collection buildTests() { + Collection tests = new ArrayList<>(); + + List tuples = new ArrayList<>(); + tuples.add(newTuple("%3C", "<")); + + tuples.add(newTuple("%C4%80", (char) 0x100)); + tuples.add(newTuple("%00", Character.MIN_VALUE)); + tuples.add(newTuple("%3D", '=')); + tuples.add(newTuple("%26", '&')); + + for (char c : PERCENT_CODEC_IMMUNE) { + tuples.add(newTuple(Character.toString(c), c)); + } + + for (CodecStringTestTuple tuple : tuples) { + tests.add(new Object[] { tuple }); + } + + return tests; + } + + private static CodecStringTestTuple newTuple(String input, Object decoded) { + CodecStringTestTuple tuple = new CodecStringTestTuple(); + tuple.codec = new PercentCodec(); + tuple.encodeImmune = PERCENT_CODEC_IMMUNE; + tuple.decodedValue = decoded.toString(); + tuple.input = input; + + return tuple; + } + + /** + * @param tuple + */ + public PercentCodecStringTest(CodecStringTestTuple tuple) { + super(tuple); + } + +} From 6f60045cdd20e7b32cb2814734e58eb2c23e8ffe Mon Sep 17 00:00:00 2001 From: Jeremiah Stacey Date: Sat, 20 Jan 2018 09:38:11 -0600 Subject: [PATCH 0429/1069] Removing unstable tests The tests removed from SafeRequestTest were attempting to validate environment state as well as the unit functionality. The intent of these tests has been distributed to a workflow test check (SecurityWrapperRequestTest), a whitelist validation test (EsapiWhitelistValidationPatternTester), and tests for the PercentCodec's impact on input data (PercentCodecStringTest, PercentCodecCharacterTest). The combination of these tests assert that the pieces that were composing the removed content will function under more explict and controlled conditions. Changes to configuration will nChanges to configuration will now be revealed closer to the component that is directly impacted. --- .../owasp/esapi/filters/SafeRequestTest.java | 61 ------------------- 1 file changed, 61 deletions(-) diff --git a/src/test/java/org/owasp/esapi/filters/SafeRequestTest.java b/src/test/java/org/owasp/esapi/filters/SafeRequestTest.java index e404f8c46..db2f0d770 100644 --- a/src/test/java/org/owasp/esapi/filters/SafeRequestTest.java +++ b/src/test/java/org/owasp/esapi/filters/SafeRequestTest.java @@ -94,67 +94,6 @@ public void testGetQueryStringNull() assertNull(wrappedReq.getQueryString()); } - public void testGetQueryStringNonNull() - { - MockHttpServletRequest req = new MockHttpServletRequest(); - SecurityWrapperRequest wrappedReq; - - req.setQueryString("a=b"); - wrappedReq = new SecurityWrapperRequest(req); - assertEquals("a=b",wrappedReq.getQueryString()); - } - - public void testGetQueryStringNUL() - { - MockHttpServletRequest req = new MockHttpServletRequest(); - SecurityWrapperRequest wrappedReq; - - req.setQueryString("a=\u0000"); - wrappedReq = new SecurityWrapperRequest(req); - assertEquals("",wrappedReq.getQueryString()); - } - - public void testGetQueryStringPercent() - { - MockHttpServletRequest req = new MockHttpServletRequest(); - SecurityWrapperRequest wrappedReq; - - req.setQueryString("a=%62"); - wrappedReq = new SecurityWrapperRequest(req); - assertEquals("a=b",wrappedReq.getQueryString()); - } - - public void testGetQueryStringPercentNUL() - { - MockHttpServletRequest req = new MockHttpServletRequest(); - SecurityWrapperRequest wrappedReq; - - req.setQueryString("a=%00"); - wrappedReq = new SecurityWrapperRequest(req); - assertEquals("a="+Character.MIN_VALUE, wrappedReq.getQueryString()); - } - - public void testGetQueryStringPercentEquals() - { - MockHttpServletRequest req = new MockHttpServletRequest(); - SecurityWrapperRequest wrappedReq; - - req.setQueryString("a=%3d"); - wrappedReq = new SecurityWrapperRequest(req); - assertEquals("a==",wrappedReq.getQueryString()); - } - - public void testGetQueryStringPercentAmpersand() - { - MockHttpServletRequest req = new MockHttpServletRequest(); - SecurityWrapperRequest wrappedReq; - - req.setQueryString("a=%26b"); - wrappedReq = new SecurityWrapperRequest(req); - assertEquals("a=&b",wrappedReq.getQueryString()); - } - - // Test to ensure null-value contract defined by ServletRequest.getParameterNames(String) is met. public void testGetParameterValuesReturnsNullWhenParameterDoesNotExistInRequest() { MockHttpServletRequest request = new MockHttpServletRequest(); From 9d3d93a39d5192bac7835317f76a1cd1a26c921c Mon Sep 17 00:00:00 2001 From: Jeremiah Stacey Date: Mon, 22 Jan 2018 08:33:48 -0600 Subject: [PATCH 0430/1069] Updating SecurityWrapperRequestTest Test cleanup. Extracting constants & static imports. Adding documentation. Fixing spelling errors. --- .../filters/SecurityWrapperRequestTest.java | 137 ++++++++++++------ 1 file changed, 92 insertions(+), 45 deletions(-) diff --git a/src/test/java/org/owasp/esapi/filters/SecurityWrapperRequestTest.java b/src/test/java/org/owasp/esapi/filters/SecurityWrapperRequestTest.java index 3200700c1..bf1a2d627 100644 --- a/src/test/java/org/owasp/esapi/filters/SecurityWrapperRequestTest.java +++ b/src/test/java/org/owasp/esapi/filters/SecurityWrapperRequestTest.java @@ -1,35 +1,48 @@ package org.owasp.esapi.filters; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.mockito.Matchers.anyBoolean; +import static org.mockito.Matchers.anyInt; +import static org.mockito.Matchers.anyString; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + import javax.servlet.http.HttpServletRequest; -import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; -import org.mockito.Matchers; import org.mockito.Mock; -import org.mockito.Mockito; import org.owasp.esapi.ESAPI; import org.owasp.esapi.SecurityConfiguration; import org.owasp.esapi.Validator; import org.owasp.esapi.errors.IntrusionException; import org.owasp.esapi.errors.ValidationException; -import org.owasp.esapi.filters.SecurityWrapperRequest; import org.powermock.api.mockito.PowerMockito; import org.powermock.core.classloader.annotations.PrepareForTest; import org.powermock.modules.junit4.PowerMockRunner; /** - * FIXME: Document intent of class. General Function, purpose of creation, intended feature, etc. - * Why do people care this exists? - * - * @author Jeremiah - * @since Jan 3, 2018 + * Unit tests for {@link SecurityWrapperRequest}. + *
+ * This test uses static context mocking! This can affect certain behaviors if it is executed in a JVM container with + * other tests depending on the same static reference - Which is going to be everything. + *
+ * This may affect some test environments, mostly IDE's. It is not expected that this impacts the Maven build, as + * surefire plugin isolates JVM's for tests during that phase. */ @RunWith(PowerMockRunner.class) @PrepareForTest(ESAPI.class) public class SecurityWrapperRequestTest { + private static final String ESAPI_VALIDATOR_GETTER_METHOD_NAME = "validator"; + private static final String ESAPY_SECURITY_CONFIGURATION_GETTER_METHOD_NAME = "securityConfiguration"; + private static final String SECURITY_CONFIGURATION_LENGTH_KEY_NAME = "HttpUtilities.URILENGTH"; + + private static final int SECURITY_CONFIGURATION_MOCK_LENGTH = 255; + + private static final String QUERY_STRING_CANONCALIZE_TYPE_KEY = "HTTPQueryString"; @Mock private HttpServletRequest mockRequest; @@ -41,68 +54,102 @@ public class SecurityWrapperRequestTest { @Before public void setup() throws Exception { PowerMockito.mockStatic(ESAPI.class); - PowerMockito.when(ESAPI.class, "validator").thenReturn(mockValidator); - PowerMockito.when(ESAPI.class, "securityConfiguration").thenReturn(mockSecConfig); + PowerMockito.when(ESAPI.class, ESAPI_VALIDATOR_GETTER_METHOD_NAME).thenReturn(mockValidator); + PowerMockito.when(ESAPI.class, ESAPY_SECURITY_CONFIGURATION_GETTER_METHOD_NAME).thenReturn(mockSecConfig); } - + + /** + * Workflow test for happy-path getQueryString. Asserts delegation calls and parameters to delegate + * behaviors. + */ @Test - public void testGetQueryString() throws IntrusionException, ValidationException { - String queryString = "queryString"; - int maxLength = 255; + public void testGetQueryString() throws Exception { + String originalQuery = "queryString"; + String canonicalizedResponse = "canonicalized_query"; ArgumentCaptor inputCapture = ArgumentCaptor.forClass(String.class); ArgumentCaptor typeCapture = ArgumentCaptor.forClass(String.class); - ArgumentCaptor lenghtCapture = ArgumentCaptor.forClass(Integer.class); + ArgumentCaptor lengthCapture = ArgumentCaptor.forClass(Integer.class); ArgumentCaptor allowNullCapture = ArgumentCaptor.forClass(Boolean.class); - PowerMockito.when(mockValidator.getValidInput(Matchers.anyString(), inputCapture.capture(), typeCapture - .capture(), lenghtCapture.capture(), allowNullCapture.capture())).thenReturn("canonicalized"); - PowerMockito.when(mockSecConfig.getIntProp("HttpUtilities.URILENGTH")).thenReturn(maxLength); - PowerMockito.when(mockRequest.getQueryString()).thenReturn(queryString); + String context = anyString(); + String input = inputCapture.capture(); + String type = typeCapture.capture(); + Integer length = lengthCapture.capture(); + Boolean allowNull = allowNullCapture.capture(); + + PowerMockito.when(mockValidator.getValidInput(context, input, type, length, allowNull)).thenReturn( + canonicalizedResponse); + PowerMockito.when(mockSecConfig.getIntProp(SECURITY_CONFIGURATION_LENGTH_KEY_NAME)).thenReturn( + SECURITY_CONFIGURATION_MOCK_LENGTH); + + PowerMockito.when(mockRequest.getQueryString()).thenReturn(originalQuery); SecurityWrapperRequest request = new SecurityWrapperRequest(mockRequest); String rval = request.getQueryString(); - Assert.assertEquals("canonicalized", rval); + assertEquals(canonicalizedResponse, rval); - Assert.assertEquals(queryString, inputCapture.getValue()); - Assert.assertEquals("HTTPQueryString", typeCapture.getValue()); - Assert.assertTrue(maxLength == lenghtCapture.getValue().intValue()); - Assert.assertEquals(true, allowNullCapture.getValue()); + String actualInput = inputCapture.getValue(); + String actualType = typeCapture.getValue(); + int actualLength = lengthCapture.getValue().intValue(); + boolean actualAllowNull = allowNullCapture.getValue().booleanValue(); - Mockito.verify(mockValidator, Mockito.times(1)).getValidInput(Matchers.anyString(), Matchers.anyString(), - Matchers.anyString(), Matchers.anyInt(), Matchers.anyBoolean()); - Mockito.verify(mockSecConfig, Mockito.times(1)).getIntProp("HttpUtilities.URILENGTH"); - Mockito.verify(mockRequest, Mockito.times(1)).getQueryString(); + assertEquals(originalQuery, actualInput); + assertEquals(QUERY_STRING_CANONCALIZE_TYPE_KEY, actualType); + assertTrue(SECURITY_CONFIGURATION_MOCK_LENGTH == actualLength); + assertTrue(actualAllowNull); + + verify(mockValidator, times(1)).getValidInput(anyString(), anyString(), anyString(), anyInt(), anyBoolean()); + verify(mockSecConfig, times(1)).getIntProp(SECURITY_CONFIGURATION_LENGTH_KEY_NAME); + verify(mockRequest, times(1)).getQueryString(); } + /** + * Test for getQueryString when validation throws an Exception. + *
+ * Asserts delegation calls and parameters to delegate behaviors. + */ @SuppressWarnings("unchecked") @Test public void testGetQueryStringCanonicalizeException() throws IntrusionException, ValidationException { - String queryString = "queryString"; - int maxLength = 255; + String originalQuery = "queryString"; ArgumentCaptor inputCapture = ArgumentCaptor.forClass(String.class); ArgumentCaptor typeCapture = ArgumentCaptor.forClass(String.class); - ArgumentCaptor lenghtCapture = ArgumentCaptor.forClass(Integer.class); + ArgumentCaptor lengthCapture = ArgumentCaptor.forClass(Integer.class); ArgumentCaptor allowNullCapture = ArgumentCaptor.forClass(Boolean.class); - PowerMockito.when(mockValidator.getValidInput(Matchers.anyString(), inputCapture.capture(), typeCapture - .capture(), lenghtCapture.capture(), allowNullCapture.capture())).thenThrow(ValidationException.class); - PowerMockito.when(mockSecConfig.getIntProp("HttpUtilities.URILENGTH")).thenReturn(maxLength); - PowerMockito.when(mockRequest.getQueryString()).thenReturn(queryString); + String context = anyString(); + String input = inputCapture.capture(); + String type = typeCapture.capture(); + Integer length = lengthCapture.capture(); + Boolean allowNull = allowNullCapture.capture(); + + PowerMockito.when(mockValidator.getValidInput(context, input, type, length, allowNull)).thenThrow( + ValidationException.class); + PowerMockito.when(mockSecConfig.getIntProp(SECURITY_CONFIGURATION_LENGTH_KEY_NAME)).thenReturn( + SECURITY_CONFIGURATION_MOCK_LENGTH); + + PowerMockito.when(mockRequest.getQueryString()).thenReturn(originalQuery); SecurityWrapperRequest request = new SecurityWrapperRequest(mockRequest); String rval = request.getQueryString(); - Assert.assertEquals("", rval); - Assert.assertEquals(queryString, inputCapture.getValue()); - Assert.assertEquals("HTTPQueryString", typeCapture.getValue()); - Assert.assertTrue(maxLength == lenghtCapture.getValue().intValue()); - Assert.assertEquals(true, allowNullCapture.getValue()); + assertTrue("SecurityWrapperRequest should return an empty String when an exception occurs in validation", rval + .isEmpty()); + + String actualInput = inputCapture.getValue(); + String actualType = typeCapture.getValue(); + int actualLength = lengthCapture.getValue().intValue(); + boolean actualAllowNull = allowNullCapture.getValue().booleanValue(); + + assertEquals(originalQuery, actualInput); + assertEquals(QUERY_STRING_CANONCALIZE_TYPE_KEY, actualType); + assertTrue(SECURITY_CONFIGURATION_MOCK_LENGTH == actualLength); + assertTrue(actualAllowNull); - Mockito.verify(mockValidator, Mockito.times(1)).getValidInput(Matchers.anyString(), Matchers.anyString(), - Matchers.anyString(), Matchers.anyInt(), Matchers.anyBoolean()); - Mockito.verify(mockSecConfig, Mockito.times(1)).getIntProp("HttpUtilities.URILENGTH"); - Mockito.verify(mockRequest, Mockito.times(1)).getQueryString(); + verify(mockValidator, times(1)).getValidInput(anyString(), anyString(), anyString(), anyInt(), anyBoolean()); + verify(mockSecConfig, times(1)).getIntProp(SECURITY_CONFIGURATION_LENGTH_KEY_NAME); + verify(mockRequest, times(1)).getQueryString(); } } From c3870f660439e39a22a14ca013c8da17aa59eb66 Mon Sep 17 00:00:00 2001 From: Jeremiah Stacey Date: Mon, 22 Jan 2018 08:47:34 -0600 Subject: [PATCH 0431/1069] Pattern Validation Test Cleanup Adding documentation to the AbstractPatternTest to help with usability. Extracting message constant to a class var in EsapiWhitelistValidaitonPatternTests for improved readability. --- .../esapi/reference/AbstractPatternTest.java | 35 ++++++++------ ...EsapiWhitelistValidationPatternTester.java | 46 +++++++++---------- 2 files changed, 43 insertions(+), 38 deletions(-) diff --git a/src/test/java/org/owasp/esapi/reference/AbstractPatternTest.java b/src/test/java/org/owasp/esapi/reference/AbstractPatternTest.java index 3fd72ebaf..9cf1e0e20 100644 --- a/src/test/java/org/owasp/esapi/reference/AbstractPatternTest.java +++ b/src/test/java/org/owasp/esapi/reference/AbstractPatternTest.java @@ -7,43 +7,50 @@ import org.junit.runner.RunWith; import org.junit.runners.Parameterized; - /** - * FIXME: Document intent of class. General Function, purpose of creation, intended feature, etc. - * Why do people care this exists? - * @author Jeremiah - * @since Jan 20, 2018 - * + * Abstract parameterized test case meant to assist with verifying regular expressions in test scope. + *
+ * Sub-classes are expected to provide instances of {@link PatternTestTuple} to this instance. + *
+ * For better test naming output specify {@link PatternTestTuple#description} and use {@code} @Parameters (name="{0}")}, + * where '0' is the index that the PatternTestTuple reference appears in the constructor. */ -@RunWith (Parameterized.class) +@RunWith(Parameterized.class) public abstract class AbstractPatternTest { - + + /** + * Test tuple for Pattern validation. + */ protected static class PatternTestTuple { + /** String value to be tested against the compiled regex reference. */ String input; + /** Regular expression string that will be compiled and be passed the input. */ String regex; + /** Test Expectation whether input should match the compiled regex. */ boolean shouldMatch; + /** Optional field to override the toString value of this tuple. */ String description; - /** {@inheritDoc}*/ + + /** {@inheritDoc} */ @Override public String toString() { - return description != null ? description : regex; + return description != null ? description : regex; } } private String input; private Pattern pattern; private boolean shouldMatch; - - + public AbstractPatternTest(PatternTestTuple tuple) { this.input = tuple.input; this.pattern = Pattern.compile(tuple.regex); this.shouldMatch = tuple.shouldMatch; } - + @Test public void checkPatternMatches() { Assert.assertEquals(shouldMatch, pattern.matcher(input).matches()); } - + } diff --git a/src/test/java/org/owasp/esapi/reference/EsapiWhitelistValidationPatternTester.java b/src/test/java/org/owasp/esapi/reference/EsapiWhitelistValidationPatternTester.java index 4a1bd5ffb..d00d4c19a 100644 --- a/src/test/java/org/owasp/esapi/reference/EsapiWhitelistValidationPatternTester.java +++ b/src/test/java/org/owasp/esapi/reference/EsapiWhitelistValidationPatternTester.java @@ -10,9 +10,7 @@ /** * Extension of the AbstractPatternTest which focuses on asserting that the default whitelist regex values applied in * the validation process are performing the intended function in the environment. - * *
- * * If the regex values in this test are found to not match the running environment configurations, then the tests will * be skipped. * @@ -20,26 +18,30 @@ * @since Jan 20, 2018 */ public class EsapiWhitelistValidationPatternTester extends AbstractPatternTest { - //See ESAPI.properties - private static final String HTTP_QUERY_STRING_PROP_NAME="HTTPQueryString"; - private static final String HTTP_QUERY_STRING_REGEX="^([a-zA-Z0-9_\\-]{1,32}=[\\p{L}\\p{N}.\\-/+=_ !$*?@%]*&?)*$"; + // See ESAPI.properties + private static final String HTTP_QUERY_STRING_PROP_NAME = "HTTPQueryString"; + private static final String HTTP_QUERY_STRING_REGEX = "^([a-zA-Z0-9_\\-]{1,32}=[\\p{L}\\p{N}.\\-/+=_ !$*?@%]*&?)*$"; + + private static final String CONFIGURATION_PATTERN_MISMATCH_MESSAGE = "The regular expression specified does not match the configuration settings.\n" + + "If the value was changed from the ESAPI default, it is recommended to copy " + + "this class into your project, update the regex being tested, and update all " + + "associated input expectations for your unique environment."; @Parameters(name = "{0}-{1}") public static Collection createDefaultPatternTests() { Collection parameters = new ArrayList<>(); - - for(PatternTestTuple tuple : buildHttpQueryStringTests()) { + + for (PatternTestTuple tuple : buildHttpQueryStringTests()) { parameters.add(new Object[] { HTTP_QUERY_STRING_PROP_NAME, tuple }); } - return parameters; } - + private static Collection buildHttpQueryStringTests() { - Collection httpQueryStringTests = new ArrayList<>(); - - //MATCHING CASES + Collection httpQueryStringTests = new ArrayList<>(); + + // MATCHING CASES PatternTestTuple tuple = newHttpQueryStringTuple("Default Case", "b", true); httpQueryStringTests.add(tuple); tuple = newHttpQueryStringTuple("Percent Encoded Value", "%62", true); @@ -48,27 +50,27 @@ private static Collection buildHttpQueryStringTests() { httpQueryStringTests.add(tuple); tuple = newHttpQueryStringTuple("Double Equals", "=", true); httpQueryStringTests.add(tuple); - - //NON-MATCHING CASES + + // NON-MATCHING CASES tuple = newHttpQueryStringTuple("Ampersand In Value", "&b", false); httpQueryStringTests.add(tuple); - tuple = newHttpQueryStringTuple("Null Character", ""+Character.MIN_VALUE, false); + tuple = newHttpQueryStringTuple("Null Character", "" + Character.MIN_VALUE, false); httpQueryStringTests.add(tuple); tuple = newHttpQueryStringTuple("Encoded Null Character", "\u0000", false); httpQueryStringTests.add(tuple); - + return httpQueryStringTests; } private static PatternTestTuple newHttpQueryStringTuple(String description, String value, boolean shouldPass) { PatternTestTuple tuple = new PatternTestTuple(); - tuple.input = "a="+value; + tuple.input = "a=" + value; tuple.shouldMatch = shouldPass; tuple.regex = HTTP_QUERY_STRING_REGEX; tuple.description = description; return tuple; } - + public EsapiWhitelistValidationPatternTester(String property, PatternTestTuple tuple) { super(tuple); /* @@ -80,12 +82,8 @@ public EsapiWhitelistValidationPatternTester(String property, PatternTestTuple t * behavior. */ DefaultSecurityConfiguration configuration = new DefaultSecurityConfiguration(); - Assume.assumeTrue( - "The regular expression specified does not match the configuration settings.\n" - + "If the value was changed from the ESAPI default, it is recommended to copy " - + "this class into your project, update the regex being tested, and update all " - + "associated input expectations for your unique environment.", - configuration.getValidationPattern(property).toString().equals(tuple.regex)); + Assume.assumeTrue(CONFIGURATION_PATTERN_MISMATCH_MESSAGE, configuration.getValidationPattern(property) + .toString().equals(tuple.regex)); } } From c4d77b142edc14d22fcba6b6d7f8ffc7f0221394 Mon Sep 17 00:00:00 2001 From: Jeremiah Stacey Date: Mon, 22 Jan 2018 09:30:24 -0600 Subject: [PATCH 0432/1069] PercentCodecCharacterTest cleanup Splitting up the PushbackSequence test for better readability. Adding documentation. Whitespace cleanup. --- .../codecs/PercentCodecCharacterTest.java | 105 ++++++++++-------- 1 file changed, 58 insertions(+), 47 deletions(-) diff --git a/src/test/java/org/owasp/esapi/codecs/PercentCodecCharacterTest.java b/src/test/java/org/owasp/esapi/codecs/PercentCodecCharacterTest.java index 9617ccb9c..ca7ddf1b6 100644 --- a/src/test/java/org/owasp/esapi/codecs/PercentCodecCharacterTest.java +++ b/src/test/java/org/owasp/esapi/codecs/PercentCodecCharacterTest.java @@ -11,98 +11,109 @@ import org.owasp.esapi.codecs.PushbackString; /** - * FIXME: Document intent of class. General Function, purpose of creation, intended feature, etc. - * Why do people care this exists? - * @author Jeremiah - * @since Jan 20, 2018 - * + * Codec validation focused on the PercentCodec Character-based api. + * */ public class PercentCodecCharacterTest extends AbstractCodecCharacterTest { private static final char[] PERCENT_CODEC_IMMUNE; static { /* - * The percent codec contains a unique immune character set which include letters and numbers that will not be transformed. - * + * The percent codec contains a unique immune character set which include letters and numbers that will not be + * transformed. * It is being replicated here to allow the test to reasonably expect the correct state back. */ List immune = new ArrayList<>(); // 65 - 90 (capital letters) 97 - 122 lower case 48 - 57 digits - //numbers - for (int index = 48 ; index < 58; index ++) { - immune.add((char)index); + // numbers + for (int index = 48; index < 58; index++) { + immune.add((char) index); } - //letters - for (int index = 65 ; index < 91; index ++) { - Character capsChar = (char)index; + // letters + for (int index = 65; index < 91; index++) { + Character capsChar = (char) index; immune.add(capsChar); - immune.add(Character.toLowerCase(capsChar)); + immune.add(Character.toLowerCase(capsChar)); } - + PERCENT_CODEC_IMMUNE = new char[immune.size()]; for (int index = 0; index < immune.size(); index++) { PERCENT_CODEC_IMMUNE[index] = immune.get(index).charValue(); } } - @Parameters(name="{0}") + @Parameters(name = "{0}") public static Collection buildTests() { Collection tests = new ArrayList<>(); - + Collection tuples = new ArrayList<>(); - tuples.add(newTuple("%3C",Character.valueOf('<'))); - - tuples.add(newTuple("%C4%80",Character.valueOf((char)0x100))); - tuples.add(newTuple("%00",Character.MIN_VALUE)); - tuples.add(newTuple("%3D",'=')); - tuples.add(newTuple("%26",'&')); - + tuples.add(newTuple("%3C", Character.valueOf('<'))); + + tuples.add(newTuple("%C4%80", Character.valueOf((char) 0x100))); + tuples.add(newTuple("%00", Character.MIN_VALUE)); + tuples.add(newTuple("%3D", '=')); + tuples.add(newTuple("%26", '&')); + for (char c : PERCENT_CODEC_IMMUNE) { tuples.add(newTuple(Character.toString(c), c)); } - + for (CodecCharacterTestTuple tuple : tuples) { - tests.add(new Object[]{tuple}); + tests.add(new Object[] { tuple }); } - + return tests; } - - - + private static CodecCharacterTestTuple newTuple(String encodedInput, Character decoded) { CodecCharacterTestTuple tuple = new CodecCharacterTestTuple(); tuple.codec = new PercentCodec(); tuple.encodeImmune = PERCENT_CODEC_IMMUNE; tuple.decodedValue = decoded; tuple.input = encodedInput; - + return tuple; } - /** - * @param tuple - */ + public PercentCodecCharacterTest(CodecCharacterTestTuple tuple) { super(tuple); } - - + @Override @Test public void testDecodePushbackSequence() { - //If the input is not decoded, then null should be returned and the pushback string index should be unchanged. - //If the input is decode then the decoded value should be returned, and the pushback string index should have progressed forward. - - PushbackString pbs = new PushbackString(input); - int startIndex = pbs.index(); - boolean shouldDecode = input.startsWith("%"); - if (shouldDecode) { - Assert.assertEquals(decodedValue, codec.decodeCharacter(pbs)); - Assert.assertTrue(startIndex < pbs.index()); + // check duplicated from PushbackSequence handling in PercentCodec. + boolean inputIsEncoded = input.startsWith("%"); + + if (inputIsEncoded) { + assertInputIsDecodedToValue(); } else { - Assert.assertEquals(null, codec.decodeCharacter(pbs)); - Assert.assertEquals(startIndex, pbs.index()); + assertInputIsDecodedToNull(); } } + /** + * tests that when Input is decoded through a PushbackString that the decodedValue reference is returned and that + * the PushbackString index has incremented. + */ + @SuppressWarnings("unchecked") + private void assertInputIsDecodedToValue() { + PushbackString pbs = new PushbackString(input); + int startIndex = pbs.index(); + Assert.assertEquals(decodedValue, codec.decodeCharacter(pbs)); + Assert.assertTrue(startIndex < pbs.index()); + } + + /** + * tests that when Input is decoded through a PushbackString that null is returned and that the PushbackString index + * remains unchanged. + */ + @SuppressWarnings("unchecked") + private void assertInputIsDecodedToNull() { + PushbackString pbs = new PushbackString(input); + int startIndex = pbs.index(); + Assert.assertEquals(null, codec.decodeCharacter(pbs)); + Assert.assertEquals(startIndex, pbs.index()); + } + } From d44275761b28169fcff1ed9a61450a68855b7a9f Mon Sep 17 00:00:00 2001 From: Jeremiah Stacey Date: Mon, 22 Jan 2018 09:50:51 -0600 Subject: [PATCH 0433/1069] AbstractCodecCharacterTest Cleanup Adding documentation. Updating static imports. --- .../codecs/AbstractCodecCharacterTest.java | 41 ++++++++++++------- 1 file changed, 27 insertions(+), 14 deletions(-) diff --git a/src/test/java/org/owasp/esapi/codecs/AbstractCodecCharacterTest.java b/src/test/java/org/owasp/esapi/codecs/AbstractCodecCharacterTest.java index f6b708a67..1101be47f 100644 --- a/src/test/java/org/owasp/esapi/codecs/AbstractCodecCharacterTest.java +++ b/src/test/java/org/owasp/esapi/codecs/AbstractCodecCharacterTest.java @@ -1,30 +1,36 @@ package org.owasp.esapi.codecs; +import static org.junit.Assert.assertEquals; + import java.util.Arrays; -import org.junit.Assert; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; -import org.owasp.esapi.codecs.Codec; -import org.owasp.esapi.codecs.PushbackString; /** - * FIXME: Document intent of class. General Function, purpose of creation, intended feature, etc. - * Why do people care this exists? - * @author Jeremiah - * @since Jan 20, 2018 - * + * Abstract parameterized test case meant to assist with verifying Character api of a Codec implementation. + *
+ * Sub-classes are expected to provide instances of {@link CodecCharacterTestTuple} to this instance. + *
+ * For better test naming output specify {@link CodecCharacterTestTuple#description} and use {@code} @Parameters (name="{0}")}, + * where '0' is the index that the CodecCharacterTestTuple reference appears in the constructor. */ @RunWith(Parameterized.class) public abstract class AbstractCodecCharacterTest { + /** Test Data Tuple.*/ protected static class CodecCharacterTestTuple { + /** Codec reference to be tested.*/ Codec codec; + /** Set of characters that should be considered 'immune' from decoding processes.*/ char[] encodeImmune; + /** A String representing a single encoded character.*/ String input; + /** The single character that input represents.*/ Character decodedValue; + /** Optional field to override the toString value of this tuple. */ String description; /** {@inheritDoc}*/ @@ -46,27 +52,34 @@ public AbstractCodecCharacterTest(CodecCharacterTestTuple tuple) { this.decodedValue = tuple.decodedValue; this.encodeImmune = tuple.encodeImmune; } - + + /** Checks that the input value matches the result of the codec encoding the decoded value. */ @Test public void testEncodeCharacter() { - Assert.assertEquals(input, codec.encodeCharacter(encodeImmune, decodedValue)); + assertEquals(input, codec.encodeCharacter(encodeImmune, decodedValue)); } + /** Checks encoding the character as a String. + *
+ * If the decoded value is in the immunity list, the the decoded value should be returned from the encode call. + * Otherwise, input is expected as the return. + */ @Test public void testEncode() { String expected = Arrays.asList(encodeImmune).contains(decodedValue) ? decodedValue.toString() : input; - Assert.assertEquals(expected, codec.encode(encodeImmune, decodedValue.toString())); + assertEquals(expected, codec.encode(encodeImmune, decodedValue.toString())); } + /** Checks that decoding the input value yeilds the decodedValue.*/ @Test public void testDecode() { - Assert.assertEquals(decodedValue.toString(), codec.decode(input)); + assertEquals(decodedValue.toString(), codec.decode(input)); } - + /** Checks that the encoded input String is correctly decoded to the single decodedValue character reference.*/ @Test public void testDecodePushbackSequence() { - Assert.assertEquals(decodedValue, codec.decodeCharacter(new PushbackString(input))); + assertEquals(decodedValue, codec.decodeCharacter(new PushbackString(input))); } } From dc20f53efbf026b9470ecc0092c25cbc7243b9f8 Mon Sep 17 00:00:00 2001 From: Jeremiah Stacey Date: Mon, 22 Jan 2018 09:58:25 -0600 Subject: [PATCH 0434/1069] AbstractCodecStringTest Cleanup Adding documentation. Removed a copy/pasta check from testEncode (originally from AbstractCodecCharacterTest). --- .../esapi/codecs/AbstractCodecStringTest.java | 26 ++++++++++++------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/src/test/java/org/owasp/esapi/codecs/AbstractCodecStringTest.java b/src/test/java/org/owasp/esapi/codecs/AbstractCodecStringTest.java index 27d0ea12e..eb31da763 100644 --- a/src/test/java/org/owasp/esapi/codecs/AbstractCodecStringTest.java +++ b/src/test/java/org/owasp/esapi/codecs/AbstractCodecStringTest.java @@ -7,23 +7,30 @@ import org.junit.runner.RunWith; import org.junit.runners.Parameterized; import org.owasp.esapi.codecs.Codec; +import org.owasp.esapi.codecs.AbstractCodecCharacterTest.CodecCharacterTestTuple; /** - * FIXME: Document intent of class. General Function, purpose of creation, intended feature, etc. - * Why do people care this exists? - * @author Jeremiah - * @since Jan 20, 2018 - * + * Abstract parameterized test case meant to assist with verifying String api of a Codec implementation. + *
+ * Sub-classes are expected to provide instances of {@link CodecStringTestTuple} to this instance. + *
+ * For better test naming output specify {@link CodecStringTestTuple#description} and use {@code} @Parameters (name="{0}")}, + * where '0' is the index that the CodecStringTestTuple reference appears in the constructor. */ @RunWith(Parameterized.class) public abstract class AbstractCodecStringTest { protected static class CodecStringTestTuple { + /** Codec reference to be tested.*/ Codec codec; + /** Set of characters that should be considered 'immune' from decoding processes.*/ char[] encodeImmune; + /** A String representing a contextually encoded String.*/ String input; + /** The decoded representation of the input value.*/ String decodedValue; + /** Optional field to override the toString value of this tuple. */ String description; /** {@inheritDoc}*/ @@ -32,8 +39,6 @@ public String toString() { return description != null ? description : codec.getClass().getSimpleName() + " "+input; } } - - private final Codec codec; private final String input; private final char[] encodeImmune; @@ -46,16 +51,17 @@ public AbstractCodecStringTest(CodecStringTestTuple tuple) { this.encodeImmune = tuple.encodeImmune; } + + /** Checks that when the input is decoded using the specified codec, that the return matches the expected decoded value.*/ @Test public void testDecode() { Assert.assertEquals(decodedValue, codec.decode(input)); } - + /** Checks that when the decoded value is encoded (using immunity), that the return matches the provided input.*/ @Test public void testEncode() { - String expected = Arrays.asList(encodeImmune).contains(decodedValue) ? decodedValue : input; - Assert.assertEquals(expected, codec.encode(encodeImmune, decodedValue)); + Assert.assertEquals(input, codec.encode(encodeImmune, decodedValue)); } } From eb42bc0598099ae19b0896add592740aa718a38b Mon Sep 17 00:00:00 2001 From: Jeremiah Stacey Date: Mon, 22 Jan 2018 10:01:39 -0600 Subject: [PATCH 0435/1069] Round2: AbstractCodecStringTest cleanup Correcting imports that snuck in while I was working on documentation updates. --- .../java/org/owasp/esapi/codecs/AbstractCodecStringTest.java | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/test/java/org/owasp/esapi/codecs/AbstractCodecStringTest.java b/src/test/java/org/owasp/esapi/codecs/AbstractCodecStringTest.java index eb31da763..b3fe53156 100644 --- a/src/test/java/org/owasp/esapi/codecs/AbstractCodecStringTest.java +++ b/src/test/java/org/owasp/esapi/codecs/AbstractCodecStringTest.java @@ -1,13 +1,9 @@ package org.owasp.esapi.codecs; -import java.util.Arrays; - import org.junit.Assert; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; -import org.owasp.esapi.codecs.Codec; -import org.owasp.esapi.codecs.AbstractCodecCharacterTest.CodecCharacterTestTuple; /** From 674dee017337b89aa1a28a9c67b5004f538ba4ab Mon Sep 17 00:00:00 2001 From: Jeremiah Stacey Date: Mon, 22 Jan 2018 10:02:52 -0600 Subject: [PATCH 0436/1069] PercentCodecStringTest cleanup Adding documentation. Fixing a java generics declaration warning. --- .../org/owasp/esapi/codecs/PercentCodecStringTest.java | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/test/java/org/owasp/esapi/codecs/PercentCodecStringTest.java b/src/test/java/org/owasp/esapi/codecs/PercentCodecStringTest.java index 91ca18b87..a851ebcae 100644 --- a/src/test/java/org/owasp/esapi/codecs/PercentCodecStringTest.java +++ b/src/test/java/org/owasp/esapi/codecs/PercentCodecStringTest.java @@ -8,11 +8,8 @@ import org.owasp.esapi.codecs.PercentCodec; /** - * FIXME: Document intent of class. General Function, purpose of creation, intended feature, etc. - * Why do people care this exists? - * - * @author Jeremiah - * @since Jan 20, 2018 + * Codec validation focused on the PercentCodec String-based api. + * */ public class PercentCodecStringTest extends AbstractCodecStringTest { private static final char[] PERCENT_CODEC_IMMUNE; @@ -23,7 +20,7 @@ public class PercentCodecStringTest extends AbstractCodecStringTest { * * It is being replicated here to allow the test to reasonably expect the correct state back. */ - List immune = new ArrayList(); + List immune = new ArrayList<>(); // 65 - 90 (capital letters) 97 - 122 lower case 48 - 57 digits //numbers for (int index = 48 ; index < 58; index ++) { From 93bf6928bb36ac4ac947d2b42c727c5513371b19 Mon Sep 17 00:00:00 2001 From: Jeremiah Stacey Date: Mon, 22 Jan 2018 14:06:55 -0600 Subject: [PATCH 0437/1069] Introducing Codec CodePoint abstraction Adding ignored test structure to share implementation thought. Has failing tests presently, probably due to incorrect test structure and/or expectations. Working to resolve. --- .../codecs/AbstractCodecCodePointTest.java | 78 +++++++++++ .../codecs/PercentCodecCodePointTest.java | 121 ++++++++++++++++++ 2 files changed, 199 insertions(+) create mode 100644 src/test/java/org/owasp/esapi/codecs/AbstractCodecCodePointTest.java create mode 100644 src/test/java/org/owasp/esapi/codecs/PercentCodecCodePointTest.java diff --git a/src/test/java/org/owasp/esapi/codecs/AbstractCodecCodePointTest.java b/src/test/java/org/owasp/esapi/codecs/AbstractCodecCodePointTest.java new file mode 100644 index 000000000..7d79d55bc --- /dev/null +++ b/src/test/java/org/owasp/esapi/codecs/AbstractCodecCodePointTest.java @@ -0,0 +1,78 @@ +package org.owasp.esapi.codecs; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + + +/** + * Abstract parameterized test case meant to assist with verifying Character api of a Codec implementation. + *
+ * Sub-classes are expected to provide instances of {@link CodecCodePointTestTuple} to this instance. + *
+ * For better test naming output specify {@link CodecCodePointTestTuple#description} and use {@code} @Parameters (name="{0}")}, + * where '0' is the index that the CodecCodePointTestTuple reference appears in the constructor. + */ +@RunWith(Parameterized.class) +public abstract class AbstractCodecCodePointTest { + + /** Test Data Tuple.*/ + protected static class CodecCodePointTestTuple { + /** Codec reference to be tested.*/ + Codec codec; + /** Set of characters that should be considered 'immune' from decoding processes.*/ + char[] encodeImmune; + /** A String representing a single encoded character.*/ + String input; + /** The int code point that input represents.*/ + int codePoint; + /** Optional field to override the toString value of this tuple. */ + String description; + /** {@inheritDoc}*/ + + @Override + public String toString() { + return description != null ? description : codec.getClass().getSimpleName() + " "+input; + } + } + + + protected final Codec codec; + protected final String input; + protected final char[] encodeImmune; + protected final int decodedValue; + protected final char codePointChar; + + public AbstractCodecCodePointTest(CodecCodePointTestTuple tuple) { + this.codec = tuple.codec; + this.input = tuple.input; + this.decodedValue = tuple.codePoint; + this.encodeImmune = tuple.encodeImmune; + this.codePointChar = (char) tuple.codePoint; + } + + /** Checks that the input value matches the result of the codec encoding the decoded value. */ + @Test + public void testEncodeCharacter() { + assertEquals(input, codec.encodeCharacter(encodeImmune, decodedValue)); + } + + /** Checks that decoding the input value yeilds the same code point decodedValue.*/ + @Test + public void testDecode() { + int expectedLength = Character.toString(codePointChar).length(); + String actualDecode = codec.decode(input); + assertTrue("CodePoint test input should decode to a String consisting of a single character: " + actualDecode + " " + actualDecode.length(),actualDecode.length() == expectedLength); + assertEquals(decodedValue, (int)actualDecode.charAt(0)); + } + + /** Checks that the encoded input String is correctly decoded to the single decodedValue character reference.*/ + @Test + public void testDecodePushbackSequence() { + assertEquals(decodedValue, codec.decodeCharacter(new PushbackString(input))); + } + +} diff --git a/src/test/java/org/owasp/esapi/codecs/PercentCodecCodePointTest.java b/src/test/java/org/owasp/esapi/codecs/PercentCodecCodePointTest.java new file mode 100644 index 000000000..d44d16097 --- /dev/null +++ b/src/test/java/org/owasp/esapi/codecs/PercentCodecCodePointTest.java @@ -0,0 +1,121 @@ +package org.owasp.esapi.codecs; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import org.junit.Assert; +import org.junit.Ignore; +import org.junit.Test; +import org.junit.runners.Parameterized.Parameters; + +/** + * Codec validation focused on the PercentCodec codepoint-based api. + * + */ +@Ignore(value="Implementation pending") +public class PercentCodecCodePointTest extends AbstractCodecCodePointTest { + private static final char[] PERCENT_CODEC_IMMUNE; + + static { + /* + * The percent codec contains a unique immune character set which include letters and numbers that will not be + * transformed. + * It is being replicated here to allow the test to reasonably expect the correct state back. + */ + List immune = new ArrayList<>(); + // 65 - 90 (capital letters) 97 - 122 lower case 48 - 57 digits + // numbers + for (int index = 48; index < 58; index++) { + immune.add((char) index); + } + // letters + for (int index = 65; index < 91; index++) { + Character capsChar = (char) index; + immune.add(capsChar); + immune.add(Character.toLowerCase(capsChar)); + } + + PERCENT_CODEC_IMMUNE = new char[immune.size()]; + for (int index = 0; index < immune.size(); index++) { + PERCENT_CODEC_IMMUNE[index] = immune.get(index).charValue(); + } + } + + @Parameters(name = "{0}") + public static Collection buildTests() { + Collection tests = new ArrayList<>(); + + Collection tuples = new ArrayList<>(); + tuples.add(newTuple("%3C", Character.valueOf('<'))); + + tuples.add(newTuple("%C4%80", Character.valueOf((char) 0x100))); + tuples.add(newTuple("%00", Character.MIN_VALUE)); + tuples.add(newTuple("%3D", '=')); + tuples.add(newTuple("%26", '&')); + + for (char c : PERCENT_CODEC_IMMUNE) { + tuples.add(newTuple(Character.toString(c), c)); + } + + for (CodecCodePointTestTuple tuple : tuples) { + tests.add(new Object[] { tuple }); + } + + return tests; + } + + private static CodecCodePointTestTuple newTuple(String encodedInput, Character decoded) { + CodecCodePointTestTuple tuple = new CodecCodePointTestTuple(); + tuple.codec = new PercentCodec(); + tuple.encodeImmune = PERCENT_CODEC_IMMUNE; + tuple.codePoint = decoded.charValue(); + tuple.input = encodedInput; + + return tuple; + } + + public PercentCodecCodePointTest(CodecCodePointTestTuple tuple) { + super(tuple); + } + + @Override + @Test + public void testDecodePushbackSequence() { + // check duplicated from PushbackSequence handling in PercentCodec. + boolean inputIsEncoded = input.startsWith("%"); + + if (inputIsEncoded) { + assertInputIsDecodedToValue(); + } else { + assertInputIsDecodedToNull(); + } + } + + /** + * tests that when Input is decoded through a PushbackString that the decodedValue reference is returned and that + * the PushbackString index has incremented. + */ + @SuppressWarnings("unchecked") + private void assertInputIsDecodedToValue() { + PushbackString pbs = new PushbackString(input); + int startIndex = pbs.index(); + Character decChar = (Character) codec.decodeCharacter(pbs); + char actual = decChar.charValue(); + Assert.assertEquals(String.format("%s(%s) != %s(%s)", (char)decodedValue, decodedValue, actual, (int)actual), decodedValue, (int)actual); + Assert.assertTrue(startIndex < pbs.index()); + } + + /** + * tests that when Input is decoded through a PushbackString that null is returned and that the PushbackString index + * remains unchanged. + */ + @SuppressWarnings("unchecked") + private void assertInputIsDecodedToNull() { + PushbackString pbs = new PushbackString(input); + int startIndex = pbs.index(); + Assert.assertEquals(null, codec.decodeCharacter(pbs)); + Assert.assertEquals(startIndex, pbs.index()); + } + +} From 46d2484972a4b5e67817c3c63c8c8ddf9b177c5f Mon Sep 17 00:00:00 2001 From: Jeremiah Stacey Date: Sat, 3 Feb 2018 06:03:08 -0600 Subject: [PATCH 0438/1069] Removing CodePoint test & abstraction The management and testing of code point handling is out of scope of the current effort. Future work on code point handling will allow the PercentCodec to be tested in this way. --- .../codecs/AbstractCodecCodePointTest.java | 78 ----------- .../codecs/PercentCodecCodePointTest.java | 121 ------------------ 2 files changed, 199 deletions(-) delete mode 100644 src/test/java/org/owasp/esapi/codecs/AbstractCodecCodePointTest.java delete mode 100644 src/test/java/org/owasp/esapi/codecs/PercentCodecCodePointTest.java diff --git a/src/test/java/org/owasp/esapi/codecs/AbstractCodecCodePointTest.java b/src/test/java/org/owasp/esapi/codecs/AbstractCodecCodePointTest.java deleted file mode 100644 index 7d79d55bc..000000000 --- a/src/test/java/org/owasp/esapi/codecs/AbstractCodecCodePointTest.java +++ /dev/null @@ -1,78 +0,0 @@ -package org.owasp.esapi.codecs; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; - -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.Parameterized; - - -/** - * Abstract parameterized test case meant to assist with verifying Character api of a Codec implementation. - *
- * Sub-classes are expected to provide instances of {@link CodecCodePointTestTuple} to this instance. - *
- * For better test naming output specify {@link CodecCodePointTestTuple#description} and use {@code} @Parameters (name="{0}")}, - * where '0' is the index that the CodecCodePointTestTuple reference appears in the constructor. - */ -@RunWith(Parameterized.class) -public abstract class AbstractCodecCodePointTest { - - /** Test Data Tuple.*/ - protected static class CodecCodePointTestTuple { - /** Codec reference to be tested.*/ - Codec codec; - /** Set of characters that should be considered 'immune' from decoding processes.*/ - char[] encodeImmune; - /** A String representing a single encoded character.*/ - String input; - /** The int code point that input represents.*/ - int codePoint; - /** Optional field to override the toString value of this tuple. */ - String description; - /** {@inheritDoc}*/ - - @Override - public String toString() { - return description != null ? description : codec.getClass().getSimpleName() + " "+input; - } - } - - - protected final Codec codec; - protected final String input; - protected final char[] encodeImmune; - protected final int decodedValue; - protected final char codePointChar; - - public AbstractCodecCodePointTest(CodecCodePointTestTuple tuple) { - this.codec = tuple.codec; - this.input = tuple.input; - this.decodedValue = tuple.codePoint; - this.encodeImmune = tuple.encodeImmune; - this.codePointChar = (char) tuple.codePoint; - } - - /** Checks that the input value matches the result of the codec encoding the decoded value. */ - @Test - public void testEncodeCharacter() { - assertEquals(input, codec.encodeCharacter(encodeImmune, decodedValue)); - } - - /** Checks that decoding the input value yeilds the same code point decodedValue.*/ - @Test - public void testDecode() { - int expectedLength = Character.toString(codePointChar).length(); - String actualDecode = codec.decode(input); - assertTrue("CodePoint test input should decode to a String consisting of a single character: " + actualDecode + " " + actualDecode.length(),actualDecode.length() == expectedLength); - assertEquals(decodedValue, (int)actualDecode.charAt(0)); - } - - /** Checks that the encoded input String is correctly decoded to the single decodedValue character reference.*/ - @Test - public void testDecodePushbackSequence() { - assertEquals(decodedValue, codec.decodeCharacter(new PushbackString(input))); - } - -} diff --git a/src/test/java/org/owasp/esapi/codecs/PercentCodecCodePointTest.java b/src/test/java/org/owasp/esapi/codecs/PercentCodecCodePointTest.java deleted file mode 100644 index d44d16097..000000000 --- a/src/test/java/org/owasp/esapi/codecs/PercentCodecCodePointTest.java +++ /dev/null @@ -1,121 +0,0 @@ -package org.owasp.esapi.codecs; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; - -import org.junit.Assert; -import org.junit.Ignore; -import org.junit.Test; -import org.junit.runners.Parameterized.Parameters; - -/** - * Codec validation focused on the PercentCodec codepoint-based api. - * - */ -@Ignore(value="Implementation pending") -public class PercentCodecCodePointTest extends AbstractCodecCodePointTest { - private static final char[] PERCENT_CODEC_IMMUNE; - - static { - /* - * The percent codec contains a unique immune character set which include letters and numbers that will not be - * transformed. - * It is being replicated here to allow the test to reasonably expect the correct state back. - */ - List immune = new ArrayList<>(); - // 65 - 90 (capital letters) 97 - 122 lower case 48 - 57 digits - // numbers - for (int index = 48; index < 58; index++) { - immune.add((char) index); - } - // letters - for (int index = 65; index < 91; index++) { - Character capsChar = (char) index; - immune.add(capsChar); - immune.add(Character.toLowerCase(capsChar)); - } - - PERCENT_CODEC_IMMUNE = new char[immune.size()]; - for (int index = 0; index < immune.size(); index++) { - PERCENT_CODEC_IMMUNE[index] = immune.get(index).charValue(); - } - } - - @Parameters(name = "{0}") - public static Collection buildTests() { - Collection tests = new ArrayList<>(); - - Collection tuples = new ArrayList<>(); - tuples.add(newTuple("%3C", Character.valueOf('<'))); - - tuples.add(newTuple("%C4%80", Character.valueOf((char) 0x100))); - tuples.add(newTuple("%00", Character.MIN_VALUE)); - tuples.add(newTuple("%3D", '=')); - tuples.add(newTuple("%26", '&')); - - for (char c : PERCENT_CODEC_IMMUNE) { - tuples.add(newTuple(Character.toString(c), c)); - } - - for (CodecCodePointTestTuple tuple : tuples) { - tests.add(new Object[] { tuple }); - } - - return tests; - } - - private static CodecCodePointTestTuple newTuple(String encodedInput, Character decoded) { - CodecCodePointTestTuple tuple = new CodecCodePointTestTuple(); - tuple.codec = new PercentCodec(); - tuple.encodeImmune = PERCENT_CODEC_IMMUNE; - tuple.codePoint = decoded.charValue(); - tuple.input = encodedInput; - - return tuple; - } - - public PercentCodecCodePointTest(CodecCodePointTestTuple tuple) { - super(tuple); - } - - @Override - @Test - public void testDecodePushbackSequence() { - // check duplicated from PushbackSequence handling in PercentCodec. - boolean inputIsEncoded = input.startsWith("%"); - - if (inputIsEncoded) { - assertInputIsDecodedToValue(); - } else { - assertInputIsDecodedToNull(); - } - } - - /** - * tests that when Input is decoded through a PushbackString that the decodedValue reference is returned and that - * the PushbackString index has incremented. - */ - @SuppressWarnings("unchecked") - private void assertInputIsDecodedToValue() { - PushbackString pbs = new PushbackString(input); - int startIndex = pbs.index(); - Character decChar = (Character) codec.decodeCharacter(pbs); - char actual = decChar.charValue(); - Assert.assertEquals(String.format("%s(%s) != %s(%s)", (char)decodedValue, decodedValue, actual, (int)actual), decodedValue, (int)actual); - Assert.assertTrue(startIndex < pbs.index()); - } - - /** - * tests that when Input is decoded through a PushbackString that null is returned and that the PushbackString index - * remains unchanged. - */ - @SuppressWarnings("unchecked") - private void assertInputIsDecodedToNull() { - PushbackString pbs = new PushbackString(input); - int startIndex = pbs.index(); - Assert.assertEquals(null, codec.decodeCharacter(pbs)); - Assert.assertEquals(startIndex, pbs.index()); - } - -} From 172828eeaab482dee229b88e0c380893f12e0c51 Mon Sep 17 00:00:00 2001 From: Jeremiah Stacey Date: Sat, 3 Feb 2018 06:12:43 -0600 Subject: [PATCH 0439/1069] Reorganizing new test classes Breaking the new abstract parameterizations and percent codec test implementations into sub-packages within test scope. Updating dependencies and references to resolve compilation issues. --- .../AbstractCodecCharacterTest.java | 18 +++++++++++------- .../AbstractCodecStringTest.java | 18 +++++++++++------- .../PercentCodecCharacterTest.java | 3 ++- .../{ => percent}/PercentCodecStringTest.java | 3 ++- 4 files changed, 26 insertions(+), 16 deletions(-) rename src/test/java/org/owasp/esapi/codecs/{ => abstraction}/AbstractCodecCharacterTest.java (88%) rename src/test/java/org/owasp/esapi/codecs/{ => abstraction}/AbstractCodecStringTest.java (85%) rename src/test/java/org/owasp/esapi/codecs/{ => percent}/PercentCodecCharacterTest.java (97%) rename src/test/java/org/owasp/esapi/codecs/{ => percent}/PercentCodecStringTest.java (95%) diff --git a/src/test/java/org/owasp/esapi/codecs/AbstractCodecCharacterTest.java b/src/test/java/org/owasp/esapi/codecs/abstraction/AbstractCodecCharacterTest.java similarity index 88% rename from src/test/java/org/owasp/esapi/codecs/AbstractCodecCharacterTest.java rename to src/test/java/org/owasp/esapi/codecs/abstraction/AbstractCodecCharacterTest.java index 1101be47f..438d86e26 100644 --- a/src/test/java/org/owasp/esapi/codecs/AbstractCodecCharacterTest.java +++ b/src/test/java/org/owasp/esapi/codecs/abstraction/AbstractCodecCharacterTest.java @@ -1,4 +1,4 @@ -package org.owasp.esapi.codecs; +package org.owasp.esapi.codecs.abstraction; import static org.junit.Assert.assertEquals; @@ -7,6 +7,8 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; +import org.owasp.esapi.codecs.Codec; +import org.owasp.esapi.codecs.PushbackString; /** @@ -23,17 +25,19 @@ public abstract class AbstractCodecCharacterTest { /** Test Data Tuple.*/ protected static class CodecCharacterTestTuple { /** Codec reference to be tested.*/ - Codec codec; + public Codec codec; /** Set of characters that should be considered 'immune' from decoding processes.*/ - char[] encodeImmune; + public char[] encodeImmune; /** A String representing a single encoded character.*/ - String input; + public String input; /** The single character that input represents.*/ - Character decodedValue; + public Character decodedValue; /** Optional field to override the toString value of this tuple. */ - String description; - /** {@inheritDoc}*/ + public String description; + /**Default public constructor.*/ + public CodecCharacterTestTuple() { /* No Op*/ } + /** {@inheritDoc}*/ @Override public String toString() { return description != null ? description : codec.getClass().getSimpleName() + " "+input; diff --git a/src/test/java/org/owasp/esapi/codecs/AbstractCodecStringTest.java b/src/test/java/org/owasp/esapi/codecs/abstraction/AbstractCodecStringTest.java similarity index 85% rename from src/test/java/org/owasp/esapi/codecs/AbstractCodecStringTest.java rename to src/test/java/org/owasp/esapi/codecs/abstraction/AbstractCodecStringTest.java index b3fe53156..e54d1172d 100644 --- a/src/test/java/org/owasp/esapi/codecs/AbstractCodecStringTest.java +++ b/src/test/java/org/owasp/esapi/codecs/abstraction/AbstractCodecStringTest.java @@ -1,9 +1,10 @@ -package org.owasp.esapi.codecs; +package org.owasp.esapi.codecs.abstraction; import org.junit.Assert; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; +import org.owasp.esapi.codecs.Codec; /** @@ -19,17 +20,20 @@ public abstract class AbstractCodecStringTest { protected static class CodecStringTestTuple { /** Codec reference to be tested.*/ - Codec codec; + public Codec codec; /** Set of characters that should be considered 'immune' from decoding processes.*/ - char[] encodeImmune; + public char[] encodeImmune; /** A String representing a contextually encoded String.*/ - String input; + public String input; /** The decoded representation of the input value.*/ - String decodedValue; + public String decodedValue; /** Optional field to override the toString value of this tuple. */ - String description; - /** {@inheritDoc}*/ + public String description; + + /**Default public constructor.*/ + public CodecStringTestTuple() { /* No Op*/ } + /** {@inheritDoc}*/ @Override public String toString() { return description != null ? description : codec.getClass().getSimpleName() + " "+input; diff --git a/src/test/java/org/owasp/esapi/codecs/PercentCodecCharacterTest.java b/src/test/java/org/owasp/esapi/codecs/percent/PercentCodecCharacterTest.java similarity index 97% rename from src/test/java/org/owasp/esapi/codecs/PercentCodecCharacterTest.java rename to src/test/java/org/owasp/esapi/codecs/percent/PercentCodecCharacterTest.java index ca7ddf1b6..76c5c1c52 100644 --- a/src/test/java/org/owasp/esapi/codecs/PercentCodecCharacterTest.java +++ b/src/test/java/org/owasp/esapi/codecs/percent/PercentCodecCharacterTest.java @@ -1,4 +1,4 @@ -package org.owasp.esapi.codecs; +package org.owasp.esapi.codecs.percent; import java.util.ArrayList; import java.util.Collection; @@ -9,6 +9,7 @@ import org.junit.runners.Parameterized.Parameters; import org.owasp.esapi.codecs.PercentCodec; import org.owasp.esapi.codecs.PushbackString; +import org.owasp.esapi.codecs.abstraction.AbstractCodecCharacterTest; /** * Codec validation focused on the PercentCodec Character-based api. diff --git a/src/test/java/org/owasp/esapi/codecs/PercentCodecStringTest.java b/src/test/java/org/owasp/esapi/codecs/percent/PercentCodecStringTest.java similarity index 95% rename from src/test/java/org/owasp/esapi/codecs/PercentCodecStringTest.java rename to src/test/java/org/owasp/esapi/codecs/percent/PercentCodecStringTest.java index a851ebcae..c769a2e01 100644 --- a/src/test/java/org/owasp/esapi/codecs/PercentCodecStringTest.java +++ b/src/test/java/org/owasp/esapi/codecs/percent/PercentCodecStringTest.java @@ -1,4 +1,4 @@ -package org.owasp.esapi.codecs; +package org.owasp.esapi.codecs.percent; import java.util.ArrayList; import java.util.Collection; @@ -6,6 +6,7 @@ import org.junit.runners.Parameterized.Parameters; import org.owasp.esapi.codecs.PercentCodec; +import org.owasp.esapi.codecs.abstraction.AbstractCodecStringTest; /** * Codec validation focused on the PercentCodec String-based api. From 2c960807e2060956848158fed146be375de92804 Mon Sep 17 00:00:00 2001 From: Jeremiah Stacey Date: Thu, 8 Feb 2018 15:38:41 -0600 Subject: [PATCH 0440/1069] Removing invalid test content Removing entries that would otherwise test the codepoint handling of the PercentCodec. The feature of code point handling has not been fully implemented on the PercentCodec at this time. --- .../owasp/esapi/codecs/percent/PercentCodecCharacterTest.java | 2 +- .../org/owasp/esapi/codecs/percent/PercentCodecStringTest.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/test/java/org/owasp/esapi/codecs/percent/PercentCodecCharacterTest.java b/src/test/java/org/owasp/esapi/codecs/percent/PercentCodecCharacterTest.java index 76c5c1c52..f9e1d616d 100644 --- a/src/test/java/org/owasp/esapi/codecs/percent/PercentCodecCharacterTest.java +++ b/src/test/java/org/owasp/esapi/codecs/percent/PercentCodecCharacterTest.java @@ -50,7 +50,7 @@ public static Collection buildTests() { Collection tuples = new ArrayList<>(); tuples.add(newTuple("%3C", Character.valueOf('<'))); - tuples.add(newTuple("%C4%80", Character.valueOf((char) 0x100))); + //CODEPOINT tuples.add(newTuple("%C4%80", Character.valueOf((char) 0x100))); tuples.add(newTuple("%00", Character.MIN_VALUE)); tuples.add(newTuple("%3D", '=')); tuples.add(newTuple("%26", '&')); diff --git a/src/test/java/org/owasp/esapi/codecs/percent/PercentCodecStringTest.java b/src/test/java/org/owasp/esapi/codecs/percent/PercentCodecStringTest.java index c769a2e01..786444b27 100644 --- a/src/test/java/org/owasp/esapi/codecs/percent/PercentCodecStringTest.java +++ b/src/test/java/org/owasp/esapi/codecs/percent/PercentCodecStringTest.java @@ -47,7 +47,7 @@ public static Collection buildTests() { List tuples = new ArrayList<>(); tuples.add(newTuple("%3C", "<")); - tuples.add(newTuple("%C4%80", (char) 0x100)); + //CODEPOINT tuples.add(newTuple("%C4%80", (char) 0x100)); tuples.add(newTuple("%00", Character.MIN_VALUE)); tuples.add(newTuple("%3D", '=')); tuples.add(newTuple("%26", '&')); From 804350f0f25605da3cf69df3885c133636f3860b Mon Sep 17 00:00:00 2001 From: Jeremiah Stacey Date: Thu, 8 Feb 2018 15:44:02 -0600 Subject: [PATCH 0441/1069] Relocating Tests & Updating references Moving the pattern validation test constructs into a sub directory intended to hold regex-type testing elements. --- .../owasp/esapi/reference/{ => regex}/AbstractPatternTest.java | 2 +- .../{ => regex}/EsapiWhitelistValidationPatternTester.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) rename src/test/java/org/owasp/esapi/reference/{ => regex}/AbstractPatternTest.java (97%) rename src/test/java/org/owasp/esapi/reference/{ => regex}/EsapiWhitelistValidationPatternTester.java (98%) diff --git a/src/test/java/org/owasp/esapi/reference/AbstractPatternTest.java b/src/test/java/org/owasp/esapi/reference/regex/AbstractPatternTest.java similarity index 97% rename from src/test/java/org/owasp/esapi/reference/AbstractPatternTest.java rename to src/test/java/org/owasp/esapi/reference/regex/AbstractPatternTest.java index 9cf1e0e20..3e3847719 100644 --- a/src/test/java/org/owasp/esapi/reference/AbstractPatternTest.java +++ b/src/test/java/org/owasp/esapi/reference/regex/AbstractPatternTest.java @@ -1,4 +1,4 @@ -package org.owasp.esapi.reference; +package org.owasp.esapi.reference.regex; import java.util.regex.Pattern; diff --git a/src/test/java/org/owasp/esapi/reference/EsapiWhitelistValidationPatternTester.java b/src/test/java/org/owasp/esapi/reference/regex/EsapiWhitelistValidationPatternTester.java similarity index 98% rename from src/test/java/org/owasp/esapi/reference/EsapiWhitelistValidationPatternTester.java rename to src/test/java/org/owasp/esapi/reference/regex/EsapiWhitelistValidationPatternTester.java index d00d4c19a..2042eee33 100644 --- a/src/test/java/org/owasp/esapi/reference/EsapiWhitelistValidationPatternTester.java +++ b/src/test/java/org/owasp/esapi/reference/regex/EsapiWhitelistValidationPatternTester.java @@ -1,4 +1,4 @@ -package org.owasp.esapi.reference; +package org.owasp.esapi.reference.regex; import java.util.ArrayList; import java.util.Collection; From 4f903d37feb4b32c6d9d1f2339d3f5c3b45162c5 Mon Sep 17 00:00:00 2001 From: Jeremiah Stacey Date: Mon, 19 Feb 2018 08:06:06 -0600 Subject: [PATCH 0442/1069] Adding known exception testing for PercentCodec Making a test that will fail when the UTF16 handling is corrected for the implementation. Also centralizing the test-scope of the immune character set to just the StringTest impl for easier maintenance later when it's replaced. --- .../percent/PercentCodecCharacterTest.java | 31 ++----------------- .../percent/PercentCodecStringTest.java | 2 +- 2 files changed, 3 insertions(+), 30 deletions(-) diff --git a/src/test/java/org/owasp/esapi/codecs/percent/PercentCodecCharacterTest.java b/src/test/java/org/owasp/esapi/codecs/percent/PercentCodecCharacterTest.java index f9e1d616d..419762dc1 100644 --- a/src/test/java/org/owasp/esapi/codecs/percent/PercentCodecCharacterTest.java +++ b/src/test/java/org/owasp/esapi/codecs/percent/PercentCodecCharacterTest.java @@ -1,8 +1,9 @@ package org.owasp.esapi.codecs.percent; +import static org.owasp.esapi.codecs.percent.PercentCodecStringTest.PERCENT_CODEC_IMMUNE; + import java.util.ArrayList; import java.util.Collection; -import java.util.List; import org.junit.Assert; import org.junit.Test; @@ -16,33 +17,6 @@ * */ public class PercentCodecCharacterTest extends AbstractCodecCharacterTest { - private static final char[] PERCENT_CODEC_IMMUNE; - - static { - /* - * The percent codec contains a unique immune character set which include letters and numbers that will not be - * transformed. - * It is being replicated here to allow the test to reasonably expect the correct state back. - */ - List immune = new ArrayList<>(); - // 65 - 90 (capital letters) 97 - 122 lower case 48 - 57 digits - // numbers - for (int index = 48; index < 58; index++) { - immune.add((char) index); - } - // letters - for (int index = 65; index < 91; index++) { - Character capsChar = (char) index; - immune.add(capsChar); - immune.add(Character.toLowerCase(capsChar)); - } - - PERCENT_CODEC_IMMUNE = new char[immune.size()]; - for (int index = 0; index < immune.size(); index++) { - PERCENT_CODEC_IMMUNE[index] = immune.get(index).charValue(); - } - } - @Parameters(name = "{0}") public static Collection buildTests() { Collection tests = new ArrayList<>(); @@ -50,7 +24,6 @@ public static Collection buildTests() { Collection tuples = new ArrayList<>(); tuples.add(newTuple("%3C", Character.valueOf('<'))); - //CODEPOINT tuples.add(newTuple("%C4%80", Character.valueOf((char) 0x100))); tuples.add(newTuple("%00", Character.MIN_VALUE)); tuples.add(newTuple("%3D", '=')); tuples.add(newTuple("%26", '&')); diff --git a/src/test/java/org/owasp/esapi/codecs/percent/PercentCodecStringTest.java b/src/test/java/org/owasp/esapi/codecs/percent/PercentCodecStringTest.java index 786444b27..665a8ab71 100644 --- a/src/test/java/org/owasp/esapi/codecs/percent/PercentCodecStringTest.java +++ b/src/test/java/org/owasp/esapi/codecs/percent/PercentCodecStringTest.java @@ -13,7 +13,7 @@ * */ public class PercentCodecStringTest extends AbstractCodecStringTest { - private static final char[] PERCENT_CODEC_IMMUNE; + public static final char[] PERCENT_CODEC_IMMUNE; static { /* From d9e12d6be4c2b556fafdaab14e4c62b68b30a679 Mon Sep 17 00:00:00 2001 From: Jeremiah Stacey Date: Wed, 21 Feb 2018 17:40:10 -0600 Subject: [PATCH 0443/1069] Really committing known exceptions test Lost a workflow argument with git, so I'm making another commit to actually add the file that I intended to add last commit. --- .../percent/PercentCodecKnownIssuesTest.java | 41 +++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 src/test/java/org/owasp/esapi/codecs/percent/PercentCodecKnownIssuesTest.java diff --git a/src/test/java/org/owasp/esapi/codecs/percent/PercentCodecKnownIssuesTest.java b/src/test/java/org/owasp/esapi/codecs/percent/PercentCodecKnownIssuesTest.java new file mode 100644 index 000000000..9b163b4bf --- /dev/null +++ b/src/test/java/org/owasp/esapi/codecs/percent/PercentCodecKnownIssuesTest.java @@ -0,0 +1,41 @@ +package org.owasp.esapi.codecs.percent; +import static org.owasp.esapi.codecs.percent.PercentCodecStringTest.PERCENT_CODEC_IMMUNE; + +import org.junit.Assert; +import org.junit.Test; +import org.owasp.esapi.codecs.PercentCodec; +/** + * This test class holds the proof of known deficiencies, inconsistencies, or bugs with the PercentCodec implementation. + *
+ * The intent is that when that functionality is corrected, these tests should break. That should hopefully encourage + * the author to move the test to an appropriate Test file and update the functionality to a working expectation. + */ +public class PercentCodecKnownIssuesTest { + + private PercentCodec codec = new PercentCodec(); + + /** + * PercentCodec has not been fully implemented for codepoint support, which handles UTF16 characters (based on my current understanding). + * As such, the encoding/decoding of UTF16 will not function as desired through the codec implementation. + *
+ * When the functionality is corrected this test will break. At that point UTF16 tests should be added to {@link PercentCodecStringTest} and {@link PercentCodecCharacterTest}. + */ + @Test + public void failsUTF16Conversions() { + //This should be 195 + int incorrectDecodeExpect = 196; + + char[] encodeImmune = PERCENT_CODEC_IMMUNE; + String decodedValue = ""+(char) 0x100; + String input = "%C4%80"; + + String actualDecodeChar = codec.decode(input); + int actualChar = (int)actualDecodeChar.charAt(0); + + Assert.assertEquals(incorrectDecodeExpect, actualChar); + + //This works as expected. + Assert.assertEquals(input, codec.encode(encodeImmune, decodedValue)); + } + +} From bf91374d3acb5cf79943ac9127a7fd39473af881 Mon Sep 17 00:00:00 2001 From: Jeremiah Stacey Date: Wed, 21 Feb 2018 17:47:00 -0600 Subject: [PATCH 0444/1069] Adding License Information Adding license content to files created in this effort. --- .../abstraction/AbstractCodecCharacterTest.java | 14 ++++++++++++++ .../abstraction/AbstractCodecStringTest.java | 14 ++++++++++++++ .../codecs/percent/PercentCodecCharacterTest.java | 14 ++++++++++++++ .../percent/PercentCodecKnownIssuesTest.java | 15 +++++++++++++++ .../codecs/percent/PercentCodecStringTest.java | 14 ++++++++++++++ .../esapi/filters/SecurityWrapperRequestTest.java | 14 ++++++++++++++ .../reference/regex/AbstractPatternTest.java | 13 +++++++++++++ .../EsapiWhitelistValidationPatternTester.java | 14 ++++++++++++++ 8 files changed, 112 insertions(+) diff --git a/src/test/java/org/owasp/esapi/codecs/abstraction/AbstractCodecCharacterTest.java b/src/test/java/org/owasp/esapi/codecs/abstraction/AbstractCodecCharacterTest.java index 438d86e26..66ff3cb82 100644 --- a/src/test/java/org/owasp/esapi/codecs/abstraction/AbstractCodecCharacterTest.java +++ b/src/test/java/org/owasp/esapi/codecs/abstraction/AbstractCodecCharacterTest.java @@ -1,3 +1,17 @@ +/** + * OWASP Enterprise Security API (ESAPI) + * + * This file is part of the Open Web Application Security Project (OWASP) + * Enterprise Security API (ESAPI) project. For details, please see + * http://www.owasp.org/index.php/ESAPI. + * + * Copyright (c) 2008-2018 - The OWASP Foundation + * + * The ESAPI is published by OWASP under the BSD license. You should read and accept the + * LICENSE before you use, modify, and/or redistribute this software. + * + */ + package org.owasp.esapi.codecs.abstraction; import static org.junit.Assert.assertEquals; diff --git a/src/test/java/org/owasp/esapi/codecs/abstraction/AbstractCodecStringTest.java b/src/test/java/org/owasp/esapi/codecs/abstraction/AbstractCodecStringTest.java index e54d1172d..c00cbf699 100644 --- a/src/test/java/org/owasp/esapi/codecs/abstraction/AbstractCodecStringTest.java +++ b/src/test/java/org/owasp/esapi/codecs/abstraction/AbstractCodecStringTest.java @@ -1,3 +1,17 @@ +/** + * OWASP Enterprise Security API (ESAPI) + * + * This file is part of the Open Web Application Security Project (OWASP) + * Enterprise Security API (ESAPI) project. For details, please see + * http://www.owasp.org/index.php/ESAPI. + * + * Copyright (c) 2008-2018 - The OWASP Foundation + * + * The ESAPI is published by OWASP under the BSD license. You should read and accept the + * LICENSE before you use, modify, and/or redistribute this software. + * + */ + package org.owasp.esapi.codecs.abstraction; import org.junit.Assert; diff --git a/src/test/java/org/owasp/esapi/codecs/percent/PercentCodecCharacterTest.java b/src/test/java/org/owasp/esapi/codecs/percent/PercentCodecCharacterTest.java index 419762dc1..2c7199c23 100644 --- a/src/test/java/org/owasp/esapi/codecs/percent/PercentCodecCharacterTest.java +++ b/src/test/java/org/owasp/esapi/codecs/percent/PercentCodecCharacterTest.java @@ -1,3 +1,17 @@ +/** + * OWASP Enterprise Security API (ESAPI) + * + * This file is part of the Open Web Application Security Project (OWASP) + * Enterprise Security API (ESAPI) project. For details, please see + * http://www.owasp.org/index.php/ESAPI. + * + * Copyright (c) 2008-2018 - The OWASP Foundation + * + * The ESAPI is published by OWASP under the BSD license. You should read and accept the + * LICENSE before you use, modify, and/or redistribute this software. + * + */ + package org.owasp.esapi.codecs.percent; import static org.owasp.esapi.codecs.percent.PercentCodecStringTest.PERCENT_CODEC_IMMUNE; diff --git a/src/test/java/org/owasp/esapi/codecs/percent/PercentCodecKnownIssuesTest.java b/src/test/java/org/owasp/esapi/codecs/percent/PercentCodecKnownIssuesTest.java index 9b163b4bf..2f3f1049c 100644 --- a/src/test/java/org/owasp/esapi/codecs/percent/PercentCodecKnownIssuesTest.java +++ b/src/test/java/org/owasp/esapi/codecs/percent/PercentCodecKnownIssuesTest.java @@ -1,4 +1,19 @@ +/** + * OWASP Enterprise Security API (ESAPI) + * + * This file is part of the Open Web Application Security Project (OWASP) + * Enterprise Security API (ESAPI) project. For details, please see + * http://www.owasp.org/index.php/ESAPI. + * + * Copyright (c) 2008-2018 - The OWASP Foundation + * + * The ESAPI is published by OWASP under the BSD license. You should read and accept the + * LICENSE before you use, modify, and/or redistribute this software. + * + */ + package org.owasp.esapi.codecs.percent; + import static org.owasp.esapi.codecs.percent.PercentCodecStringTest.PERCENT_CODEC_IMMUNE; import org.junit.Assert; diff --git a/src/test/java/org/owasp/esapi/codecs/percent/PercentCodecStringTest.java b/src/test/java/org/owasp/esapi/codecs/percent/PercentCodecStringTest.java index 665a8ab71..38bb6fb27 100644 --- a/src/test/java/org/owasp/esapi/codecs/percent/PercentCodecStringTest.java +++ b/src/test/java/org/owasp/esapi/codecs/percent/PercentCodecStringTest.java @@ -1,3 +1,17 @@ +/** + * OWASP Enterprise Security API (ESAPI) + * + * This file is part of the Open Web Application Security Project (OWASP) + * Enterprise Security API (ESAPI) project. For details, please see + * http://www.owasp.org/index.php/ESAPI. + * + * Copyright (c) 2008-2018 - The OWASP Foundation + * + * The ESAPI is published by OWASP under the BSD license. You should read and accept the + * LICENSE before you use, modify, and/or redistribute this software. + * + */ + package org.owasp.esapi.codecs.percent; import java.util.ArrayList; diff --git a/src/test/java/org/owasp/esapi/filters/SecurityWrapperRequestTest.java b/src/test/java/org/owasp/esapi/filters/SecurityWrapperRequestTest.java index bf1a2d627..c5c1c3c5e 100644 --- a/src/test/java/org/owasp/esapi/filters/SecurityWrapperRequestTest.java +++ b/src/test/java/org/owasp/esapi/filters/SecurityWrapperRequestTest.java @@ -1,3 +1,17 @@ +/** + * OWASP Enterprise Security API (ESAPI) + * + * This file is part of the Open Web Application Security Project (OWASP) + * Enterprise Security API (ESAPI) project. For details, please see + * http://www.owasp.org/index.php/ESAPI. + * + * Copyright (c) 2008-2018 - The OWASP Foundation + * + * The ESAPI is published by OWASP under the BSD license. You should read and accept the + * LICENSE before you use, modify, and/or redistribute this software. + * + */ + package org.owasp.esapi.filters; import static org.junit.Assert.assertEquals; diff --git a/src/test/java/org/owasp/esapi/reference/regex/AbstractPatternTest.java b/src/test/java/org/owasp/esapi/reference/regex/AbstractPatternTest.java index 3e3847719..f05397dfb 100644 --- a/src/test/java/org/owasp/esapi/reference/regex/AbstractPatternTest.java +++ b/src/test/java/org/owasp/esapi/reference/regex/AbstractPatternTest.java @@ -1,3 +1,16 @@ +/** + * OWASP Enterprise Security API (ESAPI) + * + * This file is part of the Open Web Application Security Project (OWASP) + * Enterprise Security API (ESAPI) project. For details, please see + * http://www.owasp.org/index.php/ESAPI. + * + * Copyright (c) 2008-2018 - The OWASP Foundation + * + * The ESAPI is published by OWASP under the BSD license. You should read and accept the + * LICENSE before you use, modify, and/or redistribute this software. + * + */ package org.owasp.esapi.reference.regex; import java.util.regex.Pattern; diff --git a/src/test/java/org/owasp/esapi/reference/regex/EsapiWhitelistValidationPatternTester.java b/src/test/java/org/owasp/esapi/reference/regex/EsapiWhitelistValidationPatternTester.java index 2042eee33..b6080344d 100644 --- a/src/test/java/org/owasp/esapi/reference/regex/EsapiWhitelistValidationPatternTester.java +++ b/src/test/java/org/owasp/esapi/reference/regex/EsapiWhitelistValidationPatternTester.java @@ -1,3 +1,17 @@ +/** + * OWASP Enterprise Security API (ESAPI) + * + * This file is part of the Open Web Application Security Project (OWASP) + * Enterprise Security API (ESAPI) project. For details, please see + * http://www.owasp.org/index.php/ESAPI. + * + * Copyright (c) 2008-2018 - The OWASP Foundation + * + * The ESAPI is published by OWASP under the BSD license. You should read and accept the + * LICENSE before you use, modify, and/or redistribute this software. + * + */ + package org.owasp.esapi.reference.regex; import java.util.ArrayList; From 113add5fa9e280eb2bae9263c8c9868b963ec938 Mon Sep 17 00:00:00 2001 From: Jeremiah Stacey Date: Thu, 22 Feb 2018 04:47:54 -0600 Subject: [PATCH 0445/1069] Documentation updates Feedback from pull request. --- .../esapi/codecs/abstraction/AbstractCodecCharacterTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/test/java/org/owasp/esapi/codecs/abstraction/AbstractCodecCharacterTest.java b/src/test/java/org/owasp/esapi/codecs/abstraction/AbstractCodecCharacterTest.java index 66ff3cb82..aa7208399 100644 --- a/src/test/java/org/owasp/esapi/codecs/abstraction/AbstractCodecCharacterTest.java +++ b/src/test/java/org/owasp/esapi/codecs/abstraction/AbstractCodecCharacterTest.java @@ -30,7 +30,7 @@ *
* Sub-classes are expected to provide instances of {@link CodecCharacterTestTuple} to this instance. *
- * For better test naming output specify {@link CodecCharacterTestTuple#description} and use {@code} @Parameters (name="{0}")}, + * For better test naming output specify {@link CodecCharacterTestTuple#description} and use @Parameters (name="{0}"), * where '0' is the index that the CodecCharacterTestTuple reference appears in the constructor. */ @RunWith(Parameterized.class) @@ -88,7 +88,7 @@ public void testEncode() { assertEquals(expected, codec.encode(encodeImmune, decodedValue.toString())); } - /** Checks that decoding the input value yeilds the decodedValue.*/ + /** Checks that decoding the input value yields the decodedValue.*/ @Test public void testDecode() { assertEquals(decodedValue.toString(), codec.decode(input)); From e5b7c0d8d9f93e1d8617db2362e2cf59b1d093bd Mon Sep 17 00:00:00 2001 From: Jeremiah Stacey Date: Thu, 22 Feb 2018 04:50:16 -0600 Subject: [PATCH 0446/1069] Updating Imports Pull request feedback. Setting Assert.assertEquals to a static import to match other implementations within this effort. --- .../esapi/codecs/abstraction/AbstractCodecStringTest.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/test/java/org/owasp/esapi/codecs/abstraction/AbstractCodecStringTest.java b/src/test/java/org/owasp/esapi/codecs/abstraction/AbstractCodecStringTest.java index c00cbf699..789c8a153 100644 --- a/src/test/java/org/owasp/esapi/codecs/abstraction/AbstractCodecStringTest.java +++ b/src/test/java/org/owasp/esapi/codecs/abstraction/AbstractCodecStringTest.java @@ -14,7 +14,8 @@ package org.owasp.esapi.codecs.abstraction; -import org.junit.Assert; +import static org.junit.Assert.assertEquals; + import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; @@ -69,13 +70,13 @@ public AbstractCodecStringTest(CodecStringTestTuple tuple) { /** Checks that when the input is decoded using the specified codec, that the return matches the expected decoded value.*/ @Test public void testDecode() { - Assert.assertEquals(decodedValue, codec.decode(input)); + assertEquals(decodedValue, codec.decode(input)); } /** Checks that when the decoded value is encoded (using immunity), that the return matches the provided input.*/ @Test public void testEncode() { - Assert.assertEquals(input, codec.encode(encodeImmune, decodedValue)); + assertEquals(input, codec.encode(encodeImmune, decodedValue)); } } From ce025aa4b5e2e8952d0cc87239cc916d88b84c8e Mon Sep 17 00:00:00 2001 From: Jeremiah Stacey Date: Thu, 22 Feb 2018 04:54:45 -0600 Subject: [PATCH 0447/1069] Updating Imports, readability improvements. Pull request feedback. Using static Assert imports for consistency within this effort. Altering whitespace for better readability in parameter construction. --- .../codecs/percent/PercentCodecCharacterTest.java | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/test/java/org/owasp/esapi/codecs/percent/PercentCodecCharacterTest.java b/src/test/java/org/owasp/esapi/codecs/percent/PercentCodecCharacterTest.java index 2c7199c23..7d4425ddc 100644 --- a/src/test/java/org/owasp/esapi/codecs/percent/PercentCodecCharacterTest.java +++ b/src/test/java/org/owasp/esapi/codecs/percent/PercentCodecCharacterTest.java @@ -14,12 +14,13 @@ package org.owasp.esapi.codecs.percent; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; import static org.owasp.esapi.codecs.percent.PercentCodecStringTest.PERCENT_CODEC_IMMUNE; import java.util.ArrayList; import java.util.Collection; -import org.junit.Assert; import org.junit.Test; import org.junit.runners.Parameterized.Parameters; import org.owasp.esapi.codecs.PercentCodec; @@ -34,8 +35,8 @@ public class PercentCodecCharacterTest extends AbstractCodecCharacterTest { @Parameters(name = "{0}") public static Collection buildTests() { Collection tests = new ArrayList<>(); - Collection tuples = new ArrayList<>(); + tuples.add(newTuple("%3C", Character.valueOf('<'))); tuples.add(newTuple("%00", Character.MIN_VALUE)); @@ -88,8 +89,8 @@ public void testDecodePushbackSequence() { private void assertInputIsDecodedToValue() { PushbackString pbs = new PushbackString(input); int startIndex = pbs.index(); - Assert.assertEquals(decodedValue, codec.decodeCharacter(pbs)); - Assert.assertTrue(startIndex < pbs.index()); + assertEquals(decodedValue, codec.decodeCharacter(pbs)); + assertTrue(startIndex < pbs.index()); } /** @@ -100,8 +101,8 @@ private void assertInputIsDecodedToValue() { private void assertInputIsDecodedToNull() { PushbackString pbs = new PushbackString(input); int startIndex = pbs.index(); - Assert.assertEquals(null, codec.decodeCharacter(pbs)); - Assert.assertEquals(startIndex, pbs.index()); + assertEquals(null, codec.decodeCharacter(pbs)); + assertEquals(startIndex, pbs.index()); } } From b468673f93155fcfbf1c0f7ed1f1c15c31731758 Mon Sep 17 00:00:00 2001 From: Jeremiah Stacey Date: Thu, 22 Feb 2018 05:12:09 -0600 Subject: [PATCH 0448/1069] Updating imports Applying static Assert imports for implementation consistency. --- .../esapi/codecs/percent/PercentCodecKnownIssuesTest.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/test/java/org/owasp/esapi/codecs/percent/PercentCodecKnownIssuesTest.java b/src/test/java/org/owasp/esapi/codecs/percent/PercentCodecKnownIssuesTest.java index 2f3f1049c..ac8c6d6e2 100644 --- a/src/test/java/org/owasp/esapi/codecs/percent/PercentCodecKnownIssuesTest.java +++ b/src/test/java/org/owasp/esapi/codecs/percent/PercentCodecKnownIssuesTest.java @@ -14,9 +14,9 @@ package org.owasp.esapi.codecs.percent; +import static org.junit.Assert.assertEquals; import static org.owasp.esapi.codecs.percent.PercentCodecStringTest.PERCENT_CODEC_IMMUNE; -import org.junit.Assert; import org.junit.Test; import org.owasp.esapi.codecs.PercentCodec; /** @@ -47,10 +47,10 @@ public void failsUTF16Conversions() { String actualDecodeChar = codec.decode(input); int actualChar = (int)actualDecodeChar.charAt(0); - Assert.assertEquals(incorrectDecodeExpect, actualChar); + assertEquals(incorrectDecodeExpect, actualChar); //This works as expected. - Assert.assertEquals(input, codec.encode(encodeImmune, decodedValue)); + assertEquals(input, codec.encode(encodeImmune, decodedValue)); } } From 5a65083bfd4d2c1966e00e04701190db6d397a96 Mon Sep 17 00:00:00 2001 From: Jeremiah Stacey Date: Thu, 22 Feb 2018 05:14:32 -0600 Subject: [PATCH 0449/1069] Readability Improvements Pull Request Cleanup. Clarifying the capital letters are ASCII specific. Applying better whitespace formatting for readability. --- .../owasp/esapi/codecs/percent/PercentCodecStringTest.java | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/test/java/org/owasp/esapi/codecs/percent/PercentCodecStringTest.java b/src/test/java/org/owasp/esapi/codecs/percent/PercentCodecStringTest.java index 38bb6fb27..edec02321 100644 --- a/src/test/java/org/owasp/esapi/codecs/percent/PercentCodecStringTest.java +++ b/src/test/java/org/owasp/esapi/codecs/percent/PercentCodecStringTest.java @@ -36,7 +36,7 @@ public class PercentCodecStringTest extends AbstractCodecStringTest { * It is being replicated here to allow the test to reasonably expect the correct state back. */ List immune = new ArrayList<>(); - // 65 - 90 (capital letters) 97 - 122 lower case 48 - 57 digits + // 65 - 90 (capital letters in ASCII) 97 - 122 lower case 48 - 57 digits //numbers for (int index = 48 ; index < 58; index ++) { immune.add((char)index); @@ -57,11 +57,9 @@ public class PercentCodecStringTest extends AbstractCodecStringTest { @Parameters(name = "{0}") public static Collection buildTests() { Collection tests = new ArrayList<>(); - List tuples = new ArrayList<>(); + tuples.add(newTuple("%3C", "<")); - - //CODEPOINT tuples.add(newTuple("%C4%80", (char) 0x100)); tuples.add(newTuple("%00", Character.MIN_VALUE)); tuples.add(newTuple("%3D", '=')); tuples.add(newTuple("%26", '&')); From 5d42bfe5544da508aafaa3cef78a0dde2b47d4a2 Mon Sep 17 00:00:00 2001 From: Jeremiah Stacey Date: Thu, 22 Feb 2018 05:18:09 -0600 Subject: [PATCH 0450/1069] Import cleanup Using static Assert imports for implementation consistency. --- .../org/owasp/esapi/reference/regex/AbstractPatternTest.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/test/java/org/owasp/esapi/reference/regex/AbstractPatternTest.java b/src/test/java/org/owasp/esapi/reference/regex/AbstractPatternTest.java index f05397dfb..89dd1f126 100644 --- a/src/test/java/org/owasp/esapi/reference/regex/AbstractPatternTest.java +++ b/src/test/java/org/owasp/esapi/reference/regex/AbstractPatternTest.java @@ -13,9 +13,10 @@ */ package org.owasp.esapi.reference.regex; +import static org.junit.Assert.assertEquals; + import java.util.regex.Pattern; -import org.junit.Assert; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; @@ -63,7 +64,7 @@ public AbstractPatternTest(PatternTestTuple tuple) { @Test public void checkPatternMatches() { - Assert.assertEquals(shouldMatch, pattern.matcher(input).matches()); + assertEquals(shouldMatch, pattern.matcher(input).matches()); } } From 7ad97030300b3c0f4e6dc2533370445e0cfa7d59 Mon Sep 17 00:00:00 2001 From: Jeremiah Stacey Date: Thu, 22 Feb 2018 05:19:42 -0600 Subject: [PATCH 0451/1069] Documentation Cleanup Correcting use of javadoc {@code} to for better readability of parameterization usage and suggestions. --- .../owasp/esapi/codecs/abstraction/AbstractCodecStringTest.java | 2 +- .../org/owasp/esapi/reference/regex/AbstractPatternTest.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/test/java/org/owasp/esapi/codecs/abstraction/AbstractCodecStringTest.java b/src/test/java/org/owasp/esapi/codecs/abstraction/AbstractCodecStringTest.java index 789c8a153..cf48dcfff 100644 --- a/src/test/java/org/owasp/esapi/codecs/abstraction/AbstractCodecStringTest.java +++ b/src/test/java/org/owasp/esapi/codecs/abstraction/AbstractCodecStringTest.java @@ -27,7 +27,7 @@ *
* Sub-classes are expected to provide instances of {@link CodecStringTestTuple} to this instance. *
- * For better test naming output specify {@link CodecStringTestTuple#description} and use {@code} @Parameters (name="{0}")}, + * For better test naming output specify {@link CodecStringTestTuple#description} and use @Parameters (name="{0}"), * where '0' is the index that the CodecStringTestTuple reference appears in the constructor. */ @RunWith(Parameterized.class) diff --git a/src/test/java/org/owasp/esapi/reference/regex/AbstractPatternTest.java b/src/test/java/org/owasp/esapi/reference/regex/AbstractPatternTest.java index 89dd1f126..f887db57b 100644 --- a/src/test/java/org/owasp/esapi/reference/regex/AbstractPatternTest.java +++ b/src/test/java/org/owasp/esapi/reference/regex/AbstractPatternTest.java @@ -26,7 +26,7 @@ *
* Sub-classes are expected to provide instances of {@link PatternTestTuple} to this instance. *
- * For better test naming output specify {@link PatternTestTuple#description} and use {@code} @Parameters (name="{0}")}, + * For better test naming output specify {@link PatternTestTuple#description} and use @Parameters (name="{0}"), * where '0' is the index that the PatternTestTuple reference appears in the constructor. */ @RunWith(Parameterized.class) From 3bf59c6c02d929412ad551ef566f08938e4696af Mon Sep 17 00:00:00 2001 From: Matt Seil Date: Sun, 13 May 2018 15:41:29 -0700 Subject: [PATCH 0452/1069] Moved esapi.tld into the correct resources location. Fixes issues #213, #244, #253. --- {configuration => src/main/resources}/META-INF/esapi.tld | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename {configuration => src/main/resources}/META-INF/esapi.tld (100%) diff --git a/configuration/META-INF/esapi.tld b/src/main/resources/META-INF/esapi.tld similarity index 100% rename from configuration/META-INF/esapi.tld rename to src/main/resources/META-INF/esapi.tld From b54019eab92f58cdf017a26568f079b028cc5f35 Mon Sep 17 00:00:00 2001 From: Jeremiah Stacey Date: Mon, 9 Jul 2018 17:08:38 -0500 Subject: [PATCH 0453/1069] Updating Asserts for Additional Output Converting AssertTrue to other Assert API (mostly assertEquals) to see the values being compared in the test output. --- .../owasp/esapi/reference/crypto/EncryptorTest.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/test/java/org/owasp/esapi/reference/crypto/EncryptorTest.java b/src/test/java/org/owasp/esapi/reference/crypto/EncryptorTest.java index 787464cb8..180a590ed 100644 --- a/src/test/java/org/owasp/esapi/reference/crypto/EncryptorTest.java +++ b/src/test/java/org/owasp/esapi/reference/crypto/EncryptorTest.java @@ -251,7 +251,7 @@ private String runNewEncryptDecryptTestCase(String cipherXform, int keySize, byt } else if ( cipherAlg.equals( "DES" ) ) { keySize = 64; } // Else... use specified keySize. - assertTrue( (keySize / 8) == skey.getEncoded().length ); + assertEquals(cipherXform + " encoded key size does not match provided key size", (keySize / 8), skey.getEncoded().length ); // System.out.println("testNewEncryptDecrypt(): Skey length (bits) = " + 8 * skey.getEncoded().length); // Change to a possibly different cipher. This is kludgey at best. Am thinking about an @@ -271,7 +271,7 @@ private String runNewEncryptDecryptTestCase(String cipherXform, int keySize, byt // Do the encryption with the new encrypt() method and get back the CipherText. CipherText ciphertext = instance.encrypt(skey, plaintext); // The new encrypt() method. System.out.println("DEBUG: Encrypt(): CipherText object is -- " + ciphertext); - assertTrue( ciphertext != null ); + assertNotNull( ciphertext ); // System.out.println("DEBUG: After encryption: base64-encoded IV+ciphertext: " + ciphertext.getEncodedIVCipherText()); // System.out.println("\t\tOr... " + ESAPI.encoder().decodeFromBase64(ciphertext.getEncodedIVCipherText()) ); // System.out.println("DEBUG: After encryption: base64-encoded raw ciphertext: " + ciphertext.getBase64EncodedRawCipherText()); @@ -290,14 +290,14 @@ private String runNewEncryptDecryptTestCase(String cipherXform, int keySize, byt // Make sure we got back the same thing we started with. System.out.println("\tOriginal plaintext: " + origPlainText); System.out.println("\tResult after decryption: " + decryptedPlaintext); - assertTrue( "Failed to decrypt properly.", origPlainText.toString().equals( decryptedPlaintext.toString() ) ); + assertEquals( "Failed to decrypt properly.", origPlainText.toString(), decryptedPlaintext.toString() ); // Restore the previous cipher transformation. For now, this is only way to do this. @SuppressWarnings("deprecation") String previousCipherXform = ESAPI.securityConfiguration().setCipherTransformation(null); - assertTrue( previousCipherXform.equals( cipherXform ) ); + assertEquals( previousCipherXform, cipherXform ); String defaultCipherXform = ESAPI.securityConfiguration().getCipherTransformation(); - assertTrue( defaultCipherXform.equals( oldCipherXform ) ); + assertEquals( defaultCipherXform, oldCipherXform ); return ciphertext.getEncodedIVCipherText(); } catch (Exception e) { From 5232e21036f2959875c7ec7996406f9692d8e5fe Mon Sep 17 00:00:00 2001 From: Jeremiah Stacey Date: Sun, 22 Jul 2018 08:04:57 -0500 Subject: [PATCH 0454/1069] Attempt at SLF4J Implementation Experimenting with one method that may be functional for slf4j hooks. This implementation has not been tested at either the unit or integration level. I do not know if it works yet. --- pom.xml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/pom.xml b/pom.xml index 086f70917..6112a4e7f 100644 --- a/pom.xml +++ b/pom.xml @@ -243,6 +243,11 @@ 1.7.0 test + + org.slf4j + slf4j-api + 1.7.25 + org.powermock - powermock-api-mockito - 1.7.0 + powermock-api-mockito2 + 2.0.0-beta.5 test org.powermock powermock-module-junit4 - 1.7.0 + 2.0.0-beta.5 test org.slf4j slf4j-api - 1.7.25 + 1.8.0-beta2 - - joda-time - joda-time - 2.10 - + + + joda-time + joda-time + 2.10 + commons-configuration commons-configuration @@ -142,18 +145,11 @@ commons-beanutils 1.9.3 - - - junit - junit - 4.12 - test - - - org.bouncycastle - bcprov-jdk15on - 1.60 - test + javax.servlet @@ -163,8 +159,8 @@ javax.servlet.jsp - jsp-api - 2.2.1-b03 + javax.servlet.jsp-api + 2.3.3 provided @@ -173,12 +169,6 @@ 1.3.3 compile - - commons-io - commons-io - 2.6 - test - org.apache.commons commons-collections4 @@ -191,121 +181,104 @@ compile jar + + org.slf4j + slf4j-api + 1.7.25 + com.io7m.xom xom 1.2.10 - - xerces - xercesImpl - - - xml-apis - xml-apis - - - - - xml-apis - xml-apis - 1.4.01 - - - xerces - xercesImpl - 2.12.0 + + xerces + xercesImpl + + + xml-apis + xml-apis + + org.apache-extras.beanshell - bsh - 2.0b6 + bsh + 2.0b6 org.owasp.antisamy antisamy 1.5.7 - - xml-apis - xml-apis - - - xerces - xercesImpl - - + + xml-apis + xml-apis + + + xerces + xercesImpl + + - xalan xalan 2.7.2 - - - xml-apis - xml-apis - - + + + xml-apis + xml-apis + 1.4.01 org.apache.xmlgraphics batik-css 1.10 - - - - org.powermock - powermock-api-mockito2 - 2.0.0-beta.5 - test - - - org.powermock - powermock-module-junit4 - 2.0.0-beta.5 - test - - - org.slf4j - slf4j-api - 1.7.25 - - - - - + + xerces + xercesImpl + 2.12.0 + + + + junit + junit + 4.12 + test + + + org.bouncycastle + bcprov-jdk15on + 1.60 + test + + + commons-io + commons-io + 2.6 + test + + + + org.powermock + powermock-api-mockito2 + 2.0.0-beta.5 + test + + + org.powermock + powermock-module-junit4 + 2.0.0-beta.5 + test + @@ -404,7 +377,7 @@ dependency-check-maven 2.1.0 - 5.9 + ./suppressions.xml @@ -609,37 +582,37 @@
- - org.apache.maven.plugins - maven-jar-plugin - 2.6 + + org.apache.maven.plugins + maven-jar-plugin + 2.6 - + - - - true - true - - - true - - - - + + + true + true + + + true + + + + diff --git a/src/main/java/org/owasp/esapi/reference/accesscontrol/DelegatingACR.java b/src/main/java/org/owasp/esapi/reference/accesscontrol/DelegatingACR.java index fa532ddaf..8c7698908 100644 --- a/src/main/java/org/owasp/esapi/reference/accesscontrol/DelegatingACR.java +++ b/src/main/java/org/owasp/esapi/reference/accesscontrol/DelegatingACR.java @@ -5,7 +5,7 @@ import java.util.Iterator; import java.util.Vector; -import org.apache.commons.collections.iterators.ArrayListIterator; +import org.apache.commons.collections4.iterators.ArrayListIterator; public class DelegatingACR extends BaseACR { protected Method delegateMethod; From fe2cfff3c415d43f5c0a5f53dbba32f636e57dae Mon Sep 17 00:00:00 2001 From: kwwall Date: Fri, 5 Oct 2018 15:43:47 -0400 Subject: [PATCH 0473/1069] Close issue #444. Delete deprecated decodeToObject() method and 2 related encodeObject() methods. General whitespace clean-up. Delete EncoderTest.testBase64decodToObject() method which is no longer relevant. --- .../java/org/owasp/esapi/codecs/Base64.java | 839 ++++++------------ .../owasp/esapi/reference/EncoderTest.java | 54 -- 2 files changed, 293 insertions(+), 600 deletions(-) diff --git a/src/main/java/org/owasp/esapi/codecs/Base64.java b/src/main/java/org/owasp/esapi/codecs/Base64.java index b92f35590..150bd29ef 100644 --- a/src/main/java/org/owasp/esapi/codecs/Base64.java +++ b/src/main/java/org/owasp/esapi/codecs/Base64.java @@ -15,14 +15,14 @@ *

Homepage: http://iharder.net/base64 * (based on version 2.2.2).

* - *

The options parameter, which appears in a few places, is used to pass - * several pieces of information to the encoder. In the "higher level" methods such as - * encodeBytes( bytes, options ) the options parameter can be used to indicate such - * things as first gzipping the bytes before encoding them, not inserting linefeeds - * (though that breaks strict Base64 compatibility), and encoding using the URL-safe + *

The options parameter, which appears in a few places, is used to pass + * several pieces of information to the encoder. In the "higher level" methods such as + * encodeBytes( bytes, options ) the options parameter can be used to indicate such + * things as first gzipping the bytes before encoding them, not inserting linefeeds + * (though that breaks strict Base64 compatibility), and encoding using the URL-safe * and Ordered dialects.

* - *

The constants defined in Base64 can be OR-ed together to combine options, so you + *

The constants defined in Base64 can be OR-ed together to combine options, so you * might make a call like this:

* * String encoded = Base64.encodeBytes( mybytes, Base64.GZIP | Base64.DONT_BREAK_LINES ); @@ -54,24 +54,24 @@ * Special thanks to Jim Kellerman at http://www.powerset.com/ * for contributing the new Base64 dialects. * - * + * *
  • v2.1 - Cleaned up javadoc comments and unused variables and methods. Added * some convenience methods for reading and writing to and from files.
  • *
  • v2.0.2 - Now specifies UTF-8 encoding in places where the code fails on systems * with other encodings (like EBCDIC).
  • *
  • v2.0.1 - Fixed an error when decoding a single byte, that is, when the * encoded data was a single byte.
  • - *
  • v2.0 - I got rid of methods that used booleans to set options. + *
  • v2.0 - I got rid of methods that used booleans to set options. * Now everything is more consolidated and cleaner. The code now detects * when data that's being decoded is gzip-compressed and will decompress it * automatically. Generally things are cleaner. You'll probably have to * change some method calls that you were making to support the new * options format (ints that you "OR" together).
  • - *
  • v1.5.1 - Fixed bug when decompressing and decoding to a - * byte[] using decode( String s, boolean gzipCompressed ). - * Added the ability to "suspend" encoding in the Output Stream so - * you can turn on and off the encoding if you need to embed base64 - * data in an otherwise "normal" stream (like an XML file).
  • + *
  • v1.5.1 - Fixed bug when decompressing and decoding to a + * byte[] using decode( String s, boolean gzipCompressed ). + * Added the ability to "suspend" encoding in the Output Stream so + * you can turn on and off the encoding if you need to embed base64 + * data in an otherwise "normal" stream (like an XML file).
  • *
  • v1.5 - Output stream pases on flush() command but doesn't do anything itself. * This helps when using GZIP streams. * Added the ability to GZip-compress objects before encoding them.
  • @@ -97,108 +97,98 @@ */ public class Base64 { - -/* ******** P U B L I C F I E L D S ******** */ - - + +/* ******** P U B L I C F I E L D S ******** */ + + /** No options specified. Value is zero. */ public final static int NO_OPTIONS = 0; - + /** Specify encoding. */ public final static int ENCODE = 1; - - + /** Specify decoding. */ public final static int DECODE = 0; - - + /** Specify that data should be gzip-compressed. */ public final static int GZIP = 2; - - + /** Don't break lines when encoding (violates strict Base64 specification) */ public final static int DONT_BREAK_LINES = 8; - - /** - * Encode using Base64-like encoding that is URL- and Filename-safe as described - * in Section 4 of RFC3548: - * http://www.faqs.org/rfcs/rfc3548.html. - * It is important to note that data encoded this way is not officially valid Base64, - * or at the very least should not be called Base64 without also specifying that is - * was encoded using the URL- and Filename-safe dialect. - */ - public final static int URL_SAFE = 16; - - - /** - * Encode using the special "ordered" dialect of Base64 described here: - * http://www.faqs.org/qa/rfcc-1940.html. - */ - public final static int ORDERED = 32; - + + /** + * Encode using Base64-like encoding that is URL- and Filename-safe as described + * in Section 4 of RFC3548: + * http://www.faqs.org/rfcs/rfc3548.html. + * It is important to note that data encoded this way is not officially valid Base64, + * or at the very least should not be called Base64 without also specifying that is + * was encoded using the URL- and Filename-safe dialect. + */ + public final static int URL_SAFE = 16; + + /** + * Encode using the special "ordered" dialect of Base64 described here: + * http://www.faqs.org/qa/rfcc-1940.html. + */ + public final static int ORDERED = 32; + /** * System property name that must be set to true in order to invoke {@code Base64.decodeToObject()}. * @see https://github.com/ESAPI/esapi-java-legacy/issues/354 * @see http://foxglovesecurity.com/2015/11/06/what-do-weblogic-websphere-jboss-jenkins-opennms-and-your-application-have-in-common-this-vulnerability/ */ public final static String ENABLE_UNSAFE_SERIALIZATION = "org.owasp.esapi.enableUnsafeSerialization"; // Do NOT change! - -/* ******** P R I V A T E F I E L D S ******** */ - - + +/* ******** P R I V A T E F I E L D S ******** */ + /** Maximum line length (76) of Base64 output. */ private final static int MAX_LINE_LENGTH = 76; - - + /** The equals sign (=) as a byte. */ private final static byte EQUALS_SIGN = (byte)'='; - - + /** The new line character (\n) as a byte. */ private final static byte NEW_LINE = (byte)'\n'; - - + /** Preferred encoding. */ private final static String PREFERRED_ENCODING = "UTF-8"; - + /** End of line character. */ private final static String EOL = System.getProperty("line.separator", "\n"); - - + + // I think I end up not using the BAD_ENCODING indicator. //private final static byte BAD_ENCODING = -9; // Indicates error in encoding private final static byte WHITE_SPACE_ENC = -5; // Indicates white space in encoding private final static byte EQUALS_SIGN_ENC = -1; // Indicates equals sign in encoding - + private static final Logger logger = ESAPI.getLogger("Base64"); - - -/* ******** S T A N D A R D B A S E 6 4 A L P H A B E T ******** */ - + +/* ******** S T A N D A R D B A S E 6 4 A L P H A B E T ******** */ + /** The 64 valid Base64 values. */ //private final static byte[] ALPHABET; - /* Host platform me be something funny like EBCDIC, so we hard code these values. */ - private final static byte[] _STANDARD_ALPHABET = + /* Host platform me be something funny like EBCDIC, so we hard code these values. */ + private final static byte[] _STANDARD_ALPHABET = { (byte)'A', (byte)'B', (byte)'C', (byte)'D', (byte)'E', (byte)'F', (byte)'G', (byte)'H', (byte)'I', (byte)'J', (byte)'K', (byte)'L', (byte)'M', (byte)'N', - (byte)'O', (byte)'P', (byte)'Q', (byte)'R', (byte)'S', (byte)'T', (byte)'U', + (byte)'O', (byte)'P', (byte)'Q', (byte)'R', (byte)'S', (byte)'T', (byte)'U', (byte)'V', (byte)'W', (byte)'X', (byte)'Y', (byte)'Z', (byte)'a', (byte)'b', (byte)'c', (byte)'d', (byte)'e', (byte)'f', (byte)'g', (byte)'h', (byte)'i', (byte)'j', (byte)'k', (byte)'l', (byte)'m', (byte)'n', - (byte)'o', (byte)'p', (byte)'q', (byte)'r', (byte)'s', (byte)'t', (byte)'u', + (byte)'o', (byte)'p', (byte)'q', (byte)'r', (byte)'s', (byte)'t', (byte)'u', (byte)'v', (byte)'w', (byte)'x', (byte)'y', (byte)'z', - (byte)'0', (byte)'1', (byte)'2', (byte)'3', (byte)'4', (byte)'5', + (byte)'0', (byte)'1', (byte)'2', (byte)'3', (byte)'4', (byte)'5', (byte)'6', (byte)'7', (byte)'8', (byte)'9', (byte)'+', (byte)'/' }; - - - /** + + /** * Translates a Base64 value to either its 6-bit reconstruction value * or a negative number indicating some other meaning. **/ private final static byte[] _STANDARD_DECODABET = - { + { -9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 0 - 8 -5,-5, // Whitespace: Tab and Linefeed -9,-9, // Decimal 11 - 12 @@ -231,34 +221,34 @@ public class Base64 -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 231 - 243 -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9 // Decimal 244 - 255 */ }; - - + + /* ******** U R L S A F E B A S E 6 4 A L P H A B E T ******** */ - - /** - * Used in the URL- and Filename-safe dialect described in Section 4 of RFC3548: - * http://www.faqs.org/rfcs/rfc3548.html. - * Notice that the last two bytes become "hyphen" and "underscore" instead of "plus" and "slash." - */ + + /** + * Used in the URL- and Filename-safe dialect described in Section 4 of RFC3548: + * http://www.faqs.org/rfcs/rfc3548.html. + * Notice that the last two bytes become "hyphen" and "underscore" instead of "plus" and "slash." + */ private final static byte[] _URL_SAFE_ALPHABET = { (byte)'A', (byte)'B', (byte)'C', (byte)'D', (byte)'E', (byte)'F', (byte)'G', (byte)'H', (byte)'I', (byte)'J', (byte)'K', (byte)'L', (byte)'M', (byte)'N', - (byte)'O', (byte)'P', (byte)'Q', (byte)'R', (byte)'S', (byte)'T', (byte)'U', + (byte)'O', (byte)'P', (byte)'Q', (byte)'R', (byte)'S', (byte)'T', (byte)'U', (byte)'V', (byte)'W', (byte)'X', (byte)'Y', (byte)'Z', (byte)'a', (byte)'b', (byte)'c', (byte)'d', (byte)'e', (byte)'f', (byte)'g', (byte)'h', (byte)'i', (byte)'j', (byte)'k', (byte)'l', (byte)'m', (byte)'n', - (byte)'o', (byte)'p', (byte)'q', (byte)'r', (byte)'s', (byte)'t', (byte)'u', + (byte)'o', (byte)'p', (byte)'q', (byte)'r', (byte)'s', (byte)'t', (byte)'u', (byte)'v', (byte)'w', (byte)'x', (byte)'y', (byte)'z', - (byte)'0', (byte)'1', (byte)'2', (byte)'3', (byte)'4', (byte)'5', + (byte)'0', (byte)'1', (byte)'2', (byte)'3', (byte)'4', (byte)'5', (byte)'6', (byte)'7', (byte)'8', (byte)'9', (byte)'-', (byte)'_' }; - - /** - * Used in decoding URL- and Filename-safe dialects of Base64. - */ + + /** + * Used in decoding URL- and Filename-safe dialects of Base64. + */ private final static byte[] _URL_SAFE_DECODABET = - { + { -9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 0 - 8 -5,-5, // Whitespace: Tab and Linefeed -9,-9, // Decimal 11 - 12 @@ -300,10 +290,10 @@ public class Base64 /* ******** O R D E R E D B A S E 6 4 A L P H A B E T ******** */ - /** - * I don't get the point of this technique, but it is described here: - * http://www.faqs.org/qa/rfcc-1940.html. - */ + /** + * I don't get the point of this technique, but it is described here: + * http://www.faqs.org/qa/rfcc-1940.html. + */ private final static byte[] _ORDERED_ALPHABET = { (byte)'-', @@ -319,12 +309,12 @@ public class Base64 (byte)'o', (byte)'p', (byte)'q', (byte)'r', (byte)'s', (byte)'t', (byte)'u', (byte)'v', (byte)'w', (byte)'x', (byte)'y', (byte)'z' }; - - /** - * Used in decoding the "ordered" dialect of Base64. - */ + + /** + * Used in decoding the "ordered" dialect of Base64. + */ private final static byte[] _ORDERED_DECODABET = - { + { -9,-9,-9,-9,-9,-9,-9,-9,-9, // Decimal 0 - 8 -5,-5, // Whitespace: Tab and Linefeed -9,-9, // Decimal 11 - 12 @@ -362,46 +352,43 @@ public class Base64 -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9 // Decimal 244 - 255 */ }; - + /* ******** D E T E R M I N E W H I C H A L H A B E T ******** */ - /** - * Returns one of the _SOMETHING_ALPHABET byte arrays depending on - * the options specified. - * It's possible, though silly, to specify ORDERED and URLSAFE - * in which case one of them will be picked, though there is - * no guarantee as to which one will be picked. - */ - private final static byte[] getAlphabet( int options ) - { - if( (options & URL_SAFE) == URL_SAFE ) return _URL_SAFE_ALPHABET; - else if( (options & ORDERED) == ORDERED ) return _ORDERED_ALPHABET; - else return _STANDARD_ALPHABET; - - } // end getAlphabet - - - /** - * Returns one of the _SOMETHING_DECODABET byte arrays depending on - * the options specified. - * It's possible, though silly, to specify ORDERED and URL_SAFE - * in which case one of them will be picked, though there is - * no guarantee as to which one will be picked. - */ - private final static byte[] getDecodabet( int options ) - { - if( (options & URL_SAFE) == URL_SAFE ) return _URL_SAFE_DECODABET; - else if( (options & ORDERED) == ORDERED ) return _ORDERED_DECODABET; - else return _STANDARD_DECODABET; - - } // end getAlphabet - - - + /** + * Returns one of the _SOMETHING_ALPHABET byte arrays depending on + * the options specified. + * It's possible, though silly, to specify ORDERED and URLSAFE + * in which case one of them will be picked, though there is + * no guarantee as to which one will be picked. + */ + private final static byte[] getAlphabet( int options ) + { + if( (options & URL_SAFE) == URL_SAFE ) return _URL_SAFE_ALPHABET; + else if( (options & ORDERED) == ORDERED ) return _ORDERED_ALPHABET; + else return _STANDARD_ALPHABET; + + } // end getAlphabet + + + /** + * Returns one of the _SOMETHING_DECODABET byte arrays depending on + * the options specified. + * It's possible, though silly, to specify ORDERED and URL_SAFE + * in which case one of them will be picked, though there is + * no guarantee as to which one will be picked. + */ + private final static byte[] getDecodabet( int options ) + { + if( (options & URL_SAFE) == URL_SAFE ) return _URL_SAFE_DECODABET; + else if( (options & ORDERED) == ORDERED ) return _ORDERED_DECODABET; + else return _STANDARD_DECODABET; + + } // end getDecodaabet + /** Defeats instantiation. */ private Base64(){} - /** * Encodes or decodes two files from the command line; @@ -423,10 +410,10 @@ public final static void main( String[] args ) } // end if: encode else if( flag.equals( "-d" ) ) { Base64.decodeFileToFile( infile, outfile ); - } // end else if: decode + } // end else if: decode else { usage( "Unknown flag: " + flag ); - } // end else + } // end else } // end else } // end main @@ -440,11 +427,9 @@ private final static void usage( String msg ) System.err.println( msg ); System.err.println( "Usage: java Base64 -e|-d inputfile outputfile" ); } // end usage - - -/* ******** E N C O D I N G M E T H O D S ******** */ - - + +/* ******** E N C O D I N G M E T H O D S ******** */ + /** * Encodes up to the first three bytes of array threeBytes * and returns a four-byte array in Base64 notation. @@ -466,12 +451,11 @@ private static byte[] encode3to4( byte[] b4, byte[] threeBytes, int numSigBytes, return b4; } // end encode3to4 - /** *

    Encodes up to three bytes of the array source * and writes the resulting four Base64 bytes to destination. * The source and destination arrays can be manipulated - * anywhere along their length by specifying + * anywhere along their length by specifying * srcOffset and destOffset. * This method does not check to make sure your arrays * are large enough to accomodate srcOffset + 3 for @@ -479,8 +463,8 @@ private static byte[] encode3to4( byte[] b4, byte[] threeBytes, int numSigBytes, * the destination array. * The actual number of significant bytes in your array is * given by numSigBytes.

    - *

    This is the lowest level of the encoding methods with - * all possible parameters.

    + *

    This is the lowest level of the encoding methods with + * all possible parameters.

    * * @param source the array to convert * @param srcOffset the index where conversion begins @@ -490,19 +474,19 @@ private static byte[] encode3to4( byte[] b4, byte[] threeBytes, int numSigBytes, * @return the destination array * @since 1.3 */ - private static byte[] encode3to4( - byte[] source, int srcOffset, int numSigBytes, - byte[] destination, int destOffset, int options ) + private static byte[] encode3to4( + byte[] source, int srcOffset, int numSigBytes, + byte[] destination, int destOffset, int options ) { - byte[] ALPHABET = getAlphabet( options ); - - // 1 2 3 + byte[] ALPHABET = getAlphabet( options ); + + // 1 2 3 // 01234567890123456789012345678901 Bit position // --------000000001111111122222222 Array position from threeBytes // --------| || || || | Six bit groups to index ALPHABET // >>18 >>12 >> 6 >> 0 Right shift necessary // 0x3f 0x3f 0x3f Additional AND - + // Create buffer with zero-padding if there are only one or two // significant bytes passed in the array. // We have to shift left 24 in order to flush out the 1's that appear @@ -519,124 +503,25 @@ private static byte[] encode3to4( destination[ destOffset + 2 ] = ALPHABET[ (inBuff >>> 6) & 0x3f ]; destination[ destOffset + 3 ] = ALPHABET[ (inBuff ) & 0x3f ]; return destination; - + case 2: destination[ destOffset ] = ALPHABET[ (inBuff >>> 18) ]; destination[ destOffset + 1 ] = ALPHABET[ (inBuff >>> 12) & 0x3f ]; destination[ destOffset + 2 ] = ALPHABET[ (inBuff >>> 6) & 0x3f ]; destination[ destOffset + 3 ] = EQUALS_SIGN; return destination; - + case 1: destination[ destOffset ] = ALPHABET[ (inBuff >>> 18) ]; destination[ destOffset + 1 ] = ALPHABET[ (inBuff >>> 12) & 0x3f ]; destination[ destOffset + 2 ] = EQUALS_SIGN; destination[ destOffset + 3 ] = EQUALS_SIGN; return destination; - + default: return destination; } // end switch } // end encode3to4 - - - - /** - * Serializes an object and returns the Base64-encoded - * version of that serialized object. If the object - * cannot be serialized or there is another error, - * the method will return null. - * The object is not GZip-compressed before being encoded. - * - * @param serializableObject The object to encode - * @return The Base64-encoded object - * @since 1.4 - */ - public static String encodeObject( java.io.Serializable serializableObject ) - { - return encodeObject( serializableObject, NO_OPTIONS ); - } // end encodeObject - - - - /** - * Serializes an object and returns the Base64-encoded - * version of that serialized object. If the object - * cannot be serialized or there is another error, - * the method will return null. - *

    - * Valid options:

    -     *   GZIP: gzip-compresses object before encoding it.
    -     *   DONT_BREAK_LINES: don't break lines at 76 characters
    -     *     Note: Technically, this makes your encoding non-compliant.
    -     * 
    - *

    - * Example: encodeObject( myObj, Base64.GZIP ) or - *

    - * Example: encodeObject( myObj, Base64.GZIP | Base64.DONT_BREAK_LINES ) - * - * @param serializableObject The object to encode - * @param options Specified options - * @return The Base64-encoded object - * @see Base64#GZIP - * @see Base64#DONT_BREAK_LINES - * @since 2.0 - */ - public static String encodeObject( java.io.Serializable serializableObject, int options ) - { - // Streams - java.io.ByteArrayOutputStream baos = null; - java.io.OutputStream b64os = null; - java.io.ObjectOutputStream oos = null; - java.util.zip.GZIPOutputStream gzos = null; - - // Isolate options - int gzip = (options & GZIP); - //int dontBreakLines = (options & DONT_BREAK_LINES); - - try - { - // ObjectOutputStream -> (GZIP) -> Base64 -> ByteArrayOutputStream - baos = new java.io.ByteArrayOutputStream(); - b64os = new Base64.OutputStream( baos, ENCODE | options ); - - // GZip? - if( gzip == GZIP ) - { - gzos = new java.util.zip.GZIPOutputStream( b64os ); - oos = new java.io.ObjectOutputStream( gzos ); - } // end if: gzip - else - oos = new java.io.ObjectOutputStream( b64os ); - - oos.writeObject( serializableObject ); - } // end try - catch( java.io.IOException e ) - { - logger.error( Logger.SECURITY_FAILURE, "Problem writing object", e ); - return null; - } // end catch - finally - { - try{ oos.close(); } catch( Exception e ){} - try{ gzos.close(); } catch( Exception e ){} - try{ b64os.close(); } catch( Exception e ){} - try{ baos.close(); } catch( Exception e ){} - } // end finally - - // Return value according to relevant encoding. - try - { - return new String( baos.toByteArray(), PREFERRED_ENCODING ); - } // end try - catch (java.io.UnsupportedEncodingException uue) - { - return new String( baos.toByteArray() ); - } // end catch - - } // end encode - - /** * Encodes a byte array into Base64 notation. @@ -650,7 +535,7 @@ public static String encodeBytes( byte[] source ) { return encodeBytes( source, 0, source.length, NO_OPTIONS ); } // end encodeBytes - + /** @@ -675,11 +560,10 @@ public static String encodeBytes( byte[] source ) * @since 2.0 */ public static String encodeBytes( byte[] source, int options ) - { + { return encodeBytes( source, 0, source.length, options ); } // end encodeBytes - - + /** * Encodes a byte array into Base64 notation. * Does not GZip-compress data. @@ -694,8 +578,6 @@ public static String encodeBytes( byte[] source, int off, int len ) { return encodeBytes( source, off, len, NO_OPTIONS ); } // end encodeBytes - - /** * Encodes a byte array into Base64 notation. @@ -725,22 +607,21 @@ public static String encodeBytes( byte[] source, int off, int len, int options ) // Isolate options int dontBreakLines = ( options & DONT_BREAK_LINES ); int gzip = ( options & GZIP ); - + // Compress? if( gzip == GZIP ) { java.io.ByteArrayOutputStream baos = null; java.util.zip.GZIPOutputStream gzos = null; Base64.OutputStream b64os = null; - - + try { // GZip -> Base64 -> ByteArray baos = new java.io.ByteArrayOutputStream(); b64os = new Base64.OutputStream( baos, ENCODE | options ); - gzos = new java.util.zip.GZIPOutputStream( b64os ); - + gzos = new java.util.zip.GZIPOutputStream( b64os ); + gzos.write( source, off, len ); gzos.close(); } // end try @@ -766,17 +647,17 @@ public static String encodeBytes( byte[] source, int off, int len, int options ) return new String( baos.toByteArray() ); } // end catch } // end if: compress - + // Else, don't compress. Better not to use streams at all then. else { // Convert option to boolean in way that code likes it. boolean breakLines = dontBreakLines == 0; - + int len43 = len * 4 / 3; byte[] outBuff = new byte[ ( len43 ) // Main 4:3 + ( (len % 3) > 0 ? 4 : 0 ) // Account for padding - + (breakLines ? ( len43 / MAX_LINE_LENGTH ) : 0) ]; // New lines + + (breakLines ? ( len43 / MAX_LINE_LENGTH ) : 0) ]; // New lines int d = 0; int e = 0; int len2 = len - 2; @@ -787,7 +668,7 @@ public static String encodeBytes( byte[] source, int off, int len, int options ) lineLength += 4; if( breakLines && lineLength == MAX_LINE_LENGTH ) - { + { outBuff[e+4] = NEW_LINE; e++; lineLength = 0; @@ -800,7 +681,7 @@ public static String encodeBytes( byte[] source, int off, int len, int options ) e += 4; } // end if: some padding needed - + // Return value according to relevant encoding. try { @@ -810,47 +691,42 @@ public static String encodeBytes( byte[] source, int off, int len, int options ) { return new String( outBuff, 0, e ); } // end catch - + } // end else: don't compress - + } // end encodeBytes - - - - /* ******** D E C O D I N G M E T H O D S ******** */ - - + /** * Decodes four bytes from array source * and writes the resulting bytes (up to three of them) * to destination. * The source and destination arrays can be manipulated - * anywhere along their length by specifying + * anywhere along their length by specifying * srcOffset and destOffset. * This method does not check to make sure your arrays * are large enough to accomodate srcOffset + 4 for * the source array or destOffset + 3 for * the destination array. - * This method returns the actual number of bytes that + * This method returns the actual number of bytes that * were converted from the Base64 encoding. - *

    This is the lowest level of the decoding methods with - * all possible parameters.

    - * + *

    This is the lowest level of the decoding methods with + * all possible parameters.

    + * * * @param source the array to convert * @param srcOffset the index where conversion begins * @param destination the array to hold the conversion * @param destOffset the index where output will be put - * @param options alphabet type is pulled from this (standard, url-safe, ordered) + * @param options alphabet type is pulled from this (standard, url-safe, ordered) * @return the number of decoded bytes converted * @since 1.3 */ private static int decode4to3( byte[] source, int srcOffset, byte[] destination, int destOffset, int options ) { - byte[] DECODABET = getDecodabet( options ); - + byte[] DECODABET = getDecodabet( options ); + // Example: Dk== if( source[ srcOffset + 2] == EQUALS_SIGN ) { @@ -859,11 +735,11 @@ private static int decode4to3( byte[] source, int srcOffset, byte[] destination, // | ( ( DECODABET[ source[ srcOffset + 1] ] << 24 ) >>> 12 ); int outBuff = ( ( DECODABET[ source[ srcOffset ] ] & 0xFF ) << 18 ) | ( ( DECODABET[ source[ srcOffset + 1] ] & 0xFF ) << 12 ); - + destination[ destOffset ] = (byte)( outBuff >>> 16 ); return 1; } - + // Example: DkL= else if( source[ srcOffset + 3 ] == EQUALS_SIGN ) { @@ -874,12 +750,12 @@ else if( source[ srcOffset + 3 ] == EQUALS_SIGN ) int outBuff = ( ( DECODABET[ source[ srcOffset ] ] & 0xFF ) << 18 ) | ( ( DECODABET[ source[ srcOffset + 1 ] ] & 0xFF ) << 12 ) | ( ( DECODABET[ source[ srcOffset + 2 ] ] & 0xFF ) << 6 ); - + destination[ destOffset ] = (byte)( outBuff >>> 16 ); destination[ destOffset + 1 ] = (byte)( outBuff >>> 8 ); return 2; } - + // Example: DkLE else { @@ -894,44 +770,41 @@ else if( source[ srcOffset + 3 ] == EQUALS_SIGN ) | ( ( DECODABET[ source[ srcOffset + 2 ] ] & 0xFF ) << 6) | ( ( DECODABET[ source[ srcOffset + 3 ] ] & 0xFF ) ); - + destination[ destOffset ] = (byte)( outBuff >> 16 ); destination[ destOffset + 1 ] = (byte)( outBuff >> 8 ); destination[ destOffset + 2 ] = (byte)( outBuff ); return 3; }catch( Exception e){ - + // Remove these after checking -- for context only. // logger.error( Logger.SECURITY_FAILURE, "Problem writing object", e ); // logger.error( Logger.SECURITY_FAILURE, ""+source[srcOffset]+ ": " + ( DECODABET[ source[ srcOffset ] ] ) ); // logger.error( Logger.SECURITY_FAILURE, ""+source[srcOffset+1]+ ": " + ( DECODABET[ source[ srcOffset + 1 ] ] ) ); // logger.error( Logger.SECURITY_FAILURE, ""+source[srcOffset+2]+ ": " + ( DECODABET[ source[ srcOffset + 2 ] ] ) ); // logger.error( Logger.SECURITY_FAILURE, ""+source[srcOffset+3]+ ": " + ( DECODABET[ source[ srcOffset + 3 ] ] ) ); - - // CHECKME: I replaced the 5 separate logger.error() calls above with a single logger.error() call so they can't - // become interleaved with other log entries from other threads. Normally this would have placed log entries - // on separate lines, so I also added line terminators here as well. (Probably don't want it all on one single - // really long log entry, do we?) Anyhow, somebody should check the formatting to ensure that it's - // esthetically pleasing, etc. But this works for me. I'm also OK if you want to remove all the line terminators - // in which case the declaration for EOL should be removed as well. - Kevin Wall - + + // CHECKME: I replaced the 5 separate logger.error() calls above with a single logger.error() call so they can't + // become interleaved with other log entries from other threads. Normally this would have placed log entries + // on separate lines, so I also added line terminators here as well. (Probably don't want it all on one single + // really long log entry, do we?) Anyhow, somebody should check the formatting to ensure that it's + // esthetically pleasing, etc. But this works for me. I'm also OK if you want to remove all the line terminators + // in which case the declaration for EOL should be removed as well. - Kevin Wall + StringBuilder sb = new StringBuilder("Problem writing object:"); sb.append(EOL); sb.append( source[srcOffset] ).append(": ").append( ( DECODABET[ source[ srcOffset ] ] ) ).append(EOL); sb.append( source[srcOffset+1] ).append(": ").append( ( DECODABET[ source[ srcOffset + 1 ] ] ) ).append(EOL); sb.append( source[srcOffset+2] ).append(": ").append( ( DECODABET[ source[ srcOffset + 2 ] ] ) ).append(EOL); sb.append( source[srcOffset+3] ).append(": ").append( ( DECODABET[ source[ srcOffset + 3 ] ] ) ).append(EOL); - + logger.error( Logger.SECURITY_FAILURE, sb.toString(), e ); return -1; } // end catch } } // end decodeToBytes - - - - + /** * Very low-level access to decoding ASCII characters in * the form of a byte array. Does not support automatically @@ -946,12 +819,12 @@ else if( source[ srcOffset + 3 ] == EQUALS_SIGN ) */ public static byte[] decode( byte[] source, int off, int len, int options ) { - byte[] DECODABET = getDecodabet( options ); - + byte[] DECODABET = getDecodabet( options ); + int len34 = len * 3 / 4; byte[] outBuff = new byte[ len34 ]; // Upper limit on size of output int outBuffPosn = 0; - + byte[] b4 = new byte[4]; int b4Posn = 0; int i = 0; @@ -961,7 +834,7 @@ public static byte[] decode( byte[] source, int off, int len, int options ) { sbiCrop = (byte)(source[i] & 0x7f); // Only the low seven bits sbiDecode = DECODABET[ sbiCrop ]; - + if( sbiDecode >= WHITE_SPACE_ENC ) // White space, Equals sign or better { if( sbiDecode >= EQUALS_SIGN_ENC ) @@ -971,30 +844,27 @@ public static byte[] decode( byte[] source, int off, int len, int options ) { outBuffPosn += decode4to3( b4, 0, outBuff, outBuffPosn, options ); b4Posn = 0; - + // If that was the equals sign, break out of 'for' loop if( sbiCrop == EQUALS_SIGN ) break; } // end if: quartet built - + } // end if: equals sign or better - + } // end if: white space, equals sign or better else { - logger.error( Logger.SECURITY_FAILURE, "Bad Base64 input character at " + i + ": " + source[i] + "(decimal)" ); + logger.error( Logger.SECURITY_FAILURE, "Bad Base64 input character at " + i + ": " + source[i] + "(decimal)" ); return null; - } // end else: + } // end else: } // each input character - + byte[] out = new byte[ outBuffPosn ]; - System.arraycopy( outBuff, 0, out, 0, outBuffPosn ); + System.arraycopy( outBuff, 0, out, 0, outBuffPosn ); return out; } // end decode - - - - + /** * Decodes data from Base64 notation, automatically * detecting gzip-compressed data and decompressing it. @@ -1004,22 +874,21 @@ public static byte[] decode( byte[] source, int off, int len, int options ) * @since 1.4 */ public static byte[] decode( String s ) - { - return decode( s, NO_OPTIONS ); - } - - + { + return decode( s, NO_OPTIONS ); + } + /** * Decodes data from Base64 notation, automatically * detecting gzip-compressed data and decompressing it. * * @param s the string to decode - * @param options encode options such as URL_SAFE + * @param options encode options such as URL_SAFE * @return the decoded data * @since 1.4 */ public static byte[] decode( String s, int options ) - { + { byte[] bytes; try { @@ -1027,25 +896,24 @@ public static byte[] decode( String s, int options ) } catch( java.io.UnsupportedEncodingException uee ) { - bytes = s.getBytes(); // Uses native encoding + bytes = s.getBytes(); // Uses native encoding // CHECKME: Is this correct? I think it should be a warning instead of an error since nothing // is re-thrown. I do think that *some* sort of logging is in order here especially since UTF-8 should // always be available on all platforms. If it's not, then all bets are off on your runtime env. - Kevin Wall logger.warning( Logger.SECURITY_FAILURE, "Problem decoding string using " + - PREFERRED_ENCODING + "; substituting native platform encoding instead", uee ); + PREFERRED_ENCODING + "; substituting native platform encoding instead", uee ); } - + // Decode bytes = decode( bytes, 0, bytes.length, options ); - - + // Check to see if it's gzip-compressed // GZIP Magic Two-Byte Number: 0x8b1f (35615) if( bytes != null && bytes.length >= 4 ) { - - int head = ((int)bytes[0] & 0xff) | ((bytes[1] << 8) & 0xff00); - if( java.util.zip.GZIPInputStream.GZIP_MAGIC == head ) + + int head = ((int)bytes[0] & 0xff) | ((bytes[1] << 8) & 0xff00); + if ( java.util.zip.GZIPInputStream.GZIP_MAGIC == head ) { java.io.ByteArrayInputStream bais = null; java.util.zip.GZIPInputStream gzis = null; @@ -1081,95 +949,10 @@ public static byte[] decode( String s, int options ) } // end if: gzipped } // end if: bytes.length >= 2 - + return bytes; } // end decode - - - - /** - * Attempts to decode Base64 data and deserialize a Java - * Object within. Returns null if there was an error. - * - *

    - * WARNING: Using this method to decode non-validated / - * untrusted data from a string and deserialize it into - * an object can potentially result in remote command - * injection vulnerabilities. Use at your own risk! - *

    IMPORTANT BACKWARD COMPATIBILITY NOTICE
    - * Because this static method can easily be used as an attack vector - * for those passing in deserialized objects, in a manner similar to the - * Apache Commons Collections InvokerTransformer - * issue, we are requiring that the system property - * {@code org.owasp.esapi.enableUnsafeSerialization} - * be set to "true" in order for this method to be successfully invoked. - * We apologize for the inconvenience this may cause in breaking anyone's - * application, but we feel that it is for the greater good. - *

    - * - * @param encodedObject The Base64 data to decode - * @return The decoded and deserialized object - * @since 1.5 - * - * @deprecated Because of security issues, this method will be - * removed from ESAPI in a future release and no substitute - * is planned. Because as of JDK 8 (in 1Q2016) there is - * currently no way to restrict which objects - * ObjectInputStream.readObject() - * may safely deserialize in the general case. Oracle - * may decide to address this deficiency in a future Java - * release, but until they do, there is no simple way for - * a general class library like ESAPI to address this. - */ - @Deprecated - public static Object decodeToObject( String encodedObject ) - { - // We will do better when we attempt this again, allowing for a second argument - // to specify some sort of a collection of white-listed classes. Until then... - // See: http://www.ibm.com/developerworks/library/se-lookahead/ for how-to. - if ( ! "true".equalsIgnoreCase( System.getProperty( ENABLE_UNSAFE_SERIALIZATION ) ) ) { - throw new UnsupportedOperationException( - "Deserialization by Base64.decodeToObject(String) is disabled for security reasons. " + - "To re-enable it, set the system property '" + ENABLE_UNSAFE_SERIALIZATION + "' to 'true'." + - "For details, see: https://github.com/ESAPI/esapi-java-legacy/issues/354"); - } - - // Decode and gunzip if necessary - byte[] objBytes = decode( encodedObject ); - - java.io.ByteArrayInputStream bais = null; - java.io.ObjectInputStream ois = null; - Object obj = null; - - try - { - bais = new java.io.ByteArrayInputStream( objBytes ); - ois = new java.io.ObjectInputStream( bais ); - - obj = ois.readObject(); - } // end try - catch( java.io.IOException e ) - { - logger.error( Logger.SECURITY_FAILURE, "Problem reading object", e ); - obj = null; - } // end catch - catch( java.lang.ClassNotFoundException e ) - { - logger.error( Logger.SECURITY_FAILURE, "Problem reading object", e ); - obj = null; - } // end catch - finally - { - try{ bais.close(); } catch( Exception e ){} - try{ ois.close(); } catch( Exception e ){} - } // end finally - - return obj; - } // end decodeObject - - - /** * Convenience method for encoding data to a file. * @@ -1185,25 +968,24 @@ public static boolean encodeToFile( byte[] dataToEncode, String filename ) Base64.OutputStream bos = null; try { - bos = new Base64.OutputStream( + bos = new Base64.OutputStream( new java.io.FileOutputStream( filename ), Base64.ENCODE ); bos.write( dataToEncode ); success = true; } // end try catch( java.io.IOException e ) { - + success = false; } // end catch: IOException finally { try{ bos.close(); } catch( Exception e ){} } // end finally - + return success; } // end encodeToFile - - + /** * Convenience method for decoding data to a file. * @@ -1219,7 +1001,7 @@ public static boolean decodeToFile( String dataToDecode, String filename ) Base64.OutputStream bos = null; try { - bos = new Base64.OutputStream( + bos = new Base64.OutputStream( new java.io.FileOutputStream( filename ), Base64.DECODE ); bos.write( dataToDecode.getBytes( PREFERRED_ENCODING ) ); success = true; @@ -1232,13 +1014,10 @@ public static boolean decodeToFile( String dataToDecode, String filename ) { try{ bos.close(); } catch( Exception e ){} } // end finally - + return success; } // end decodeToFile - - - - + /** * Convenience method for reading a base64-encoded * file and decoding it. @@ -1259,7 +1038,7 @@ public static byte[] decodeFromFile( String filename ) byte[] buffer = null; int length = 0; int numBytes = 0; - + // Check for size of file if( file.length() > Integer.MAX_VALUE ) { @@ -1267,20 +1046,20 @@ public static byte[] decodeFromFile( String filename ) return null; } // end if: file too big for int index buffer = new byte[ (int)file.length() ]; - + // Open a stream - bis = new Base64.InputStream( - new java.io.BufferedInputStream( + bis = new Base64.InputStream( + new java.io.BufferedInputStream( new java.io.FileInputStream( file ) ), Base64.DECODE ); - + // Read until done while( ( numBytes = bis.read( buffer, length, 4096 ) ) >= 0 ) length += numBytes; - + // Save in a variable to return decodedData = new byte[ length ]; System.arraycopy( buffer, 0, decodedData, 0, length ); - + } // end try catch( java.io.IOException e ) { @@ -1290,12 +1069,10 @@ public static byte[] decodeFromFile( String filename ) { try{ if (bis != null ) bis.close(); } catch( Exception e) {} } // end finally - + return decodedData; } // end decodeFromFile - - - + /** * Convenience method for reading a binary file * and base64-encoding it. @@ -1316,19 +1093,19 @@ public static String encodeFromFile( String filename ) byte[] buffer = new byte[ Math.max((int)(file.length() * 1.4),40) ]; // Need max() for math on small files (v2.2.1) int length = 0; int numBytes = 0; - + // Open a stream - bis = new Base64.InputStream( - new java.io.BufferedInputStream( + bis = new Base64.InputStream( + new java.io.BufferedInputStream( new java.io.FileInputStream( file ) ), Base64.ENCODE ); - + // Read until done while( ( numBytes = bis.read( buffer, length, 4096 ) ) >= 0 ) length += numBytes; - + // Save in a variable to return encodedData = new String( buffer, 0, length, Base64.PREFERRED_ENCODING ); - + } // end try catch( java.io.IOException e ) { @@ -1338,13 +1115,10 @@ public static String encodeFromFile( String filename ) { try{ bis.close(); } catch( Exception e) {} } // end finally - + return encodedData; } // end encodeFromFile - - - - + /** * Reads infile and encodes it to outfile. * @@ -1359,9 +1133,9 @@ public static boolean encodeFileToFile( String infile, String outfile ) java.io.InputStream in = null; java.io.OutputStream out = null; try{ - in = new Base64.InputStream( - new java.io.BufferedInputStream( - new java.io.FileInputStream( infile ) ), + in = new Base64.InputStream( + new java.io.BufferedInputStream( + new java.io.FileInputStream( infile ) ), Base64.ENCODE ); out = new java.io.BufferedOutputStream( new java.io.FileOutputStream( outfile ) ); byte[] buffer = new byte[65536]; // 64K @@ -1376,12 +1150,10 @@ public static boolean encodeFileToFile( String infile, String outfile ) try{ in.close(); } catch( Exception exc ){} try{ out.close(); } catch( Exception exc ){} } // end finally - + return success; } // end encodeFileToFile - - - + /** * Reads infile and decodes it to outfile. * @@ -1396,9 +1168,9 @@ public static boolean decodeFileToFile( String infile, String outfile ) java.io.InputStream in = null; java.io.OutputStream out = null; try{ - in = new Base64.InputStream( - new java.io.BufferedInputStream( - new java.io.FileInputStream( infile ) ), + in = new Base64.InputStream( + new java.io.BufferedInputStream( + new java.io.FileInputStream( infile ) ), Base64.DECODE ); out = new java.io.BufferedOutputStream( new java.io.FileOutputStream( outfile ) ); byte[] buffer = new byte[65536]; // 64K @@ -1413,15 +1185,12 @@ public static boolean decodeFileToFile( String infile, String outfile ) try{ in.close(); } catch( Exception exc ){} try{ out.close(); } catch( Exception exc ){} } // end finally - + return success; } // end decodeFileToFile - - + /* ******** I N N E R C L A S S I N P U T S T R E A M ******** */ - - - + /** * A {@link Base64.InputStream} will read data from another * java.io.InputStream, given in the constructor, @@ -1439,10 +1208,9 @@ public static class InputStream extends java.io.FilterInputStream private int numSigBytes; // Number of meaningful bytes in the buffer private int lineLength; private boolean breakLines; // Break lines at less than 80 characters - private int options; // Record options used to create the stream. - private byte[] decodabet; // Local copies to avoid extra method calls - - + private int options; // Record options used to create the stream. + private byte[] decodabet; // Local copies to avoid extra method calls + /** * Constructs a {@link Base64.InputStream} in DECODE mode. * @@ -1450,11 +1218,10 @@ public static class InputStream extends java.io.FilterInputStream * @since 1.3 */ public InputStream( java.io.InputStream in ) - { + { this( in, DECODE ); } // end constructor - - + /** * Constructs a {@link Base64.InputStream} in * either ENCODE or DECODE mode. @@ -1477,7 +1244,7 @@ public InputStream( java.io.InputStream in ) * @since 2.0 */ public InputStream( java.io.InputStream in, int options ) - { + { super( in ); this.breakLines = (options & DONT_BREAK_LINES) != DONT_BREAK_LINES; this.encode = (options & ENCODE) == ENCODE; @@ -1485,10 +1252,10 @@ public InputStream( java.io.InputStream in, int options ) this.buffer = new byte[ bufferLength ]; this.position = -1; this.lineLength = 0; - this.options = options; // Record for later, mostly to determine which alphabet to use - this.decodabet = getDecodabet(options); + this.options = options; // Record for later, mostly to determine which alphabet to use + this.decodabet = getDecodabet(options); } // end constructor - + /** * Reads enough of the input stream to convert * to/from Base64 and returns the next byte. @@ -1497,8 +1264,8 @@ public InputStream( java.io.InputStream in, int options ) * @throws java.io.IOException * @since 1.3 */ - public int read() throws java.io.IOException - { + public int read() throws java.io.IOException + { // Do we need to get data? if( position < 0 ) { @@ -1509,26 +1276,26 @@ public int read() throws java.io.IOException for( int i = 0; i < 3; i++ ) { try - { + { int b = in.read(); - + // If end of stream, b is -1. if( b >= 0 ) { b3[i] = (byte)b; numBinaryBytes++; } // end if: not end of stream - + } // end try: read catch( java.io.IOException e ) - { + { // Only a problem if we got no data at all. if( i == 0 ) throw e; - + } // end catch } // end for: each needed input byte - + if( numBinaryBytes > 0 ) { encode3to4( b3, 0, numBinaryBytes, buffer, 0, options ); @@ -1540,7 +1307,7 @@ public int read() throws java.io.IOException return -1; } // end else } // end if: encoding - + // Else decoding else { @@ -1552,13 +1319,13 @@ public int read() throws java.io.IOException int b = 0; do{ b = in.read(); } while( b >= 0 && decodabet[ b & 0x7f ] <= WHITE_SPACE_ENC ); - + if( b < 0 ) break; // Reads a -1 if end of stream - + b4[i] = (byte)b; } // end for: each needed input byte - + if( i == 4 ) { numSigBytes = decode4to3( b4, 0, buffer, 0, options ); @@ -1571,18 +1338,18 @@ else if( i == 0 ){ { // Must have broken out from above. throw new java.io.IOException( "Improperly padded Base64 input." ); - } // end - + } // end + } // end else: decode } // end else: get data - + // Got data? if( position >= 0 ) { // End of relevant data? if( /*!encode &&*/ position >= numSigBytes ) return -1; - + if( encode && breakLines && lineLength >= MAX_LINE_LENGTH ) { lineLength = 0; @@ -1593,7 +1360,7 @@ else if( i == 0 ){ lineLength++; // This isn't important when decoding // but throwing an extra "if" seems // just as wasteful. - + int b = buffer[ position++ ]; if( position >= bufferLength ) @@ -1603,16 +1370,15 @@ else if( i == 0 ){ // intended to be unsigned. } // end else } // end if: position >= 0 - + // Else error else - { + { // When JDK1.4 is more accepted, use an assertion here. throw new java.io.IOException( "Error in Base64 code reading stream." ); } // end else } // end read - - + /** * Calls {@link #read()} repeatedly until the end of stream * is reached or len bytes are read. @@ -1633,10 +1399,10 @@ public int read( byte[] dest, int off, int len ) throws java.io.IOException for( i = 0; i < len; i++ ) { b = read(); - + //if( b < 0 && i == 0 ) // return -1; - + if( b >= 0 ) dest[off + i] = (byte)b; else if( i == 0 ) @@ -1646,18 +1412,12 @@ else if( i == 0 ) } // end for: each byte read return i; } // end read - + } // end inner class InputStream - - - - - - + + /* ******** I N N E R C L A S S O U T P U T S T R E A M ******** */ - - - + /** * A {@link Base64.OutputStream} will write data to another * java.io.OutputStream, given in the constructor, @@ -1676,9 +1436,9 @@ public static class OutputStream extends java.io.FilterOutputStream private boolean breakLines; private byte[] b4; // Scratch used in a few places private boolean suspendEncoding; - private int options; // Record for later - private byte[] decodabet; // Local copies to avoid extra method calls - + private int options; // Record for later + private byte[] decodabet; // Local copies to avoid extra method calls + /** * Constructs a {@link Base64.OutputStream} in ENCODE mode. * @@ -1686,11 +1446,10 @@ public static class OutputStream extends java.io.FilterOutputStream * @since 1.3 */ public OutputStream( java.io.OutputStream out ) - { + { this( out, ENCODE ); } // end constructor - - + /** * Constructs a {@link Base64.OutputStream} in * either ENCODE or DECODE mode. @@ -1712,7 +1471,7 @@ public OutputStream( java.io.OutputStream out ) * @since 1.3 */ public OutputStream( java.io.OutputStream out, int options ) - { + { super( out ); this.breakLines = (options & DONT_BREAK_LINES) != DONT_BREAK_LINES; this.encode = (options & ENCODE) == ENCODE; @@ -1722,11 +1481,10 @@ public OutputStream( java.io.OutputStream out, int options ) this.lineLength = 0; this.suspendEncoding = false; this.b4 = new byte[4]; - this.options = options; - this.decodabet = getDecodabet(options); + this.options = options; + this.decodabet = getDecodabet(options); } // end constructor - - + /** * Writes the byte to the output stream after * converting to/from Base64 notation. @@ -1748,7 +1506,7 @@ public void write(int theByte) throws java.io.IOException super.out.write( theByte ); return; } // end if: supsended - + // Encode? if( encode ) { @@ -1789,11 +1547,9 @@ else if( decodabet[ theByte & 0x7f ] != WHITE_SPACE_ENC ) } // end else: not white space either } // end else: decoding } // end write - - - + /** - * Calls {@link #write(int)} repeatedly until len + * Calls {@link #write(int)} repeatedly until len * bytes are written. * * @param theBytes array from which to read bytes @@ -1810,22 +1566,20 @@ public void write( byte[] theBytes, int off, int len ) throws java.io.IOExceptio super.out.write( theBytes, off, len ); return; } // end if: supsended - + for( int i = 0; i < len; i++ ) { write( theBytes[ off + i ] ); } // end for: each byte written - + } // end write - - - + /** * Method added by PHIL. [Thanks, PHIL. -Rob] * This pads the buffer without closing the stream. * @throws java.io.IOException */ - public void flushBase64() throws java.io.IOException + public void flushBase64() throws java.io.IOException { if( position > 0 ) { @@ -1842,9 +1596,8 @@ public void flushBase64() throws java.io.IOException } // end flush - - /** - * Flushes and closes (I think, in the superclass) the stream. + /** + * Flushes and closes (I think, in the superclass) the stream. * * @throws java.io.IOException * @since 1.3 @@ -1857,13 +1610,11 @@ public void close() throws java.io.IOException // 2. Actually close the stream // Base class both flushes and closes. super.close(); - + buffer = null; out = null; } // end close - - - + /** * Suspends encoding of the stream. * May be helpful if you need to embed a piece of @@ -1872,13 +1623,12 @@ public void close() throws java.io.IOException * @throws java.io.IOException * @since 1.5.1 */ - public void suspendEncoding() throws java.io.IOException + public void suspendEncoding() throws java.io.IOException { flushBase64(); this.suspendEncoding = true; } // end suspendEncoding - - + /** * Resumes encoding of the stream. * May be helpful if you need to embed a piece of @@ -1890,10 +1640,7 @@ public void resumeEncoding() { this.suspendEncoding = false; } // end resumeEncoding - - - + } // end inner class OutputStream - - + } // end class Base64 diff --git a/src/test/java/org/owasp/esapi/reference/EncoderTest.java b/src/test/java/org/owasp/esapi/reference/EncoderTest.java index 631dd96b2..255519752 100644 --- a/src/test/java/org/owasp/esapi/reference/EncoderTest.java +++ b/src/test/java/org/owasp/esapi/reference/EncoderTest.java @@ -661,60 +661,6 @@ public void testDecodeFromBase64() { } } - /** - * Test of Base64.decodeToObject() method. Should really be put into a - * separate Base64Test.java class, but this method has been deprecated - * so hopefully, we can kill it off soon. - */ - public void testBase64decodToObject() { - try { - System.out.println("testBase64decodeToObject"); - - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - ObjectOutputStream dout = new ObjectOutputStream(baos); - - // If you don't get the joke, google John Draper. (And, BTW, you should - // be ashamed of yourself if you call yourself a hacker!) - String cerealizeThis = new String("Cap'n Crunch - every hacker's favorite cereal"); - dout.writeObject( cerealizeThis ); - byte[] serializedData = baos.toByteArray(); - dout.close(); - - String b64serialized = Base64.encodeBytes( serializedData ); - final String propName = Base64.ENABLE_UNSAFE_SERIALIZATION; - - // Make sure the property is not set. - try { System.clearProperty( propName ); } catch(Throwable t) { ; } - String capnCrunch = null; - - try { - capnCrunch = (String)Base64.decodeToObject( b64serialized ); - fail("Case 1: Did not throw UnsupportedOperationException"); - } catch(UnsupportedOperationException uoex) { - ; // Expected case - } - - try { - System.setProperty( propName, "false" ); - capnCrunch = (String)Base64.decodeToObject( b64serialized ); - fail("Case 2: Did not throw UnsupportedOperationException"); - } catch(UnsupportedOperationException uoex) { - ; // Expected case - } - - try { - // This case should work. - System.setProperty( propName, "true" ); - capnCrunch = (String)Base64.decodeToObject( b64serialized ); - assertTrue( capnCrunch.equals( cerealizeThis ) ); - } catch(Throwable t) { - fail("Case 3: Caught unexpected exception: " + t); - } - } catch(Throwable t) { - fail("Caught unexpected exception: " + t); - } - } - /** * Test of WindowsCodec */ From 32dd026374de4db9527ff0b50778c74078a7cd87 Mon Sep 17 00:00:00 2001 From: "Kevin W. Wall" Date: Mon, 8 Oct 2018 20:37:36 -0400 Subject: [PATCH 0474/1069] Update README.md Add short paragraph of how this GitHub issues is not appropriate forum for asking questions about ESAPI. --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index c36670d59..09f0df50d 100644 --- a/README.md +++ b/README.md @@ -42,6 +42,8 @@ When reporting an issue, please be clear and try to ensure that the ESAPI develo ### Find an Issue? If you have found a bug, then create an issue on the esapi-legacy-java repo: https://github.com/ESAPI/esapi-java-legacy/issues +NOTE: Please do NOT use GitHub issues to ask questions about ESAPI. If you wish to do this, post to either of the 2 mailing lists found at the bottom of this page. If we find questions as GitHub issues, we simply will close them and direct you to do this anyhow. + ### Find a Vulnerability? If you have found a vulnerability in ESAPI legacy, first search the issues list (see above) to see if it has already been reported. If it has not, then please contact both Kevin W. Wall (kevin.w.wall at gmail.com) and Matt Seil (matt.seil at owasp.org) directly. Please do not report vulnerabilities via GitHub issues or via the ESAPI mailing lists as we wish to keep our users secure while a patch is implemented and deployed. If you wish to be acknowledged for finding the vulnerability, then please follow this process. (Eventually, we would like to have BugCrowd handle this, but that's still a ways off.) Also, when you post the email describing the vulnerability, please do so from an email address that you usually monitor. From bbf431afab90b5b7f0012b13bcbc4f96cbeeb927 Mon Sep 17 00:00:00 2001 From: "Kevin W. Wall" Date: Mon, 8 Oct 2018 23:59:52 -0400 Subject: [PATCH 0475/1069] Log special (#451) * Close issue #448 * Close issue #444. Delete deprecated decodeToObject() method and 2 related encodeObject() methods. General whitespace clean-up. Delete EncoderTest.testBase64decodToObject() method which is no longer relevant. * Close issue #385. Close issue #386. NOTE: Was unwilling to comply with request in these 2 issues to make logSpecial() 'protected'. Could not do so without introducing potential security vulnerabilities. However, since the intent of the user submitting these 2 GitHub issues was only to override logSpecial() in order to completely suppress the output from them to stdout or stderr, I have arranged it so that setting the System property 'org.owasp.esapi.logSpecial.discard' to 'true' will do exactly this...it will suppress all output from logSpecial. (Also, the calls to System.err.println() and System.out.println() have been replaced by calls to logSpecial(), which will log to System.out if not suppressed. So the end result is all or nothing. I suspect that most will keep the default behavior, which is to print to System.out rather than suppressing all output. --- .../AbstractPrioritizedPropertyLoader.java | 33 +++++++++- .../StandardEsapiPropertyLoader.java | 4 +- .../DefaultSecurityConfiguration.java | 62 ++++++++++++++++--- 3 files changed, 85 insertions(+), 14 deletions(-) diff --git a/src/main/java/org/owasp/esapi/configuration/AbstractPrioritizedPropertyLoader.java b/src/main/java/org/owasp/esapi/configuration/AbstractPrioritizedPropertyLoader.java index 75e6497c7..61c5c7d26 100644 --- a/src/main/java/org/owasp/esapi/configuration/AbstractPrioritizedPropertyLoader.java +++ b/src/main/java/org/owasp/esapi/configuration/AbstractPrioritizedPropertyLoader.java @@ -44,13 +44,13 @@ public String name() { /** * Initializes properties object and fills it with data from configuration file. */ - protected void initProperties() { + private void initProperties() { properties = new Properties(); File file = new File(filename); if (file.exists() && file.isFile()) { loadPropertiesFromFile(file); } else { - System.err.println("Configuration file " + filename + " does not exist"); + logSpecial("Configuration file " + filename + " does not exist"); } } @@ -59,4 +59,33 @@ protected void initProperties() { * @param file */ protected abstract void loadPropertiesFromFile(File file); + + /** + * Used to log errors to the console during the loading of the properties file itself. Can't use + * standard logging in this case, since the Logger may not be initialized yet. Output is sent to + * {@code PrintStream} {@code System.out}. Output is discarded if the {@code System} property + * "org.owasp.esapi.logSpecial.discard" is set to {@code true}. + * + * @param msg The message to log to the console. + * @param t Associated exception that was caught. + */ + protected final void logSpecial(String msg, Throwable t) { + // Note: It is really distasteful to tie this class to DefaultSecurityConfiguration + // like this, but the alternative is to move the logSpecial() and + // logToStdout() some utilities class and that is even more + // distasteful because it may encourage people to use these. -kwwall + org.owasp.esapi.reference.DefaultSecurityConfiguration.logToStdout(msg, t); + } + + /** + * Used to log errors to the console during the loading of the properties file itself. Can't use + * standard logging in this case, since the Logger may not be initialized yet. Output is sent to + * {@code PrintStream} {@code System.out}. Output is discarded if the {@code System} property + * "org.owasp.esapi.logSpecial.discard" is set to {@code true}. + * + * @param msg The message to log to the console. + */ + protected final void logSpecial(String msg) { + logSpecial(msg, null); + } } diff --git a/src/main/java/org/owasp/esapi/configuration/StandardEsapiPropertyLoader.java b/src/main/java/org/owasp/esapi/configuration/StandardEsapiPropertyLoader.java index 6a896a2cd..850b16905 100644 --- a/src/main/java/org/owasp/esapi/configuration/StandardEsapiPropertyLoader.java +++ b/src/main/java/org/owasp/esapi/configuration/StandardEsapiPropertyLoader.java @@ -90,13 +90,13 @@ protected void loadPropertiesFromFile(File file) { input = new FileInputStream(file); properties.load(input); } catch (IOException ex) { - System.err.println("Loading " + file.getName() + " via file I/O failed. Exception was: " + ex); + logSpecial("Loading " + file.getName() + " via file I/O failed.", ex); } finally { if (input != null) { try { input.close(); } catch (IOException e) { - System.err.println("Could not close stream"); + logSpecial("Could not close stream"); } } } diff --git a/src/main/java/org/owasp/esapi/reference/DefaultSecurityConfiguration.java b/src/main/java/org/owasp/esapi/reference/DefaultSecurityConfiguration.java index 403fd4922..504372037 100644 --- a/src/main/java/org/owasp/esapi/reference/DefaultSecurityConfiguration.java +++ b/src/main/java/org/owasp/esapi/reference/DefaultSecurityConfiguration.java @@ -153,6 +153,18 @@ public static SecurityConfiguration getInstance() { public static final String VALIDATION_PROPERTIES_MULTIVALUED = "Validator.ConfigurationFile.MultiValued"; public static final String ACCEPT_LENIENT_DATES = "Validator.AcceptLenientDates"; + /** + * Special {@code System} property that, if set to {@code true}, will + * disable logging from {@code DefaultSecurityConfiguration.logToStdout()} + * methods, which is called from various {@code logSpecial()} methods. + * @see org.owasp.esapi.reference.DefaultSecurityConfiguration#logToStdout(String msg, Throwable t) + * @see org.owasp.esapi.reference.DefaultSecurityConfiguration#logToStdout(String msg) + */ + public static final String DISCARD_LOGSPECIAL = "org.owasp.esapi.logSpecial.discard"; + + // We assume that this does not change in the middle of processing the + // ESAPI.properties files and thus only fetch its value once. + private static final String logSpecialValue = System.getProperty(DISCARD_LOGSPECIAL, "false"); /** @@ -702,34 +714,64 @@ private Properties loadConfigurationFromClasspath(String fileName) throws Illega return result; } + /** + * Log to standard output (i.e., {@code System.out}. This method is + * synchronized to reduce the possibility of interleaving the message + * output (since the {@code System.out} {@code PrintStream} is buffered) + * it invoked from multiple threads. Output is discarded if the + * {@code System} property "org.owasp.esapi.logSpecial.discard" is set to + * {@code true}. + * + * @param msg Message to be logged. + * @param t Associated exception that was caught. The class name and + * exception message is also logged. + * @see #logToStdout(String msg) + */ + public final synchronized static void logToStdout(String msg, Throwable t) { + // Note that this class was made final because it is called from this class' + // CTOR and we want to prohibit someone from easily doing sneaky + // things like subclassing this class and inserting a malicious code as a + // shim. Of course, really in hindsight, this entire class should have been + // declared 'final', but doing so at this point would likely break someone's + // code, including possibly some of our own test code. But since this is a + // new method, we can get away with it here. + boolean discard = logSpecialValue.trim().equalsIgnoreCase("true"); + if ( discard ) { + return; // Output is discarded! + } + if ( t == null ) { + System.out.println("ESAPI: " + msg); + } else { + System.out.println("ESAPI: " + msg + + ". Caught " + t.getClass().getName() + + "; exception message was: " + t); + } + } + /** * Used to log errors to the console during the loading of the properties file itself. Can't use * standard logging in this case, since the Logger may not be initialized yet. Output is sent to - * {@code PrintStream} {@code System.out}. + * {@code PrintStream} {@code System.out}. Output is discarded if the {@code System} property + * "org.owasp.esapi.logSpecial.discard" is set to {@code true}. * * @param message The message to send to the console. * @param e The error that occurred. (This value printed via {@code e.toString()}.) */ private void logSpecial(String message, Throwable e) { - StringBuffer msg = new StringBuffer(message); - if (e != null) { - msg.append(" Exception was: ").append( e.toString() ); - } - System.out.println( msg.toString() ); - // if ( e != null) e.printStackTrace(); // TODO ??? Do we want this? + logToStdout(message, e); } /** * Used to log errors to the console during the loading of the properties file itself. Can't use * standard logging in this case, since the Logger may not be initialized yet. Output is sent to - * {@code PrintStream} {@code System.out}. + * {@code PrintStream} {@code System.out}. Output is discarded if the {@code System} property + * "org.owasp.esapi.logSpecial.discard" is set to {@code true}. * * @param message The message to send to the console. */ private void logSpecial(String message) { - System.out.println(message); + logToStdout(message, null); } - /** * {@inheritDoc} */ From 74a38b0096f68a2dcd17427f054d5fe31c5a1335 Mon Sep 17 00:00:00 2001 From: Jacky Date: Sat, 3 Nov 2018 10:16:58 +0800 Subject: [PATCH 0476/1069] #304 encodeForCSS breaks color values (#453) --- .../java/org/owasp/esapi/reference/DefaultEncoder.java | 2 +- src/test/java/org/owasp/esapi/reference/EncoderTest.java | 8 ++++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/owasp/esapi/reference/DefaultEncoder.java b/src/main/java/org/owasp/esapi/reference/DefaultEncoder.java index 8100632a7..9a0eefa1a 100644 --- a/src/main/java/org/owasp/esapi/reference/DefaultEncoder.java +++ b/src/main/java/org/owasp/esapi/reference/DefaultEncoder.java @@ -88,7 +88,7 @@ public static Encoder getInstance() { */ private final static char[] IMMUNE_HTML = { ',', '.', '-', '_', ' ' }; private final static char[] IMMUNE_HTMLATTR = { ',', '.', '-', '_' }; - private final static char[] IMMUNE_CSS = {}; + private final static char[] IMMUNE_CSS = { '#' }; private final static char[] IMMUNE_JAVASCRIPT = { ',', '.', '_' }; private final static char[] IMMUNE_VBSCRIPT = { ',', '.', '_' }; private final static char[] IMMUNE_XML = { ',', '.', '-', '_', ' ' }; diff --git a/src/test/java/org/owasp/esapi/reference/EncoderTest.java b/src/test/java/org/owasp/esapi/reference/EncoderTest.java index 255519752..cdef5e06a 100644 --- a/src/test/java/org/owasp/esapi/reference/EncoderTest.java +++ b/src/test/java/org/owasp/esapi/reference/EncoderTest.java @@ -382,10 +382,14 @@ public void testEncodeForCSS() { assertEquals(null, instance.encodeForCSS(null)); assertEquals("\\3c script\\3e ", instance.encodeForCSS(""); verify(servResp, times(0)).setHeader("foo", ""); } - + @Test public void testInvalidDateHeader(){ HttpServletResponse servResp = mock(HttpServletResponse.class); @@ -91,7 +662,7 @@ public void testInvalidDateHeader(){ resp.addDateHeader("Foo\\r\\n", currentTime); verify(servResp, times(0)).addDateHeader("Foo", currentTime); } - + @Test public void testAddHeaderInvalidValueLength(){ //refactor this to use a spy. @@ -102,7 +673,7 @@ public void testAddHeaderInvalidValueLength(){ resp.addHeader("Foo", TestUtils.generateStringOfLength(4097)); verify(servResp, times(0)).addHeader("Foo", "bar"); } - + @Test public void testAddHeaderInvalidKeyLength(){ HttpServletResponse servResp = mock(HttpServletResponse.class); @@ -110,7 +681,7 @@ public void testAddHeaderInvalidKeyLength(){ resp.addHeader(TestUtils.generateStringOfLength(257), "bar"); verify(servResp, times(0)).addHeader("Foo", "bar"); } - + @Test public void testAddIntHeader(){ HttpServletResponse servResp = mock(HttpServletResponse.class); @@ -118,7 +689,7 @@ public void testAddIntHeader(){ resp.addIntHeader("aaaa", 4); verify(servResp, times(1)).addIntHeader("aaaa", 4); } - + @Test public void testAddInvalidIntHeader(){ HttpServletResponse servResp = mock(HttpServletResponse.class); @@ -126,7 +697,7 @@ public void testAddInvalidIntHeader(){ resp.addIntHeader(TestUtils.generateStringOfLength(257), Integer.MIN_VALUE); verify(servResp, times(0)).addIntHeader(TestUtils.generateStringOfLength(257), Integer.MIN_VALUE); } - + @Test public void testContainsHeader(){ HttpServletResponse servResp = new MockHttpServletResponse(); @@ -137,7 +708,7 @@ public void testContainsHeader(){ verify(servResp, times(1)).addIntHeader("aaaa", Integer.MIN_VALUE); assertEquals(true, servResp.containsHeader("aaaa")); } - + @Test public void testAddValidCookie(){ HttpServletResponse servResp = new MockHttpServletResponse(); @@ -148,7 +719,7 @@ public void testAddValidCookie(){ cookie.setMaxAge(5000); Mockito.doCallRealMethod().when(spyResp).addCookie(cookie); spyResp.addCookie(cookie); - + /* * We're indirectly testing our class. Since it ultimately * delegates to HttpServletResponse.addHeader, we're actually @@ -158,7 +729,7 @@ public void testAddValidCookie(){ */ verify(servResp, times(1)).addHeader("Set-Cookie", "Foo=aaaaaaaaaa; Max-Age=5000; Secure; HttpOnly"); } - + @Test public void testAddValidCookieWithDomain(){ HttpServletResponse servResp = new MockHttpServletResponse(); @@ -172,7 +743,7 @@ public void testAddValidCookieWithDomain(){ spyResp.addCookie(cookie); verify(servResp, times(1)).addHeader("Set-Cookie", "Foo=aaaaaaaaaa; Domain=evil.com; Secure; HttpOnly"); } - + @Test public void testAddValidCookieWithPath(){ HttpServletResponse servResp = new MockHttpServletResponse(); @@ -186,7 +757,7 @@ public void testAddValidCookieWithPath(){ spyResp.addCookie(cookie); verify(servResp, times(1)).addHeader("Set-Cookie", "Foo=aaaaaaaaaa; Domain=evil.com; Path=/foo/bar; Secure; HttpOnly"); } - + @Test public void testAddInValidCookie(){ HttpServletResponse servResp = new MockHttpServletResponse(); @@ -195,11 +766,11 @@ public void testAddInValidCookie(){ SecurityWrapperResponse spyResp = spy(resp); Cookie cookie = new Cookie("Foo", TestUtils.generateStringOfLength(5000)); Mockito.doCallRealMethod().when(spyResp).addCookie(cookie); - + spyResp.addCookie(cookie); verify(servResp, times(0)).addHeader("Set-Cookie", "Foo=" + TestUtils.generateStringOfLength(5000) + "; Secure; HttpOnly"); } - + @Test public void testSendError() throws Exception{ HttpServletResponse servResp = new MockHttpServletResponse(); @@ -208,10 +779,10 @@ public void testSendError() throws Exception{ SecurityWrapperResponse spyResp = spy(resp); Mockito.doCallRealMethod().when(spyResp).sendError(200); spyResp.sendError(200); - + verify(servResp, times(1)).sendError(200, "HTTP error code: 200");; } - + @Test public void testSendStatus() throws Exception{ HttpServletResponse servResp = new MockHttpServletResponse(); @@ -220,10 +791,10 @@ public void testSendStatus() throws Exception{ SecurityWrapperResponse spyResp = spy(resp); Mockito.doCallRealMethod().when(spyResp).setStatus(200);; spyResp.setStatus(200); - + verify(servResp, times(1)).setStatus(200);; } - + @Test public void testSendStatusWithString() throws Exception{ HttpServletResponse servResp = new MockHttpServletResponse(); @@ -232,7 +803,7 @@ public void testSendStatusWithString() throws Exception{ SecurityWrapperResponse spyResp = spy(resp); Mockito.doCallRealMethod().when(spyResp).setStatus(200, "foo");; spyResp.setStatus(200, "foo"); - + verify(servResp, times(1)).sendError(200, "foo");; } } From 422ba9f27ad1956682a7b0903c9f8119e6cd598d Mon Sep 17 00:00:00 2001 From: "Kevin W. Wall" Date: Sat, 8 Feb 2020 22:34:19 -0500 Subject: [PATCH 0611/1069] Issue 521 (#535) * Add formal deprecation policy. * Add property Validator.ValidationRule.getValid.ignore509Fix. Truly a kludge if there every was one. * Add static field VALIDATOR_IGNORE509 for kludge. * Address issue #521 by splitting out failing JUnit test cases, testGetValidSafeHTML() and testIsValidSafeHTML() into separate test files. New files will be src/test/java/org/owasp/esapi/reference/validation/HTMLValidationRuleLogsTest.java and src/test/java/org/owasp/esapi/reference/validation/HTMLValidationRuleThrowsTest.java * Address issue #521 by kludge to add backward-compatibility flag to restore the old behavior accidentally broken by the changes to address issue #509. * Javadoc clarifications to address issue #521 for behavior broken by issue #509 commits. * New test files for GitHub issue #521; initial commit. * Add additional sentence about ESAPI deprecation policy. * Since we've deprecated Log4J 1 logger, let's go all in and remove it from the default ESAPI.Logger in ESAPI.properties as well. * Changed new property name from the horribly named Validator.ValidationRule.getValid.ignore509Fix to the more appropriately named Validator.HtmlValidationAction whose possible values are "clean" (for legacy behavior) and "throw" for the new behavior as fixed by GitHub issue #509. If the property is not encountered, it is treated as if "clean" had been specified, i.e., the legacy behavior. * Added string constant for new property, Validator.HtmlValidationAction. * Changes in keeping with new prop name, Validator.HtmlValidationAction * Rename JUnit test file. * Rename JUnit test class to sync w/ new file name. * Convert from JUnit 3 to JUnit 4. --- README.md | 4 + configuration/esapi/ESAPI.properties | 40 ++++- .../java/org/owasp/esapi/ValidationRule.java | 20 ++- .../DefaultSecurityConfiguration.java | 3 +- .../validation/HTMLValidationRule.java | 60 ++++++- .../owasp/esapi/reference/ValidatorTest.java | 63 +------ .../HTMLValidationRuleCleanTest.java | 158 +++++++++++++++++ .../HTMLValidationRuleThrowsTest.java | 167 ++++++++++++++++++ src/test/resources/esapi/ESAPI.properties | 41 ++++- 9 files changed, 484 insertions(+), 72 deletions(-) create mode 100644 src/test/java/org/owasp/esapi/reference/validation/HTMLValidationRuleCleanTest.java create mode 100644 src/test/java/org/owasp/esapi/reference/validation/HTMLValidationRuleThrowsTest.java diff --git a/README.md b/README.md index e6701c280..f7c38d99d 100644 --- a/README.md +++ b/README.md @@ -23,6 +23,10 @@ The default branch for ESAPI legacy is now the 'develop' branch (rather than the # Where can I find ESAPI 3.x? https://github.com/ESAPI/esapi-java +# ESAPI Deprecation Policy +Unless we unintentionally screw-up, our intent is to keep classes, methods, and/or fields whihc have been annotated as "@deprecated" for a minimum of two (2) years or until the next major release number (e.g., 3.x as of now), which ever comes first, before we remove them. +Note that this policy does not apply to classes under the **org.owasp.esapi.reference** package. You are not expected to be using such classes directly in your code. + # Contributing to ESAPI legacy ## How can I contribute or help with fix bugs? Fork and submit a pull request! Simple as pi! We generally only accept bug fixes, not new features because as a legacy project, we don't intend on adding new features, although we may make exceptions. If you wish to propose a new feature, the best place to discuss it is via the ESAPI-DEV mailing list mentioned below. Note that we vet all pull requests, including coding style of any contributions; use the same coding style found in the files you are already editing. diff --git a/configuration/esapi/ESAPI.properties b/configuration/esapi/ESAPI.properties index 20421a0bd..7ac0fb9d3 100644 --- a/configuration/esapi/ESAPI.properties +++ b/configuration/esapi/ESAPI.properties @@ -67,8 +67,9 @@ ESAPI.Executor=org.owasp.esapi.reference.DefaultExecutor ESAPI.HTTPUtilities=org.owasp.esapi.reference.DefaultHTTPUtilities ESAPI.IntrusionDetector=org.owasp.esapi.reference.DefaultIntrusionDetector # Log4JFactory Requires log4j.xml or log4j.properties in classpath - http://www.laliluna.de/log4j-tutorial.html -ESAPI.Logger=org.owasp.esapi.logging.log4j.Log4JLogFactory -#ESAPI.Logger=org.owasp.esapi.reference.JavaLogFactory +# Note that this is now considered deprecated! +#ESAPI.Logger=org.owasp.esapi.logging.log4j.Log4JLogFactory +ESAPI.Logger=org.owasp.esapi.logging.java.JavaLogFactory # To use the new SLF4J logger in ESAPI (see GitHub issue #129), set # ESAPI.Logger=org.owasp.esapi.logging.slf4j.Slf4JLogFactory # and do whatever other normal SLF4J configuration that you normally would do for your application. @@ -499,3 +500,38 @@ Validator.DirectoryName=^[a-zA-Z0-9:/\\\\!@#$%^&{}\\[\\]()_+\\-=,.~'` ]{1,255}$ # Validation of dates. Controls whether or not 'lenient' dates are accepted. # See DataFormat.setLenient(boolean flag) for further details. Validator.AcceptLenientDates=false + +# ~~~~~ Important Note ~~~~~ +# This is a workaround to make sure that a commit to address GitHub issue #509 +# doesn't accidentally break someone's production code. So essentially what we +# are doing is to reverting back to the previous possibly buggy (by +# documentation intent at least), but, by now, expected legacy behavior. +# Prior to the code changes for issue #509, if invalid / malicious HTML input was +# observed, AntiSamy would simply attempt to sanitize (cleanse) it and it would +# only be logged. However, the code change made ESAPI comply with its +# documentation, which stated that a ValidationException should be thrown in +# such cases. Unfortunately, changing this behavior--especially when no one is +# 100% certain that the documentation was correct--could break existing code +# using ESAPI so after a lot of debate, issue #521 was created to restore the +# previous behavior, but still allow the documented behavior. (We did this +# because it wasn't really causing an security issues since AntiSamy would clean +# it up anyway and we value backward compatibility as long as it doesn't clearly +# present security vulnerabilities.) +# More defaults about this are written up under GitHub issue #521 and +# the pull request it references. Future major releases of ESAPI (e.g., ESAPI 3.x) +# will not support this previous behavior, but it will remain for ESAPI 2.x. +# Set this to 'throw' if you want the originally intended behavior of throwing +# that was fixed via issue #509. Set to 'clean' if you want want the HTML input +# sanitized instead. +# +# Possible values: +# clean -- Use the legacy behavior where unsafe HTML input is logged and the +# sanitized (i.e., clean) input as determined by AntiSamy and your +# AntiSamy rules is returned. This is the default behavior if this +# new property is not found. +# throw -- The new, presumably correct and originally intended behavior where +# a ValidationException is thrown when unsafe HTML input is +# encountered. +# +#Validator.HtmlValidationAction=clean +Validator.HtmlValidationAction=throw diff --git a/src/main/java/org/owasp/esapi/ValidationRule.java b/src/main/java/org/owasp/esapi/ValidationRule.java index 25cc95ac1..8f186a54b 100644 --- a/src/main/java/org/owasp/esapi/ValidationRule.java +++ b/src/main/java/org/owasp/esapi/ValidationRule.java @@ -15,14 +15,23 @@ public interface ValidationRule { * the value to be parsed * @return a validated value * @throws ValidationException - * if any validation rules fail + * if any validation rules fail, except if the + * {@code ESAPI.properties}> property + * "Validator.ValidationRule.getValid.ignore509Fix" is set to + * {@code true}, which is the default behavior for ESAPI 2.x + * releases. See + * {@link https://github.com/ESAPI/esapi-java-legacy/issues/509} + * and {@link https://github.com/ESAPI/esapi-java-legacy/issues/521} + * for futher details. + * + * @see #getValid(String context, String input, ValidationErrorList errorList) */ Object getValid(String context, String input) throws ValidationException; /** - * Whether or not a valid valid can be null. getValid will throw an - * Exception and getSafe will return the default value if flag is set to + * Whether or not a valid valid can be null. {@code getValid} will throw an + * Exception and {#code getSafe} will return the default value if flag is set to * true * * @param flag @@ -59,7 +68,8 @@ Object getValid(String context, String input, ValidationErrorList errorList) throws ValidationException; /** - * Try to call get valid, then call sanitize, finally return a default value + * Try to call {@code getvalid}, then call a 'sanitize' method for sanitization (if one exists), + * finally return a default value. */ Object getSafe(String context, String input); @@ -78,4 +88,4 @@ Object getValid(String context, String input, */ String whitelist(String input, Set list); -} \ No newline at end of file +} diff --git a/src/main/java/org/owasp/esapi/reference/DefaultSecurityConfiguration.java b/src/main/java/org/owasp/esapi/reference/DefaultSecurityConfiguration.java index a91e0e9b7..a9fd89cc9 100644 --- a/src/main/java/org/owasp/esapi/reference/DefaultSecurityConfiguration.java +++ b/src/main/java/org/owasp/esapi/reference/DefaultSecurityConfiguration.java @@ -114,7 +114,7 @@ public static SecurityConfiguration getInstance() { public static final String DIGITAL_SIGNATURE_ALGORITHM = "Encryptor.DigitalSignatureAlgorithm"; public static final String DIGITAL_SIGNATURE_KEY_LENGTH = "Encryptor.DigitalSignatureKeyLength"; // ==================================// - // New in ESAPI Java 2.0 // + // New in ESAPI Java 2.x // // ================================= // public static final String PREFERRED_JCE_PROVIDER = "Encryptor.PreferredJCEProvider"; public static final String CIPHER_TRANSFORMATION_IMPLEMENTATION = "Encryptor.CipherTransformation"; @@ -157,6 +157,7 @@ public static SecurityConfiguration getInstance() { public static final String VALIDATION_PROPERTIES = "Validator.ConfigurationFile"; public static final String VALIDATION_PROPERTIES_MULTIVALUED = "Validator.ConfigurationFile.MultiValued"; public static final String ACCEPT_LENIENT_DATES = "Validator.AcceptLenientDates"; + public static final String VALIDATOR_HTML_VALIDATION_ACTION = "Validator.HtmlValidationAction"; /** * Special {@code System} property that, if set to {@code true}, will diff --git a/src/main/java/org/owasp/esapi/reference/validation/HTMLValidationRule.java b/src/main/java/org/owasp/esapi/reference/validation/HTMLValidationRule.java index f0196fc04..0670860d9 100644 --- a/src/main/java/org/owasp/esapi/reference/validation/HTMLValidationRule.java +++ b/src/main/java/org/owasp/esapi/reference/validation/HTMLValidationRule.java @@ -97,8 +97,60 @@ public String sanitize( String context, String input ) { return safe; } + /** + * Check whether we want the legacy behavior ("clean") or the presumably intended + * behavior of "throw" for how to treat unsafe HTML input when AntiSamy is invoked. + * This admittedly is an UGLY hack to ensure that issue 509 and its corresponding + * fix in PR #510 does not break existing developer's existing code. Full + * details are described in GitHub issue 521. + * + * Checks new ESAPI property "Validator.HtmlValidationAction". A value of "clean" + * means to revert to legacy behavior. A value of "throw" means to use the new + * behavior as implemented in GitHub issue 509. + * + * @return false - If "Validator.HtmlValidationAction" is set to "throw". Otherwise {@code true}. + * @since 2.2.1.0 + */ + private boolean legacyHtmlValidation() { + boolean legacy = true; // Make legacy support the default behavior for backward compatibility. + String propValue = "clean"; // For legacy support. + try { + // DISCUSS: + // Hindsight: maybe we should have getBooleanProp(), getStringProp(), + // getIntProp() methods that take a default arg as well? + // At least for ESAPI 3.x. + propValue = ESAPI.securityConfiguration().getStringProp( + // Future: This will be moved to a new PropNames class + org.owasp.esapi.reference.DefaultSecurityConfiguration.VALIDATOR_HTML_VALIDATION_ACTION ); + switch ( propValue.toLowerCase() ) { + case "throw": + legacy = false; // New, presumably correct behavior, as addressed by GitHub issue 509 + break; + case "clean": + legacy = true; // Give the caller that legacy behavior of sanitizing. + break; + default: + LOGGER.warning(Logger.EVENT_FAILURE, "ESAPI property " + + org.owasp.esapi.reference.DefaultSecurityConfiguration.VALIDATOR_HTML_VALIDATION_ACTION + + " was set to \"" + propValue + "\". Must be set to either \"clean\"" + + " (the default for legacy support) or \"throw\"; assuming \"clean\" for legacy behavior."); + legacy = true; + break; + } + } catch( ConfigurationException cex ) { + // OPEN ISSUE: Should we log this? I think so. Convince me otherwise. But maybe + // we should only log it once or every Nth time?? + LOGGER.warning(Logger.EVENT_FAILURE, "ESAPI property " + + org.owasp.esapi.reference.DefaultSecurityConfiguration.VALIDATOR_HTML_VALIDATION_ACTION + + " must be set to either \"clean\" (the default for legacy support) or \"throw\"; assuming \"clean\"", + cex); + } + + return legacy; + } + private String invokeAntiSamy( String context, String input ) throws ValidationException { - // CHECKME should this allow empty Strings? " " us IsBlank instead? + // CHECKME should this allow empty Strings? " " use IsBlank instead? if ( StringUtilities.isEmpty(input) ) { if (allowNull) { return null; @@ -114,7 +166,11 @@ private String invokeAntiSamy( String context, String input ) throws ValidationE List errors = test.getErrorMessages(); if ( !errors.isEmpty() ) { - throw new ValidationException( context + ": Invalid HTML input", "Invalid HTML input does not follow rules in antisamy-esapi.xml: context=" + context + " errors=" + errors.toString()); + if ( legacyHtmlValidation() ) { // See GitHub issues 509 and 521 + LOGGER.info(Logger.SECURITY_FAILURE, "Cleaned up invalid HTML input: " + errors ); + } else { + throw new ValidationException( context + ": Invalid HTML input", "Invalid HTML input does not follow rules in antisamy-esapi.xml: context=" + context + " errors=" + errors.toString()); + } } return test.getCleanHTML().trim(); diff --git a/src/test/java/org/owasp/esapi/reference/ValidatorTest.java b/src/test/java/org/owasp/esapi/reference/ValidatorTest.java index ad48ef53b..b0fd24789 100644 --- a/src/test/java/org/owasp/esapi/reference/ValidatorTest.java +++ b/src/test/java/org/owasp/esapi/reference/ValidatorTest.java @@ -238,39 +238,8 @@ public void testGetValidRedirectLocation() { // instance.getValidRedirectLocation(String, String, boolean, ValidationErrorList) } - public void testGetValidSafeHTML() throws Exception { - System.out.println("getValidSafeHTML"); - Validator instance = ESAPI.validator(); - ValidationErrorList errors = new ValidationErrorList(); - - // new school test case setup - HTMLValidationRule rule = new HTMLValidationRule("test"); - ESAPI.validator().addRule(rule); - - assertEquals("Test.", ESAPI.validator().getRule("test").getValid("test", "Test. ")); - - String test1 = "Jeff"; - String result1 = instance.getValidSafeHTML("test", test1, 100, false, errors); - assertEquals(test1, result1); - - String test2 = "Aspect Security"; - String result2 = instance.getValidSafeHTML("test", test2, 100, false, errors); - assertEquals(test2, result2); - - String test3 = "Test. "; - assertEquals("Test.", rule.getSafe("test", test3)); - - assertEquals("Test. <
    load=alert()
    ", rule.getSafe("test", "Test. <
    load=alert()")); - assertEquals("Test.
    b
    ", rule.getSafe("test", "Test.
    b
    ")); - assertEquals("Test. alert(document.cookie)", rule.getSafe("test", "Test. alert(document.cookie)")); - assertEquals("Test. alert(document.cookie)", rule.getSafe("test", "Test. alert(document.cookie)")); - assertEquals("Test. alert(document.cookie)", rule.getSafe("test", "Test. alert(document.cookie)")); - // TODO: ENHANCE waiting for a way to validate text headed for an attribute for scripts - // This would be nice to catch, but just looks like text to AntiSamy - // assertFalse(instance.isValidSafeHTML("test", "\" onload=\"alert(document.cookie)\" ")); - // String result4 = instance.getValidSafeHTML("test", test4); - // assertEquals("", result4); - } + // Test split out and moved to HTMLValidationRuleLogsTest.java & HTMLValidationRuleThrowsTest.java + // public void testGetValidSafeHTML() throws Exception { public void testIsInvalidFilename() { System.out.println("testIsInvalidFilename"); @@ -881,32 +850,8 @@ public void testIsValidRedirectLocation() { // isValidRedirectLocation(String, String, boolean) } - public void testIsValidSafeHTML() { - System.out.println("isValidSafeHTML"); - Validator instance = ESAPI.validator(); - - assertTrue(instance.isValidSafeHTML("test", "Jeff", 100, false)); - assertTrue(instance.isValidSafeHTML("test", "Aspect Security", 100, false)); - assertTrue(instance.isValidSafeHTML("test", "Test. ", 100, false)); - assertTrue(instance.isValidSafeHTML("test", "Test.
    ", 100, false)); - assertTrue(instance.isValidSafeHTML("test", "Test. alert(document.cookie)", 100, false)); - assertTrue(instance.isValidSafeHTML("test", "Test. alert(document.cookie)", 100, false)); - assertTrue(instance.isValidSafeHTML("test", "Test. alert(document.cookie)", 100, false)); - - // TODO: waiting for a way to validate text headed for an attribute for scripts - // This would be nice to catch, but just looks like text to AntiSamy - // assertFalse(instance.isValidSafeHTML("test", "\" onload=\"alert(document.cookie)\" ")); - ValidationErrorList errors = new ValidationErrorList(); - assertTrue(instance.isValidSafeHTML("test1", "Jeff", 100, false, errors)); - assertTrue(instance.isValidSafeHTML("test2", "Aspect Security", 100, false, errors)); - assertTrue(instance.isValidSafeHTML("test3", "Test. ", 100, false, errors)); - assertTrue(instance.isValidSafeHTML("test4", "Test.
    ", 100, false, errors)); - assertTrue(instance.isValidSafeHTML("test5", "Test. alert(document.cookie)", 100, false, errors)); - assertTrue(instance.isValidSafeHTML("test6", "Test. alert(document.cookie)", 100, false, errors)); - assertTrue(instance.isValidSafeHTML("test7", "Test. alert(document.cookie)", 100, false, errors)); - assertTrue(errors.size() == 0); - - } + // Test split out and moved to HTMLValidationRuleLogsTest.java & HTMLValidationRuleThrowsTest.java + // public void testIsValidSafeHTML() { public void testSafeReadLine() { System.out.println("safeReadLine"); diff --git a/src/test/java/org/owasp/esapi/reference/validation/HTMLValidationRuleCleanTest.java b/src/test/java/org/owasp/esapi/reference/validation/HTMLValidationRuleCleanTest.java new file mode 100644 index 000000000..dfed45607 --- /dev/null +++ b/src/test/java/org/owasp/esapi/reference/validation/HTMLValidationRuleCleanTest.java @@ -0,0 +1,158 @@ +/** + * OWASP Enterprise Security API (ESAPI) + * + * This file is part of the Open Web Application Security Project (OWASP) + * Enterprise Security API (ESAPI) project. For details, please see + * http://www.owasp.org/index.php/ESAPI. + * + * Copyright (c) 2019 - The OWASP Foundation + * + * The ESAPI is published by OWASP under the BSD license. You should read and accept the + * LICENSE before you use, modify, and/or redistribute this software. + * + * @author kevin.w.wall@gmail.com + * @since 2019 + */ +package org.owasp.esapi.reference; + +import org.owasp.esapi.ESAPI; +import org.owasp.esapi.EncoderConstants; +import org.owasp.esapi.SecurityConfiguration; +import org.owasp.esapi.SecurityConfigurationWrapper; +import org.owasp.esapi.ValidationErrorList; +import org.owasp.esapi.ValidationRule; +import org.owasp.esapi.Validator; +import org.owasp.esapi.errors.ValidationException; +import org.owasp.esapi.filters.SecurityWrapperRequest; +import org.owasp.esapi.reference.validation.HTMLValidationRule; + +import org.junit.Test; +import org.junit.Before; +import org.junit.After; +import org.junit.Rule; +import org.junit.rules.ExpectedException; +import static org.junit.Assert.*; + +/** + * The Class HTMLValidationRuleCleanTest. + * + * Based on original test cases, testGetValidSafeHTML() and + * testIsValidSafeHTML() from ValidatorTest by + * Mike Fauzy (mike.fauzy@aspectsecurity.com) and + * Jeff Williams (jeff.williams@aspectsecurity.com) + * that were originally part of src/test/java/org/owasp/esapi/reference/ValidatorTest.java. + * + * This class tests the cases where the new ESAPI.property + * Validator.HtmlValidationAction + * is set to "clean", which causes certain calls to + * ESAPI.validator().getValidSafeHTML() or ESAPI.validator().isValidSafeHTML() + * to simply log a warning and return the cleansed (sanitizied) output rather + * than throwing a ValidationException when certain unsafe input is + * encountered. + */ +public class HTMLValidationRuleCleanTest { + + private static class ConfOverride extends SecurityConfigurationWrapper { + private String desiredReturn = "clean"; + + ConfOverride(SecurityConfiguration orig, String desiredReturn) { + super(orig); + this.desiredReturn = desiredReturn; + } + + @Override + public String getStringProp(String propName) { + // Would it be better making this file a static import? + if ( propName.equals( org.owasp.esapi.reference.DefaultSecurityConfiguration.VALIDATOR_HTML_VALIDATION_ACTION ) ) { + return desiredReturn; + } else { + return super.getStringProp( propName ); + } + } + } + + + /** + * Instantiates a new HTTP utilities test. + * + * @param testName the test name + */ + public HTMLValidationRuleCleanTest() { + } + + @After + public void tearDown() throws Exception { + ESAPI.override(null); + } + + @Before + public void setUp() throws Exception { + ESAPI.override( + new ConfOverride( ESAPI.securityConfiguration(), "clean" ) + ); + + } + + @Test + public void testGetValidSafeHTML() throws Exception { + System.out.println("getValidSafeHTML"); + Validator instance = ESAPI.validator(); + ValidationErrorList errors = new ValidationErrorList(); + + HTMLValidationRule rule = new HTMLValidationRule("test"); + ESAPI.validator().addRule(rule); + + assertEquals("Test.", ESAPI.validator().getRule("test").getValid("test", "Test. ")); + + String test1 = "Jeff"; + String result1 = instance.getValidSafeHTML("test", test1, 100, false, errors); + assertEquals(test1, result1); + + String test2 = "Aspect Security"; + String result2 = instance.getValidSafeHTML("test", test2, 100, false, errors); + assertEquals(test2, result2); + + String test3 = "Test. Cookie :-)"; + assertEquals("Test. Cookie :-)", rule.getSafe("test", test3)); + + assertEquals("Test. <
    load=alert()
    ", rule.getSafe("test", "Test. <
    load=alert()")); + assertEquals("Test.
    b
    ", rule.getSafe("test", "Test.
    b
    ")); + assertEquals("Test. alert(document.cookie)", rule.getSafe("test", "Test. alert(document.cookie)")); + assertEquals("Test. alert(document.cookie)", rule.getSafe("test", "Test. alert(document.cookie)")); + assertEquals("Test. alert(document.cookie)", rule.getSafe("test", "Test. alert(document.cookie)")); + // TODO: ENHANCE waiting for a way to validate text headed for an attribute for scripts + // This would be nice to catch, but just looks like text to AntiSamy + // assertFalse(instance.isValidSafeHTML("test", "\" onload=\"alert(document.cookie)\" ")); + // String result4 = instance.getValidSafeHTML("test", test4); + // assertEquals("", result4); + } + + + @Test + public void testIsValidSafeHTML() { + System.out.println("isValidSafeHTML"); + Validator instance = ESAPI.validator(); + + assertTrue(instance.isValidSafeHTML("test", "Jeff", 100, false)); + assertTrue(instance.isValidSafeHTML("test", "Aspect Security", 100, false)); + assertTrue(instance.isValidSafeHTML("test", "Test. ", 100, false)); + assertTrue(instance.isValidSafeHTML("test", "Test.
    ", 100, false)); + assertTrue(instance.isValidSafeHTML("test", "Test. alert(document.cookie)", 100, false)); + assertTrue(instance.isValidSafeHTML("test", "Test. alert(document.cookie)", 100, false)); + assertTrue(instance.isValidSafeHTML("test", "Test. alert(document.cookie)", 100, false)); + + // TODO: waiting for a way to validate text headed for an attribute for scripts + // This would be nice to catch, but just looks like text to AntiSamy + // assertFalse(instance.isValidSafeHTML("test", "\" onload=\"alert(document.cookie)\" ")); + ValidationErrorList errors = new ValidationErrorList(); + assertTrue(instance.isValidSafeHTML("test1", "Jeff", 100, false, errors)); + assertTrue(instance.isValidSafeHTML("test2", "Aspect Security", 100, false, errors)); + assertTrue(instance.isValidSafeHTML("test3", "Test. ", 100, false, errors)); + assertTrue(instance.isValidSafeHTML("test4", "Test.
    ", 100, false, errors)); + assertTrue(instance.isValidSafeHTML("test5", "Test. alert(document.cookie)", 100, false, errors)); + assertTrue(instance.isValidSafeHTML("test6", "Test. alert(document.cookie)", 100, false, errors)); + assertTrue(instance.isValidSafeHTML("test7", "Test. alert(document.cookie)", 100, false, errors)); + assertTrue(errors.size() == 0); + + } +} diff --git a/src/test/java/org/owasp/esapi/reference/validation/HTMLValidationRuleThrowsTest.java b/src/test/java/org/owasp/esapi/reference/validation/HTMLValidationRuleThrowsTest.java new file mode 100644 index 000000000..6726ef56f --- /dev/null +++ b/src/test/java/org/owasp/esapi/reference/validation/HTMLValidationRuleThrowsTest.java @@ -0,0 +1,167 @@ +/** + * OWASP Enterprise Security API (ESAPI) + * + * This file is part of the Open Web Application Security Project (OWASP) + * Enterprise Security API (ESAPI) project. For details, please see + * http://www.owasp.org/index.php/ESAPI. + * + * Copyright (c) 2019 - The OWASP Foundation + * + * The ESAPI is published by OWASP under the BSD license. You should read and accept the + * LICENSE before you use, modify, and/or redistribute this software. + * + * @author kevin.w.wall@gmail.com + * @since 2019 + */ +package org.owasp.esapi.reference; + +import org.owasp.esapi.ESAPI; +import org.owasp.esapi.SecurityConfiguration; +import org.owasp.esapi.SecurityConfigurationWrapper; +import org.owasp.esapi.ValidationErrorList; +import org.owasp.esapi.ValidationRule; +import org.owasp.esapi.Validator; +import org.owasp.esapi.errors.ValidationException; +import org.owasp.esapi.reference.validation.HTMLValidationRule; + +import org.junit.Test; +import org.junit.Before; +import org.junit.After; +import org.junit.Rule; +import org.junit.rules.ExpectedException; +import static org.junit.Assert.*; + +/** + * The Class HTMLValidationRuleThrowsTest. + * + * Based on original test cases, testGetValidSafeHTML() and + * testIsValidSafeHTML() from ValidatorTest by + * Mike Fauzy (mike.fauzy@aspectsecurity.com) and + * Jeff Williams (jeff.williams@aspectsecurity.com) + * that were originally part of src/test/java/org/owasp/esapi/reference/ValidatorTest.java. + * + * This class tests the cases where the new ESAPI.property + * Validator.HtmlValidationAction + * is set to "throw", which causes certain calls to + * ESAPI.validator().getValidSafeHTML() or ESAPI.validator().isValidSafeHTML() + * to throw a ValidationException rather than simply logging a warning and returning + * the cleansed (sanitizied) output when certain unsafe input is encountered. + */ +public class HTMLValidationRuleThrowsTest { + private static class ConfOverride extends SecurityConfigurationWrapper { + private String desiredReturn = "clean"; + + ConfOverride(SecurityConfiguration orig, String desiredReturn) { + super(orig); + this.desiredReturn = desiredReturn; + } + + @Override + public String getStringProp(String propName) { + // Would it be better making this file a static import? + if ( propName.equals( org.owasp.esapi.reference.DefaultSecurityConfiguration.VALIDATOR_HTML_VALIDATION_ACTION ) ) { + return desiredReturn; + } else { + return super.getStringProp( propName ); + } + } + } + + // Must be public! + @Rule + public ExpectedException thrownEx = ExpectedException.none(); + + @After + public void tearDown() throws Exception { + ESAPI.override(null); + thrownEx = ExpectedException.none(); + } + + @Before + public void setUp() throws Exception { + ESAPI.override( + new ConfOverride( ESAPI.securityConfiguration(), "throw" ) + ); + + } + + @Test + public void testGetValid() throws Exception { + System.out.println("getValid"); + Validator instance = ESAPI.validator(); + HTMLValidationRule rule = new HTMLValidationRule("test"); + ESAPI.validator().addRule(rule); + + thrownEx.expect(ValidationException.class); + thrownEx.expectMessage("test: Invalid HTML input"); + + instance.getRule("test").getValid("test", "Test. "); + } + + @Test + public void testGetValidSafeHTML() throws Exception { + System.out.println("getValidSafeHTML"); + Validator instance = ESAPI.validator(); + + HTMLValidationRule rule = new HTMLValidationRule("test"); + ESAPI.validator().addRule(rule); + + String[] testInput = { + // These first two don't cause AntiSamy to throw. + // "Test. Aspect Security", + // "Test. <
    load=alert()", + "Test. ", + "Test. ", + "Test.
    b
    ", + "Test. alert(document.cookie)", + "Test. alert(document.cookie)", + "Test. alert(document.cookie)" + }; + + int errors = 0; + for( int i = 0; i < testInput.length; i++ ) { + try { + String result = instance.getValidSafeHTML("test", testInput[i], 100, false); + errors++; + System.out.println("testGetValidSafeHTML(): testInput '" + testInput[i] + "' failed to throw."); + } + catch( ValidationException vex ) { + System.out.println("testGetValidSafeHTML(): testInput '" + testInput[i] + "' returned:"); + System.out.println("\t" + i + ": logMsg =" + vex.getLogMessage()); + assertEquals( vex.getUserMessage(), "test: Invalid HTML input"); + } + catch( Exception ex ) { + errors++; + System.out.println("testGetValidSafeHTML(): testInput '" + testInput[i] + + "' threw wrong exception type: " + ex.getClass().getName() ); + } + } + + if ( errors > 0 ) { + fail("testGetValidSafeHTML() encountered " + errors + " failures."); + } + } + + @Test + public void testIsValidSafeHTML() { + System.out.println("isValidSafeHTML"); + Validator instance = ESAPI.validator(); + thrownEx = ExpectedException.none(); // Not expecting any exceptions here. + + assertTrue(instance.isValidSafeHTML("test", "Jeff", 100, false)); + assertTrue(instance.isValidSafeHTML("test", "Aspect Security", 100, false)); + assertFalse(instance.isValidSafeHTML("test", "Test. ", 100, false)); + assertFalse(instance.isValidSafeHTML("test", "Test.
    ", 100, false)); + assertFalse(instance.isValidSafeHTML("test", "Test. alert(document.cookie)", 100, false)); + assertFalse(instance.isValidSafeHTML("test", "Test. alert(document.cookie)", 100, false)); + assertFalse(instance.isValidSafeHTML("test", "Test. alert(document.cookie)", 100, false)); + + ValidationErrorList errors = new ValidationErrorList(); + assertFalse(instance.isValidSafeHTML("test1", "Test. ", 100, false, errors)); + assertFalse(instance.isValidSafeHTML("test2", "Test.
    ", 100, false, errors)); + assertFalse(instance.isValidSafeHTML("test3", "Test. alert(document.cookie)", 100, false, errors)); + assertFalse(instance.isValidSafeHTML("test4", "Test. alert(document.cookie)", 100, false, errors)); + assertFalse(instance.isValidSafeHTML("test5", "Test. alert(document.cookie)", 100, false, errors)); + assertTrue( errors.size() == 5 ); + } +} diff --git a/src/test/resources/esapi/ESAPI.properties b/src/test/resources/esapi/ESAPI.properties index 2537757c1..14b47d32f 100644 --- a/src/test/resources/esapi/ESAPI.properties +++ b/src/test/resources/esapi/ESAPI.properties @@ -97,9 +97,9 @@ ESAPI.Executor=org.owasp.esapi.reference.DefaultExecutor ESAPI.HTTPUtilities=org.owasp.esapi.reference.DefaultHTTPUtilities ESAPI.IntrusionDetector=org.owasp.esapi.reference.DefaultIntrusionDetector # Log4JFactory Requires log4j.xml or log4j.properties in classpath - http://www.laliluna.de/log4j-tutorial.html -ESAPI.Logger=org.owasp.esapi.logging.log4j.Log4JLogFactory -#ESAPI.Logger=org.owasp.esapi.logging.java.JavaLogFactory -#ESAPI.Logger=org.owasp.esapi.reference.ExampleExtendedLog4JLogFactory +# Note that this is now considered deprecated! +#ESAPI.Logger=org.owasp.esapi.logging.log4j.Log4JLogFactory +ESAPI.Logger=org.owasp.esapi.logging.java.JavaLogFactory # To use the new SLF4J logger in ESAPI (see GitHub issue #129), set # ESAPI.Logger=org.owasp.esapi.logging.slf4j.Slf4JLogFactory # and do whatever other normal SLF4J configuration that you normally would do for your application. @@ -529,3 +529,38 @@ Validator.DirectoryName=^[a-zA-Z0-9:/\\\\!@#$%^&{}\\[\\]()_+\\-=,.~'` ]{1,255}$ # Validation of dates. Controls whether or not 'lenient' dates are accepted. # See DataFormat.setLenient(boolean flag) for further details. Validator.AcceptLenientDates=false + +# ~~~~~ Important Note ~~~~~ +# This is a workaround to make sure that a commit to address GitHub issue #509 +# doesn't accidentally break someone's production code. So essentially what we +# are doing is to reverting back to the previous possibly buggy (by +# documentation intent at least), but, by now, expected legacy behavior. +# Prior to the code changes for issue #509, if invalid / malicious HTML input was +# observed, AntiSamy would simply attempt to sanitize (cleanse) it and it would +# only be logged. However, the code change made ESAPI comply with its +# documentation, which stated that a ValidationException should be thrown in +# such cases. Unfortunately, changing this behavior--especially when no one is +# 100% certain that the documentation was correct--could break existing code +# using ESAPI so after a lot of debate, issue #521 was created to restore the +# previous behavior, but still allow the documented behavior. (We did this +# because it wasn't really causing an security issues since AntiSamy would clean +# it up anyway and we value backward compatibility as long as it doesn't clearly +# present security vulnerabilities.) +# More defaults about this are written up under GitHub issue #521 and +# the pull request it references. Future major releases of ESAPI (e.g., ESAPI 3.x) +# will not support this previous behavior, but it will remain for ESAPI 2.x. +# Set this to 'throw' if you want the originally intended behavior of throwing +# that was fixed via issue #509. Set to 'clean' if you want want the HTML input +# sanitized instead. +# +# Possible values: +# clean -- Use the legacy behavior where unsafe HTML input is logged and the +# sanitized (i.e., clean) input as determined by AntiSamy and your +# AntiSamy rules is returned. This is the default behavior if this +# new property is not found. +# throw -- The new, presumably correct and originally intended behavior where +# a ValidationException is thrown when unsafe HTML input is +# encountered. +# +#Validator.HtmlValidationAction=clean +Validator.HtmlValidationAction=throw From 74492fee25b9640c985da28a91db478f21e6de92 Mon Sep 17 00:00:00 2001 From: kwwall Date: Sun, 9 Feb 2020 00:37:18 -0500 Subject: [PATCH 0612/1069] Close issue #538 addressing CVE-2019-17571 with security bulletin. --- SECURITY.md | 8 ++++++++ documentation/ESAPI-security-bulletin2.odt | Bin 0 -> 31990 bytes documentation/ESAPI-security-bulletin2.pdf | Bin 0 -> 103931 bytes 3 files changed, 8 insertions(+) create mode 100644 documentation/ESAPI-security-bulletin2.odt create mode 100644 documentation/ESAPI-security-bulletin2.pdf diff --git a/SECURITY.md b/SECURITY.md index f4d5ecb55..bee2b246a 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -40,3 +40,11 @@ can understand what needs to be done to fix it. Unfortunately at this time, we are not in a position to pay out bug bounties for vulnerabilities. Eventually, we would like to have BugCrowd handle this, but that's still a ways off. + +## Security Bulletins + +There are some ESAPI security bulletins published in the "documentation" directory on GitHub. +For details see: + +* (Security Bulletin #1 - MAC Bypass in ESAPI Symmetric Encryption)[documentation/ESAPI-security-bulletin1.pdf], which covers CVE-2013-5679 and CVE-2013-5960 +* (Security Bulletin #2 - How Does CVE-2019-17571 Impact ESAPI?)[documentation/ESAPI-security-bulletin2.pdf], which covers the Log4J 1 deserialization CVE. diff --git a/documentation/ESAPI-security-bulletin2.odt b/documentation/ESAPI-security-bulletin2.odt new file mode 100644 index 0000000000000000000000000000000000000000..c65623126fc7cab343fded85c86ac6b1b636ca78 GIT binary patch literal 31990 zcmb5V1#lfb(%nUKb%*@QpF*C)?%*;M!KAZR7{rB$Pdh5G& zRi%>Dqkg(I8jxoCQIds#!~_6f0f0I>X(hd3Rzzw50Ps)#D+1VB+M2p}I+z+dIM`Sk z8@gE9+cCS@nK0QKI$Jt3**lopnb;e<+M3$AFu9m|xG4Q!V2k*hy~3;jz(4uRU$E~M zuC_*YhL$$Y%r5_3Wpc1H4_8u_*+bu2tAwG<3>wX6)aoDCH{%r(r-%*|}vtew1EZ7kgF9X%bb z+}+&303dWy5Iq5imL9~&24bfN@q7dEh=7FoK`Q(pP1^u{ho9D-;T}H04*qc_{-tuV zAUQRVrVL0!1EgsRGSUW_nS(4{K`vGx4-b%DaGh6RglA}qbx5UUWTQ=7yGLY+Lv({* zLa9e;hka_VduG4wPmpI2DAdg&*vBp2$9#cLc1>P>ML}atO>Y|B7z%gk!i+HK3x zd&g97$J%acbVNzB@PC-sgUUg2lYmW)K#&!trjWH*eY zw=cyOfwBrd3!*`VnV|Y?P(kfLMay7m$9zlYU_g}oRov!R% z=;$B&J+jn2vsFI2*EYG^G4$Cvd(b@t>aR}gug@NAt?X;9>+fjl?`|LKYMkw}c!j>Fuim4RwHKyFr&7 zLpR+6qx~I=Bfod2yLM;0?*==@#>R#x<|k*T#^+Y%Cx(}2$CsCvM@K=U%ZFq0poKBe z^2XlEGH7dOYib|3ws*36^myI(=Xz-NXnAyRW9fEe1USC%xH$f>w6e3kv%dnmU)u+6 zfll_;?v7UX{-(pTqtnBK(~Gmi-HX$Mi;Iinv*)XutD~EzL*Uyb@b3KX?eh8K?&A32 z;`8}$>uGn!pbh$*-Fe9c$PDR|w3ol9Ia4Ybbea&xL(dgFk=9pt%xzk;t7Um{ulMUb_Xfpx$= zYPodNSC862U_x3N6N@ZT*jUXs^{qH-Ub#ZmdQnwW9*eZGlLe>7YS*nZE4{K#-P#v; z?9ug0mLQiu+5C_E)B+RQuaBDZAg)idr=Zpz^H+B-`yRK&dk%bokI#)ayNRAlLRqK% z9x#4heyvYt^z14+=(710e{3&_O4+8keHGpNy&)^$r$p=aN(+7{prH zaD*%E#ajYVr|$Mwual-dHG0nS^~c$*wG$c3_cGTH`gi!l>uFDiCHC*=9?Nt|J&u$1 zP}@|@FSVtHHeMq~+Z`YL%=&MZz;?yPR$IT3`ZJE@s=FSLbr0bUvu69*+IjPK7ZPnx z#~+;pU5|S5Rv-G;@>n;5XW&_LM{4bd(9yJj3yI78q_&x&4^6wz4+&`IXYl9E!JIF78EBwRnK5u;}=gVZ=mTaKELX%00 zS@&a4%T|4}tLeOe9X=I#-}l@7;dRz-fX;r~=WwGleo3uW2v1LH!{rHXsUi;rtN&`MjBnD3@nJ|BWwBp>C-ud{Q2@#8df%AHMib90?P zC*S`AA{K?i-mUp(D1#_qkW%(n z1s8*Etew6W^VCDnO(BmHhSyQ%Nb2&DW$R<_ttLT8%Ch6x0wbgU9@X-Yb_~%;Scf?` z<$NHpSa)EV@l>bX#BH+S33*Mn_F6o%M!OBP%->k{GL=f`@&=e{v|l7PAV#*m?ZWSf zyY6=RBAfDg54~Ks z@|@ctMLX*o)_H;sNUyETx;&P8Ag1@<$bOb(o|8z|3)|rt%Vx~A%3ZBdmHyWWlgn35 zn_8)O%jy@~pg9R$Nr_n5T|sBs^r(6z^SsI>BA8ba@5FdxqQTn3lEFxX4MS!dH z9+g3NT88!a4x%<<-N^38?5D$wYrGz1(50o0c_Y-mZgr+b=YDZ3bK9fDTQ+jTcJxKf z_(Ak8CD(lcGf44Huq)8Id)RqEKW}om>-zJ(IXjy&_Lv8!Pd*>3k@`~-kr0%qH0sJE zxPtbltuvs-kL3Hce#b^M?;J4S_Jjo*6^U^~=xfP9>!+nn4BmEH5bzV9^8)nY3M%&O zDncWPW3I=9$?FG#hU=lJR0U7}%CFL$E5a*0M|3iyY%J(k`-nn+@3mC+5+1{nwK+*NRNK z>`7+id87n2_JZiAk@5%Q-zTx7Yrn%$yX~HE_p!c43B>`c}p`h zHu6Znx3g;g%s^Q!d8haJl?8Wq^|k8ZO-i>M!&*SH|Mtbg!0ydfXsa53mpwdU29(fx zXZHSG;)!*l>^Um^dVRDBR}r-`ME?o|(Bdj!6S(rdlVHuurhxf#^0PZ!qly{tEY^<= zTaXAP)aV0a#RN{$IF#+HUKgHbXA4&hKJwC@;}h4DrFe+(tOt9i`?cMZiMvm0hcn|( zap~s&^M$4JHOe0G@)hgn?E4Krj6MEq@Mr4N2FUd&S758dy6kCZ-FB>hHFioo-8E!@h)nn$hIm5YIXnODu+JU^>FUcy^M}Dc zeh)fB&Eb{`OK=ej9#L4@H6GrSwPJj{9{w;* z!v_zh7rXkw2Rf1^ZpB@!0S{}xX+A|kaH$Y5YHSot!f>T%wwMJ>9vnO@uLx3;5pMpT zp#s{1k+d;;3{3{iX`W4FaU`8#+!GTI`~(f_S1m0#B=k)qnM{M6O1@geW?GSWN}GyR zm__?CVolLqHW@59Hi54DOol(?ys*&#Ui66=`xot8l9h)z!#>ssh0;;xInK7Py>($S zZ|L}^1}M5>i}>&8!b$=eDS-}vS-(dtfk`DmoRC)-w2uR>E)4b;YGmjXan$HmqKw61 z(*!Vf-8LgD5>fTV^r{3?UfGI6$m2{|$Y>-wC_-91R#lp6YUOqdEW|bwTCy$c6qTl! zf&@}}Nu*}@WSvGxdKT+4N%M-M4C$gTba>e3R_gT$i+_|Utw-6)z$#Ju0`v3J3)%AE z2~S2-RG2hE=!=HGsq74f5cg#^Hwz=-jjKsJgjABi@`flsEu%Z@5hRcm2BJDX)vTBK2Y6o=$;s)07VV%0d(SL%!n9M2oJMC0TSPRg)a z6*!rgSS(UkqCF{VIXe+oZ45=Bi_u~@O$lMBELv?qa+=T6s7IZ46v>{x{VD8;%VRaf zpw8j(^qOF2;@NJ&Tk=aqezD@UxVQ2mw$9t9lTjCAb$Ju%9Oxj-?Wfyk)$ctgi0JYp`}>a@C0%G?N(0p9&GU~e;_o4Mq6B$yb==1+UpI<-q5Xrj|F5n?eYIASq_aV~0Z*KT@ z{rpKI4AaHDD5LZWUn<&}GryW16FXsAA)8qi4in=F@*3BnZJ)L9vP|&D8{>u+ zk95R6EF7~a_#E{;i^|Wv@N5wPKm--aja=P6r-rBp5bO#FGx+<)NULMpHPl*@+xhT7 z>8w6;lVPkrTUt{owA)ef`wi<0b*3nU+#>GF4E2Qavt--;O?e)rE}P~el+M4d4ce=r zq!hdp6Wnv#E$G)L-U~sC8ph9#X#}XV-b|oP&6?!xWAkKp^AX1J~Iab#7&s@%qM^R zP9SHp!!EQXb)$rGh*Y}xN(w6dXfHPGV$eT z&@?$hON{d5d&O&17Rc=o-cXDw5wB)DbW^1AeJ$039D$D5yE>s1I%7Ol8p)?M69!|( zAm)~(QpE5VtpnXR4To~H)%Lf56RxMLsS5#CeD~0M*7|be^w01KNS=+{aMM?3;N}NL z$Ly{Ll>k~K@p^~%-py4SZ#lJIjN)KyeisIYx1$yfk$dUqMf8XjkhpeKT=Y<*&ELq# zicFy18=jbIFWs1W4`wf2!?VBI3V}ywYB08KAQU5Ar(-y*HL+xb!^mMCfHWL5LS)!E zjEr#i>)V&e%Sk?hP5juYPwqxu?C4MK)<=MVoVcG$Nns% z_ZyqGC?N(nX;yf?1^ZgACpS^#qoldCjv z_jKB%z*oDVBlaRE!qmvud!T;Pdms1+SraBBsiw)n;46k7Qc(_}{@v)x{8JU*F!bLOxeal`0KI2)tguMHeKbCBf5LkJZi%>RtBpuxO^ zb$4+Q8^~weJ%R6hTRL;#!-NklBm_EmxO-S{SI-{i-z>$j)RdD4H$D#SafJ@ptu`C# zoL|B4Y>2}Cz^c$~d*%JQtrJTykU!yr;C@_Iun(iPI}xI3+})g;=3$#dm%Bz-t}kG( zBc+lJJ5oaRT9B^vg$w9#FU61uH-ch3#qIF$pc<`LbkXXjXtUaWIhtZWY!o`M``n_z z-q^h1X#LIY;bm_xWZbMJWb=_g4^1l)-t-S~?-w z+(fj+5UT&r^v60<_RxwG=eR|c8uLS&hkcBNOzkqj|s% zu94Ag{BV$98MhA=t8^0K@1dpK<}Ot!P}=9cJdY<^svE8rFcl|)uxy5LHUj>DKzqs2rv0!? z-EnBH>W*$|K~6V+VD{Hnviwtfa^{|-$2TFA9>WqJKPyPY_(ya^;dwAW3;on4#Al*! zqxa9SLHSDVs8QoYJ8pfR9~Ou+h(SWA@tcy%P~QyG6@0n8#aRa3%4R7b&4`P^iE)(p zy@2lvJKug82e|@xG@YDEc%{qxAXi2mJ0A}T&Pad%T$-Hy6}!jHeuT4}oH?U?+!c|l zb@#^%nkuFXxbT~G%Ob}-xY$#xiM!e=!v6QGwW>W08hf^=OeM2P4Ht!D43_!gLu27Z zgut>s*Y>8z@J&s`(RWcSXVal6Jn72ESA246;y#Z`-6z<&64Du3*f0*bI7G8cH3pJK zHr$Oxq+bXao6JWvJ~;8Fd3bKuu9}Y zVTdRF?mMXv!33yZTlO@jnHp0|LOV_a-T|k%q&${QZ5Z1GD?JSdmj%5nf`Bh6k7Ryw zgL4Kx9Rf@1^PA~-f*OYa6h1^-k7n5ml{%(6Woig<_fj!A4W)xg+HsZY1KF{H;>rrU zGu;>b&8m@0}AtF6HAyEny3(Q=qBlV+=w(ZD1pi`Qlg|qpDEPfhiJ*U{&PE#2ICTrxyBWILFo!{t|SB# ze;_?$nu+N4Rj9y0J;jC5*WA z51Vx6B-zTvcQx_^j^`vq3_l96i&&CdO06JhN*Tng0GFkb#KbUD%#?xQTHc0z#uIQc z0`^8gH@qVnjjOpO4S9$(#1aZ`z8JM_Oc3ftZf659TK{SR=dS59qkLUbk$PeMD7r=M zSdG8a-;1H|p-$1Wck5fviagIivfUvn_U|QUh^GXHU#-3+$G%{K?-M)2l78#ve{RQ#wtK zMf?8hS!C$U)75LAu3+Y;-2R>Z^XU`X>zHmRGQYjxqR{lV<4Jj`zR%~(9e2;ieLjC^ zj^Mou#K(T)??>KBMKu({*VW|SxVXtE?+=mSYZD6rBPN0^&;{t_Gspi~Wbw0u*Z;G} z|8_;t=ekkR2juMj{rdId_=)e~rRR;{^#zBh!y4uFEVkQ!Br*48gjeT_{>Lc}k*I#x z`HN)k`_{JLOS)vPe^r3r=SgBNXoUC5=g;THaqhPB@qTWl-<7xG#>+f!?(g%M+O7KI z?HR&Y0bqKEz|-PL59le^x5`nRynd3zmPG@FMI#6P7ToH4?(0_mNX~0t;|Y*D$8QhD zo$n>6l$ihi03~;`@faw1=|AB9aW|6tzT1=p`c-ZhTo`s{`CE55&%Qa}3C zwjYmOgAVG?3I$(RV{3Z1mniuT|&Rq4}Kmn7-4wt7!TaTJETezpeA zu1!<_{)Wg!RJwPAz&ruvxTIc=voU}`jnH(Exg_>P^0%CF!jWP#y-;BE(VU&oCHMhOK7?|$o*<1+Z} z9*qEpAl?r0u>7DFIRV12ZzTfH+fSck^{B6R+6Ag@s;yxPG3Je~BAJ!8lGjA2HzBFOQkF_!jAx&BLkFfWkcnf7x4c;Jyh+@+y-1dxdh0D1 zU-uVPVfKIyB^x+M_6YJ~``Y^$yDMv-a@@ZfgZFknXBM^`xP3_QwDIXj3cX8PY%|xe zBd<2GQ4|SB2V-o)-TAm&4)^ys^YZgBNmE7kWnrSB&U9(0If@EPR+e!#@y=i$LJIO7 ze8kI`3A~^~`|~X@M{Lexcf(>FBt6`@@lZkwEB`LY7tkVymVfnsaBK6$7_0A$uB_SH z!adnLR>J)i{|J5M-OFbL3V6KBl-FYnkhJZD5u60m)G=7=){P%fZNC1D>$cH8$!3^qjv>hj@b@#SxdFHf| zN=zH4P7g&>DQaJvAM-4`_1rXV(c`jX)DT%UaiK}0xen1eoPC2sJ*%j8!jl=WDx$TN zQVUk~rRGtvcNIEWOx$BtPA@;L!Qr`%+9MRR%%A&XV{K0nm96oiChRVBH+rTC_W}lE z1Aaj#Z%$k1pL!_C?hqL6I{0X)h=VEh%LpQfVjuS|9*!kgs;87Q3j)%>l9&>R&*hvg zmxmBgp?`Lp#vgQ#KZaj8iroLxA$zm@)SOc8O?9Aa8UOsDYH#Stvo-2uvLLs3NFm{l zD`0sfL2uxke574U%UG#bvhX7+*Q|Ju1pj9aT8sxWYFONtIf$Y+nzLwvFQ}VCoEyT8 zB17MJ`)b(F6&kc(9yU5qGFQRb!flwv84p|PKQ>D`)>;Mdb`gTH9MS7_MOW}6aFKo~0BbnD z@k@-RxFpe)h*KAy4Mj&}O{e1>6%87Ft!3K!J!(z!=GL}(>c_tbNg@b~szGjU$)1%6 z3yJ!>y{sOCBabX4LfuX0*JhGhQ$#7ED{A@`USR``Q1Mumq-;?8X}N>cEw9{sVpm03 zz~VL%+5Rbo=PL;zz`9*Z)*Hc1JhDuO6X}n2CKw@ zgo9W;5CIx3Imu}Xl{)6l)OT$bKSiy)Q>VTzT)A2UYrIdeQ;p%ube#2cFs}bARh(nd zpaxrSgVb=wxB+;x(JFVJ;}dmHT)hm5DZecq;)%bwoR~p#`xPqo{W;%G^;CE2aH!kOR7lIAS9&&^GHM{SKa=P zXLw)lPiV}hia%HwYu~hVj7(Bw+|4oknk@Kbl%lUWA5oqfZ2a%*ucbw-_Hr$u_a%imC=KIOC&~1 z6sM~iw>jqf@k8==TUWpK*j3-aDX2ez004SwVnWqLc_JDmwI`&sT#>|=IJ1LBQrkyz zjLxZ)h?SHc!>k5)*JHkZycu?wM|%`6nox;i$x&&cXRQ8fKmyU@xOuIf5@{py`CG9n z#5zItDrBPEbx|=P1!;--w0d?(|13H)SsA@%vAWEXq57=c>H=c*&z*1AmH1YR0gXK_ z57hZ3bIreSwYTN(+G>%ass&4{=FY5oyUTo=hH%vQ7rivcbfx#QxTKY(MQeHOo46if z5K1;C*6O}9_X7Kz|I}bm{xZEqV*%%+lS3J$z9LQfj_z8T;OQ4MQPBhqNixgaG+1$( z?z`z(*j3+?sIx|s)5@Ssu!U+c;w$Pt-!W<`%raF~aLykzdLQ_eLFvuMRf=p zsUo0i;B5=iYK0)G$TA#ym4e^tRc%1kwD`Lr^ytb2jV7%q=l3_t>LTOas?n>?XRY?K zq-3LF_+zh4xfl68Bwi5}e)igUG}bvbq1I)r&CWlFu0E}(+95L)LL(|0uBg~a@<9}A z8+!wxZ=U_VXcau0rrUuT*Oe+gxtPi1GiT9_78?YEjt)Sy}s*EI~L{iWeG7?M>8Ac`u60jC0fCXdbrfPjg z!l*Jll(9wvi%AJe!E4SXaf^sp?Tv5ig%WGA8Q&AVY`fbdvNfu>Buw5H5MP?d^@bEK zk`2#;ntpvqK%HJp?I~}c*B_+ccvYoSHSG>|i?{wizvY2Pea|aF#5s*~ zT|&`4xZ?u@`!8ov+W7YyQV5HoS-@u&ai$_}+EG3fka#NPY7cl6`o z19YEyb8ZT>|CD}zwY}XgK&kZi&U_!k5%j$7@!4DN*rH=`Lqt}GCT2h}JIp6sg%RX@ z$;Mcl`D$yy^2Qvr+SfPMN*2j0nrN?v4Z@25K5M~;5xv}>uJLUnv>(Wgyxc#_ji>A- zTmcr31W3e&qNQ|vc_2Gez-66DDpU%El}IWq`Gp<@VI6U$Q6W~ATxbD8=A;qA6)q0D zdAcI3h|hM+_Rt7yEUBn0ix-owR3F@G>G-Zyn3Nq*QX6LGu!dMQKQogj-n%62%@Q++ zEGas>!j>O?B1RJ-f`KtxK69nS9%11eX98j1YgV zxvm|uCTBzZPNG&1ruJaJq{=cW1A|7EFm~`8(IBD-p}YulSG))ybV2jM>7=6&k$-s39at9qBR zhDYsU2u>RDDmT9Z8<(M;@a03s)S({tVYSEbYhWsTVV`FBLRd+8rxs`M>Q5~NdH zC?;=atcP&A+BaBJEYI%qc?r{4s8um_w6FM1W0*4NT5P1Hh9W^MWR_;BBr+M3V+mqi zgTk#e`~}e}okPMB3Gpy<91tR=y!R(MEq&1xSjzIFTEV=NH=+!^HkWr2c@$Nr#0}(>8Vp|YCOGmY zu}v1wmjx+IN=-Tn)%F*Wh{=K`XvdkjhlISPN-vc%i(hdf0q`md4WvRE3#H>3=(AWp zWMsOFGiC5!;qKWNl*uDy3s0GlaB88wT-o9)uw55vurOqBBX^p}7L?Jal3Xp|S4(2T z(`sZJw3UA3<#_2X=-+a&MKH$&;0qEhVehu+L$q)#jRAOA+SLLqd}wBWIKar z$ZI2-Q}ubya%|UbKZt>un)DIS49eE+ZE%JNyG<8C)_Z4WMo5JW!h*ou@uV6{>&xkb z%nM37VA2QN>T*Lpz*Z^2AtpdRiyL4Tbz-gEi0B@Whwbqr(l>(lNj1LVLeu^(egnrt zU~v`>L`LM~V=qe=4wQa_K%{6mV#Tc&7|ZRY0z*rhwT?GuxV~qhW<7s=UG#^qoL9{M$%B^UI=dRs8{c*{KwOP}K5JL4ux=i*ElCi)S!j5Q>P|jr|mz-OnP%U1q_3wImkDD7EC!^NR zzrx68l zSKf=vqZWTXV-T&;EN5*p*K~&m(rZE&r%uJZq04a{MO;o2^()4LAy>}Vo05Rh{6Sa? zA-xsJKz|WZ!G4z_`#BoP*Voe@KL<_WNYxsTv^2uKIga8o5S`TO=~tODgFDOh!PR??lr>|0zjUg=?M|4P9qqY`Wd*% zAcPcroUxxJzyq*z`nrf*NK4%!YEsEESuE}j6pmb$G8YTh3GNjz!M>%vARawitl?tn z#Ety;{!hHz#7pDUw@7!6sbyJb^_6e#L(Jz{GFgq*>50Aw%e0NwrOheMV+j$ERt1Hi){EVNuwpA%$-!GGqzqasOIB!001U)QCN zsy0S~s56JX9Tc^T1KXy2_kqjqU2*Z|<3%ZZeTApWSB@`mvD2kYbhPLdc>l~I-tgKa?N z;D(j2(K$@al0MH$Q^`r}*ld&OObd+2z%x}@DI(M4NHusw5)M(r&Bj1ADDhS%vgnLs zO&GCRO~F75yNx2DYCtcm{2f&gc0vE<`x}uTA%ib^GI+Kj${@q;TSXx_wTSq$xL!$O z@Tj|pMUnR}JxT1=MK>Sc!a0Sk&8Z%$m9h4{NBNa_SrecN(zfQl$+7X+VY|d3Uu)FB zWaH(UnPUz)J&k*(UQIIFRRmnK6Jt>2w^pte9o{xN(aVEZc;{nh`tjwX=(BLy*7Bm! z5~S6GvW_EJ6&cbM#zs0*r+W(el5}PJAzW$JhhZt~dR=JVmYn&YX>p`Max!3=a8#CQ z!8tPBt^P)IE$xB?wfPP&qb+L?FixW%^a171-zPqf@8#Zwnh0`X73J6I_V3je$|c zh3gWg(Q^iC^zZ}U78>#ds32H$0@)ZNN`Af+JJzR=kw53RMtDWLyfoZ$PdI9l^X*r` zv#Lb>$={G2s&@zWrDLWT;sMY8dJDvm!g>oq5np-{M?@wfUZO4ZNPBuoQJ{v4y+wf! zUpFIOhgesB1zR$B%LD?{ccr=w`86X_OWJcPgc7)D3a31V#Y+=x+VTnwMyg1tdEGP& z^Y2Rrggd`SiG5)6Y_}47 zXTNR{r1=V-?k4dXHvEX_Znr~d$Ga3ao>Qav!0yS==qzU>*SbdJa{XG_$zb5eSoI^c z1u!vBje&@wVrWfi`O@=C-OxPr4@`KZ)g2jA_siY;F(%`%C{ItaaBlR1*(5)`>m@Ay z`Bn7BS4{eYg)V4=5*8(logh3U63Rse2i(+`vwUy^ z3L4uR3chZ66w-^p7jRI!-e7x6UkX+lc(v0X1j!!fi)JPJsfe#k)YPgo*(BQ6v1O5!!>$ z$zecl9iU)DXj&*%=Mo4E+R9u0c*>IwEdIDKnLj5FGVquq+_J0{g1!$f(o(HlOD)${yAl5809DH+ zd^?V^-OqE<82j&zx0Oiete*Qn29)f}c>Lbku;gIbZV5tvOZ1I#PH1X4+$3^h@ZR*% zkqpdE)C2~35`I@IFZjVfR8VbR`F8_Z^JgbZeseZHAxmC_Vv=n0tNG>9HfP46$}FLp zhy)=XN0OLEr#6j+neBuLlV&28jR>S_=Stp1UYb}qEUPeX^aSDB(w^`fg z@fU!HGz;s>fYQ&hn>Q|%%`GM`AC?*Z{%0^S24q2vEMxV)Ak3WRdmzyRi4CnESa zA|4Zwj`RB-gC&a%M8$(ZfR~JTOLt_h5V-o`t8Ws}+1K1r? zgSb&J*1azQ1Paj5De!3)2yuNd7}*F10DzJ#3@j+OkA&*4Hpk!mf74q28~dN~A6kp3 zy`7n*xvSHEv16QBnO*GdZHx?^nE(H2nc3JIx|seaE#iN%Woc_@ZtBb|V(DUQ=-~Vx zO#bB!{Hvo5hIXbl|3UBH2Kblm>XSk{!#r8)(HN^O9v-=b0<@0=l^W+ z-!u)4jZJM#|IUm3e>$!I=Bu-dp^NMPVe#KCErh?m8r$2s{KI|nu(i4LU60w9a@%?H zgR1UFZivF9R8gTz=1;z?l2!bnsOFx}*0B~jWQ0m4icT;6D+7o2uyptt16yPL=SKs| zTV1aa@zPKIcMg8y9sLz&vOsqkisS?p&17Rm`G~0TaA$=qZ%BX8L$)zJ!r2S9vwjnM z^SVlzHOstHDYKT~rq8^9y|3>}uHQ5j#&{kM6`M$CyYRY!9RO4*WWU^lYOmiUGTmh` z*!qr;%DvD78->7GYPWDsw)q-3O_}rD_orqvn@iyd`9xQ#m91s+o^t<{?QZyVt3)!z zmDgBX$g2GNL%qKelUUE>?gmCbmU?`#HC-+RHlCR6HJLn*8M+N?yOa%fL9 z80Vhb-Atn={rrhi=AMb4q-wLm!csn1IRbHue9*zKsWh)#x0yA;hfOrf39)a;NDXI;hLMZQZKj>j z2E*RiAYN?LdtL)YUgM;nOvqgta<1ds1A~va&DJFun@GmFZOc%cihHrdD6C85TKr&G z>USPQW7`oJ=)mayBb@Rr&5xaL?hn5PCi^C&{RXr0SbTbLl7It~v4lR$5)ZKOLQ_es z2=_~-CJ?YMyY72177q3UNUi1&~BTCc9#6usG+JD-| znIN;3ADd-NRQCc51?#WMG@;gb5irTkYbWCl*Di_cehjt@=cs`z-I{$I5IIYoZXoPw z`kenzxb7t=a(O+OBq~+R$4}2k)~wS-YPbNf5qzwrBmsZ;31z-jvK20f&{9^3kVEn<2kb9VuijILvLz`WZAgE4aKn3sGM7vn1!O9<;ihq zjQTggHJS+hCYxHqsFz#S%5tp?FST~=8~ckHM`wCelt_g5=|eAi4cJcn-%Zx9NMZes)6~@JQQC1)-7F4l8$$9B-_;mq>N4H=Bt-z$Vluw$Uq!uS3AR*~aQ?}-TsBmA*DY{}T zcFucBf^PJf=b&1_+d;Hi_hjfRB=bOchY}FwT|36+R!JJ*qNai#p=foZB%QbKw2&V9 zfh!P0WcCF&CpO)DZ?5CafQUw*QP`XR{1(J+{-wHugN|s`Xxvgyl?Z9!ZWnEgz?Ruc zXx*&T+8dRJ|G|b=sRfINH=cgY_u!8(SK_#imcJu0d}RhktQMx(zS3OCu{IT8b&+b{F}6v?elL8jjq@RZIgQjCTyOcF=uet_@430LF}DT4r!Qd> z=jLNJF6#$?d2Iov+n;79y01>K44~Ikj`F^v)T{UlWAlHtRs()sKTJ(tnXuh7$ouYu zhJyRC5aD6rtGE2^`jY=ske4_et3c(S)=AmV>*y$|xe|-ft1qwUUnG4uZ5o)HKP*&c zF8G`plBVX;R?R9Aufex?tYu(q2J3A1vSB>AVNs$l056HJz{_NabK^ckVnDD<`P}u$x2ij3) zm>?GxlGhnV0)S@nNqy-nz4m3UMg|HS`|NyWed+JM{0OGUXLD9-V^!cyM`14;kIWN~ zRm>;WNK{*gnK8VeyqjQDcM!toz$PmmJdJi^)VHGe@w%qOhDV_yc#mw+XE(e|jyBpQ zN(uf{;N=|}A+{9YCj?!1M+N@dy-do4|K!wR1@vFMB`^!INJ*E z_o?eDgm>k5x)fVHA^)DdY#SqOzm`7UOO1U*82De0S2%atkv~3wJI(645{mN@@`!I+ zA9B^r9+$qm9+%tG6y81_Sc;;=Hh$wAs|}3E?BS>;J0XsXJpMVn;TYmnzUS9F^(B)K zU-=zNNz?nu>6Q%ZW{~)Vjue-R1IAyICLl0EI91J+5};w#7lZ)RW8}M$TeI9&^Bh6S!{csE9s^Or8pp6(>RCJO@7U&_SYFD@7~;w^vhSw8 z-0(35#@tqDk3^{08@*nrd+Rui^Z@>x#aodDDUGG>*i$;~h0xZWXIqHt*S#r-nxY-6 zOEXFcUHq9{;U6jvcAPaPjFMb&TSKB!Nai*g(~hcDgH;9UHA*7V9EieLUK=N=3pQ@u zdnFp=s~#^wvnN$!QIkdft5N2!nnAGB^7YvDr%}HYrZcl%=&%tuv+B#Ayi}{!t$7Mr z#_%URn1QE8c2j!HT7O*;kF=Qu?08F$!RDbCs&Lc6=>G8?GMRrLm#;slIE|7}%w=aa z%?uxF7FR|x>#Sv1m_)O6+BzuFn3{7K&M7(9O2o(-B8wYl%ZbjI^w8H_xySr#|33dT z>O+9=DPv+q4!foq7yoaWmK=hXLFm**Tk@Pa#Z7?!p^U)=>}DIbKPZ_s4RvG+WsrV-|A)ogpsAGpl4*34mq{e00HmGuEBk8D)(6n0;imoc}I=;`btH0w&( z&~4dm`iF+zmG*Hhmu<-QBjJ8RlM>r@#Ko%P)yuFFPm+lY#rcS4jlqfq;a<~kLW>&D z*s|~Y7HiKNe8~5tyU3!cA}VzT-sFo`O$M7sLnRxw8EW+S>u1)yL_H-JK=3R{hszD* zlux*rsq2*{u-BaiuFVM&hPKEv5iM0fm_YQwtn6e7cVwL)#wYki4O+k8Vh-;NSpU&iH2-A9b&g^bFlXvy0#JPdb!o%9mX` z{XfRu{BJ*rN#%1oLHmV&5}*7{?mcclV-x5Uw?M$zgRwV3feJoP&|l&r^)-m5{Py63 zm{gf4e~j~ti(FGyk0KOjEro_|4N{{e}g-Tw z!rDxn9wR3Gox@om$_119uh#w-8}u;6Orf4K5YAJ495C4i~c=LS_JCjx}ue#k+6*bt<1SM%iz3SVFn zO>Tal0oUx0y=yM`ch75KJ#Q1sHq|NeLyi61_e0R1X;8`1Or!u@)>i1Pd`acI%+=b` zGf@8<59q$r=DL-ir<5X(%Bv^)BJS@h<0!9%F}7CcCMf#SfFqRlr`>O{@N!ROr=1@c zO#aU=XRkrGN3UlC+9oWAybUDi)XriGb_Ln;o6%c=Hja@qn8trW=AEvI3c&F1H072!U<>v!@KHvdN6$fZk#V*^nZQHhO+qP|Wv8%dV z^{T)3&VSCm@7@>x+_57vBXW+|xifR+T62y$rkID&K-8*R1lw|lpc%g0QekVFyol{M zo9l4$L;%u#@h549-8RLIU6AMhfDh9g?urL#09!o*eQ%TsZcOOw0?7F@6q!wFFFa>J_F zEsRF7M_0wLO^;I=Xkn%fWiWZ)tb1d(Q?D^(pzdoshSe=J5^8U6@)MDkYV}=1`I|Jq z{@XNM_j<4|90EnJP4a;5xq?=|st?mpNO@l-rCaVw68MX(3v_ii_O5xH9Lv>pF$cGC z84H#3geR$J=X9C35=kC#hXaXNhVrS4SC#h))|rb|*SN)XIUJqT;`__8Ixe=7_JvDpB1N{i$=Oh6@9t0!3FOpM1Tm0phIukeV&7Q+V2Y~Rg zMuu!UTD%J{>+|}cv$+0a_aOH*>B1k>Snk)J1m9{U*(hC3FU0k-@_Og9G49#E>t*%w zRW_A{vdj_+bc8TW8`Ef`{KXwvtmru2w+w&L=YWy^=Vb&+GN%l5R2M^peJyqeyh(vT zaAk)OMqFr zU;_r*r8V5<>V7`6R^fe=N7Hq5H(=0jc@G{14cq)H6aGKjCMbIY9a$^O*etcWi)mdk z#-;2%peLjE{KJm=5)DB7skSgW!Q3VVzxTxqwP|iqlIgaEZrD~n#ktV! zTjB2sDBhzcRKE4NXN#(1_&Nc48>CYm5gquk$RRCIl)?ulkUPL{0xPkM$Gf!z>fj7% zEXn`yW$%_*rcA+(919SeoK;DH91R&@yyr|ffD%o9!K<;36JA*AmiN&vS9eQ0uoN@?IWPO0tkd zUAT*y6B*a>|(MAwwdCd{YGMuHRG@}v&et8vaC>nh`Lw6YqY^Zq@P`)}5 zkOde1O@4fr!#&8(Sjl)(UnkDc)m20IujM0g9u--bU3tf#--Q)$l4ueLx68feN3w~~ z?QkO>cKEp53x1q0(sl2o^|LfsB0CSpF)&(Z)N0M6E7vA=T{Hf|nzCLXGg)pUzKdGM zmn?Pd(6Mz{6*1^LF4fvvofn=@h#xO!G;a=Gw~8lALoHb?H#-#(HWdL;7X;{Rmn(aI z?nW?pjUQg3o2>9JjNPO>Nl;qA`rU(h-6zQ)=_wON*s8_iTWuzK44IMdO?`fp74B>UcqLLV*H_It&meN-n0dvZsj`YE>U)@{x>QH=pPh*X5T2YcAu8JwNfaJT2Iia!t7D{@+Z=NLsXQxG>uXnYox)b^~Il%v%9qgClx*Iy;VtJkd zu^nEIkO_+1HXa3z1P4Cwc4LO!xP%IGc7CpPZwgiz8qhMH3d$t=w&VRy)_JK}8|$x! z#R5aEk))(7Ype~B3-rKUr>5I@?d|@UIb#nRd>DBbm;ZL3Ro>}z0MJ^RJ-FmWR=3I?(TraNQ=DK>L4{c;$ z#IIhj-_3?xNw&%RgMMP~-*I+N;Z8*oFucKY!olv2sJ_b8&XzLK)m-6w-WzT##k2$z zW7bi!3RT_hE=(7y}rHu9Ph03K4$Ft%l@Q8&fyxEhr8dwZ68QAE`+sU<&4w z@jUUvF;I+pK;!$e2~-LxmP7WE!bO_Vhg@`%+!nt0*)iN1w~f;g6r(Y$Ju{sa7NG)6 z5o$$b*>dvIn`D}**$V(Pp|4T}N&5nfyDSz&ix@+las^a_>;%9$@tbgwBSH8cs&>a5 z$O^6?Fd{nPPo_xwD~bw&Xx|Xin0tX|(tu!C34!8g6}>>n#t0K;v*QD*qvg%W1Lseq zaEZu??V}(!Riltze2VNN-FsCZT{pe% zn}tB6)X*k8B&>~c>CDrIy7QWWYo--*a1ytbpD!LCV z#HXdksd#YHu9N+6C5|XwHw*Rse8vx!@zhb3?RkHR@iCZh16K`V@Av|Ri00176#t_5sGC+Z4XcY1@0)Mz9np8`FTo~LiX*VB?3H-A z2T6MLbmgBYgp{~|60h$Y7g(|_*I}{g<0G@qW#}Rm^lZ$c)$Gq72Nx%T;C?=P1UL&q zb_LS>vu2eq7C$cd22^SW4=IE zB|}#w#SsmoX;5k#UEzo7(}ysI5)6bQ?t{97qgHn~28{7TSkcKZ#t9LO+wOcle~7s& zWA;m}b^1Ylc)r*_CCs5Heni5`dLF4*c>eg)u_5d$z$tKKES?z2Tt=ukI(Q%$hJ@~_ zahu-3eLVkUhjWt_;mlenPElzT1@s_lpzUGC8jzn=^gM1Fr2xBCtF4qxj8AKhfSpLG!B7Sfl=*FyZo zcBap1cR@KufcRCB_KZgX?w@ zZC{9djd=qgn{aQEX>nvHcY^}4p9%j;k)kcY_2b^*o2o^#Q$)8Lw(hUfP14}e^*dEI zB*d=l0UJM?N`pNge0|JRnt3XE|9u9u63jxKbY~QSl)%jvagGQXgCkexPY*PcF9M7j z_B2}bSN*jM@AsHm{My|r^(#{Q%}o1{$-_D1D^j}?>Vp>uGRFShldxvkZwhP??YyMs z^qz>MWbe1+ZYcDJ>>VKp5;$+8y5u+Hm(-1XXFl`eSBcz{Qq^x6@Kb{THt5NFcqiYkotiWn=) zS05Aq;4ZrWAe2C6DF=z64mJ9E=H_pB)Z|p$NPwu*<~{0MwW0iz2$BYj%{97V)LQfw zC{u~i%YBjAbJ)oPE#1|1E`HxP{HnKba7ejOX=Qw;soqy_nHXWk66@$Z@DLp#NZ#nX z%VRk`aWD+^jd~Ohy!2Zy`+Uk3=VNrd#+e(|rk|1`ud3T|``)>+LY>(!I!!IQ!)AX0 zSi8|WV*1^aDvO}U;5%4W&TKOpRam84&;4FEgC~3`F2=goF(?m9+wB8__|!~WLn@NC zmama&$(uFoYH2Xe%l>+W;t&#rcyAK8zeK7+f7)w)gKfrl+_Tg2FyYi2#L-k5(?k0r ztF{Gwd*x+}@bs3+A`n#`eSL7l5)~; z8YUOVAI;yZ=oY?{#+xa1%R#o9x$S!CN#vC5I!SS764gh|`32sCaDUQjbEWz`qHvG= z)N_O|5T%B3wU0-C#j?y+LWlZ^MjJ1mb0R%8|IG^Ty6xt~H6Q5mEz0eAiV7?NzxHS3 zbuL0@eKX}=7&|&RsQnX~UIXzrBL9zP^TXC`Jq`_JUkLj4Ue+_SWTiAj?J_jwU&=9l zB##C0nC8ChjtM|sAxLZ#D)*)2Fng$-NMHXdUFAlm z?XcO(U@ucISgeX+jDh~p{Ln#h3stoQqRzGYQTf`-Uft^s8t*G#mG3ekf7qs@g5OG4 z4WB&`&kOVxUgBI^f0&3YC+aV+%}E|@6w0Wz41u`=sb%OJB$Co;qO|WmMG8na>OGWs zI`4W!S$@LN=Is>32D0CVpIR{>+!tU3&~B^k)_mb)Z?;!~!D8J&#;(0S*y@F|e_-JP z`soc)f?KqI4KewY1W&&kR-?j7uPIUmqAvtuT(3CWD+hW|0BhYw5G5rc-!Ne=-)88! zADa}K{)wc1mkWi2y~Ou6_5h5ZAWe~?pW))1lj6@fgTi>M2ytgwFNrUKjwmqD$!Lj5 z1!nyc$7vZyIUOAh9u~?VX zsN(!KC4q6hi#uYYaCIY$He)wy?D|^w!%=adn})8 z%5T_HqgUoDdg}*g6qPdg_a=)5qe5(30vYf6l>uX#%lGv2!kZIsE|gBB6K0sZv@}V1 zN3-d|ZWxZTDK{Ri(Zo&a)LI-kh}w&d2@#WjtM~KfrtIW++fE?~#ULV)v{!L{0N%G# zL${g#gnF&)$`{ZeMV!K#N0P|eH|CYG>6ThWQ@M$W9}=7wF}EDfR%u&7r22!h z`OH&}DWE@$1$^+yRzqK{%5RFo0K-iLI`WhZvgKNn113|<9{>QTvJ=!bi9_|#9R=%~ z`qGj&QS>l_W|DCY{Zu^r$lfgAGbqC0en*}a<%cxw;M735O4PpG;sOx)Y?zps%)%wr z5pSKXdHxPrm}E~%+$3;RV`wW@g|b5-s88n#$1$@v#GC-}nUvBzvEWJa$-Z2A8m7@V z4P`P^jFQQipue8>9z!%}j;)>rfZ5cm%JJcal_g6xNG{vhwS>I-%BI|MTW}fnbKh9r zUbUDO@4pJ}4_8vgNemxkj(Zy-&B7SVH=i9>%XN!WRsgLGgCtLTHD{7N>>3?eC8D+T zNHnsO?_lkd%+HW_ql!#%_^Sm*27gCIzE&r+IUMWKY?>+H;I9|Kup~$8dlG7U)+%K- zlsQFTSu(8>{`7Uf0MkabIlye>OnK5v2PqGqsYnFeledG?G@qE1)(~(a&D&R5>|sGH z+;kpn$y&TH@cYI_1I7%h1X;RvG6GbE(g7Nq4&OBl^;Jqc}^T4(*COLE?If2WCpADh+#lP#ep2CB)K@_V&fR?>>wOo zq)5@mipetYDliw5+nS>65G^1RbJ9aEu5~ZZC_AY#4kNuQ5$YD8v{&}CkXOZfm>k*} za^F^pc9~GWCg0rm;Jb07NE+382=_`K*DVr=^jg`t#6I)x4e5adZ+7d@jP@5@OgH$bbu$Xj=kh=LG0|u!HsK$GR=^`bi0U2}(q?Ri!_CXDpd$ zJZB;2B4&}-((Dd(^K?X=__Y1mH)8iTLVC(yN#2y{lmR&0wE;n4&5RO@@AYCMY5p~r>^8egN+GE-N}UAku4$ccCRq4E18L=gyeqVcWY5FeTPax@EzdaSb~ zsSc*-q0?p9NX`RwPE$Lg;Ovlgpi(K*0P~s($$RCfC-fMY4%rtQ#Sxb1iyT6jf=|2* z8}WVYRE}AzbjJ?8)Nl7fr{rN4%>gZ9?W;xVP<}Ta%OWuhkON`Fi6OJ%Pp4D&b3lXrio7+_A@$p8{!KkJqa=ndXMea(pq5TMm)8iA-i- z)k)lC=z2Rj7nt0V(mG;Vk!kE;nZWRPAq**?KTsCh)NU=MHfF(vIG=hvnXg33=mt~n zHcvd=82I+RKENmqk2-7fAV+M2Fk^FLW+{SC6e9|=pZGJ(*IsVLr>OZ@Bvht>NBLFw z!sbPPd$_vb)WA81ODFp3ajyd`lH1&S3x9Z=QGb6e*QI-h4kp=)|B;}F-W8XK?R|;# zSq<$i>kX61A?YoATDxVA%AZ4WGB$uRRaVU?WeSNnpI&7tR9Il^jid4n zOkagOv@Bij<3SCeTJ|I+A!&UkXNOb$pFXTxemTdL+^@sO6W?Z- zexF+PQ&Cc9u8e%f&#jhw@k4*&X>opPDK@{FjaN9AD=ls*&afvKChB>=TC>P9+Sde5 z#(G{RU|HO&x&~k!fu6o` zG1mrH7a@c`5}_+7ii|X*%@}TL3eS4(33KWcW}sEyn;HlhCPvAT7 z?xOkQd(6)M*n#083`9)Oj~zb@yaQ~V&Z^eY&Fv-ai2)q6kUD?+ZK23#200D16+ zC}n!!7X;{G0Ud$$jP?y%U?YLFDyMOoeR_WH6=UUfelRLpf{R1v5R+3Gc~7fH@S?ua zfu~wnp&`6@&5F)k`OSsDU&7U2=o0XJy)4lWxsPak$&iGV2n%uSc zJdPfQukZ{_>gke@_%8u|C?(#!VY)cIyz1v!|D^2V|KKP9s&1mxCT!_q(GB^;D}y}@ zs?Fk>x(;gGkJqU6Uc#E(o)~8|79XR#Y4D%FM;wT%eG)5jpzM@R>x{8vt~4&~?){ic zxXO9;D(?9o{%^?WhR`+;0Dvd>U;N+y^i2HW7hCCBn;9E9IMUi18c!umSqIP~1YUCo z8BMS9(}9B8laIHV=k&4!tAf(qr6X;3M<@J{FDyULSv;r~W!i~u%(&FU9*w9k?Neel zpdc9+H*Gqj;_RSpE85^(Uu~wsOUJo24=Bo^YcTNAr{k@a0 zZ;9Pb9A>yN8UhkGO3>`#&=ZqmdLPnQMc(luH`eVDpC@9Avn;nBO0QX=E2J4e&W}F| zQhGelF#x+=R%(bT`IkILU*6^Zh;G+HKt8wb4OR5a>UJnh@saj!SM8N%z>a5tyiI+Y9A(8QC^d;drUYJ4Fjp0TmeN_KG8ZZ zDAhUym9JY%IZEIbx<84C+3-lt!L1&+T?bz~HL;AW5m=cP&-iuLVqbHu zguir{agNvaQ$2sJFrJJ7 zc+6sJdGzDZE?(_%vC(|Re*7zwQLQ4m=7W(K)#mG4FkF`&fU&2W`>qdyeQ_X+Co;EA zSYL__^1;nx>Qg-oujpsRZ(MWw1x6Pa+Sd479=Wl&ozgcv4$OzWO*HJt1g_dGJF`Zi zRZv=Y9jbMLZyX%i(%n#LtOB&zO#6E?c|M#h5I33wtSYJzW3S@kKJDfJB}ha%(;1*QowOk1C`lfBZ74bu4AG zqDi;OV!N=%*7$7ck+ojM=nQ)BvtrE|rrDGMW=@D_vP2jn?PB0H%7C1nw|Wme=n#n~c~bCHjL=;Uwut?AkF5DhccKzs;E% zp6A_cuhsP#vk}0z1Kj9H1yWoA-O}z`IO89GnJyAuc0bV9N-fE52R_5n#XmUrdxRw0 zXKw*%I8}2VO)rcR1#q{e-w%*hYOM9wOFC5;q*e(m;YLPX*~)3H6Kz;NL{)1= zNh#ZC`nOGL$aYKN>h$L!kB(Bf%5$cGGK$HO+p<6$l}AFF5k<@5MP~RT*oOrw0lD7! z_Ry!qAlmc$<3PNLcgl>iFc2HN(1l|oE?BT|$`9Oio+B;uBbw?CaJK?Zg06vy3Dg6D;Ng*2;T0%ChVG3m9LV*@0N7K-OMrcTJLq4r|UOAd2k~c{yt1ZI3+LD!t>mR zhg@9k5z!!%Ix49lT(BPW>0}_Chdte4+nI^PR~a1MX8^_K+j6fxYlmv829tOoHJZ&&9QSv;ZGjTAQ;0rSA2j~(O0E<=(u8f_)>5<6@j~;6EF8apE!`VU zhzTQ;&7gREG}3d5z6M+`)fJ>YoH^)dgcUYHG#updQkPy3^eVi+j^jKnvP>hmDxg#K za1F>0G~rd=j#C#*a^H#OYo58^xV$^lkILo34Bp7zMpSk|ho)8@R9CPEgahxkVxl>l zf)c9tPd!2-H8b#NEea-0uaJJC3*^?9noEtNs-Ei?&yX7A%o;R zHWyB!Ltl~MV4c5yFV(I1hR*Y<#9R1N9?7nr7w>Idbs52 ztlVvoWLC^VS^XBEs}BgS5PKO+dsPau(yZL%EXh+#1X*gsxxz=Wl(XhbeLPc`6CQ9P zr8Rv((v%3!X#K1URIxRNDJP^nPo>t4v(qh8%1luw&YZBlHV}mJk&eCYy%sR-`(}pz zU~37ZS`HCj;?%Nh$$=~)0=gQIRqAK4u51l)-dH=3&j4f6b$N$JI1Y&ljFPy{hVUMD;OWUK13)7Rn_pGa3UZ_}9jW6ij+M`R%!_Ty4*4~13B4Py5fX^9tq z7y!^pFi=Xj;8ly)>hUE^6k@%34M^8qD%JS#SDdXpUA0n5$T8kmEAWd{&oglmc3<64 znxooy8~5p?U|-Yjh)`{p)y#8>*E7MUL)W{Fd)LZf#wQFDPW8I5r;b9EGS~QOP}M}n zH2Tl?t=b5KE5NdX{>SVwKDLEz4TCkhmEML|+R>d4_&?K<^kzyRQ{Z8xywYkSHdaHy@1G3%SlFt>eqsri{P{2_QRA`uq!x zju_ptW$D(_whyg-1KvULJP|;~FNI_x3$M+rk0F*YtF11$SXhA_*2$ zbbB!Ve6&4W@D=`5@d1yw&58a_xAV5gk~I|?LkE0rB86Qf8(%Gyd7o>Ss5w4f;XRx$ z6^<0IDQX&)FtONE$Pj%en!@=|^i483blNflTEELF+(vO@IJ0>kP3BSzZP5L~H!sP` zKo**DDPyM8>R^M|(RlIpj8T-L%+3xg{&YdvC$9MRNVitKO7=$q^7t{Z3u79LuvK5J z&g>8dG^p|}ylv>S?pQC69XQ7&QpspCpKL_~iqQmx@p8TZg}Et9TbhQ(;8dpw^9K-v z#RkHl@^VFRg(+(Dn)p%v6dkEVDq+@l8V4XKz@|0rcro&Od(+9S?_O$s zqpqPbyc|AGjctucJIuyb#L97@0~&~^g02ZOfC{jXi#3FH*SSh;g9cziIuHoV){-#7 z8T-;&FAf~L%pPlQJd)B{r|&z)6k#d|HL9fV6YFr~4JAsraY_=dBNg-+PrH&u(6SIH zKO~$3JWa9T&tL_UG^9BM=Nmyc!36g5;hRGv|_jpD0+lIe=zwBf&bLl_O z2)2uuZ7d%-+JoEZE>dEnW-Z|0nd4HSe(I_&^-AYdcv!V*3Oes_5O#zsZ zLn{xYx>md8G$GeU=*d|q-;srhz&57v1BsfUlfZZnBoKz97(A>IEio~0T))0W#Y5|( zr#7GN8k@{{@L+Op^RLFnEYbIiC>`*rARPk&bvy!SsfFQaQF-4ByU=bWW?Zhxc%T?u zgDWIimC!#FQaGe0C*o7=R+2J*Y>apbo|$5s6(lyd${XlG0bKC3*PhBFOF-?w+vi>-Q_hemrxLR((;;9`1 zokhue7apHs&!4Rj{L~Eh+$0Z8dC)3CqyG>WQGUVqC>5lA|*jli66}5Nn90U!y82Qyxg)mNB(>~%Crm$TywK*~T4OH5PdG9y< zE&W5;?L@{akj-I$hdymV6tYcEJkHvI({fh!#Kp~9;cKZEL z9hvu(8I2zMPIo_Hf*y*bS}{*d{Kh=v>gO0g*aRiZ#NU-?% zTmSl5ZMngD>7Vbl9|E%E0wT$T%mY&VT-8}clQS&Zs!$&QCs_~Ejp;%V4YQ| zTbx7sN5q-;TgpQsb;7~y)XFC29zp#TGne0B`v#^$_cMqe0;hDeM56`vbu|e}79bPP z5-+u0BBg${C4@X`!y5cRIfAaoEkao;0h|;{$`+=bV$Mr|LC0#S59Zp-!Otz^y)7L~ zQiQ=5%o8*iv)+`>3$gJ;Wft0lC?Ydk!M`!}N{ZYg`Z!!wI9*-oIWe31=S1yFn{fyR zW?zL%3ConkU`-Cz2Ceu*oFmsJy<6!9jzc${nsQI)NnZAhZxdnabtye))1h^(Ku>IwC9q$TFb6i? zE}i}C&jS0wev}QY!18Xe!hV!>)Zut%m^X_YYQ;VuEe3~IeW(PF7cxU0{) zbR%)2(+O+Ft!uxNhL}k<&lfI2V$s(6zzpS5gG@oLQMw4GGZ@Q?+J5!0`vH|r!p>yl zP{+SgrDVcq9N?wjAb%BJ_HkhEolw&t)Oax+v&1kJt3e^u^@J02F0TI z(OE^;`Ya!xwIVi7I1)vOZIpZHfOoK!2cmZ)9Pw-USKJrLu{;UeT34o)}ygw^zIgl^PPi*>!MW_ zBdKCQC*{_kuf}XSr|@Da40o!t<)n3m*?{+I+cTDV*eYa7`q!Dl3!20B&b`+s(|vGQ zm?g{3iiP^ROMS5p5ek-XxaM(3lcJ=!7f#z_kri@yfz(=Kw#0=pGr@j7e&I2p^T;!c zdi3#~0-X>2%A!@2@Bo@?C z7}k|e0Qe-MeAlyt^Ba#3PAe8g?^e+3jxi1ZCmpy=RE3jG+`%bDqZ@N*bE};@=h|nc9Y$FiPVm8vky*|VMEkRXg{Acg=%pgka?ryrykviSPNTXt zt{6-EZ4Q;-szg`xt<_!`C(M(yyMJP!a)0jltxHQ&TQ|TWz352FDm?#qA*XmorId2X za2kT$&)p+BANkdC$|hC`3~#Ypq1gMbrM_1)?D`NDmX>Pl`PEge-DW~*@D=!EC_;xe zTvISHSqH`pwf!6?F`Y9J5rJ0WSWImkK?19Wrh-8CBbtVY-rU8-;#x=%6|0QU=OmEt z&_@Pk{7KZLc$`8~GVYLkp4ec`Q7b;pjJV(qCxV6t;OA`&fC{f`Au?o~%VNdEGY*PA zujK8ny1{k-q$Lu;$NKt<$q(Xvb}2`VG%IUcPyzn~O_^uafxR|k`RB(n-Em#jd_R0l z8Cn=&Q&QF{8RjY*4ne@r{HS}+F!1_JKzFKQ?hhGkwelWmv(ldA@t;D^f#~0E(T&3K zfVH61^%@H1H!`lOD3i%4UdTligE~vr9p0BxoX)r{YID7a>XzSo-!NX-KEeMi;;frH z7@}amn#6klzZP+*UyC>y5hVc{30YCP|1U=W6=beTl(8PbM+kc42pVc;Y*bSwmqkl* z_rC`Kfrmqa3C$tz_NqfAt1cIBPQ-ByzV32^xQ0`)D2EO##bG@R=uUu=YyV-e5}R}2 z@-6}QIyDWo2&KQjkWYC+zuf{m2H1uyFBaV|cqn0i0;oaO04n`v!-to2)sf~$)0P(% za4`srlfyr5WGOV~#NnwK)%Vg5Ey=KuQ-`dcFAzoY!?ui^R+DF2j< z`R_P?q+k9vhyR50FA_5Uf03&G1Jb`r%KUeve?7O8|A6#QiJAY6^Vd!M&usi3k(s{@ z?@Ns6ACfbF-NS!5{xfCyCt3fuA!vT3DgRIE{@=a+nW_8NXNFkkUz3~v?)lGP+P``l z=zayz{*l-Fch7&0;D3T`f7^@cKSOW-?)T4e=g;{5xAi#xm(gBM3KZmj}Fh)zygwsi&o0m#?;9SkMW;GfmX!a%E{P)R>Vr*$ymtP z(ALP9hX>Np$-!9P8qzJRT0=bcvJIi*ThSC1O}OVb|#HZmC2lzIjzuxE(6X$Nlqme7_gr%Ut8@{M5JW``xwl_e|UM<6YMl zSJzf>s60`VX7T*;wse@)cNPVRvBb^+Ek#fXb+s)}iZmBID<4&;(@)BYcIXj8e_9HpN=U%u zsqyLX{%&&c;SVT5_zf9{L->U~%&ajcm3ysvc=!LHQXbld>#>(C8Q5bT;h&i22FuK`#n83d_A{e1DrcPbuej zU46}K!=Kutn1x10=lw9z1$6?J%aUaafFweeHiwBOMN}iV*6wJbW1iG0h0R5Lc0sKZ zNAnX8MEX+_cFx>#&LI$T^E&iYizSK>aEgRY{#l6@WoINS49q>&ZPwYu7+%OTH2zuVYBIq%AJk{$ zu!^Fa0>o3x0Q76<7n}o)RYBzJyhO< z1>2G&R0M{;unp-QTwYhC{gI zQ-S(sEeGRVd(dn@R(Mmt&u%`E6{8N6sXL6hF21YKlashpv`_{(k)DOk*eLBP0bbdO zYW)m})`NU(VA+j915P=b9pS!VzMT2w6R!I1OrPirg&Xie{56tf>o4-WI;-I>qv9B( z9f+o>Ux{a_JilY>Sb&8mF$Rssi z0geTq4Dwu~>ct5t^>dsgoRu9SNaAFWLSP0WRDgF-+29bV!7Um97*D0`hGyDacHup# zKUBK#BY{6~as^lEmXV>n9vuR!37s}+$e%Z&VCOJfO(OQh z8=(bYDu1+^@rN#-tcK!@F>iQXq7Un8^XMyN_!K4b^drMY)g%!9G)599Yom;=%>p%o z{T3!5&-4guFY&X~GVsEFgQ(w%H~p*H=6UWkbGxmU#qa>Y3@Ot=R!mySOMRh5`A1`y zMX7DP*1e3k)C{3|C9fUIS9|r51zVW*Xe#Q{P`7kOZgtskA~RmizNk`k!*$4$7@L5m zLKiRhFR5*ATFr6Dmf-F4M$g_ER_c3aQJuOo*Hatz=bMDn)am1;VpK@Gdi=%+-gNOT zH(5`8h-k|0IN+DntbA%*uYoveN&rT7{XxP~IL7BrNHrKga;i8?tNzP)jqB@7+CfV~ zkQ%aTB#t%rQFE9!V#ka8%{((YSJQwU-A9uNGLgD$EunH)$=qML}VqLO+ z8-fWosx(nfj)AlJ*Qh;tBiTzTFL6DE=gNQX z;~2afm=#geO$6pO5h|u|w9alv_uxli$YSe)HGl~MfLmem{RON#T7s1Vm*vK<bOpG=NM|Ypc2=27)-C%MRZ=`UE;`SsG??R|jhC(N79O4ZXYi5{ z!@4IAdCsxjv_&;sm7uf=y@Pqkr924u4;OD_IGPuV+gqFZuuj4dqm|0_&1xslcRH&` zJ~0MGmuJwG&4EV`PSml0Tig0JAMPehrs&X51JfW#c3sV22wWrxKx~)ZJbT0A*1a1l z9B0@eOwl0WD0d$c4; z+?2L@zc1uD=46AiINIQbkUhd$4|wi8dDB9yK(dfM^evY+QfkoW3^g&VGATrV-%OwG zNf~YT#|K4Ll0mgv{N~(#mj_v2GAZpK(uhuyUfBYzJXn6ZjJ9nRR;vgwze>;e=HWNA zQb35K2W8T3LC&lg=`$WGyYtgYugp4UtnK8f&Wg%sZ%Z1v8(#^o1)?u zbU|esShJj}(RhYc;mj*u_At>;3L$y|TF+ey@d6%`oS$KJ@Te4g+891-h@HcUW0D+% zm4Z8+6c@9-!E`Q#MG=dY=|S!b(_v-5b!(wd{sR!|%%|9fQH?QGYN06gGe(UK8(}`B z4nVW+@JfBRF@$&$T(-z+|lbEstPfTsfr%+cW?2)ZS;3 zOh3QL4RqE?3`i%E@GS00*fXQ506h^48&S5q&R&y{ShbBzG-lLS`SbKGDt1}_{IRU# zutcGwFaJ4C?!quWhlRgfF3G3Pv9uM9=jixnj%l+Vk7-6PrjqV{YV}%e1pvl|cC%{1 zi$ED{UV#jqH(iggD;`jFJ*PQw7@Z1-mdus&J*v9C8lKMZ>ziS|NH!3A@hHr zKg<6E`ZsHi+fcP5bX`(@SmkYQMJR)S!z7Y#}4__%-U3b*}MDWy8Arsn?MKc((!|48;C|1&>7eY`iJy`E9L zY-yxL&jRHs6fi1$sP2M(PGXXBO{@=?_@g-#%pH zK=68CN|*}%&5g(HS9*H5qio1Qpmh~AiR<)z8lI*~&fS68*#3J4#X9TZ3c5L=MA0d^ zt~W*9!)ApqBc(}aBOKyGT>V+9;1kUBcHOg(U>+A8C<4_*gB{E0wC-Jg{dHn>`s)k# z>sxm%^x6LXkvxQ35MRJ}Krt9Gh{w8e3wTut>@e7Heh#pz2gY0A5SFA3-}$XGiWws* zt#&_q8GNU2K7Vx8#{7D+_t=KVDm2kG5Aez6{FDXen?RV(r`sO40#E%~X!csxgOX zR!!3NCHyX8M+LOoBLxjiA?t|-9;>@-1o9ap0(6Qf*(Rb4Lur)gxQM~mPiIa7-SKer zAa=+@t$t-{!{jtBM+0)a7+UJ`L2k1~+rypkP~?v#QEe^nC#`<yuA13qCqv;eMyVhYk%AUR8 zxO9{@SDB1sV}}K_H^}j&zp*V>W>eN$FEDB8@2izzx_>f^GTow^P|g zmtWoB&2=rRw`y)m{D=;C_y!0KPX?`4R{6+Y-+-_L`DFTCT~#J(ba94jHt-qn$B5{q!im_VA+u1l!E7ZZ1JbX-$*+KZxhiLaeb`@(~+NQ z_*?Zqmk%;6x6L-nKt58Xjr>D@gJumv3E3_aQ6>e(_SqMxnWT`p6u|Izwj^IZW0c6o z%SZGD%xt%aISVTpuwB5h-|*XYuOnexd+KPzkA@?U#hCtARdeUe*PuXHEV)K&Bdw)5h9IsYSNz&!dd`^syg$Yv?a z!mWzq0~Dq-bM0m@*v|l2YfX;Dfs+_sn-wm|v7i>ou&iQu!`*Aj^QCidhBPduU1rjS z>U@EJCk?$2(5OpyG}ME1N{g60b_?akCG_7f%w;Hn8#@9MgflM~zLBS16Z=MP)kz zI904Gh(y6dm^TxRzTJ;G<~@(kncZf^EkuE+%7Y){+0bQK<&p;TTYoRGRMAI}=@(VP zeLF6jl{nBv=*PS}UKqZrrx8%!TFMF zsXg7G_kEh4CT)0Q<|tBFe@HUfsmaSohVLYEcnYTCxiq8gZgA7$pU=J z0LnZqBul0lj-fVH_eEC5{YXt=o&~-k-N3hK73ym6L3IKs5`Q!%BU~&4ULhV4*q-{d zV%CeSt6w?Ll*>U8?Gi}I(G2SV8oVlqRv|X@m!xdIv3PoxU?T53Xh6vjwrJmHT_s)3 z8)nF?^{t*p7~g(w;y5yj{fuHdA2r#=Sx3c#!0zu-F_mejwcGg)pnN9fG^FQ*X~<$? zykA({4jZBMjDy6;)fPM-ewcc1TcJLdbl@|mKOzXsQ z?Fw8>!xrx^cM+DwF6b-hXe&hqO2|=JtrjktYQa*f18%z!XrSVO#pG$m{#G=>O5f+( zkPqoKFlH*P?uKD&$3TxmAx?ri&*SgT_Y`2Ok|uVpx(dZ9WwVVcYR(k!H^&SD{~3ft zJwJIegiX|FRGEac{vXHByR4>0hd~&>c8J@$r}CI0t%JWwY$Qs$T(c@Jwn0x=+Fp~Q z`^#o(dX8+I8=Ha(0Qg8`@bGk-4Vc`fNNeTx^4;q!; zp&OO?S?oBR?cC291_HzJv4jaM>mtSqmieMJWgMSvf$VF_0?+3iGWtf9Sco_)sAd+J zQ!DBtj$q={G01xKhpL$&=ojn@XohK(`0U64^3#rD*^8Jmccew!iwZ)W(;o%NN|djV zXLBz%jxw#~;EchYx(aa!q>t-8f*{0#cs8Hmm^LXI8`>;T^@cri*A5r-dMS*;R|y0s0-Ero2kF=URj8~ivPp6Y&@ILC|g8@9R z_Xe8#rN3RKYISOO}Dp+eS>ph;0WO|^AG#PO8Q24jZ#N}P1&BUIO zkCkzgO9}dGQwbZoUhH*6b>#ioO-ATViJ3DMrm^kqY_}U{EQJ*{~vB#rFo*bcbCA>2!$nxS~UrqGxR63FXJ61=oMU6$vSw%@AFqQ2Q)eh{HMJ2hVqt=iHH zV=i;{WbJpn9pC#8zE5AZoBxfN{A(Ql$_OJpBjf)T6Sn^l6ZZcHFaN%%|{dvPU}0+Yy`*I#V{A}>WdC5Z3c-NqDV zKb}4pePtnk+^uCUZDe}CAD&uyvwjkJluLJi-k)CXo7mc%ls_LmKHlD+MmyQfy+7VY zV`(?EXFi70B0cF_C)c~mo*iCjC)6GNbq7!n2Z>iQS|h#fZ{GtZWpRD~S)6ly$Kez& z7B!}?9KkV4_^u*vLc-pi=cUC0 zWvUx14(xttGdqfYjL;W{%zX+dSfoDEm& zcs^K%e6Xrs*u7`}8OzjGZn`NFZJSuK@&)kj$LI(b8UZELZoC-@HZ&Pcxf1C?=c$GO z=Ou}Y+RxFc1USgC%**FM$xO**sT@SXItrE)7sx4fIjoqOok6dKNh7F>5h~ki7KWTK zxBZMLd`L2HUCrZ2)-hiyU9f4f2RC@v!WXtROl{haGRcX;`1Np=napuISj&YcmpY43 zU6jDmgG8)q$Z1e_-%+-$2@_#vJA5HQLS2F-m?o?8gTWgy4%PgMfx@DX&y5OkwQKvr>MKsNfjH81CwR#dzM6^mqr4UCznt* z54jU5=kd+j)BpXDQ=0}56j&lQ#oftd5=ltH(Zt*xHDuQZ?^flZ2XQ3cm}ix1n&!Bf zlijvmPHx5o5++GD9!4DIvymrSPb$qCTP1#Zih`CRr*1$xZc2P55w9NQ9Lrp>jq{LF zm#mKmj*kEt>-y%qXyN6y1e&5qNkEMU0XY5~@vn6Ss6yv0;2A^8gMb~8A?jLd5345 zp8E;Asu!`N(plMIK0ozYZ{xkSC5D z3L1S$G|M8MeoH5=9wVp-;vKNHbqB zd=)D+q(ez6^*N+z886%a0a)qQ_AOrJl?Xz(niPdn4XmukgfWS{La$sHV`ZlYWsTX! zrx6sGDE+LCtRTms1oF_t4JA2WizEw{L!~=^0wr48KxUSzPSn>f9dZ@#IwHLcEWl|M zcwYKU97$--aa+9iD*VXb^!r*?s}sy;f|z)Iz_3cblivm8{ z76cjrhq#49%u1X|ZW9kru9{Gk++Cb9zRs!XQEOcgSv_}%0$0J?nOweVnhm?i`Ep>* z)?KUIyIu0aDXxy_^cHqtJh2CMkQGc{bHhgEccr=5z|&~uVUMI0d}zaOP*#05t`V02 zjBez`G5~0n?B7poUXej>B)OuT2!*XeSj0BK& z1_Ke%l%vk7Mw;}_)6sPy*6dFqa5!MwZa3&o*`0TC0AaNafq&7%$y_za6!enYYu7v@ zH4)xmbgiMpgcar1QY7IP`JqUo<-5E*Zkf!NNA$(+!76qzfa(?yZe#JtO%!!wWQZD` zxx@)`VSw3`6GanM{*KHW~pfZAn9vx9h@nrU>nII#jUN7x&iX<@|muSJ!bG%q_~VZsq$@yxGe&k%yLWeAk2GLTwl6-{`db1|vUm*SL2wl;$eA zj_RpZ(DYA7^pJqLG}F!jn`v2Ey`MNk0dwv%i_*NUQtR@OcGUfLq)~JqTvH4xOUK5> zKr?H9LCtRTq9ylY!9afN=-iYcv8?8@Z|98nyc z0}mFBTAZHEWh&SnTA#;|8e6<~A}K@J4Pz7lE=~fG+Bm>9c%~T_3l+{x8S$uzb_I&U zwbXgh^y7RRe5HDXOg2%22(JCWV219uFp+Qp%-L$jX3KOqXLA~_*y8o!Vs_x?ni{>9 z*QZTs%IfBTIrrLhYmU{p$68hsW|)yYHHWN+|nG7aq_-G>MwvfbgfH?xLT^3zWK;%@0jHE<^9+0wMwDXJ!D zQ?I6K!hBamAo^=Ag^WgRKk_@YR*OeBON4bjqrlUHi%$qEq0N>qb{VgdoYH%vi%cNP zPE|$%;CfDDcSh+wd#Z*BtcrRKtY!Iq2CpTgbu+x?^6+HG!eXj!pSFOdwLgs*ar&5?x-mufy*!UryOP9(q5JZ2*mv!7u5r$b zQ#8`>vx_-T&eayQBjhko{rYj_dM|r=4DhWRebXR0?%* z5n*nTV>6-SQw}+0gX;;2 za(qI3WK;)6mek55WNhY-60%lrVKb#rCZ9R9Q3K0_qGm555wW;QkkLQxQJJAywmrVP zCyYdqNh9G88o+@`{QeJx=nEk@ki1kJr=bEDP_;916&-a8-$PKbhEz^o!VSgLl}zBu zh}0Y)HQ08SL3oohkFmogc3K7o*WTS3p9dY>-qEmH}`m$-< zu+D6(Y^%K~!VD_zaAc@X#(JD!kp&#Hb+K%Ge&LlG^K))Z*U+u?no%d=P9-P1po|Or z)NAFmG^O!eGse2(zM*9#?gIGY?^l|tDXr?gftR7XNe38B18x1l2WSBepq!)E#$|j1 zY!U>Zk8c46Fo)h_zH?W2A%f}mH^?1iwCUha3RD_#M9qJc*kDD?Z>vzyFM^%*c>cy{ z3=Qc95HjfyzhQtrTDEIJ2qNJ>B)@FGAs@8ca13y(*U-uIhcacTK9~nKl%I)1Nbgdy z0bM}A8Ui%4p3<-DCJsWUFI9@1OypVI0xNz)5b6@ZTQs^wXticYVt(MKNK%8IQzvpD zcu!|E`^5q+$wtjg>854F1+%>-yV(tKs1Ih_=X8P0G>$3tSKiqu9C9ja2y;ATPuNVD zVV;*~@)jKzSB~DMP>l@C5LbMg)w`PPaH12kt&Nh`?wKy_uzstD#1;zXW@8elx1T+Q zF8%_rCXJT=H@fw&NBx&>F*33KZ{4D&{}11y|F;(Qe=C74Ye>c(vOxFD*50}M7Zngq zvcUftn*{OLbn(N9$l2Hi5^m*tZSd_yDhD&3lwjB{AOm%evQk_cjYSd{zRv%Acu+09 zoAtj55qHhy?fQDF`DBc$xOudtJzY)RIj9cIcoBU4uza1a;`FAQDQmY>N%xZ(NN0UxY#{0 zRESi7R2cfTCY?|N9)KSO!hj_0A|>hI>u9$XARQFa-?~_kf%3l)Smp)el5FnvI8-nu z6aedl0)d0FjAuIsrne%cP#jMtwrGTl{>HP93J7l3*L*agCR@e7GgaAO5&Io25T+sB z+G_qy%%?DL4|MdP{$5_&BoFSMWQ1s#_4sKZRVHHi5^EA4Sh_??m3fGvfs{vXp|^s{ zQbrKfmnr3#r~^|H5tU`_b_S98Nd5VqY%>~%WIsou#sC!EG1a5Rr81kLC=@#I2c}B^RYg)rs{|xC5F}vI*`N_!S+l z_aVtjED~>_k(^yQ?!#Ehf+O+Qlwo)UMs(s1zUM|{5y`m+Eg~9^!WdZvBAV?mf864v z+ICg%)rHkagZbbILB1Z&h4srm5ox@CuOly9{N8*ooMLgGzGH7fljEg$ue61sw$fv3 zdu=n9quH7 zeJq^C<(XrslFJ=-s&BLs90yrS1;w!<{Q%0-Lp18gM>4vJXK(QDFB-|itn#7hc22q_ z(V!~i=u~Rx#WB)zgpN|@LNyBWNjctTOM&ub&45bm#eH7a+J!O7U6>_*uMOu9SWU6O zLn52?ls%+)Hzyj)`dRfmV6v+MW95H8nLbRT%|!euGRYlU?>f zGvzmaKi_bk*$uG+*NUr!>w4q7P?mjz-Bgx!{Tv(WWbFQY5<}4ueJ$6#o-U7LNk3HS z(fy2O&m$J2!fv~3k9IA0$j0CZQEa>A!_eIuF-(pcExmlA0g6hw?>ej}LC8!)I%_H1myX?Gu`(!o2HLE(lJ z8P&(`ENW(fyU6*8{mMklXn&pu9VdlI1ohx-5nZQo8Jr zOv(cTmIOm9b>&lq68xxeqZvI!OI8L0vq=PgTog7L1uFix`31=*OL?KK42;E4-W(aJ zbBq+Szs`|(_A*H0zRtGXSt5$lM}D%URi}Z64nNQvruZ0b3BfyKtDlOPAU(zfHf6Gyq)N`D}Eh%n`v~qEI_+Y3oXYTXuvs z`(9&Z+PiaV0KIK*x|y5@z-(#L>_~;ys?uE*9afG=SK0i@ir%XBD=c4<#tJ(7mwYa^ z!`Jc@wx!6ggEWvDwaIPmj$d+s2*o0PDG_aIe@Wsrj~^~b*I{K^<4>*&N^D-^rHH36 zo*ozkA{v4l(5UC&9ppY;%vi#@Q^_LMdj6-iAG@^X&Wdbnjk~HzWMkV zD#~Z4X4IkP;e{jp=?@dOCBRc^|^wz1^ti3qagf)RK=w zy*kut@gc|wzkeE9G8RQ00!`Tetf{Er8+vPFBMkMM=RK(Cw7&`hVNzAQ*iNm`ozz;( z(_kLD?DxH!Wg5>crY2*`%$A6zckdP{*B@hIXn?&gQ^R^(l0fc1Z)^d7=b8Q+;Q23* z`ImSY=-K~I^M#)2KZ)nxHq8Hxc#bv3V+h+3x>r@ttoRsOjVJ*5`I3tm_BPi*-t^v% zVCc9&xl-S~!3f*9XvE7sPfVCPIwI4>K@HvS3B;TiLUVV2JmsE7EgjWZ9pBDxZGAtc z_j&D=uzfyGH%C`D56+gAtxKQpo?8cPd8hd=2UmH2ywtyxb(7jNBHguOGk$KHdOIR@ zYF-L>YeoNx4A72f#oiRCcqH%edSIyXaJfE!aJzCrNIMsn(>x^YK<1jMh8X#arp-Js zVrUOAP>vhS=>JhUWqmXD!TI%+ZCuo_%wVnFKWrrnjVdbEPOwSN4%>RloS__p&M~!n zX+)GJiNV+pk0N=8;cmc{uc5YF-UA}JG-fl+x>jtz)a~*4eR=cv)HjmorH;lw?C2ty zL@tH`o)9_SU_2+e?Fw6{DL&NLh(e9D5RZ^{956Am5A8sbG{KA%8NwT`qq6nE9a@mD zBUsS&^-?^VJp46(${vxL+T=D-;c97F{u{Y*^6BN%RH71x{7{T7HhQC;Yoe zaaxxFv)j)3UAN*T_BI}bxB&NZD;h?W1C&EtzT<9@BW*|Gj=Q%+t7w?CeScB16!iv5<$=jSP2wFfb*>drT_-s>I1(}4N`cD zqYB8@8v5Q~963&zr=2D&o5bW7qM=)rz2jVhQfpv(#7~SWfl-X=3^FnP&-W7SBUtE% zX>qC5mFg2ObHzRBVh9ZYuH}mGkOXkQ<~o})i`3pA9F_!mJ1;M6Ze86!&3dKcauyTp z(IV{D6n8u=f_->(j@Nf71m?T)6ppt6J3UkEy43u!{l1o|#)22z=sbw?oS~{e7WD?= zSVrHo;7RKM#B2pZj!>4~7VIN)UcrJ!Xh542A_RF|lbw?LMqLP=f0d?n%AM^e3vUW% z%{r9_a-&u%OMR~`P(*)eey(6_Oz-`D?p&_;IoD%gE#ANh%KLJeEy=;h0xwvU=Ni^v zSGPn_E%fdWDqpa>8|oN6CE-De?&Z6>!NRTxK)}=FPqIi3)Ar}e=aw0LU55z82!>-> zMzH+rUeB}?l_+g&puqV|L;~akP?z)*-GmF$3rDNcSX#&4wwzop=PD_rv>4N z!7jtIver%XX+j#pY5aie5&q$k86zy4EFnUeB@+j9EZP2O^I*p#3^wbkq)D!LXs6xR z_Da~GqknB2%6e5A-AQok2^p8@q5@e$Ow^e5sDY~M8s}QQy00ro-)@kyI`x`s z*#97n$i! z9>Hc1U{wyomMeY}j?rO;+|}=?A?w?UFm-kq)S!C4bI|S-HRlZdozW3f)nnxOK+K^* zV7PX*3;i=idA)&L`2m=v$|{RaSHy_%pBcHh-WVrzkbYR?xcPOn7>Hg8MNh@9Oiu9E=aOl$#rH@FUq zEwt=J6Qe4uqfyF#*%0s`%TQ7j{A#rcp2&sWg540ZU5-_P5AGR#P6iZ5!(7^U?@&EY zeYZKwu>sB)xJjjM@~KLaoS)-i>foKdP$ss^p`_TxIcH&biP>87;@}~~P)c1DIx8c1s z)ES(L5y4^Z`d7LyH`+{)E2Fw-TRK{OX0_?ZM>=&@G&x%l@Gpq5o9KP?qks?mF) zCY}U$Sf-IoShG6BP|#&|)ycxfkq3f{y2?z;s;)!*+dJ66s|pD~Ia<-#@-%~8I3byA zoTwR`S1L|(?_PkHkAzuviA*qnT8jaQ93@gsZBhD%(t#o4No7p~8^=}`vZbS#5(Imz zt)8B=Bf2wFo`B=g1m-rC9j!#|b8F^%ob60PlC8IXNN=yf@`sL`$bzAo6lFG3cHQve z*}WM)#rz}1?>s+i0Dk4s{*4M$@$t)KCxfbU*rGb&Rq=F&#yy2gb!FZQn(uK<5j6T$ zw^V6ey;i1kPlk6L)o44>O#v--a=;H0zEO`F3g;VGzcqUi)(f5IA{qbST_x+-RG9}f zvx97wcl5W%6es1@^GZsvzHKjdL2JrZeIc4*1ed_xKd4y?PN81Aizr#}Q|0u}CZCBH zN(rbADeU7L@Wb3vt$Yt6s9`NvsKx&3NyIcgx;X<|oTEF>?JzliU^C?jb1NwBO}PqU z85EcMp4r!TzJ*$8N+0`3hRCy9QEDEGF9Y9(kAU*$KU=}JPevnGUcqu7O)5q@ErC8z zkSdtZR;tqKs_U>D%GzS2pxNd0$PoQ=5J%Th?oxy-Y#RXCBZ~QP2+*pKB|o6 z@nMxbGgjjNlwPQ)Z<1SX8Wnox)vcyud%NkcCNYGsVDtPM#5!kKBX`kd#a-*v9jkoE z1(=dl)>QI6hXjeRq7sSSe=Y-Y|Je%hc#ii-=a`>Tvw@NsgRo$~j0!MLW58sGTvE?& z7y#%dz~}6EfRe~q_kfCy{H}YR=MWdB{nR$H2@+NB93d+r0LI z@KRdtzGHvgVw_ATW)vr(6t@A=kB=Jy;O8?EU_lf>O!UhuNH-!ON;4`+hX@PP(|lEE zQu(vgU}jHlA(TTGwnJx~FwEE)NOUwGY<2d`U z+v*q)($V2P7NJ5Iur-aH)Ay3uVk->Z9Rwd^t%IR372Np)cZRjRvNQIFZcj@m->{bTIWp{vG7*n_PhL3ecC%iKpX% zO&}5;7tZM&2-V8sBG48D|8RRQbpL=?#SG)ERmjMKE@NuSpn2U8Yi6y@O;x}^!9n68 ztmz4wn9-8xgZDd7^G;|h;3cn@&l7Yb;zJBHVb8)}=VvDdQ;s%@wSS88xY7=|i}R>6 z?$2T6Q7T|i5NQF)6R?@_r0YJn8LJ1bif;Jo-1?Ijk0#zh9Z%uxf$X`GIfVwE>lot^ z_CxXmY}!0^>zHZ*j+;!fPP}cZ>e%NovKe40wCa84qCd-l1Q()p63YQ%R+MB>>ilBB zPr4gnj*M;*ZGqq!d`)or<~&3A{_a8VK35mOt)Fsk{;VCSmvjorVeEab|H1Paa)Td3 z6GHWH{arIyH?&i4>`zDl1D~+vIleg;cTkS-ZT@OLZa%SEfGI{gA6)-4c?TFZ;CNRk zvBwy^?NpLx(X2e}PkfW~2UhG5qEiHU(C>x+pw))qz(&#H6*q<=uR=X5m)M%Y$xGGKaHI zKjIf_T;EEh_fl$V5;f1X;rFA+4B-;)&ENw8V}1T)o;fE(J-lFFasOq9eZje!j&(HQ zXk^Szr)Ic-OIZ4OU3M1vJoUX~h+4GjI)1~2q1MAhNeYA9vJU|>mB`!J3 zIQzu)G{p$FuhOMst-M-^xist+_!Y9T`bGPA0ZpUk1*yO;08^n70ZBmC6lTu47i?P2pgPQGRE=8w4a= z@sXcW1nhh4jP9KE+wAOhfa=O8oG$e!?w~x7LybjoFN0({sboIgiC{8xlDa0cD6%Z{ zIswOk+pbtHT)uJKsstsFnO}k++=0P4{a~6r1?dvaoC=-rpI^`t#wzz0f$}({F%YKR zINK>!l{$?4=2VKW3CU5?zT#M5~W!R-1OrylnlRMGSdNm|?^AcA}vDkuT;N><)FT zDytDSFxRepqz8$WM$U+nhL7q{n!zgj$|cGt@SQo<2D1IlxHW@Q2$mT7Gj#!e(-F-A zG=;|{>n9*a`CPd>hm0ku)3$l5ca{s7BZ4KY)9zZN=|q1cBgc$KTePccl1BDXi|sEa3}5$P zX!S!s$hntTbtY0SgaiaxaeoQ30eILzx377C9TdD064e(NjZl45T(~oz{1kogK3z^f z>d&VV;w^;A$6wqG9ahvvH@o-FKC|i-_eEXhp1nsuXNJn>zB|7Q83!);E9vM@k?n>VmztO8BbkC=t)dg1Kt{*N|yVO`X( z^qRQ*R?-gt2Vw6V@T^rc{82gJJQc$Y3#EEBww#(M}ygILu%3EU;5IT`B} z$UK0!W|*>l)|Jn5LNyok-h0Kp#wn1;`G!7U_e;&Zk77^9u(eGFXR!9;7oaxq0n?9*Y=D#-U{!GXFK2+v`n8F0Z)3l<)8%b!*slD9KB__UCctvCiU=F%Bs=pEnHt z;@+Y!^y@#%Zq9g%aQ(uHvqfi?(4d+d3xZ{o5P_+rD3zYvvECoyL7kTLmOIAq!a8*Up|g+o)GWI=mSyHpqvCui4_c;lYimU(~xQZ;*@kE)7CLi z8@wotL>fZublg|itLyVzLfa4aT<)jjbvIt+7w+6K)d%qtds4!!eU1rN5fBRgDd5;= znDptd;z4~lf}ADMyQga}PKqj>IAhZ^Jx+_u| zf2-!Gedz&!K4#r@qsmR zX&d|LcMJB+?-*>2lDgS;?$6Qan%`NhuEj4VTo~fNJs@UX&E#8yf+ZiQSQ2=xQU}h! zglQX_*>8hjXP@-qHkWnGZ@TXTI33_-J)h3=ApBSvUxlGy%y7TDD;rI&(Tx=uN2MBaOaD5`k%`%SqujP=v#X zorLd%Oii5ewmP6OWIScg>9Ja6_NdPQy-p7OB~ovRI+=_uQdK3mB{Rx#zE>d^K)N@P z7b@+E)R)pW!4nK_T(#0o%f1zgHLj50B2CIh=}P#yW&yfyqq zKF*F1$9UU(OsLm5=_@k}P4eQ;KqtPz`qgMbc%qEyAOI@ytQUfn) z$Cv*MV+O*aocf37V(B_&^@U_{Ct1=|s0^9T^dqtXjHGz(Lia5 zsdPTKh8Yi+@ID$P75!y_>JpCWXWXXU_XMJVc882!34tQvu#*D@$3FIX%oL7}-&s6G ztFEKCW5lm4KZ;y9u(1Qnb5_o_>dWh8@086OlYDk;Z+7sWV8qGiG#khbjIwTWE*G^*w45r4B+Ymth1&KGv+ZrSI_vh8ZHI4|`mYE364T< zZ83)M4*QgGC^b`?3&7)dM(P6xKj#iSF=>e&4g6{3P|(Fi0YB2uFknUc%;s=aU`GJj z)IVd~c=ZFIILwi;Lw05;8NF@9;g|=BfbTujj{_#_?ZQZKX-~2B#sbScOyDqG#QwrY*UK&9Tn0ev@!b`ZDqI z>n7$6u{-QC(QBIjl6uOxb?kCZn(^1qW8Isk=yC>9Iw@Md8w zt=~cGX8`OuYQ~oPG?~wr>EGWN-dwiwq2ZL6fr9G;Uw9T^JX1ND?cn(JylwNKrBBb8 zw>e}4(c*xz>Bb)lOMD{fq$W#0$B-SCG9O2s_e%}`obEtVr&y3@-s(2O4`DO{Bwu`= zSf`)E2zGFEd->K|yty|>>0b-otRF(!K~URiFrEbc=1yi_Zgtj&QNju3kOW8b+qUhp z)EaeQmRJ`@pm9>P3?(EyTh*d>i2&ESbjmTIclP(K%^T$0qx;AEQ zQmI=r4jBl_I^>Fp(on^O1w5lwVCnV)yODxvC?WK_+DklQ%_(BNls|Q^9{if`6JJDZ zWodZKd>daZ{2sE9{s_#><#Bm<*N*T39_k2oxB+CYKQh^Dt$cH5l6#-hcw5Vya(02J z0eC(r2VxMJhXSk_2YE44Y@;KZzNyzNsLiXEQ&I}J37Lt>=}4FtV112RhCyXHjm208 z=wre@V?M=udmG-HHT)0E6VX{xEP5FS1lW|<6B!35xU`L}$XR?h?)9=h#b|MgUO2m! zs)F`>TjgKia||lGV=gi6>Yv?kS!YSzg&G?8%7VUnJt%btFBe?TNWJ&#KIdaw9IYa9 zept_X(q|AaT*IJXWYc5>^o^D%jk{RJ8w6fm(FDnfx@a46&qvBbC>gds_D05O=qzOnS3dPb95fRc_RJ6>r&|BP&fJHi0=_!<{M5Ezn zWIo`$AjlN{TK6EO%2d1edoAjwLL0f4w>rIN(I*z0k;7Vd;4+ZSiq{@3jpXKD80;ZKS?ZrE4J~`merj*Ck`{AA7iXb9dXVA58ckY4R5~P`zN!>3C7pE4wvwv z9}Qk&?@KRcj39AlY&%zY>sd;49A-Z@)1h03Tc?$+p*10Fryw{?y)_!u#0GG!{kj)CBg!CbRMVZKd+Q$q`Uj8KTSQHEuB9c5}#M_4amqFLSdOc{6m4hiekKpGKY7I5AQ-ttG|G%#!Nj zmSSMlmsU?=KWRD9S%^83095U}K2SWE-RF0N-Cj_$_B|)|nDlw{y#a9xA!H$D?ULUl zDop34e+LMVe>356Vj@k@=*R_+NRu8RmvW^V;*!Utjb%wRNH02jbXuZJwWUZo9dVbU zoV4l1sLNU(U%*+*Rc+LqVOSP*57aL-$8Ou6CFCaNu{_oHEc$x%Su%Rzs){9;_LD#l)v za-po{aeZB1YM1^DDJNZddnBoM%N;B0S|aiI0RQ9pk$1GuHok9{FoVyRgbCHW&ZBUwI6o?GyI-Pd_mINd^Gw>g}lhb3X*}E1?tRWL9E4 z9m3aiL=X?aARx_)nKwK)LV4$OCr!zZDad!(!SzyOe<<~#z))I0v1pPxH#xHZGE$vW zV^la;;t=q8T~b#M3RkK74lVm5{-8i0x&$Ezw0}7BP%F@s4Fa>^5Y9Y(G)q#evBtyo z=R;uEyk)56D0OL5HS5)JqAT1_yG&a>sgtP(Im~oiI@lV_O`T$ijtCAMoR}C2M(bv0 zSzvA{&`^J71DFPXaE`f9!mL_WecO(K&ebocCEHcptIcAs368g)@3KohC$_17s_RoY z_>4TK-*LO+x`_nsxyX8if2Qpg-(-CmeI*^&t-^}cWYg&Wa9_9a-p{4Hb#G@>-BnBz zY>?DL(7E;{AxtXyV&p`v&`0+hOJVrs^7e%Jadwd2^c~CcgG9IYUE&FHzt5IHzbG}s z4q~Z)9PKTL(xOLHF6s;QEM71oHc?-K9->{Yt`Iwv6}f1xFzYw3McgBw*n#QowWt@= za=$aWw$N8-tE|u1+B`X|}m*FwmBlSPCfWoN)n zaOFw)gJ?PG%!Ka-+Ec1h8&|U_3*b>(GiU-jR1>MmIg> zcvgi-+F}nxQbGSM>Y>dc&l25|-U*mf#GCem{KIMWYNBz+bmPuFrEK4mZO*2W1zqA} z-y~0bmTZ_WndzWTXfN9OcZg}DuaE%NhfmK=s=-cV#vDKk`)kmxJ$CnI~=$ zSXyBlxd{OM+%^zxjoVWv^jX|9tB`CB-E-4_QSaih7234J_1qRV^TbomezUQRoa?a_ zzeT=oc@rU-$tP^bm1ODhH1adxSkE>}M|5)i#8Czq?Mr3}V5+V|g>`=K!2?K+oITMk zDeuv%IBW`($zeRZ&^~`SX+%xMxG#avEy0)l$cakP%3V#Oqr0QOo4!%c)Zx#G@(~_c zI#&6`lOp7%;-%Ac@Z{$Hi)YB`oIhtk>&w-Cxr}A&#amyaerC4+5v+C zWqmGaH>gfM92o>)R!PKnWa%#_+<(;|ppP01#TpdZJ=u&Xb6{Grijl7D#LQ0;sPqB4 zK;0if`Pl_R*i%b{OzRLRUE>Zc=<9?7anJatpOafy9qSRaqo?+-U&7MH++qzv6)$bU zsR6G|qtG&)-T@4KZCx9b{qCFo4Xvfu@o`K25cK1~?3`_GUfkqUzeH-ju91+I7D`DE zusYe4K-RB`rcW=AsPF}5*-;@-Y-S<`6NVg5-nx?W5g&n?HvLs|K8ee@9f3Id!QnlA zgv%3Yufl}741&~#re4>|3!S0D_V|*Y%_#+L%gK7r5bq4VS>Lq?6~zw-_- zbS5MCrP*t?rp({_*NuRN1K0~8;#}QRgWCE5_Byh-oNNkSP(KiHfR+!RU}o7D93h{a zf%Uepav4UEJ$wdQM{)wAM>6XMN$d)8&45sjXa^s`!i)}xu!)dB9-0vmh4fKNNkQPp zFKqN{o5eG@4=2~VLh(u5fr0G(%q?{#B%F7L9Pb_=k+J+sVO3W^ zB6{b6oCwy@8}96paVyiH0=V*NK{ir z+|}Zq^gxGivOZy_Vk)XF?=&n>Rw+eOV^lp+RZTmhDuK1Skh!?flFLzMZfmV9M0=;H zjH)_G`FoPK$w1FVE=XC8xvdb`pJO?^gr_`m(G$VO&QkBr=9Ml}O-XH`o+&IvjZGEw z2T`p&wX#*zISx(asT%qUb<4xKmG!wSumrS!O>H?t6)0ValWYJqkgBdqSAA`vx2q@u zot6JA-m0R8sxnVoxt)A0NWYc;F^~9<32++VP-UJx#@akRRkf1oN{e#)a$5S#sZ+r3 zWR10HdQ(&Ohf3K<92%?26s?kenT6^~S?yprD5=Jr5Np;&a6k`Z_#ig5xmCGv6l+KO66em4T={%)p7JlCB|~4i8h`dBx4X8dh2^imIUbnt>Q8vpb0IAJ(yQ5{EYOoxG*C}|#N*L%zdp9bc z#s#XJlzh{Dt0m5ERx?!SL?L2VXj!H7txm7~1;& z8XyqLN&-n&hP49znAw^4G`KkBj+rD;DV*BCR_=M$yh zc*s$4TTPUEWhRDkB2VUOs`pJWXTG#okfR*F{7r~(kTo#AK-J`=0x}Fb8a>opYMGTrdT;0yWW-7Y(>Y%~XVgBqWq=oM{jmXf}|I z1T(lD;yoV3qloaRo-d!C2U|n3jF1+14q*U3l7_e=pxK0$`!jt)9@KgtXceNr8O?z{ zfIWvPYiU1#T_4^Ux;eGK^Q|1$jBQ9a&i@3JmM@SLJl}bc704+Q$PRZ}#h{0<-fMVW zk?RRW28rr^$oocGB4vpnkFgv9grP6L7|0?oycA9w^C$a0nWE0Hwj2tvo`Nn5@t!r# zWYARAoIcEhlVL95j$VJ2LiH7j@&q`RfTjutI6YlSeW`piq`)Zo4eGB4U>DzJxD$Y$ z4=>JM{G_1?_p~KC?WO@8(HR1?trN6!1p1Z$*K&zyF-vk>c!&#PV||k2&>l{#o17Pc zg}CgMn2vdVSDjzO(tV!&u65X&Tkt6dmSG_}3jfw5PjEG3fPkRIUf#kWcNO|A4_I?J zVKa25_DH(IsdYpDVudxoG*u3a(?zGk1YiDGeaovR*5dNDS`ct%qvrIE$P&GJpBvwoBaGL>{WN?uk_nM zv-)`iU~)2v!F$U9^{#7aV@Qi=gT02(B|?xtE7f_%(c{4{V8Ltdz@kl>_@$$?Tu{(D z)`1K0SSt?oDhA^VJssFo7Z{3c8FZ)|^E8F>QfWWJ&Z17JrbX9?s+P;IFj%G|77^^- zpnR-4&6JSIpsJkY2PJ2L@;pcasX!V}5OFR!^eTf|{H$t+5|_&1;WVA4=FuBEp(Lk* z`FP}EMb~k)#g?y^xgW(1A0sKp5=eJ+QDhAyn*7UVpfBpgkC5maL)e$;9K66sYQ7kh}gEbP`bNKm3;ffp{&2AU)lXT@v?Y^*cc znspDjLj*oarl>owd53vX1mz_$i4!8+Paev+bZbsgp*d6C;`3)K^c8yR zmTN+B64YrGoHC~i>X@_n1PRESV$&9w`ZoTgb@|B`Rje{NQIN-K@G4xPp21HRt+=Di zssaOklPc*lrA|lySJulS#a~r{0b}fRY6W#<QFzUT(qp2*t^zj3Tb3IM*<~lH=Ka2E9_LnMO=eA_B?qM<_Y&jmV^?% z=wcJK#Qv;wx(+?EK~2LCNgea;N0k_Cc~%N zug48O$Wyvn)DN~pTGxxMtE9*qe)_XDV_%{1TQ`Kgp= z&2He3PjeT=GcTz4_tjC9kWDZ@C;t)j?19%LdGwVHth=v^7ErR1LZC$NU^8G?>~Rk_ zBEOCeA=bNAPltLH|1-nktMjR&`@502Yyb9bE`mofbVWElZJ#F0;>``(2lBB;H7lN} zj?&4cU5~1pF={+mR)|;Q0;}*T{K?T`X?|V>oFQmLDcLKe8hdf}j7y&4K%CHDK`e`=kQ}H`(ypaOf+wy5xINqIXAHPRmoH~c)iS@{30>5|4 ze*9Qpk64+0@tYj}ic!c|k5?%j|M~KV&(!GqF{qe8MenS?CT`AacDwl>Pf7v0^Wjs~Q-s}7Xt^`6S35LrO9K$Z@5xVy?-Lq@ z@ne+s2Qb^8O6KMtq?B2y@Q;Vh)*De;3z&M&#mt28n0l2&*(7>Ikba zpB@*%25_aU_Z`Q8Vw#^c?Rumyh!4ktxu{n{bj>;lb5JC5;M@0w z-YCZOyv5leDOj%E*&#nz)`(1!J5GWc-y6(Otd5hV5*`OsZ@K%V5e71zbRlFEC4f+0 zIyO&KHRaJSL-dc@y(z+Pl?lwev?WIM#JQ;n!S``(na$)0H6;>4d4p4<0fex{E@Aom&)UyG6g?bHFj`H6xr34%`vFZ+i+DR@c~glhNt zG#(1%Hw@+9`Q@@4iO6Iu2qOPqhkaW{?->r9pe9~0K}qsryY&ynS0ulEzZoXszfhUK$}c!#Zg-HR!Jgn+ppg_G#gfwI@|XcNXw}Ljo7I+@|0P7 zEfsMeAnD&DP=GKQyS^(&&n_9e&Gzt}XRZ$0mTpH|j_vkv8|Nup)kcD9;~p8i8S)E* z{Z8i>M*u8ck2tYlTVhth#R>P3`R#j#j2#H~RC5k!h^UE+l* z;EH|YQ9DI9Nv>9XLXGCl5%_I^=Uy~Lw<2+6!UUaEquiuKhCyQ@QUIVus8yS2D%-YT zN~6^o;~b`J=|%`$l4wBqTU+|?qxTmKdv}sDC;WXzdgXJ3l0v5>oh754Rny$!Pp2*| z3w-B+{ZuhL6~0E#$;tf7hFmV%9Y=4uDVEKM($+a43cEv!JW3Tn?5$s(2s^7#f?0m# z{mLVlLZ>pE^Xi>4ts*VTQxQk$a!Rq(DF9%A%g*{?Wu0hgOIT6ix|nIA zd`0H8B)yXDu6<16M3dtz&sR}hza&FhQJ=KoQ`}9Lo7yF%#YcTbNqFZDt6C!B?L%7vAMHd8DJr0J z0osRbEJe<#G~wB1y3pw?i4 zgFpcdj)<2;F3JeC2-Myj8b^2O=xt%+1F7ks8`nh7D~<3HHa^4JLUjF^HnHYO7Af^*86=41eWoOj(Hj!J$os6s zXQ+iwyE(I8g_{XGU3gXP$OP}U=HEV8(-fdO=seS1d$#%bgcaQT()fe*F~v5F4{?yQ zk8{QjIV%Xe_7*9aan}FhQqD$TGD=_n`tTK;e_cyVSP2TWM)0(PAn$@e6tZfd#!*2{zmh<8A+{EBHXZNSK7YIdP!XkIg6*yN=cw^DA%FcMCLffo#c0 zu9b1-HFCh<6Bf5=Y&@%xcr|w($xZspxYq%lr~uQFUfi%Yaay(@5h1teOD>mak9qCk z8;g+y;lx+w`DNaIPvWOb;PiiNrJPUpoT0DC^iN~wq{}>I-=|&TCci+t#Lp&FmOqrk zRf5MG`iFic>fb0CpRC=o4Sj<@Ti&fbho4$*xgB?WN5$COPCvru6&G#xRGl$=PhIgO zE(>=##TR;D{!iHaZ)*Ji8-$E1E=JD(5UC1qG5rrTMGT!x|6$<&jzvp=le4IWA>bdv zei;5g_CHW*SeiInIO%XOF%faGGyO#t6DtuX=ii-$iHMVxg@~1ni-?1Zm57y-g@}!n z{jbeQ#K!$!KF41SbNodd2PYQ{J2wjv=ij~>tZaYdIREDS9q0TDbarlbA}&@=B96aD z4zB--`FFj4v0SViL>z4F|Kec&<^79i<>da0eO4m9|3dZuT5v@FS&+&wjH>o(c9#F- znTXi`LHmCP`+s*={9ocu?419XGS~lEG{DNr%*^q>6%BMlyQ8VC3VPdbSDv4r_blc3 zR9u3IprC?)A@Kx&fe;TPsf#a>;iHg6X$ghHZ3+oF05Ru>BcUTHGLr;F9K;fl%z{kA zk@m($7P5wSKbT8|6u-{)tmJ^tJ1%SOoW5Cfb#yk7>UJK6));N zs85}@A&J}&1z(XnU8E2P9M=uqbD zP`|Xxf_@2@F^LRK?!Wf~xEAu&fA6`$e#BDORrmc-9q#6N^?_RLvXys<=$i8(R3l;2 z4Y;UDx$d&&vcpI0^Mmh8p>ZoI+i&^h{py2!;;pHory+<>-w~;JDDBx(x~ih$nji?@ z$4E>z&(GX_CP<1G`N%2hF1#%5xnaPTL9d>*J-7de2n7d&01Xctr$|y%Xl!(N@Q3X2 zDs5emn)|qPxf%F3C?xzW7K`)x%#dU|OOr(G_UBdCGd&Gu&s*sMaEh{E>jF1(MnX+F(}6;6n@6 z?#lgl$8PjIp!1*oQ}fi1MEf1g7PJE3-uR+^l2cO1ZBUmiF|?rXasAq3hF@l?W2F0N zsR@%$K~6&am+VPCp$T7u)X$ya7z4B77G3KUJkAUCuqo^E9}ohJc0%-LU>z}A;yXiA zZx+6of)F(;WI~Y(+L~UhIi#ukZ~eSJ?29WM9^TA5K?0{yF06)EoYa!_r@pmFTr{GP z`}_*cAPS;)fJPXq5{;pG$8Ajk?2{pw&X9g~krguhnb0DMX3Q(m&k2cU40WTA$e`f9 zp=tV$u>J>aZ^9Qh=j><^=X=26Wf62C~M+?SOkMR)|1IM1i`zgyIQ0DTL> zG#HdSz+fvDP7$*^DLhz2(49+_Gik<&BHK5+W-Ekz+lfpJ%?=7r_JW{}vVJTYEU@XY zc}o!bWc|r&+r5tV)Ztp`2%+uSisN0(5dPhDaX#h^dbaKlGOwAIw{9nSHOlcPQ>bSf z0NcRZ1ZsFEn>^c;Hmk!}++HXN#$G7lm|&OCEY&CV)5L;}frXxhv5~Hkp^^QUP9Ous z#w{EkIS>AqWd-OGOR~L6U+9uW&C#cOijs ze6*e8*m!+tLy$5`5_=?*iTp6eq9j<7FYkIJl8`feh%#o3kpl~cT(SEGkz}lrsFZ^z zWTv32KW*&LFSlTRk%wk-kB(|f}G^G>x2favvW;ty< z#d*|`bSR2yB>$Gp!M-_c)5u0AP0uFNl%}nn5WKrUTm04xBN|3{)G&CbcxPA1+q?L? zgh#DmK|_QNbv3)aNY*Xr7DQu+pQgK_x8h6uk<5<8me~$8vjuNk4&Q_z^(BVeMQ(B{ zZXWY@qE~A7a359kZ+&LMgB_xqTYK0Cb0Vf)q#xfy3lMnx9al>V@_z5qS?@c)zdtW5 zd)tjkGvszPw8uwdHw?y>CA9}6$xKA087Qq*83$(6Bbp|qpjb)@75VMWQ*EaF{qhC5 zer#x5Liz^UTEeuQ=lI`+%5vdLmJ?QvEz@(o;eOP=aDEWxyjjLn{*mQ zvAM%+p5bOXX0;jnFBZYM5)NdrkkwIFQJydAqYMY3*g)d;B)BAlp&~j>sI~H_G>WWr zXViGBL?2nNK@hSN_OY^U zF_Y*nI`McIiHW(V+CataiqI<1(2a-L`;5sz<7ihhB$^y$df{jMKoFvNf?os z84;_ahy_YRK_3r2-c3N0qFhU9Cf9Qr>WRn>jsy!ab#&DyLBoq^fFX>@hhgEPW9H48 zzy{D^qvZ)07-&}tYUT7*~ zi*|LK^|xed73?DL2i1rneCfhAuD>PJ*PoE&&(!#Eq}X%*iK?6N+{w5fPnZW&v-CZY z%Teo*3yV&sO?|H&@6DY+b^WLs-o(>qOy;jJf?Tv!TqBBz(RQ5lC_gLmua z#JF6@{l7l|g7kU!RayceQp6k&x~$qH><C-CD;`Pxuzk~5= zdg2mSXALsNJL-5tVauM&xxIJ<-p0OMjRcHrfIA38mvMq7{Q~ z;SNKB0>RWsI*o+xb;iHXmsp6;FYc8P%Zj=c=n-=uS<)fp3LwGbATS_Fg37)-IKUEk zgE>PP{Ar2p=JD3g^!h-hz2#&aLr1&0H;&8 z_c%g+y-AR)HRyR?NufR0&}2j_A6s$T^TCTIhDYqTq_@iH6tPtfP~{_J?0-?h_lDw0 zM|dV8O`FOP*LJTJ*DMsJ_oNnqBc}qvrBbg)3&>_30Y61_$$pu9B}-zKa_I}vbhjJL zm&%GK$dZ5;8!=u9t3S2nYc>8u&};XXm^oGU%#@o3G$+^ZL=-8kqPNf)PSgzo-ZIYn ziqSkNSUA$T<0n_mz{a-7h$k){k||;&4AI~Fw-3_ddc`sHx2SL(WFOAG)mfEcT^M2H z{8a;Cv!5L$>ut0!oz2E45tUfMBk*|EVc`K&l&N;|A13>$_BMIV(95g`$|sms-(cw0 zin+?XBin%?4ZF=&lpSPSX2T23k$fu&prjChJcy;|XM z_-0J=CJht7V6g|W3^6RlYYJnHf}|*8M-#fnt8xf)`mM|}9jxw;$wdU|Ywf__QbFhq z&s;Uw2&O;ZX&hY40)hH0om}?en5fPzASYA7A(1&X9ZM^arwJI1ro@`6rA^I8# zI)y^!iv0#E2^NRv4uD-ikDL`3T+3k2iEfheXH$nOBGMeAJ(WN2pE)PX`aDmU?E5|+ z>EkdK{2lv73V*&nd{_FZBpY*<;H@_v3;n<=B0NCd0eVDDe&q8EEkepG6R@YhCsr}@ zrCv9cLDm%sP^7CyZ-pii|L}hVJ`_z4auH(gWE$U9vgo9hZ@vJfmx5d3j*&Bjv4rKu zGFc(c5>Ffk$ugmaf=>=~D%2uhSaAFqMKTnc)5CT-|E7v7x5UtwoF15-neM_&f9pwp z5|m!W>^Ux+?B_?BjGd|U#3$v{809VUTqT?5(b3atcs2X{%j4zB#-4D2$8R9XWQE`9 z`&sGKc579#t-HIYDPr?|{crR1osD74n;iBBf?@Ata2J+7eD`{hV7zM>Xgf(W$u6Rn zjJ3VA@d2i+Z6+RnNt85H32B8D67aL%NNQScMK?O`F*24_Eb65QQc6xR6(xP?Yqp!d;>1GI*e~u&#b|8 zE)X9ls4(z@p$H72=Cc;Aff}}LRMf?~k$w@t$5Zu(gGl8|Gh&m3AC1R?YVUGGA6d;h zoL!b6FjWsNSFqY(FV|Hm4=N9;kSHf7k0+NS-_2%8A2 z3VX|a+}D@hmWET5Py;-cJ6}^@qwe;5D|)4Sp-1*dkVj-3P2}9=#A1|DJGi_ZhmA}f zP4=P*&^Kq? zYzg^Wa_}K?R!)tr^gJ$BjAdp9*PLHi^XjM{+wVN<)Vk9X)Q-?(eiH=gBE|WhjI8Un zW12W^6XBAc7R5q>WZ8PNdJ?(%aE`{pK(oB8fpZtWYtG^E>@L6>=b0KTc&@MF8teEF zZGwc%MuQ?lPe7xhj7M7+FcNz3KgJZy@|9la@VfGG+Mfo-`PTK;Gi5xvy(-Qva$x&kgVYsuS>dwZVJBB^*8K zclzmO8rY3o3>$kuV3QOPZG%T5whD}e@>{wmW*ns_J|w&rJBi#=nDMvRD)b_3<3N~T zSPs9(PVNHe>tp$_&u@vU$row~7^x<@^+7FXjETU~!j5u?8evcTRIY?G4?nzNxlxr4 z5Yl|(YdfIKmw0Vog5+*Fck;INSE3iBjd4gJk;OwP?TSPxlo@B;4hoLM1EyZWR`EmlbDu2E-7)p3IuN%MR4%p{ieX&Hc7Tm=Q?r(Gc*gM-av^V-*V~ zLK=g>gTPOt8gmO-u*P`8p)7nM^cXE{0~hVl_FK2}`mud9EPMeb{XU_e`SI8dma`Up z7EqL=4)z~sh_4GEz2&UNly*F>mopj=3VFE+tv0j4r(11(9936A%u8hyW3q+imF}Nr zbBhy8i}rn%+G?}oLD)h1yQytf_eV#MM5pz@z1`H)W?a{@8iWz6^gLPWE$;NRvM5|4 z^Q@jq{oPax!&dY8y)&DOcO5XhLz?bZbSMR*fWDw1zOuBB4gnEDOC(N3ETWhbKybn$ znH>gaS{5Z;5kAsy;A_L*c&A{vD33-^(2LLp9i2jpl4q39EAU5?hO$vP7VvZo`mxzfQN92MWljpnh5#`8l-Sc zyA59dD_8A9q?Z!f^o2r?Tx9PHx*KcnC)#txqQ9jimfN{F=>5o!wCuew@ZS2VYj$V) zrR5xtT0dNb2dxZz(VBaeFK@Zh^$7lMvAw z{wgAgr`WWn4a?@QXh_p(X{tPw9J&i>rmGwXzq*~@tej^# zUrTcHKI1?5&+14hVY1-_Xf}@pM53$5hdTBLfw;wI4@Y6O8f9}KQUPU5vJ9yeJzJ!7 zC>T??9cIKo!XC55kQia4fDRY#P!gRzi%(5A!lws@CwNPUidL#`69P$Sc*G-D$Hp5V zMyk`p97Uyg@+gX>K%cNfk(()DTG>;LzbH*2v^wb~$U7d3j_xFWVdhNArIupDPMdDg z9h{iMj2Jc4YSOHvO_qduSTNWfT~j=~dK&g}>2oSnCR}qcurRRH?PZS3)b|{}d%;`J zWWCy+m`}I8+r(sK5lF^)LLv@e6E4Lap3|uLsQBoi%j-2fV4nQ}2o89@kvuHD>=`pE zTN3VjKuvH#fs5eh8-5EL7~kY@6;v|SZmJjMN@j7<75tUkW=LAmAz6mP1Xp5JWRhRF zoAKxzvvlsdCFnyn8gSRpw}ah>-U(z<=X+v+%IxjR-~S6 zciyS;?G1aj>|b`0&yEdwsr8@?d4Y|6wC4+ zz-d3nub%U@u5WFe_Jy3AK&m+G04vC2%K(*=aX~6b15^d;OX0T13{w21Hp!E+nfsq@ zg$+Nj3A_Yp>dHsPRE-1Xk9?3qIFsZ$`H!IY{jcS=Z=CfLqK;_4NC;?8F$@*rkO~LM9=CH;bXh&;B-euiT z-COy09-o+kkK=v)(+q5d;*BC-!zltt>Qowoap)%@f$IosEH718_MEx>Z6Whap^ zAun5ygm9AdvP&P78NzMgTlE{rzmd*zY;0t{n;sw^#^0Y;HqI+_R8(YiaM=C`{45L# zOmGkEk6$CT$MAiSJNjppKD{qB3h(*0y3F@GjMCf|yYu-ApH%nwykd1UE|SorwYCw2 zmq6=Y?(H$;{5Rz0O6y3JM)k)Aitg*8w72Uf& z*fITu>W85F^K20Kl)8E62`F%i3V3=n#;*>KPH6J$L$`2d9_2BGfhjjO?dv!K8R9n=B+2ZK>M#gTkg#wM4dHa3J*y@ zDDAzVXwR^dpD03sFYc>#DSUS#v$S+yKO+_V=%$va`?FA;z#BQY8ZX|AYZgLpo&l{b z$5{YB!_)wemNo-pML%XXt(zcXNy?Ol1Y2dEsJtk83#9M-WpuNCnuuf0axaW1V1B=g zID$r!qn&c@jLP>_Smjft^Yi?IUvwmrS5F^v%Wz81$s zsgH)la#&P{l(MSVs_%D0rH8EctdgO0aT#|;cM)7di+(RsuiJI6)+4M}7}{&u>j88C z1^_RqO8Dlo4A{$5qoEf^-qbs|@k`tmlD9w~&T%lE zOq5!G>haig~PRd$lnwq46K}dj2rThv}gjje~HrQx_YrXA9r>Di(SOt~H2`6VmjG zp;YUvs;n|1Z$>Kfhr}LdTH*ru{Re;2Zq4r^9T_s6F5i1Eb#)%bXIM^I)zXbDx#*bM z4`jd{bH+3;BP|nFGR_1mWn(VRgrHE^4Lh`!JHr1S2Fumhn z^@2n`oo_ZBzdq#@ zC1An!A7wBGO_htq(?w)I5~C|0o4+eu5tSKfN2h(+J=O5XqxpAbJFPQ%)e6i~Sfa%6 z36Ii0jfuhWDv0xE^=a!dNyYJ2vjkx#dgCCGbrt&YBrII4Z`mv%o*fO>_8((AqsGt1 zcWyoCA-5jh_uKje=XSrvP0FJ0f}%@U`A>vJ_4C#vJY%9!cvqr-gcF4M3J>up(C#5J zH2?5VOc0->GOlyXvlvSbQ_^`SOO)(|da2>acoy>0bR;W=s`8 zfemJ?QnVd*_|T1}6h9`_oDV>iqZvUn9!|XWmxpDOMmzQRJ9tYGdZc<(@jJA&;=C*o zcY~Gq3*IgQha<0<|8l7fn!$J2m?<5K3StFjV{n9X<#_v)d96?9{xSh#vT&|tZJRB0 zvx#nuJRKgsV8;B<@Zo?o7gce>3gX4*vjv8;@Ik?xCu=>Hplz?~pEe&<)Ih*+gW^JQPl@79?ib%l6fn*J0oZ1 z;1B;g#L&mjz5Uqk+7{FiZZkiTg6$&fCcEpFDHMk^h!P%enJKkRl8@9o0QbkTOESGG zhi+ntsqZeq{F&K_nJzF06A1N5QPL@(6P2m|Wm65CKoy$+O~FkHV$<9Il^{YgMdn8Q7`%SP$nF&g$Z{0h{j(=5sde3Bx zV@@m2&A!X(8UGfOi$GPyB8Ab95`L2TO%gG!YWO9$`cx8T+n2wOyN?|=WecYB2mLT*m7UmZ>CmAn z@1d9)Pi1jMi2}iCr+&ogeJcLB)M`=lpKY$rJ4_)e-PQ+G$tu{^^#d2e0?0smTK(!z zGsj`wwP!b-P5fsYz6NEi0jZ9edbk~ITZHfKPY1#U)X;Ya={}~vw+vV+ErL5G zclfiztS%#KBsU(S&L$t?Z`~P}zty+)6Z&1v>I)-DR2><%Nz5S65<%)C-tX3$CnA6C zhJng}flM8g~R&~T2A2i08us*V4 zhU~lirhP>zz)SrHVw_WKpmaEZi>=b zr;`cpjBe3;N`UWEbHFq%DnXX6mCc9Sb>#L^^Y#cwZC5pH-e^Sk4aM)dQN}6lfU}f6 z2M=Cu{cB;L#}D>=5glHpW0!z2u_rIL->?BH;m^D1&wFhyT4Omr;Qd12L)ph)9nWHo z>WaxYH}yD$WSz*+#sxQW!+cbmb>&o>Me!K;G|-YIPeoq?{kAFC*S06F6MbLV3G=#d z*`GeAi{z{=$!W~#Ni7O|IUnh%$d6jH8`4m%1 zVPFe?zG-4e)J^&NH%Zu3Zd1uZu2h`p2oattFL@*$L!;-1WJ8C{`opky;N{BB-%c$< zi=2&7i>v_Z!ykYiLeEg$F8kqFw`2D+B@cT2xa!v_*rP{^JE3a-n+44y&DG&b!)Dik zu%fX=7YBQL>y_t=pT!f2Ait=br<t|-9M`x6!N zADy-%yO$~486V*%_Bgy72N^?0gFh|;RyvFc7FHf&uk`QvgUjgp9T+fhLI8?HP_?9w zh)dn3KAWC?=PWm$Zd7m3Z$3#;B7LA?0xl4=lv&JgZgq1H-900LY;Z;$X{6x#Cddo5 z>xHaWk8L??QWis8sOLFWZUDiMAPYSv7aBAcp=(~rnwb91bKdq zHM!Bh;W~0y@GwoQoL-=}C+Z+t+uRlIQ`&dx` z>fa^FeIs8K_NxWT9}d+Ztcg^q?zkBBllAOhV{ej?K@RA9?abzgi|P13ADCf;I${=; zt{J0ld+loRsc9FH9rn*5wZ;Sk2m5(yBg8a}86UrK2hSKeU-UH_b&!1z8o`hzMdC)& z!}E>oDtP(LCjjhv_;B+=iqBV$Kdja`-XFXm`Q@+U&DLeI_K{BmA%o`T;4kr`vDW-( zdd<58%7vJgva^Y!?(q3EPdY!LkSneoyIyRCl5rN?LuFnuw#I0~era9}Gvc&#VBT0# z?;ZR8xhL#694>}vph$=0M=Y^?cm*N%lhhrSl5&I+-g6bAq3r7JwmY6R))dQaaq@3f zR47?It_Q_0Q8T1z8G(9G9Ua0?`7}E^4G6{U_?1;fEg}h{-nzeG=Ag4~e3t?kVfAK7 zq*3yer>{u&%t%eXJZJ3LeVZ*NF>uCP+6tR1@6Ve%vO8)Na#qMa!&s_etJ<-tu5;mm zFJMOw)ng^H{``sXaP+Hfz5>J!)%6hDcGB2nhugNcPinb1h2@2H2Wo6!RHz`#^78k=P5Eh7G|`Kk@;vO z3KfZNa^r{e(+X~qI5^}Z@5-?}%PtU~L|16>5378E2WqR88N&R~TrOvJWsW&DvwFta zP?Q|X4y>!<_G!DuT#X!2YIQo0MsO!T?Op`lx6GSr!$rqW#BYn;U!h7M2Hg?LaP2{4 z-=>Z=akKTZ>CR$wd8Kcuy&SgqmGC z?)#~4p?kwQx%i%r(@-{FFor|qtUZ%WUh6S2XT3#l?BKVH(qSSmh|>-6*4 zL6gR)^kb^s52pQ0K-_pO_9=pKN;mr&!Ip*$nHT#GOiOx~+cXDCTEf?3BF%s*fA^<^ zWpM}fZr?(`;f+rdx(~!MDFK&oMN3 zs+qKT(y=dHXbfwNVibioYb*dfvzQ7bO*1(L z$q*ZpWpPK47Gt&~j%iz2*G&|tOdi(&wW+}_zi2$m5Be~-eVur^HEoA7e%Zm$Rm@tF z1+QxjYS{0P+?ed!mQGfST@V?Z4F`Bq^go@reVsmpZ5nE*b{)YH!?J5+vD)zhQe2Km zds;MX%-HIf{DDsR-MjZP5rmI1xGKR7Q6ifFS6;}+4UMkDi&pKBlH zr2RV6#Q+pqZEvL5@EI8|cK}-N9M!gI8#fx$(BXC=cNdx6(7thxyhx|2qB@&#*c5VNP%l~<^s#*=D!l7K=vpc?|`D6Ekn%#h@BQtWJ zX>&zOx$fbdmnUB~d<{WmfIIH?%ziEr0p2ldl+M-(XJD*v%O;zoVmq#u}P|*oQJ0s71TN9uUhz$x=4B-qWEe_ zmvp7rzv0lwLLXh#5mzvq<>!R=X%w?YSn(1)7&>A zSS0*;b?&Ep%ea2o4;of~#Xs#OP2WEQQ2B>lo-CFt(xLM>Oa3PE=Y+1n(dzM@dwaSS9J+Pe+C`e3!vsa~Pl^Q_|P`yyl`?$0n-h#bGn6P7^ec++-^ixDAk{qb|8VDM%1 z$f0M-hZ&g?i;!FV|e<{7K178=RE2B?_KwQndV+h=xGld^0Bp&r85Nf ztZi_XF&FJ1kJa|%!9~9K9YxMP7(Tpe+#E@`)!S1^A$uOae$ohW1zBrN30ZaRDe9G6 z3@hWBO%yy+9WXj^q>op@)X4qV+@I0+6;flc6?5zNHNzY~puS_F#e|xc@Z{=y67^&X zykqoq*tHjxbQ_-j67;F7H%Bza7q>l1tU%_*#TGfS5%^9)G-t2USt57>r zXI!sVEgwcG!nCQUP$#2P3p!hx!UkoE7hO;6TNUw_oBl5*wcL8PUzv_H4*cn~i#WWs zg){<$3F&fJE?`nv)>5tMnX6g;7K`o)?x|d1jj4>8CJX88RN7RY>%7?>o32{`>i8R4*$w`f{*zbxyRMjlv` z!5go@;5=28sjtr=3wBNk7#md1UOodO$w;?#SSOb#I4yZ6{iH1_Hr~tpiB{CuKC7?1 z`;BczUp+-#-|ro06F{c&u|B3PuSl9NY!q2JB>Xg5USYK4ZIs{H-EA)A>`arHvl_3a zxvr$UO;b^wwJTkIIlo_PoCGr}j=dw3!y6}=c9^Cr!1P|uln{f8reJ@(hNec9me!`+ z=2)OeM%&J<2&jFWc1-cB1vXJBm@+SWb;SAZtT#<}M(P-?VeO6Y4ZK7L@i|s$GvEKo zyIeKD)GjLkI$Plb9o5@tChS5f+5yA~&;!J?RL;t7dJP(^*VTKyNiUFX``F`gMtGFV zYBbi=uRwT8IC7=g+9+rhAHKGN(1>-y5^i=0b24RQ%IR#3xCxXDkt>3e1k~M#yQnFF zrJuDb)tr_V)Kk=2jfL78+Y9q%Rn^a{ORkgD$3IfsR zZg0#R97v|A`Y=Rm&N=TxjpSSdADO!c@FbP2+ILNJ;>Tct0-l(n)8r{ zRNc`)GAO$`<}fOn*}qNW#M&*K-KtdfKMM37AP_H%y>jTPZW}G*(H?kfqZ6!J8t~vO zx}lfijpA8X7>S}}Xz5hVt5easA+cHb$Q&Kwjv8v;;4^cMDoMMkTS4y@*hkP~_c@=X zEw~6of13D-^HE!_F@|}4sHpiFTtV-n>h(xQngJL&qca{ZgI>O2Ny$5F zVS|Ot2N`h`wiFt^Cc?)6F=Nw15duu|g{Ub3ej}G`9Zo9)H#@5=w1wSpWmktyXr7<# zb{OcmmXuS+Ol$&wq+J(araywfYezpB`}sYVf`_Tk((xRb?#6ity+w8iG>T!JTU18Z zZ)Dm!kTtH=Zh-Jj?e@ov>UiZcU7tM?Y1y{fk$Yq-+!kC)HQAb!DcV3h?A||W?qL>N zWzSrvpoV%;D#`)YZb~Uft70T(Z0jC{|Qs>&=Ht|}^na`XH9cuQsV(q{0Px=!~|5%u7*c;f- zG>Y+1wNMi+e%?j^$rTYd)KYsu3OmQY7a6G-_k~0**A7u@aSCw>m7uzV33HI-dcTuV zVaU;q69^Pjf2G9i1BlyRQ{yeTqKckMcA=CTB?4(D@sSvUM`1Kr8!q41y8i$rc%`-Npo5gtcGx6U z-F-8rjq&)_x8n~(W|kk8L({w>M+bvW656gmKvH#61?nSWJ}(E8U*fcpQ9%^Py8&pK zKb*Q5&~|A|>zA>5g(;u8fXYL^r(oR=dBxEYqS~d>fP5@`%3%8OEGZ74D)+~cqQ}hm z0eT~}kwjXiO^|#B)N}5mI>>1eW znah7oikUa*pngCn)Q66($TAI&Pws8dTAaz(_2@4?yThR#idZi% zo=myTt~H+Ap87n?JA0$0UA4eTw#I5d6^MtlkiHSt(Sd z#vvdpvZl~K0)=*-uHpA~(ZUTYaXYzqGVb={%=F=t$#M=1EEo@2#UBnC*x?j^`N=8X zR{;4E<^io)C5y>o?L)5nC#;8IBZ^<)AjlhXCAp3FeTp%Kw##p#P!J>sl@7eFCH)Yn zC4Cn9#s7l_um_2bW|N2xEKsYWcy|i@zvP>;Kfsev+0nQlS!X6^?e%+AQOdS*orjrt z=~roz#FJ3DQIv4G%cgkg2a8~7%2C=iyz7Pj!!BL@)zL0Ym!O^=9f~=neyXppnVAyZE z|GqyAhLGROJWfukUv~cFqXW_r2d!nPsTw4nAek$yaV;#?q;1ZUx*j2W#MR3OI=Yk)v|qEiw3a@pGg zyEh&>rGi5f5T_68eya1CZz8no;0>z|K|+;xjd?$3)d>TCBFrmM`BOA?V4Zz)diaDR zTP%iF5rUwN(YTye3)Bz|a}S=oM}y5$HCsZJ3M_{D!11I*oA*vbB5tT#tv$yo2j+pC zXs~xo_g@5nZP<`amnM4Bop-J;oJ^Qzna8spPR{3@@~sZ6*1xrFP0{D%S_7Y4oV_de z5`z`7muxZ(3hHl|Up$3n7Ay&)i&pNf%;YrjkTmxOQk^*)psE*`+mF)(%RXB#BU*oS zU1(d9+SL(kw=2y{LGt^?b&cZaFxXMd#)W^Av~sgZU+^llO%cF^@^JWaIrMn8*9KKR za#EU|m8YQrHuIy(tbjp!Cexv|fVS;89FvzL0+h}?phk6?bP^VZes~d?y^~AqpfZ#LF0^F$!l(RfacXH z&~E^sTxm|+=6h>NW6a@a>5g_E8l!mowM=2ZS)h$8iU zDI4JOYe5_V(~H#?GEFVFJsCDR5O!;ucKY`0nFTwyb^Hm_%hG29zWdI+Xz^28rtoY+ zzT|qvc1hBwVqhu~napqRmG@PIr36!dB{*}!WPWcHMQB_5K(#olEm94ZuJm66@=I_AWWsREXvXf5?OO{>R%$u@K-o>an zqg(dyc)k~GFSSUEFjr_vDJV1mK32D>QC@YmiLYjjc1F)=9W@p5j4Rl5Oj;(Xq$S@@ zdns020hGALA~5OA$Bla+jIlDfMQVoX0);FUng$E*m%o&_8VD!c{e1N3a@qq%660i+ z$3$8}+tOmYg`m6c+B68c@)>3-&UtJNezl0Xw8`={(NDH(>m3>_c@$TVk@~$l+udGx z?qr5LZLL}n?vy7@#I=hEMjvfIt>CiiT>(~03IDM;8ypV+%B#C0!bVQdmtG4hmo4jG zNaTAW9&9|CUIJ|}20FLS4G~9&=`*V5coW@hmq-Fdnc|+IeoJ0f*UYkyoP2+OwSW6{ zrjiU_p1R>PdMu#=@{y`8K@KRbS*$BP99vl5*t;VCiNEJ+9H(<0y+OCg9Mz33XyL_C zyen$0?yRZyqA5}Cu>W2WSMsRqp|(<1#Z-xN)dFfX+|V@#{-Od-{v*EjNcC<%5458m ziC!9Fk3`?HvK&*|2|^d)uTCcf{5edjTxnYdyO{6yg*J-8Y_4{pFU37WefPdvQIiuA zs>_7SwJTz|wov$UO~V7r{P&Z$u?1`r{b|w{8H8~X7@^iry91S^dwi~hMdAgGaFZVA zD;ec_%>{Awe=jOf7m0Sd(&26P>u;!EA`yvZc6^z#(jf@TM`{wTfc8D9Toh`-8XyEzBmoP0gH&fxvA$mYDPn$&`Xl#sPM)&zTzAQWuV5J=$v#cm z6m5qo5o`>#N)i=Qpi+1_e*Eu2o(?V;B7P%5L-fmi)C?`nQ&hpX;%0s_rc(55#h87E zDMOkzE+!k!c|7cIo4y6R&p4nn_IgxDSGB=sg7VT^%7c6-!TJ{KH^-`tYMPvTS6i-k zdjG-s-n)}ieCJ|@lICV3v{|7~Bp`0PpZ`q?fN=H8mXkK|(Ay*>4#NCKa9-rAk`n4R z{6pH{DRWv}+u`Byd4%iiX%N|tVRNI4gZt;QYIaJ0r2g!*#K>k{HC9JGNe!r$x1^iS z{%&$jK-rWF?=+GR-DhwmTS_>pzhL?IsLhaD&O$i_gN54?uRz^u46Uu9TcxoV(b1^{ zkY>Fx8BSRrO!adZ!Ufm4=@WW0iuS|-NuGx z83h4~hSIOG{(U_qHRJ^W%{B9W@9|FP+@5sLyJuSfn-1rURyS>7m^T7Qx3rdbyfmX)SQ(ln93p0 z->eM3(lu5To<3lQa)S5+jKVISqaY8zo&ybs5Ma$)tAU>wUsLMLdStR!rEbe0+MvdA z2rlS^fkH6@tg1MKpa=p|-Te-wG6fUzEhCAYcgwk#LEu3rYG7o(qmvXh*yJ7@_>T94HxWHZ~yApR96uVP0|Jh`MY`fdYnzfNWPR(KyS@3L@DD z3io0lfk@_Cfr4hiX=?##m4GjAc2%BEuyM_P*~-FxFz}q-CkKbFi=iO`qi@{z6SVL z16Trdas*_`4DIQJ*7=ijjgv!kz&Pl*Rlv*`avw6>B4C1EfH4FBydeeSOX|_$<^i|3 zRztUtov)aq92>F_zgeS&wN>kkD52x#0}(-pgb-91L&{(nolp^ER_1kL#73a&CtOs8 zSvm3>%Ha5NU&J6!7k|RDv1@ ziPGDq(t4F55e@NLpCXl|8)fq8$=Pprm26XHMQ3nw=__kU3--2Z#`|F^dBUjoH{kuReEzvRn1R`EY+FYks1 z13M=xF$>qbj={pp#`^zGdwK8pf6!k3aTos`z~K3>A;$Ihk4ipR|{xvDg*i zckSgBfr%>W>sg7&cPW0DNO3|0Ff2|7NpetKwD!f_-zr^jbo~c?4^7ZimHm>f&u@Zu z_e{TU_BSjs4{fLEBOkxq1VX ziN^KVsU@0Sfj`3yifteZ(WJ-4LLb8gtI~Ga(PZ(u(UhkbT3P+8%?*CmUDxUW^#Kg) z;_3X0tA)azY66=kT^yC!_&i_4NOLelsDsh#N({8$R5gTu=H`M^c)gMP z3pYQWeW%7jDK--9)b+uc|MCoT*B!S3{RpoT?hoaSeLzKT=6R6Ri_@3+#Fu^f$bvX$ z2{fi=gP$uso$c{j-t;yDpQFTa@Ie0$R~{$xfBioHL)ZL=t@%HLHviL=_CH!S+?@Z- zvf+B?XE^^6Gb~)hZ0{Qv7xR1jouhfbBsqEhkv8vi4cj|c^PUjrKZfSNHiZBCGb;}} z@js@ClkI(DdAF6=d5GEGFXaEU|Mh>TayZ!D`@h==96ayEXSv>)8#Xos9&Yac#QfL( zN8fO~lQt~O@4(K#j{kPBzbE#u1KWGhe?k83=V1N!{_g6qa3TDsB@4Wws^RFNG|4NRN`+pDeJvpv_ zrq6p^u6N6alZE*|$#QVL`#kS?y!S=;_l5CrzUTID@bB^d#r;q0_hbJDm*?NC`QKfh zf0NLEo0d5L|BLy)kMI4U|6ykO|F}HwIu`eTE9SfF{l!ylq3vL$;Wqn@jV%#u!|G!$ z_5SJ)i4Y@42f{~&kduEz!vp}+;=wjyY0^e&>BL&lYUOrbWu-LGd8J9^P0py6Wh<%- zORAk!!;6B08|OBvv8P4ZsuUSdHBEQqi4n~ucN^!vcOA=Sx8Bdk0X>T$p03;xjh2YU zkW<2INq4)G%R^Cmy)+3j0r z!wF~VdwW*oZEu8X{6kc{AIbOw$(zwdNk@1$%9VRa=4pZ>y+7TGH>B97-s99Eb!Yl?AlB3%+~lSIYbx9DQ_hF|ISVMa zTiS7g=Fz1n6bFxZzuU47gO*<|+P+m;oSEN#v_)?xSdTsv4G6oby6y~KMwoK?d9JSI zrHpp)H$6Jvyj5el50n)I0vuQa;XitZQJVXgmoE84?@4|CVy<|+SK2JPvV=@ptN4EGip=oSB z>S);<{~$^0W37C$-be2__&t|3{?Pu0npP51UQ*|WHrcaty)dW$q5(2TpgtTN~jwC~#W=HM3XHBhgGOLc5%pn^9+2bUOQQZx_Mu z0CoVXD<=0Jks0-Q0^IuV)0vIy85lqDU`V_W8JFhfWK}1YMi?)85ykJ15+G;>whOD~ zGE8G0e8GqI4Q%LD|5L4|X-(S%YM1dVDgf5FYP8d?6C5GShc}BZ<()gaUJy(oYle3i zuGbtc-5x*Qyu|uWbtg8dW8N?Mav#3p$^4y~2cH89I_QQULJ`~BptJXQj&JC={ouLh#0(BVK6hJo6P0f;;?P*|AbtBk;%-6Q*Eqh<*nYDXYn-ukUR2kv8kcT?lNFf~$c$8X#CE z4U=mAlup5N#o?Gcram59$}F$a#=?P}p}p=ZbbXns9&n7K+P0vb2itZ@CwGQW0tPBT z!bp4oHN-W%jk|oC>%+eFl?6RMbbNQ#HYQV9r$Ta38act)a*PkPfQB@X2 z5tH+LI@NuXwTDNeP&sM$VzF0Z6jS~Ldrcxng{ekk6FQYtC>3k#*EDeeXz3ay+L~zQ zEZrBTc4DAiXfG?UaccA~f2d(ojR=wP|B)u%22!Ljt@8H+$Hd0jS4|u#YmmKU1PrFU z6$cwu$zT&wf|h5i)JmP_`h88RkL86`=%4|c9lM?<9ZY8#odMT-&3xXHR5^J1E8bL{ zM87*y9*$>A!p;bWBL&|=Tc|dr$y4JD&;1}{i2(`(TR#W0-E7*W>6?RJP!D=z5vGGT zpxYisjOCVG9->Y4a^4zQU!<>MqfdgzTUqmmJ2xhZQ&!C|+h9tE?F;00XbS7tw*Km& znZk6TD^LhspLz{57b+V-D^>#2(a`nu*=zl8o&*2eA%xB&=H?eG z=f=aH#L#4ENbsH8U6rjp(g_a`i_VxDXt7Fm=hnuaK_w+I34|9-S;XlqwYk^RV$|0odI_ zhnC_2_O6*^duF_GthA;XOZIWumT$Xwm0YBu=CJS1d94#a+bNv?PSG~2_=%?OGx99C9L)io~Qss3pk0ks~T)@@b*KpDicHR%4jG_TfzffEYNqV1S8 zW9S#L+;vm1RqcBGG(M`E?6F^m894}Hd{2?}Lzq1@SU0b}B+r%|w#oZTM@hW%S-l_ERNMRTEVH_{2=@W-M!IZ^1U_)i8#W~kCq{iFCnlRH45~MKH!8+ zAQ5d9mq{2S)Lk;n@lZrj zuBO7dLM;)w!Rv-!oNi?ef%luC<5Y6hU97M5<{d;8R=g+l9?%lFX-*Qi6lTj;JJ~2& z!5cxg{OD|lRZkD{sSjL_a+q!RK0JD;*zb!5P$BV!N0Zs)YA3$JfNMqnI}!P zZ6AVF3cGo=h)$$VbF->WyW1#PEX`=8WP&S}97d252hfVv;;iNYhN)>Zh*TtJ%qn*w z`9_v8Z&_30?OVC)+t$w%@_w9zTX?9qDCc%*<8;U60SMb}ekcYv`+N zz@f*hpcRk=l}*n7ox;&CD8C`C+lGEpj|}-HO7BEHAl;eHnkjKMNn-!2~X6O zjs;{B00f`&9MgEdW$?F(d$v4{!B2KQSW`A|B;0#m;zL$C_(BY4pK0meC3rz(wdm~) zw1ZmbHJIXyc-)v(&MvWPaTV+2BPjVPn#|&JrO?gwIetqazcM_ zFW$$Nn2>A{r!J)4;mdxA;q8l!ZGrL9qUhb!CecF`Gx*P@#O;*DD=qb6co?q(0bT zMmO1ME+;TBMaMyffy0xvm#-$G8y<_n2b_91p_c>Sm$={7XO`sI5NRMI1Q;f3S#NjR zIc;&kcnx<%UU`zEjMApfv=Nd$Gq;$8@4w?>74MA=qQUT|d{(~~^Ry`6aM@{Gr0D#m zjTJgM^v7xPw(-%(J)MwkXYDK5`wHI4vXpPM;}ZG=QMC0C32nD(oO8{=eg*t^teZ0T zx)TbBBQb1n(JeG@irz{aBww%0#j9+-Zq=zjFp;Lu`z75_GILD!DFyu0{HSpUdsg%- z{hFyTb&U25Mc^+(Usmd@9n)Ys7LRfPSDXlCN4h{#k)RovV1L+kCs}GyB)Fj8UQEI- z)IQRrY+{FW<-J(iQBQ=Zz5=HalX{!v`cy|G1}2R{AH}ibtQFiFcjh&8u}w{`lpDrD zXR2W+ITd$ytI&c#YDLB&!&fH<(G8C8$*sSRSz|2Oq$U?cCl^MmaIIjnn$!TP7PD>Y z@y>v1`dPiK@Ox}b#^Tkn>^LT$S3YhzS$A?DdLQ0ZZ{pSW<$;#yCB?rNdsrX(JKjk2 zUgvU1?*!eAbVJM*g}Psddf>xCPJ%6n9!6VA6{Ll9VR@T=D5Uu8b5~{3kln?z?!*(O zFSD>$$5Epc1DF=*S;lB9DJ_HT$!Twt?(QpP6gApN;2HA4AzGQG@kuPx)MOHwr3XtQ zR#$i8f=1k1b10-wdtaRQ68WBB`1oL-&dAdMmwnCZ{e1l;7kcC(L)V?o_k-a?f{H=W zk4+~Qy$uKq7scRVkSRT03Jc~)X)mT;Q+jjp{6;+cSSMb1v08&cgRaZ>0g)v6CbIf+ zzeMBhfXL4zG>_O%2!l~jys^1U6vI+>VmmTBiR}bfyT+Yl$GH?HT!`rGs)Rn}-ZNRWi-A-fOlp^&crrBG#)8szC?-)S2X)62yJH`ZWxP@-lTKxTq=159;ZJ>`5j zW$wf`{kKhHImwgV&?oz5TA@ zw&Kp_*5>Z`_W17VDeFo9XtUOmJ?yUb@1%iEy|1 zbfO%t+RDle(RCkCu`&3y(_wJ0QGNfS&g#$p=Np$7+XajIO6zV!f}V-yh@0ao+c#Rd zg+mj3{mgjMLu~eB+oI;1=JjTD&Ik%N0G77^oRc|i^(#$%IS;j1A8h_4vNI{2Kbks-6x+p-JKNm!J9Z(CMl2=DBjF-H90;b*NJQo zy-QgFwgPqIX{j-8kOlbu`7rlu9CL%A`kE2sr++iJd`CDuMUko-l7f~v&|$r__Bg*t zuvygky}S3A#L#luq()q1G!bDmQO6+HbuPUSv#XB{MAiutTrIc1&~&hPOxn(pWl~P8 zy`C$q5;VQsK#oZ+*n}R+yu?UWSpNa(%VXD_d*+uw>M?+sjweqp$Ko+gxIs;@ujxpr zcj45+;n#Xgd>y2*IvJ4M=^t+QYy+}^5nCl=`@ur>LFRfKB=0PZ0*#-B54e?9&J};R zGgS6{0q|`jMU4|n9X&rZ3}eYf$NG#Im zoY_Il;e%Yaef5T&Ylg2}RwXji($cri!&A9%`>JjVfke(F+94XwMP{W)vWL|mVt&{- z;z}YI_IOkBZ{|X z5l^&LE?#1=DMzdoCaw!WD>>OlWCl}MG*`~xDFAe}GHIne7>UgPzm@O-9&ipiD- z%!=)w^2a`WJKhKj4jYZN<%AMUg`v~BqdWbyvC6*6V1DBcT0yLlh{0P^Sr}o4yHB*! zq{bX~AEX8$xRB|ox#<*0Coh)HS?#ASAZnz$I=3qKfo>ustXN8LBh6@wzPrLJjc!z{}iWQ0I7a9y2wW$=Sl=8&rWaLdq+Mk&3Bd+5Ino0vwCCN2Xl2%7f{}*NN7#>-)w(G{WZQDl2w(X9cbZpyJ$F^DWofRtKkg?Q_0;)?Vww{#8|D&N*iNc*ne|#&y5qqDNg!+R6Rb?4T<>q)V+Klm)%j$+~O*+e->x?tMhtehdm4V!z=S5 zHf`2t>FEzIyv%1Hp7h&jUS7|}gJ6=MG0Bd1IUNyxP_l}jTcC5Kt?=2-TDq2B#)R39 zF#qQHsqdAoYQ<*oS)20G41+LdeHh>m;1{s=>?cgKbElyXVb^Nk^~S#DOu^#~!?0EQ zhGHk2w&be>P%k>5Bdx&=g}$%0;BlgIm0^;$#)s?+Xgh4)vnyc@qUHh2*7k$I1 zBfUU0VKp?+Qzc;5~B4T`)HH2RHAMZkkM~O z8*!#U5aO2bUl2G%eyM@9*U4RO^5Zk#z8p%~Qh{}Q*+#~K2Nu#pZqwP0fj*Hf=^$wo zBu&Ws_-JF;4c|42>*XKS9&*`M3Pa9_Ep0@#AKTbCh}A#v6iqeJr}WkGl1CKF*#Vb{ zNKb<n_`H0B_vbNBXC+w>1++ zbSxMs`PsT5pw^gnw$2IHk)m%uE#eX3Zxi-v0u(t3p%C9hM5NMN4B3_%>G+T&bM9C5 zG*P2Rg!hQIRg`-qH;cUPRs6jMMEWH0*3M%_NmGi8fi(%S86rZ$3b*=m9b81}OsTMH z3k`$mN!F7ky@lwo4m$%u_vAvz-*<>bBeeN_u-5JY?WWYrAC~++cKf zw`GDzcn=bPUL9s_=&W32n0T-vmGD7a?3r-Dv~deQ^WB*gr=CTH+_@k?f-)mt-P4~D z$zPx>@DYML_lX=VEI<{1LzF<9c5zZ!ZtiU16GQ`X0f#?>{7_VOpn`z;p_I}F1FYr$ zJZijm6;F5?0MQu+86@K9mwmH{iUWj?i4^?+w1aUKVGoki2}4aeZ%BrN0Otlto%sz4 z4AvEXG$=U?8#1zAdJA+AcwoFlv5S)r5Ar*N2nsO>A-Wp0MVJYY!VvW_WRR+r)sCjU zBTS63a3dSuk%>?p=(jL`HK}*4B4}rS4q-I>G*t;`+cS7X-zOkuky!^9C@6^E{om5# zLZV*R95f8-c;(ea@e%!HPy#vzMFNl#NY%^LTLE%TT0WE_lyH4f>~yEVdy~=403dAid#tL3(6wwS-xNyR6YsxiUkk`lIx5b24#g-vfER&5(Xrbms>kxwQ!;wh$=Fm*~ryav_ zwh1G?g}-(}1e%H@6Ee-R$kqzZJinRvGjp-2Fn+UJ9|V2BQT%4S#?D&XCJNm<*1$a0 zi}Y*BWk}K2*WVN=JdMn7XlLts2Pja84k4uUZgl5blT}h^0R$rmls~xKlh9ywb7`iP zeYLr^+V1fd-qu^j`aUyM+kpPM!Y-)2GTmTfPJgGXBL^~k<*ejMzNbC5*6!?3gN z9kbeaZh)1$4YBMhpQO#ULCfm-4V4Uv;#Dk`wos% zTXj!IeS*Qt-1f@$S*5;|VZ@JAPWDLqNHZ6CGF+Y ztyb)GK)Q)wSVh|sQvN{8H?^To;=}eJj?lSJ$16|N>MI;guaU23E1?_%!g|)Mbut&w zl;P2pLU}{(awxq+;-;64+rWy4O=O1EwH?SvEyx@Kps?FV8=LsiLlO2e{q!XgnbO1W zTC&~hvLL$ylVL;-!|Xg@exYLpDH2l-i~VBKPW8#YQg1-lp;lxiJAIN!4TjNxWf8|D zMUf@UjPqL&0&-A!E9w=}A1gaEFwpxqp}$EzHHSHbY6FpY0WnttHBO`m%ihOe9S-~! zdCGp7YQ=Y%Z)H=*f;iD)Kb1qo13@ED{9_V5#kry3g*w@(BIK7>@oQy|=eT+UND%#Z z=aN%y&KNINr8z_1M2d;Wl(J+qzZqxKtdn10>uG&LBUch1a@~{;t8P@Hy3h;+?#^(h5%BI zu`*hM#9H}jN}qycHk^jp9}CdQs1qE0G!K*i#sd-SW}5O)C|%Z z%J>DNl|4SIR?CH)zg|SjzBJtL9-H~;oh27Y6rMkpMuJo;Kk6`2-!G!vi4&U@y@Sv; zng}D?yngE|CR&3QvxxH3eY<^_+uB8ApC2gTL|5qL8b+bR40^WcArZRNPt;AwhW}g8-qwWAIc)S}{q4>{RX=5~G)xfjH$Ae?1Fj^_3Es zhwSX9g``g9+LAgeg%k%(B*c`x<&p~!<=qoRs3&^k&gZH|Z9oZ_Wm3d0@s@W+{fRPE zUhCR#mwiQH=`9dqOr6>h&}bAXwlq$02HMSbe31AZ;w?^Zh|rxd7-WdCi(i3>OzDR&tTHT1>^MEYCCUv{MSF z@e%b;7EAGQTOS*@T+3CDdYfWI{-BCyQS3k@M`b_eBS;TIuB}s< z(hhf{sN?QkChd+z;Am>WJG1D=N^;UHSO=^ zcoNg{P6nMvA(3DQ99jo6=Dt~$ZzoD#Cm%DWAxqbURS9}LPTOi1xsAWyI z70D&mi|JFqEY``cDsKEs?b|Ztt1A2QmMrxU6!E))46*i4?YAna`Cm8df#0jgZ=(NgN=Esg)OGn%UV1?+`J(1Ey9>xFR@|<-cFtka(9i!a219Z7Ktk1-;oD%*>%D0?hQ_#7{ zuGa;Rpd7yRM?ZQ2og6n+erkmLDb07C9?7cy+~PipX6XXgIC#Y?VS<7uRwf5J!Co_v z{M>E216{&=tKu^iKC$7Q{zIa?n{*+EMwDIlZ5##K9*z9ke36G32dQ@pC)@1N@|tvw zX8uAT`P5JZ<^*_rf1lGVxa7_6;7_x1#kKDXeK8+|#fgS+=M7ZV^aa(|N^894TQ7xy zaBIQ&dlwzUq$_;VDFLsy_CCY@Gs=-zVnd=W1~At2iNYul6GeHdvZb)`0|Ex#)fCj z_fB$`9B1hVW(Ur-CxykD{`H;s5dzvBoz z#E@LgQNPEu<@^yxCJ0-7p?-f*(b`-D@uh(kEFXl-;~K_CyFrbsJ;>z>$-^=s^rMUO z^2YbwoD_gqBbXe7e9CGAZGY*TT%GZ(t^XtEo`-RL-{5uW9tYv)gZP<=;Kzz<6d?ifLfgr{sX+(WGq`?gKol%GAZXbQb9w34v$_&O+%6vBy>uT1)oH^A@dc9S zs{{Ab3c8*g-Y{y|3ToZ5K5l`2*^Ys@(q|>WU3N49Az0p1V*Eqy|B~q6x_PkM0`)=w z+4HAHWpfLphqgYp^Bk?mI$F+V<^bi~VPMj3W-tq551EU3Aueo>{T(`X!%?+?a4%33dY zE#?jWZ}*&$zG~DPTZ+wJMkhQc2ME0S=l#}fZQb-y);mEP4Gz{A`pbzk)f0pCo}{aZ zu~~b|HiL4s&TS?1?q)%;IvGJ4p1;IJ5LVlOYW*+K3A|yeb{fDrPaQs-*fn~=88r@g z@?tCR4GpSO?|;`Rut`;8tkO;HT0QNWVYjVUo>a6NUc=MORE};lx11dd*PNm0!_*d^ z-%NkoKNjvfEP=C=!s#hziRq*U)nHI}PO)^a$JuEc>)UDrXAE47f^(n8@(P04*)^lZ z`0jx2bu&PM-pT};R(YZON^n8FH+pe+jAqQ) zwF&YASa_A-;xHv!B__v&u6C#^&9$$_gy5+s-a2#kNZq&-^~9$CmwNV5e+EG|@CG(W zOEIQXi*GhUOsx=>Lz_wKx%mdG`#9GQ`vWD>gn}(a})IGDkd|N4O}rMw(0mL%qF`II3xP% z5TqGE&QsC#l5rVn8GSy8%wk{mpFh8>AWHl$PW^!Sx^El*9j^EnQu_-Eb91q<{|6_` z@js1*6#XYo*v!fD`+uGd$pr}4qi5v;pn_b0q&sG2HunD%7H0h~qaoP)43&Mr=7Mz%2S+5bm0Bq~8na6AeM8HP`prJ(+nWYrvbVIm6@S2?0W)kN_mT~g!?7uS#MDRrjs#=W1Mzl0n)s5)I-)z zu`%{*6F+Qbjj@Zo{6^C+toy_d)C(Hn+EoSDxGr3^p#QD}|1TpT z|MQyrx7;iX_dj#9EPxnB7ES=K$_`*e*#Ph=3xGf60)VuDoJJ0&zuYJQfaL}-r~p3n z?}>lk{{yz==7#y3-T0S7{hJxd#R0e$zd z*#HFXze1(|SYrPKm2&(`*dyQ@{IA9Nzr!8@H~4R$QWh2tF0TK7Jzsjmx~QzCoVGu2 zR?a3=)%IdtSdS0?bAoutn%aC7jDVc`TV$G2F)A0SYDjDMTiTimXF`76yezxkl zO7hwJ`OD3%Au&8AG@d|?Bw9DX$ZtvQNHY-06P$Pk1xrQ}*3W?r=3uAatxS-I>HUl_ z%r^+>sV}1wGL+)Y>C37$@EJ`HXnKqCRae7p#0(b$60fJ`JiiI2m>cj zk+729L8MgUjKXnBvNu9yh&`|l%`Xstx}u&6`NzTtROBk1+E=lb)#mw4o$dxo2pVkmzWKuzNKwvA_8R## z^7aOb*(ydSG^)mGw_Q%9zb_&R@7r=Qb7L`{G4Ap$EqI?q&*5h+E?aP<_6AoFDINmS=l$Ygbn^Ur?LCx3VX?s;<@5p z?up*&BIOJGXL?et+!|4Kr(hneb@Q`wfqxOwaOshp=Pe9i7@FPhBxHX5be!#uvv?Vu zu|MeTu~7R=-JR`m{3QBv9X%_kCgwr&jC+Ka9U19)c?mMpKU?R!(|f`EevM)QFyTp} zo1!_xlZ3f=<{tJf+8sIBm&W#s>)E2w=agVBL`I`oWm ztK>Ls7i(z%5kaW^;ZO#44qHEH!{~_B#8HAmzv_Jah*~MLYgAI858X?2g(;us12Cm6 z63Y73RW;2WX|ZscMG73G!F2kI@YiJG!rIf_Vc5}cYBx1Hn9pAgY>ByHZfh0kB<{pt z)7%`jVuIa!!J@p*2ZFvPfp-ufEZxzw6lX0c9Lc80{VKWGp~=%O0+-~zX5kkTQmCv{ z!&6&=0X1)>*ZraAf!=B?u0Z>}3AC|m^SxLmwHArbtJD_Px94j&j;KSETh#rt2UN!w zB^kot>@_Pd$w2<=J}aSTu$T7aIj{F<+;F#?*MRH8BZj?|S5f8ORBZT}fcAWwv%Z)1 z<-9Z_mH~~N^Olwf0XrVNd%uD+3ZrnC3?~NTSU|J*NDP$4fkZfzr)m$LPw3b`;bQqh z;!pHO)%O`wGWF{5vUyAT6me7b08CLOMk@)1CM>qvGmj4!Ik}kMON&XNQuI58T);2S z8H=j84H8AJ3AX2395ZTWY&dcN=up{;jy)w)hQ4`3Tcx^vWx@J_{V~&v9@%6Fjah=O zouA>-*LtX9@T?hze^a?iy&Qg%#10%Cw^h~lq@v_2!I`e+^E17(=!bVzI44{j(S}ZvI)pwhTx${(m zkj;O$`DLfG<;KBX|CMU~dB5Tl6xU&u#_%$0a4FFH`)OP-yMz99`*?%0J&n-Y-`f4U zN942bEsum7VZRi+8?nR51*{ybQQLaOYJ$F`5h=T=o9J01H#T!)+B;6{2_j9uScR1J z3G0XTbGdcE)vAFJgJV}s_awUly;T#A&|$swl9kg#mk^`^iB)rEdmx&7`09j`vl}ty z$-W*$+Ps-lhTh7L%9fKMC0b2)cuv}z-9N{iYFl4fcUhLwghWDDx*C_OYHKKK7cHhqgQTq{Uen)cD#D(i z^TE35aHyu7Mn0hA)*jD!2Px67SIXA5;!*ab9$@UOq#i)*tkjste+-nLcM{KM^N0 zR`YjKYrpP#KcTLWIny{7N-HiKX5ZN~wwk;>4D4A4n+@D*$0b8G7a#J6u|Y|vH=UBT z%?7V6h&+FTPGrai(fkQ?*s*8|f_vdA&*YnHg>r)HqKtoa#X03Mn>&~-)Ls`kOsn#!hKi_EQ=oYs%;8EsaRRavq zgk0Po)f)85)=T>&LQ57)VCUgPFp4_g!iPj9+^24N-023o2x1unae8AFpZhKHVQcrnHCwWZF2NZXS)Pq7%A}eufl|cW$&K^zL;k9CReeHaAhoMcaETOP z>4O|*W)`wTza~mL^mSrnwh+1u6fdS7cr~t}P&gTaWlveBQLfRM@dK_!O0%w^lyRMW zB-kXY(Wutow9agnGp)9xy+Ua%>sZ!d(<{nDgeGP#gQ@t#-o5Vofz=Cntu9wK>SFT7 zJM5+ER&hPIEq}G(BlQ)3^YbPCjQCNbS;EEO=gM`(E5jH4Ev{*#?EUO5I{b1Xke`GLPU~ zJ#ECZSCTq6+8DzonFC)l9%!bbzLRPw8NO%ddlR_AfUY{fJ(jwmRWTSg>jvmfGu!5v zrM?A#TS|Ke7coW?xF7qcUTiv^UQay9_HtiZ zeEhi;*;#5pxky+q;oEt_>~;S1&3|t5J6ZBM!gmr7Oxq;OHf`?<*mm^jr+Cz^-<02B z1+PM$oV?^nPnGtT%CKN;J<)#UgQep-mDkGvKj$Z3OKR;j4YKun?FnfKL7=B;)R{=g zuiG>-kd)InFH}O4)sXOec%YQ1KTJIke={i%Ja+Nshxc&u+lw7o4jB;+n25tLq@|zg zR+v=lE3T}s>VPx89^a0%7@mhI^JrE{hd;)KM6vP zNu~}2`Qgh&)dusG!0HN#HK{k)|Fx8Lsss^ePT=<3a8W|HvT3SJ4J;}Z1eOs)Gjdml z3Ik2^j84<~hy>ijBJW2ngPXpRk&tihvTz}SCX2bK_yOM{EOsO-)jH@0R=KZiDUAAr zYHs%}xI6KOV2Y=E6wZb9q%Ac-43Q! zY?%JTw zJ+uDb$UhaEC5t7K%$ZUhKq^>%^6Aj4DG-?Qyu$Z)B^I|Wnp;$Oh}1|e3a1Yum}4JB zY!BltA1>{W5C4>!Ds0F=>&={$mjI`ANUhfn#>~@6c-Oj9_#Dl$i9xjUJgat6Ij1#I z5D@&a*pUaf`7WB$Wb0J*%4o%pYoFnMwnO|vFa0TVw}X-VKKI&w;(L0&{=&0tX zAkp@$$?{Mv;<{?bGAI~6u@Ado)e9z9N z%kv<9bxyC++54Zql_m>f&mpx~60%zI*69msG6PkQi65ADW&UL4Vr%hPVV6e`UiH<1 zcY0GVE8RMuQFnz$yb~H}QT*m>G9_9JnlsrYk1$=3+2QKSjSk$TR0Co!Py`F2O~@$o zViTmXV*{O4Zb!b;Z_T;~Lg)RPK^~Czahh?56&5pm^}2)GgfxCi+-mI|DEk|5_r|}9 z&+x{uUgV+?uiEc5CT|;_^Aw?XeB4)eY#SltO5^r@hj+MoG|m)Y^}kN|IQ0udP}y@}$|FG8HEjE`L5 ztl(kF@hOlFh9ZTL%$b1WW|it^qD41d;wnv>(%PL13>^_mbH?<;6&yEVukF8~DVV^- z--q$dSc}dI0q!x7y>WC@b%pe!?9pjb4B+q91O!Z^!9vdXe%;62hRo&(-VnIUpyr0| zCq3!CUM?~kr1SL*x*rfKm4~_#8hABcK9|dr`Jk%TLfG#mP;BLjO7dsx#?RNm1#$v{ z;6gN%df&{J$lhu1ZfP0OpzRAU-TAHduOX-zgu&mX*Ulw2)Z7Y93c%ST+A(M5*=-~s zhjg1DdNx8>Z$4;OdYkgUcJ+5ResWBN%rbk+pCZu?UzfL3_7tpCDp;&oe5O5lj>Mra zgH$^$vGs4X;538;%mSZ*Yi5mBD|bvo@l!bu__{J3Bzrr^lKo?Kf_#ZQi@ymYyo^4M=M_gHU49qK}zbeqr;e8rjaS^V2F|2_|wp0jj&TH@?kbqSkjA%122}9J@{b3kO~7t z7nJ!6s_qJGJAl3o0tJ+tw^&s4E>2cbNfA#Mki`AX*a#`W00w9tOr8Qlgqj;1Bw>#V zuM`p`2nfFj4<{n%!A6#>g$4-l5WICKLwW)gK#2wu4-+AU_6uBCBqRVKS5pqZ2wcMc ztq>jbVyhONs7Zwr1ttMX3_*bfF*4v&7=ctAV5;X3mZ76ZzklS#O94eA*Jpe~0a5~kuSHxva%1liF!r%9sDZZjk5SE=Bp-;U0C#on0 zz6hHDRH*Abctz;v{2CMyTzyo3I9eQ6@r%XIj3SYc29@7O0`Xr7DiI3`oQlA{Yw!zD zb}u6fhB4ll`Vdq)b6|ob$P!YTM!9-{C2ah2(aTRii|~gKv~pnVUGh&l0a8*!OlC_i z6xiwUmgMyC(6 zJe@oLVo3PGprfJD1*tT!$Wy9a0?LK~%_MDvwBAs+VfrOMW?$YG3D;Ez7a%?zDSVMJ zfZ~e~#s|k&%!n2nU=0rWLEdLV=Enweb##pjGY_BP<}V9LR-G*5Cyetj3T$jaCPT4w zwy=VqEG`iU({HH^O)7tUBkG3_}4l>g@%3fh^D$a$SN)5|y1DnUm9GrhLyKsF_ zvQNwG?_FiKo0WZZT9+0!S5A{R^)B(RX5{xT<1=+O_^0hOt?jIDY;0`obSSF>ylnOJ z4lV6$pMVwf()856v3K%P*QvWnh@i@zud;b%`fSNJwES9yUZvRZf6X_pZMn>4oPEcdPmlpI#o? z-y`4%Ap*Y~movB)V;_;Mf`dPvNyQyC18qY3TcQw=6Y0WcGpS&~#U3^ue zq285VfFhfat&XD|H^6O5Gb&HnsLc)K4_%83Jesn)hO~gJa6L%nM$>APBa)&uaRe z8%>IQ(1H$5W5i?+3=DN#5T>&J#*Q3*G?{IfD}Dh_k?!{{(IO%2FyL3@fJuIS1P&K& zVSBU^3PFIjt)@{<1Y>-eObQINr9UvDI0_g|2TlZ%x}(qG!f)xxO=}fMWqAnbZ|@o~zuQsvqa-W*osBWB&&7m zB)_$#IwrIuOqanmlxP!VmZcX`R;9@b)hAqbs+^$jxWOW<)fkYAbrS`Y+eI73%&ucr zJ4{JPk$;n>$sT5=%8F8F6jd^2j9ny3bz~++Olh%55tf*e^t{PcpLZu;yp>c|#7bt~ z$0_3{sJfE*B%g(ws!J@WsY#g5oJqb>Edz|EbP^fV-ecsjgug}51P<>);#|N(5RF0< zeq7%4gUgY)ZVyL7D1*jjPX4H|8Xe>eu^vPc`mrnuS=cO%)!9}ailYc7HV+n=&xG?W zh$?_gH3FmzCRb+8_=PS+i7-2U4u>o0o8!xfBGl_Vlhc3|D^9>YJ5z;($W4OcC|ng+ zR>&@PAg{>{ZgA2eOdUgB{~4&PiN7`FCU(GLGJ@dds{Q^sIfoIj)0kcM)>w}yZ=C(G!3B_(|;ta-?3$kG>x?%6Di>A_cqw!2@CO1j4m2EMn zETkQ15*)aaK#xme76q~ruCK2%2KR*!No7+qq`*RNyrk}%QQ}R`FD05=WVvjh4@>`= zXfdCxL&06d>&7*m(T8Q)ix!r|UrzR8AOa|e zGEeGv-1==i$}eRc7^Y1jaYxPkrdW?sh5Q*rBvLw-BG-8As0EZZE|loL*Wi8l+p0!t z11T9gRtVGWB&=Cqac-k}?u`{w%dz$$KWray&D12cEyPB19$(!E1{6Bf`KjL;TovPq zOGj7A8M0LEVk7cq+p`vMG^uPu7i=7jBr4KoI8$>TCCkMuWqD>pOi|b(Hsas8U|6Kf zd&F6anB!A4%w)^4&dgUxH|$_WZC-~2<4e&}ME8hnjeJWAX-kQ4XKg&D#IjW8pOHE- zdqmkNS=S@#xz5cU$>BMhQS$1?l6$9S2#CpNLa;EYWe=h&i8}4GND0@!zvTJ^_36`W ztg}?fK4H&Ry)TTu^Ct?Om+eDR^I`{W-Qc0X=IhDMkVaBU?2(!?3PzRXXW(Xdrdi5Y z$8+mK5z$E%*08cNL}oE!PzKwIm5JC-aaWbIpNc+WCe6PHUXnm z5ccpvNCu*9C{IJsCHr6|n5!=2g-xIqX%z=c_fjK%hrP*m8}``$06}TV66CpM)h|_c)@bSpZKD<7o2=w5Pyel{U8T32DuIFnju3e*Cww@b6yy+D zBMu-j0*=B;+`V52Cp;N^=h(9>&9>2D&2^0T``^(3nG4ib{^q>9C6p_`dIA*Sk>jP= zE-d`YzBs>pBAY{U5~=TLv`Y@yl5xaRS@2aqv%WLAv3&C1Bw&3BXx=h!ErGtHZ6;q< znIKVZ1xuNb9@OTJ_fIla8J(1&mGkklVyq_Qr$A6JR&YvtkPfQOk}YskQc_!FtY=K} zjKPJvMhkK#C5Po)fpK^2dEZY(+QdyquR@5eu*m+pGRwL#m23^Tm1OS(uIoV0sxWVu zJPtgwWGxh&4yg+8JZdEt`EGgHO1&;Oe_)dC<1A^$dN0Pwt2XPh#eG0(o$=K|lWd<| zU*`h1R<>5^7TRF*TJlcidL?Zf21gshs}mG6mCFYclEb_?sG^C~Qo>2q z)p*lt{#HMH5`SFGiq?KQ%`|b>9c`8!gDPz%{EDkKv+%gpxE-qUkr(AkG+iEq#^|hu zkZ~8s>?4ulR?d{fR847(*2bLd$tW`!EdA}O5`K2UbN!8~iHEJT@|zh`ypTuycqUQ1 zgoGmsMy)b`jY99aOLb>^Ua>FI_!;DMO_xBAFH%M$A(_v>2a%K5$IlCCfp{U6j@1Mn z4>SCwb{@Jzzz&QQ=3~m(*EOx#Lkb`W^()thJTb*cu)7B(Cgm%bMjJ7!?H2L#;%Z@YD@LKDQe2jmxC|2&M!JAHzrc=(>(7MD1Ii;xjgIC^7tbX zI_JZMIWOljH>B+^dxI~;fpKKFt8_2@-7f*=w|is)V3Y`#!_Snr8do}$zGjR)_V_)| zhV}0DD1>w)ei=gs^7~$iFIQwe=QAj`HDo=FV1Aa+J){v|B!q;L`#MQ4sbsmb`yWzW zj1kW*TtnSL@55xn28=0hbGW*rs z?y#F{PFQYk*h5#+n`V7kvwL#5dFY(vx8eqIZMRxoj>Dx6s{#*JxTG(FVHb1M_l`@TBfK z`Q&T%U~UZa%;yeqr2ontHgo8y!jdWGQDQN+d>f!%Mb1|ZlWA%jBBx{8dv);sG}86P znx%CLzYlCHn~|IWzy4{+xnpdAw@=l=%n{-;7t@qYsCjO?806^v~x z9bL`-KQSi<0Ga!%8Wa|`ch{k3W&)@H=~>yCh?qG5oD(w>2j@S-cJ!=FFpNUZCT4an zMBFU@6>bN(^gqGv{>9D7@vm-9RwlOp4!28?vkzi|3%!0t=l&5E8SFbJ4OLz!wqJ)B za|!v)xI>auG=SjcIqJYJ>4)UQOY_cHeNuzK>dMr3C7~K9>vhaBLCNhg`VB(O2up^O z?UHxges$q=+d`B zZ7g~-A2lbZyiFvV{l3(xh5?I;U@J7^8WcodY8a;Mp-c) zPbk$(lvXtAHdB-~hPlu>hW`6BB@oNiL8BiV?mZmNi&N>_b;o?7x zkpNrdKWvZy(c@p)<6oiTU-RVOvA-6{zgEh>vd6!|#lK_!uT>IYo&*RN|5_>ma>#$f z#QlqC@gEiSPe>={zls()|9jCQz_j?!qD2ocZxywUQ*E#7u1vPL5~;-!HePriiyinQ zv;3?U=nxECZ5VhcHd|#Jo2>MrF7dEHD9v1$phAaWCWV`|69-z&L<2D1v4@dsi6mikHQVu`0EClTR3A*47JnK*jif^)od;g>LX(-A7SV4w}HmD9fddk z9`-#d&a{xvkGT^sAAVp169euC17XQ-4!p<_jGvoa!ml9Mh!P-suwK2EK?0i%uKt5J z#Ay)UxPPk ztbG251!U$!l8pjLUKVu|CjFP??-wI9Ve7dMqG$EpXE^OpNT#TDML64UB19X5@J@^) zxO1g1Sf0q0bBqqajfgI5VcSU`5nd;daMz-Ug}8ejAh~~_17A_L#-~KPr$DPLZf5&d&&ASes2{tch z0d18S8pj+@ZUJc>w9FTfThL}(*mNhc#^fBSgs>BHLu+13Nzu-^q#L#Mq0F&Y(a{tY^(TkB zT*n%@q+mAueAD;CD0iJM?Bi3e#tYzI%)g>6=(QPk`dsr>ub$cVw_|I=*n1!yZ`s!1 zp&Qfao?2SyEbKC`ExUa6MLssUT^qM*)-%tvgqNqVQjI9w+VBO4YH_X`HzyEd~U zVr@F76RqaXo5s(`Qk03)9O;6Th#B`e(#DQ&AX;bloLs^%MvrIU}Z%-dXra>k>GW?x2*Id?hW7T0BG&xt3rw-Y?h`LGVM>Cbes=dNHv6uwQN3*UjDbFb+#IXT6Kq8#qohwtLnR|1sVcGY z(m%iox)JtJ)?NMW!0p9{vnjF)(E?I)wXY9jUXe)upbWiM>$x6Wod5|IznP)l-=W$b zCrQOmiwwhz+Y?go20rkJOW^B5I$B|1%=ux52qaQACOk{~^01+!e+3lO4)U770-%kE zd1&Qz?=~@1EX0QD?Ash$grN%+&)xoxzO~U^&{GYo@GLFk#Hz(3^jhjFN_2#8f!?3!RN`}~Pg=OY%1G)x!CqPX}>bZolz znB>>#7?wvCsJG6}CJ&JlxAQv!rlxn6!i9aifmc9&=W) zf@4t)x=bjFF_ivo#AKkJ>+`-@76Pq-{<sQSS=K}oPI{o#pYro7-ycP`UGJ*ZJE`3J z2lXYa`(y?X=v<~fXyFkUV^YAYAE#sAUZZqMr6b88pm-TZUD0tRD%txXKonJvsb1Ab zCOnM_2#~sPfh1hX`NYIxQSA7!vW=y7)iv&_*CNHugK+lQyJ!_@r%69((Wf3b_;=2rxVmB)^wIdL zO>KwxmL?LJ%cjW<=-Uc7XEY5dOvCo(5tvI(8R}v=Yq+wWMmt;z;8ZnX#0UTQc6sy|zrdCc2>~8J=Dg}MFvL76xNBioUDTMLyJ@T^ zVOsptQ@8hLM;Vf|&f`x|`y4^4-RkKm9fh;Ksa98^b7lxucH8Cc{s^WO&%`jfAY<%6 za><1~sW1ntz}Uo@)?75Cv(iF5u{qySByqG-ykVmy<{>hNx8R?;G?L%oq8%cTja)b& zxy(RT6&xRMM2Ad;6J-@)XoLpISXYE(6-R;Y*|L??u<%!RO>OYODkvfe20nsl`ZLq# z5m|$SLv?f*qU^LbJ#~ZpqXmQ%+1rV9>GXDO*3@;o^1vphw`&487^OH&Ay)e^qt=*F zHxJXHBgfc{6G;{60WwK21F;)5=&tNtbfki{=zuOl?`ve{2!HbGACgi62Wsd3pU(T# zjA$vq9|lq1+8^uA?F||Fbb>+5pmDgB>%!G4Ob?Otomvj7h`|-IzB>RK)QaVUw))oL zpC0KNi4lsXLiW%~D08I|tQ<%>!*qu5g`8Y+`kccPNV&R9h8&}-qzMvh(;A(KhgB*6 zAI{z>K$3P{)2`~W%`V%vZQHhOSC?(uwyiGPw#_cr)VJo}d-mGvn}5yBK^){kWFEY4 zWZsd_71wR*3=dptRMt?aSH@_(7KvEmmkxOSVT|TJ+KpioroE}0+ zEE<)w9wTr|$418S2#|w$Qa+N>u6$LUr_Dg2nox6!hO)!<(z4`ssVQU}{Yd=9Fw^Uq zwt{uL&3nA2xze4W6-&xWpM=(`2MYId>v_aTyAr_F_vWpI*ZofsE8q0G2+#h;#-p9U zG+&fJ9!Q>18^NZT5>dmF6x5v?buv-c*1_%Bqj=@8r;LyM)Wdk4R<_Urje2_`nYNx7 z|2!4(%nnnzXTJahbxEPPrI>wk3Az-UW_2xN>nrQJ>cw=>%uU&$%DZXTOeuGU-c73)j%s{=JK8p5U(aGIF|9RM@I+FBoZ| zQ~WNXcIM0QIDwoY)JMheQ+_JSmoqb34UUwuHe(F@_SwfM&>F%F5t#z-WUiPpYgBvK zv0}_PxLrd&N9jPW6z~HK3RpxEMH7n_k-53QGnTZCcS=rVIYS^zD<=DGhzi`p7;V*v zz^ME6)G)#L`lrF+!*&$4Anm0naQ-Y&50>i3#EfC2aT;3Wp-(SEGe# zsp_{k=V^k$88**gicf>-DYBgpuhMWRbrM(dFSwU)HeKFX4eQJGeb~0xE3rDfxVnG( z!;?~jFb#*!a%nA7Ozz?&X?-75pC*sL-Z$DhqCJvx#GdaZN5GH<3n8=#3oQ;Eg8GeQ z<}stB5|&F<&ktguRS$od@g#$_xy#78k?d2+?n~)^+3NVrrGJ`rE2wNWo4c!$c1%AX zGTKMGH}6J8``fXp>F3srGr=ACNii!$c;Pb(R|BGH%qerks1}*#701m{F4l|-JWi-J zwddnaynNpGd>r3wcX!`hT)JLwcX|Hl%aTGh`e5fm$ftlQ8UEt_9PRLZpI^7_`s1tD zMatg{v&ZVq^i?;MKURw=MKa=>z`j~2)Q&stYbA#4TDHRIzF^sg0=hB(IS%ud9S_+% zF<=?P-7Af2!%?fkH7G>Oom~pJTNGHs_{FL3SSo~-+@|+2|4WMfcjgaRM2aMmG)0QU z@vd-Wm8gYreK3KrL7aJE3Cr{q7bL3!5>F5=UBi+=-TtH$ouQ=L6r*aOZC~ddfD|d? zUE1EA>)jtaNhCydcxu5npTtDih|uENj+i%SIeP)moVghg#nAF^RH)(f60_#&ZBj6p zb&!(n3USr7Cbb$>8gbb+@)SMg*jCvA z&b2az;pTV91@mSy8B7U)ejmr@9mR*-?{7%M$!qtKn%UbUCFIPQEO>jay6Y_O*Lq9a zgJ1ObGe+-rUh=h^mbZ67rxOmCiXS`6Rq%?du@yf09&t3>gu-bl!}k~*&5cyVzTK?E z!U<)Pa78(iJ*hnLG>Q$Ur)g;>F|3L;_HLaFOxu(Ru#3E31*_x1r2OKYw}|`ZVFc~MwH_pvG6AGQs>nb=(fNoe5Oj*&gg8== zNk4gr!@w-iktt~S$=E^288eH>(_CD9P(En)A@0<&7X44-GYhWILEqbzA?|t@<6h1M zRx~q<(YB7)o6eD*z$buO364_>UP-L}Z?!49(Nhd@(3OzT&0YzUG}wf(55sLN5P*R-I(9(19$(U} zXUrvZ681a0vx?Y#fg!nViX#vTQ`V8PL~4;Vcnl*ztL5xsEJclGu^IBQ^QIo1`9AGqLWM%%eC@`AUn)mS2KoTZE#OwF2ryox^DV8 z(R{p@e}Y|3tb23&Znb?b z^&}6=qH+wF3UEfv2oi4wZ+Ft$^Z$?WrIQB_x$k8s=D$6(kSZmaHirmyRENj7cZmUQpU9!$qCWpC(q z;1$)2#g9a$$$?tlBXg!faIsS(uR>XosTBN@E_um$Az{jLT zQo|%u{ZozR`?5)L<49lzC@!V72|lZD`_n@?-hjA~qP`(ew@o$Qu#t}!h0ZuK!`d_A zd+*KifGvWr4d^QCj212OL>QB-!aT4juh>o90Zz{9ggbe~^sXyj3A;UM6+WdIj6FXW z31LfYNgVMwMp3wa9kgYhlUZvi8fsykxdanF3>bwi_2zF73XUDu1CJ0t`~wPl;!-usat(4#5FRw z`2l7E`_RmVG(ShxaK(Hbg<63VSNC0{pwSp&QVW6oTr4DprswD6@3)Ajk9=P|9HTQ` zcjEiB76$0`2G%N*FV*~feG4F?wU_nJ!}c*-bj>~96jh5QZl@PjPqsEVe-}gFk(qaa zJJH~4oImqC-Df2}6IsO9f9#fU_*(kb=j|I2lm%_kGO<|m6Hb`f^i$<{J;3P@oG?w= z@Jwxk(bu7Q@<4E;e@zPj4-+$;k`K`#H)2!Ak!b0>` z(y7)h^GO<{E?>1$=*B-s29-pBO6&r~eR0Fe2bpaI<5y77>MC^|WN6uI7pqCu2d$9n z2i$P;xk%lIHL*(7$51HT6R*eK8$zt+zm&%hp^~q}CP}P^GS^Qj@;Kk>Z5=w+AtL0To*=u^y;7dkAoY8KNr5_Zvfq)j}IP zv`Ek)<6PweQ_d`JFd|k8y?iH~N`bU`S7NrX7on#l;EGwL!>=Sgn09{S*d3!rg+;=02mjCn&BA{?lH8e zCsDWEI=nr4k+pRVj4KGaBhh?BM}ebxYG@~I^|XkQuYuS?8jWsJANIv&(I6@y&+gr7 zRPZp5DsTCCP)rpNkHUdn{sKPEsOBb^am z;_%Eoy!9UA_T;U5`LguXcvg_2f2qg|N91G2@#Uf&yu5cm>ABK@cD8#V=z^ufSfO2n zDm3^pKiAf@j4Yx-1Oeq<AX$1oUXho3t-#)}5|-#$gnVPT#dne3Ai3jdjern$z_S*zNMN&R**3&2 zYPy27sXI9dgCO*zB}r52XN0gQygH;4&Qds0Qx~}{B2>goK>%0rCoPEPXgQeoL>nh- z`Pg^C^Dj3gCdEq?`Luho?1uuUO3YVzb8UzW1RU=Lpe{esCpDNg_*HG>C;5z5axg^w zE<|U}L9DNadbhA8(wE{C51XXIQwnj+ItNm3k+Kb)*gl|(f{LTE=0SoiRr5Z@ikDJ2 zM4xN_S@f)Rz-OE1%B1=#FssDaHJZbDi}WmQ!*fz4hzuRLOAAky$A_e5aUr=99E7&h%?b%;pR-y(>bu@p5eqMk*9M&94unaxvo@SufL7& zN=ei6Fqvn@5R&(Xsnw%#(WOalOi$@f)nhaY~t5~+yFbVSM8u0SR>vOz3G&O+Gu0gE4N|@;4~dVlWZ3KqR{&XQL+@3*fY32>C$wui!B?5H|Df|IvK@ z!+rgyhqinAbZgj`j@^1(7rLwUQ~0Uv`|CbiOY@oONH!&#$1xoy=ZbgoL}pcnp17o3 zCP)UG+xaT-;$Xjx#i!J1gb^9%3^zW`x11p)a=G=9l+L%`G5_nATfn)mgje`1b`6Km zEgR>rgrERI=Vl-KmXXjsj9WMYO^3!aGRG79Tl=GleP6dsa`(zJ`lFqK=ZQF8EPWGV zLu`s+4K=WU5*!;G8&s7%G^rKG+)KV^0M9?&TI)5NZOwBXzdF!ocAt41i!)C0*GiaM zeH&WlT+EUoJLHH5SFFmM4O3#TTRz4(4CXJn+yAt6Ro1!+q-m~LHunn_IG#TxUX-mh z_)HIRGi=VQ1q!GU-b`e1I#|0h#jH$S{x0gMvvwV|A!+pdumQcCyWVrHd#GvGPL^Wr zX>cN)Z#8_0o`G$4;#{(Bv8G*}f?R(TY58QY3|20cC*Qaj!Kma({@o#q$aiz1xVg@9 zv}Ti|X};cW9Be&aEU|vnM%WzKqW?kSqj*Zv{Gih5Lx{>!&U8igObXn$2;~ae+*rs- zO$|x9P5?ID+_&P;DjS_S9B$aG#R>wPK|kMRWuJ8k>Y8Q^(HN1(oLtg4Lbu+Z)Bt}v zR#KV7f*m2d=VZmMqK_1uD?aWDw@ZW%hR)(%*&J~%dc@fO9$sdn?<3RwCJo24?Dum6 z$?EZnBf}J2GsuMt?VTPBxgyY$WkYoHIrA+)v(OoSKWl5<<$`Y!>1^okNTtp?phf2a2oF9N7vSR; zKV$imh~cc7F8^LXBDydLNF91(qoC-H2uJ}M3APq|)KESmC|NEV;wpa zKe`@nkOibrT>x3IH#lZ4fq*~eFg$TYzo8P5odzm3-*|pw2eg=q5Dthf%X5?E+qaxp$92*17in$Cv#gHX#M>@ny+XT|Vi~Py#A9q6V$M+W!5F>{`jQA{V1jBoZ zB|tI6sNZ>doS0EL`|)7q%gALFV(eWl%^OlkA;rt zgNmSK;LI}1IMaY!hRwu1p4YwQ`~_+q`{^wt9RKqkQ|s2o;8B6qduyBK8wgOUx)8rS zI84^{{leNe>JVGr-1Id5(P`y9s=MP|hi8B(sqz=<&&kRx8?p8jQDSAp}*jW|HlLJ|M9%PfhhiQ zb^ViA=)2_dzqM7e{;OE%8`AhUVj)KQe`Y8yRo-khRk6Qtdgh9>ajMpf?r?@XHkabg zxiXvb6`Iw>Gk@FTRA(tI6C!ri*;=(cbzL=OF(V)e?$L9V{$lEbM@pxF$3xHjg^S*U z(nmxh4^fN&F-YXIkM;x=pi5%*lDVHDD)|X)we85WJ^j*s-FDo5+DyeEQWKwp7?iFK z$0+jr9vzBJTgmz|R+hoP(-SaWxhUBLH*ND3HNqM&PNB@YboBXLMH>3U*5HHGc${j> zLMIFnqy#UAHc)J{3p?PQeZ+LE=_o4Wq6^@l1s}JJ14OZ8+^-MU0DN)c=VQX>a4er3 z-%+!A!xeYDH}>6iomcS{Q@Vcpx4-m+_t0ar=?p0F%Pr(Y4gC(DMufs!Nr zOSt5NvkxthH-8%V4dgUHrf{f)Avjfg8fs1l3qFs3|D|(JXY?Kpgp-~L9!cuh7j+oa zu)n0FK}2C()Udxs6|_~%D6&-Sz>pwUa=sLGlZhvMV^lQP^qut1o+n>e*lq@X{@lGO zc6vZp&e!d*C>8uvQ?`B?KC7p>9uWbURr_@bLzwufl<@V|$48%y1ZVDrl3~PlCm8BW zkvuA*cfKKjL1f(`Ln}a5G=J8su&U5XG#;>nbM;a9HUGv<=@_@vA^Oxy%?YL&JyUm~ zc(P(+XJ|1Rs_ai~WaLceByDqvbk*c~I%J9XF#(|T0x3jXZ6QDg4$A3EmcJQOzw4SA zP^7|$$?7LmFb%Su#Cb8RZ^q7s`5^7)U#A*fx0Y%%F1wYE zOkl22)Gw0|q^7d?Pm$LDX0cDFlCol)qJz+ZFR2oBHOyK7QF{Opl)BQueIj`Y?=#Mn z(q?KLGIR6lpqzQL_o768AAfG%1{2ft5eFmg+C=d<+TLH6x-~JQ!B==OmK-rT)}?9sWglmN#5CJ{!*YOh3y>^@DTo1pB@DbpMT*6^W*Z>?(x;sbNEYG7E2y( z>(bzh7M3Tp95d6iwCX;NbaVMirlF2I32d4&=lYuI@C|A5hikM%vB&)R%MJh2EV=kw zn(OQ1jI8TNE$fpP!&&ekR|UNINh;cZ+T6xVY7i&xPOM^u!^d^xk* z7!9;D?IclyI8RgR#BI8#zLER9O$y9N`bmPU&}@QAq!+QRP)XP>qd0CWfH|nrX@;| z1ThXScZuUXjk0kg!!xt7yfX`RhtVYoPd_aOtkZMh{u1_bB0lPQa@Klj6~|(wvW2p= zYtD*jN*s{W%ZZp5xXu&W2~~8U>22Y#Iq)_VAG<|dlzTRXn&W65sP&FTyfE_wmMzIo zO2GwbNhU$@vAsRFPc-^tKUBYDI?_=;8=2iedy(5iHxUgbVQ5KVO}qpZ|BRb-c^p!E zX?L7qauHfAOhVD|nZ$O5w_3Icim~?a1A%P*LW=Gwol0a2X`Imd!dB=>@~7P2A6vsp zDN@hm107`^j0H!a2FkP4NKLDu3aXfI2po4OUv6AriNDh)X7cmzNZ)MtVV!t$J-0eU zmvhOhq$tYVVCYcHY|A~cGX=2*D9=u6utV{$ABx913OSs8(M0`d1O{&3cd|;X&Mlb!l&b*~dLP7E!*DM8 zyQmzJyVgN$4lu#)(|CHHJReeouw@s^(1fTzbJ~V@5<}uXh1_bpTuTOcXU*-2{a!J+ zF!8a}_(VeVeojD}`q7#03;m_064?n3hxmck(C8rYcggmzpUb}z20be)!~aUQ|ED;w z@c&GwYM z|4Xc8_-+pW+gSTA0Ppwb{byQ|GPW^wGQ(qF__w~ySbo_-JoupN_i*+r9G1q+3=)$` z67Cbs3SPhd#`qO~snfi#EpioUF1|ar>p<2M%RejfBdUBaAfm#vyb&$YP72 zUUn~X#;y(05>V^5lE2#u=vkuXYWAqpSlPIr*>==VIXw^fkTSPwx=wtgPmxRup_42$ zZ`j7TWm&k_F`V;gvd}mWE(u0-cdRABi%(uV!Xt1}jIisAAe0rAshL_dD`MYyuknIs zz15P9sKpgA6zhjiWiAar8Ub4vBoi1s%W8RXzAww=^#2LjGO{p#x1KZqUAFrdK=vOi_D{Da-*-a)eGR`i&%eob^o$HlO#f9Hd8h$p zr?{NPbDZsItln%qnjGZ5(w$7N2Vai>9{{A(pB%ME8b3i8?F}omyR@+NfU|Y3?33<<8__mQ$Ly9LE|Q7w}Od z*AmVK#Q3RwU!Sl2%yaxKtav$k4nJ@Q;QUXa{Rs_45ji?SkunN^u7ZKG&O~} zQFsG!xMARbw}11-B_gt7ua^eCnAh$CsSVr|*BGE%oMaMq=915#o`r`?J`|mNZ9>rw zJ3pIy0dvQ&J$mD~7Fe6%zR+J#{hGFVHk)PXMqkcj)f3HI3l4r+Js@7ujE2qgSxMLx zbbVute>OmmxgR>9dfy7hA+e3sT$y^?K>q{E8q5a%+dg$WAX|hsUv$>{gqX>nX2(O# z8)kXm_ypYsxa=Cpn9%3v`HjVb>=qwR?Gu{^`Um%~d;L(uaMAwt6J{sW4}dJREz~Q) z;oUduUGGLPL6$s#!6E;zO+ z$Msh4jgJ$m9wgqdt;JNnZR!&rWU!jCnV&`|+hII>w)Eg=0bsK6N&N_iFfNv}aKBpG zn#^#U5G&CMtIVk7mhxGlqAed<>vl|bx$H{d8Soz>Y|+esvjNamX6gDCI|7lSqcjJ7 zH#9mHI1DK;AqOEAIOQ2AvOB%nT%mYXS2*w|GVmVPAiBG+E<~-fwJk1BHVI@p_bil) z+FhFa(_NvUPob0bHy6jAO6MT*a6a2yWkUU9-AZ<++K)J46z&y(yMqtxd5E~6vGV{X z?UC5e$GPi+CX@ww$IUGS)#~b0P7g8`IjRnNn ze^qp2p}J{5_LaL?8$@5}>30(4g-wdnXsFo;+`|M#9A%g;qE9Ff){oFRdpKC!W!70S zclOpfqNNi1a@b}zwL{EM(w*prspzT50|fgX4X@nM8TRkV9JABwU_fR|U;+?Ej~;*YSrg z-VG-IPF?hgUJSf8{;avaLW~$cxNkNvRFHtRf3UH39-XSHx>4&4ecq#s)CtR0?IB3) zQLD@O0o6^L{bXC&D8r)W?&FVV)0yt6;4>w4z+>zsg*E*)l8r%T6ee>hc$!1E{C(9u zlj=6TB9A`xFxl-WD+aHrcR!Vv` z6*VaCm=PR?)IQ!1*?D=M2U*XOZp1W6zNEgapcK9oy4$=QYhQh`pYO*xS_X$vvx>Pb z*@qTc17dPNfZW17vgXr@&_N!(uFwNNzL7v14sD(>fskFG4~$#ZKx>kP9lBHLLb(trA|oy(FC|q4GZA4FfLS6Ca4B}j zwGpbmN-oHURszd-cT``;`M&DQ5HzLaao*pLlG)fg2^###YE@ItPy}_7itkh3513XQ z;ip4=$nD$DsQ)tIR~lo^gG8p_R*I{nVoF&JZEMX#q?3c!s{%6Z{KeD@+PD!N394ku zuSLeifVjoX;BZQo{gH9+*8Dzx-xKF69^H8}LP$FkNlekdvG3N^?MVDgm zkFPLC*BcN`Qmjb03bjsT%3fP5bWOr`94++rYtYg3j-7|wHqOX4+n{Nh9dx-#T7klh zrQ|3&!(PD}eO7ZRe0uY$Trn%gR3$ri(@|rV^5j7fGdz7MK3UdUxJJRK7k0Iule&4O zwz}m(_7Lkh*rvDX7t>?qJ5Duf^r)}v)7%T}@oI?A;H~K`M=v;JGH*V6D5N}u({s2% zmJ2lYqMczovTS&*I$!$_Ze?+2g`h?k=xtkyl2JbAg*nJ)^5t+sMXcyCQ+t@|6yJ&K zOb)jvD%VK6y95hs0jgv^D%5;YEKA97c5iv9M(Xa1Dz;P^OP7l#Ul;irkIiqt*0j;Ly+JAKu=Sqmu`!@%*D)`W(m*ky}$Sf8gR!V0L zLAB}cUVd36mZ({eYCglf`;=A*k6fxTnY!33?*;@F(kUtXv|24-Q4r^~3qr*tZz(R; z>aj4Ot&cOA)a3ydW*2*d)pAxjW$|WBJK>~@zf}Fs9nA`{q%Z6WV#FLXgHuzNJd0m% zxm<0q_C#F%;{UvCMq2NZ^?oYulDxukJM8+Sv&G&GyzF>BoWps0_$I?e%8UQC+v0e< ziN_s6SlmygQ0i_^xk%R;G?=2ja_8<8EVmZ(f<_a+U}dt0VrsiaaS|SSTp&qNe0}K8fftk#vMErdtT7bVp z2*;5hO?5Oxxu0hd!;!vM2nXs;+A*MWrN7S2FODSw!j1MMdxY|UpWO^JvzD%oJNDxxBE#^jRPE!oDE&S%B(1=J5(XhGBM-P7P z8DdrTEXRNcGnfzXZ8PoSq%Y>J>v z(_=A3=FcfTnyDFVV3+~NDV&77uR{wuZYsHO$$3)ztX+5?_yRsTxqo$EA#ZYWfQmgH z#1zm^HfTEaAt$YeVUZzV066H|PbN03XqbRx0CZ{cd3;nFmFFSJu-@+xlg7hQ#>rdNHqDdNdAZ4U~>b0ZAJd-@rj6| zZgynf+WS5_Hq`MhU2%*e1{W#L4gGCl}{5CuNj&AM>; zF!#y53`q5RC$+Mv-);(0;_zU(uz_XeZ;VQc`+QabKeANv48wy3Z%;xn%+NuDE>$AAnh#3(h$90yYS`g+w2a>><)V#G=ZsByvQ zi;-6_QG{uMl6e|X9DtR)5~DEF0mX0dPENZ)-w}|D?2Dg<+eDns{$knz6J{AIJ?)VH zB1^)J0viC*PdED%!&HC+#rw;BHH~s&=KYeOw=6f9V-y~sK*5D>XaX%Ff|Q4BoR4t^ z=NF57II@zEUM4=;n)@S_H=a~47(R*CP*R2<6+I#{eAm1TmWG)4b(vm75{w*@Bucti zh-idU++L3@8@gCXvKf_H$<;)#ab~WGZUv7c%VP3DQchaw41Um`qmC743X>$NFwa8B zmjICX=sHHsN$?hzXc}BY z_0&Ghre@{*boIc-xhWjA1nmg5qILCheP;d`$-jU9;>jAtb4|$fk4oLZ=B5RTwx$O+ zO^xcsZRl0?mP`xO(#9rrmUq?Fm5V+1jq{)9K^fPSbua;M?nq^nLl^h5TAGv+V<4%l z^UW-qZIz0ZqbFy@G;6#C@+S^%9#=`-l6k_XCwC9ePMX+RNP~{eO>3N)y)y@YB)rn1 z64#31&F(%^5E5J}4dI;O)w6HO9Y^ItKaSlhAp_!1H3$ZwWshxRTS!nQRPL(*V=1?&43xAYRdII$;2N6imTp4va(^Z-v7DDd!pYDs~x?fmkk zo?tlgVQrN>eD>h#t|ZUc)Jaix^4QR^5XdM2n<%;khCMUH-4PF5u3D2c6k6uV#O4!j zQwG!2o|1t-DKha2MXeuaMvO%?PhYpNr@V}7QYiA(P+y{u22g0_1`VpHtiwNGR6^x+ zPlQdOr12oqTzryT2Qy;L6p?s?xk}1~DmJ6^e+`{CVZf59lL#@S!&IH_O62`9=0_e4v#cSR|`!#U~Wc|?+dPyusdBQYOlauZsNh$G||XQ-bd(whrLB9 zEMm=YuwGoSE}v#r#po3AJj8ZC3ecR#n=pXs;n}d900*Rda**V~v@Dk!D|QWL zX@OwgfvyL_2k2UFkebGD>LFT~E!F}NH!$VbEDwqU22-=YTg$-V7tn4lV%vtMa!v!UnJrRse%a0Ol&yqO6cd{^^5Wludq*}52) znbZ0x!f}^x>X8M|i<`BmAtOGS)C&zdB(!6NhxPJt*an{QXUll=h=v%bMh>7W)rpJ6 zLTaNVlnsI78unM(@IiNIV7@<_$W?P1Kd0-e+DA-pNfN~FZpNPDV4KUnku$$Ug<0N^~0eo>HI)-cvv5nJU+GHwL~8IVLa zZ*+P;bd8u10g`T$-^5<6sWDBtME3A5aFFdBx()r{Pd?TU+$-RR1F?$5*mRMMvLq~n z7jC>9StFjeMX^WIHy4s+WTr%!s~!oi{fkJ1#TC(2+iG90c(ua5dtlC%_j1u@b206x z6a$9zCf>1f+6fEx`PtG-^QLoh%J3rOc|Dzp`vw|EA9-p>9YMUI>1PHUkhCHuojNWi zQ2%HTB!%U8ckYGhdePC%$mq1^LY}3PcO10LWPKmh0!no|XUXF8jIi+}tHyHs7`pBo z<#rE+OFqN~S`KL&6XK-F38~EqE@7gkZSi>mx`|Fm2A^@1DPhT_>olxH9>+6{yL#!e ztSO9RDc*`Vx9loG3vMHVIy9F7RX!;JLg?dEjadjc%97L^XBG1CI9%VMqY722P-l<0 zk7A{t#L4owS+wDN}oUHn4zuCaw< z^x6gi#t$~nI7)=YnGs_jUPmAMz6FNfjjA6OQH6_+_IMEspa%vX9&)S`rP(;CFEb{s zvK}khKbM4Br``2TktQ_so05ixMqN(2gOvA8&=teDOJhbAjNEoI#C6Y-`kZq#%6y|defEcy9n79gm>HQ+|{k+c`cw0Dd1Kf zH_Eh(O){BKod=|W9oWjbX5p<*9>~8{L6dV_O|7jKYn&OO%a1CVK&dT^nm9GV`FQ$I zjo&yif={Yo7NEP|4!ts4V=qb!Mc=*ZOF18v-ou$`VzM@E=Q(ZWXJrt(5C3 zMWYz7j|*3ns8Be`J~QgmyC%M5+#)7?aVwwItTOR<30V_3FQo)jSz=yOePx0L;pS_z zFXfTRv$NDCWObA@&~=z>7VcDvM@Qjrb!HPH8&7B36kLp$8X3Q|pvz=LET^C>WADRUPsf;Wkl^2%|1UX3i8G$3|;2u7VWs7d|u5B!Dx|TqSW#+D=?6 zX01nmw}%+7hRCl@Sj-NUW>PI}Q9)W)`gACbzuhrVAr12|^Lo}NOAoUuiej|O&T%5E zV{Fb7u8iH4DK^by2=DJ7Qgn@j9BcS-Ul%rbd%lil`Fc)lO59)PEOqK+_#(-*+i?~| zP34YPQGBSgH#kgly|}hgW@)9B7#+&wbbOfD;7VMpWJlfE_VByNs$K<@T~rCN^X{!? zjMA-7W}S}B>m|w`@Lm_C>I^mDCm9w~9r~P_wt$pVJA*LQxCl&o2}sr6>&!At{5F3y z5%~D2V1D{~Y{G>?YRaYTtp&}{?S=Izj(Yv;RSLxfeEzLaIw?aa?YIGp1R3zKMspTJ ze%cr}X(`fVOOPBi&^;M!F_hAmynw^`E2^i4nW58lpd@Y;-H4U&r}3RCLz^gxs4)Wx#;>}MgDXOkT^mMrhZKG`0|Hl! zkzId_-P)`Tbt%|QdkHq81Wuc6nhpE#4STN5%ihjZ4vCPnxS&-8yVK{ub7c^fzA;oTvg}JaLcp@ z6e^NnGqi1K(`fO^Y#bU52+#S%HC~cdFFEuz*KAa$qoyE{oH?kcTiKPI;yU0-XBc6gY&6L3LI1$QmH)Kf! zpVzcmK2=fXh>T@&DH*aM%82E=1VY!Gpn)S4V=!%4e2Bt8LwL--NzNxOaW!mK4=oZc z4A2F}MWK9~An}USBlWZ;77<=iK{`A_B52P-K}o`?wwjs@wXsO3!c7eT2NgNt$C*BU z4FV)cRJ56l!7zjl8BxD@BvymFPbPe|5Z>aAG~Gz=B5)Fd7b2J~XF$q(j!#}s?p68M z4@=jq+rOKY{RjTX!b->XpF>i%|5N-=^uIfg{=)zMsbBD)89*kM?`bJJ(|2pzcgG+d z6aD{gTKaE%M@-+7xW5~h{$sHA&!e_~r2rY&|Gn>M{4WYn_|GRK?Y!_HVJs7(fP{Fg z+CyHv55Njg(|5gKCWx=@G@Er1ja61!+cHi}K{-3C#<$gODj#GRtf(l+xLX~tGeq7z zxloH@_X^?C;0!vVvqc*XOtB!OjZE#~$}*yXay#>2V*n^ZrwX{@VT2V|`F#J$WCBep z7du8>-2ozy_qdIbb`uz(tvTw!_;9JJsOAuvQRPmOHHb*)HN$dF**E`%J;r=Kj6iC$HiDo=FuhhBx9QWJw{@`)5HK zOuLyk(4lFx<=^e4|4WeX|9A8J{Q&lRH~pvP=(_`u?*I9K{8zQnKQ{e8QF(0tLglgj zE0xFgZ>hZR8clYFf12ESK)ER`wmoUwUvei%*)X;t*yxkmw~ibL{2(CXNHF?A*p~Jy z1I37eQ5=*3&6GswK&}qi7PvqZq^?onEKjT|q|Cz zulBvC=_z}Zh~PEz^UE>&_^V-b>-?_j`pze%Nd+f=mxflH_$N4g#`-nn>XEj#47ht1 zeEO*llJXq1rmx@SABfqcrl7TtKz;?$P$Pl#k~PldW_O8pRKqWLeDY@4nsLEtZeJ99 z3_&(uWiVJN13)1B;pY}J(chmu z7nFc0+n#6D{t_2okR7IiePRzJ4#&s8CfI%-3+mgNDSnNwWZS3154d3!Jm2IfS)IA- zg2h*|5)uDYRG6P*lscNIw7UCRl*M(Ie!nV$+iQ`I+^FgZJlT`QWe>mS;9nNiE%-Bs zR@;>Uz3WN%LdpD%@Uk?&+FvS^0f>5z!q(NJ@epa-g?38o5R=wD;%5Q1CU;9ip5Vr= zcMT}y;(ChScEP^tlV=Hi96-D>IcSA21RlU*`#4LFyuh4FV|fQ|L`@4GKp+mXEL#zt zeR1Altx8SBi#p@RWz>HBaEEx*C(=&h2ODueXjEmDT@H4l#cvrNF?1rtz(WX=BUH$a z2XmT_J9jh##IG_6;lkq`yY0R9dq>N}H_Gpp&9NpEpk%Y@IK}4>RF&QO{dg`0wE^AI zFWxRr5#PQ`I9EYf8(&E|Sxr{n!oo3aXdn!_fiT2n*(PkcPq-K2xg-IYs|$=)9hsFrMnO#_@WWZSSnc#e#VJoDJy*?DT2FS ztkqp7KdoeL!C-mn(Q?=v|oDuS=uUzkg>`|bjxv(vn>R3{B}g$pb+&W>yPNUK~=b~bRS z9`SGbZ>^ORO8GSyrW~G?nfF}YE;EBu+4NbQiYDSE!jSYCV*Kt`shNlxN~P1IQ&nU= z7R^R&yB*S2p;?8f6r0a#UH5s1ygs`-ij=rb(`h_eI?@fdZLbpN)0d9hUyHYj%UU7E z$r}+KRD9eDBj^f7<8yzE;d_P6eu9H=+)K7VY4epBd9*pDC zn(gbuib*b)=W*_`oIVf=XvX>CjlDHQB$`jNEMs7_JuAJYuo68mgEFQPO&!|U$s7r& zN67iwKpG%TCOgXo((j5pqPIq`P7HyZUK6Iz8d|njuCA~tf>=Gz-`v{xy>j^57RgxC z3gZyhR|-Ff9#d~Q4?=ZXbOY9q%Tp{z1q$%7L2_`g+LM2Bs`+?`{g2|l1Rlz+efX`X zRJKyG7V{v9X5W*_zE!qFmc$s0Z8S5s(q>8al923_Eu@7KLPQEtQjwHUiKJ{H-?_)r z`_Jh89(n)I_x1Z}xaOSuT-SB3?Of-&&)E_7`mEp8hfA+ZWh^IN7|lE;w`1y~V8WBu zjk)XO`}+fQEL$`!8nzVfs1rOIuOam8Qs}OF$?Y3NF+}@r+0nPXBepvl*G`R#YGz2> zV$_+Y>LuhiKHO~Tw&A4kmZFmQ+oNC60|$J#8e*Glu|;M9W@Cy9;z~;1b(ekoC$qh` z@T$D_tUC7aGWX5*1B#!@?u;fX4h$%MvF;z}d75Ax(M~z?m*i^7oFz zH4)Q2OD0~f5|2Y2Jr?BNT9!Vn;ChLfbwal3RtWu&8wq(&S3#q?+Cy3?vX1hGi1vCqMymdRo}SabDi$~&U|z|3DtGqJn4n%=wNxLb&C1K^E@R)Hgs)!HAXFG_oLdT|wRTl2)(2$9f3O4@a}Qs&NcERVs2*7{RA=j&~xWF0XIYAM0D zLZh0vjm|&UaK2t=izP;Cyl`;D*&{L>9r5Y!VHpCw9U0 zNyc7<$Zfq_SlysFKmz0b~{;YwwTkIQ@Bch+nM(>h0s^hNX_KZwmWw>S>w6Du+ zPoQ}8^sl?kwD>mUuY+;&;8o-O>hx~2b+C1`qsF$D;l_#YD_uYNe5D=gEXOyxjy3fq zP4yccYE@O>KbF?t*<6pTzi|1Tyepr#D*X;BcjQgNZKa|~pVkWLHJ{85w>&J{WE2-y zCuw-3RG>gF@=jn`qV)san6sDz+tH7uwI{-~H{CbY8@}}L>lsn9*N7P_aQUu8@pnT` zUUs_^tC`KAZ4HP*8}-)DMm3{DQBikl!oTisAJ@xW*?)9b(bbs!fta>!`B8RV1AoXg zMyE=TYT=UJ4JofS7JuCN5&d-|-1o+ms{Q_`W#1n(GI@?%6+9BDD!VGs1#^F?V|#pw zdeUu43O4M!kF(z1V=qR6k}n+4X=qu~>%a4{Z;+Hm#OIxcPQA1<=Jt)YLn5i}LZi8Oud=xF{UgKr+|uADfcd8c9H} zn6eLd?!~iDRxgI%maTVa``3poO{I*}hzoGIaZDn{&hV(=)rJG>&TijyhhcLx5xwD9 zWRxo5@m<4_p7^`oN_JT_&Ge3K?x|>Mv)3Oqk$gQ*v9Heq!m}J{q7Orf?a3+ZcP4>+}A-T3xpVkYC8pzrFTFJK1IB`4469OXQ*BK05EeGtkpZzv=m) z)4YDdcPtk1I3@pZWCUXcE8CvM4^9K zJ;F1Bp1>Ywy=H;Wn9<1cqKS;r&Jp`HhXJ9ow?B z@Yccn>lN>*dh378*ta$G#;ceE_0>Wzr-bho3kMc=pD4X-_o;g2NOlP^jZQDeb_ou- zv~7KDu3Oe)@j!-^H@Lapn=ldR-S+V7z9CiG1**apf|dKnx1X8K_O{|Zg3-Iw*M^5N zHn+Z=6#Z(QYP2iPBuTs3pt$|yx!N6kTTt)bF|PVt&$*~(qKCNH-fH4zl~g)j9^CQr zyXM|f_t>c;wWAFmYQ@*Lje00O8_!Y2^{)(>=(eO=F#9i=zg(~GjqUjMzI>v(`V%?l zlclg(S}45#)p*^p-O5(xt6DQwnXE@gYPxvXq^L!1NKlXF9m^@Iy&C1bh_d>-VTeFz zmG3etYVT^4bJ|2_qAi(xKUsKLz3r_hu{PiOJ7dXj_MmYhJa5zWcxA`e_$ z>cGFk%SqDr%Z&~q&m6z>PDbloB;U96+wV$?Wx|&Wo~ktXmwIvdqT-mTx9g4!*Oyl_ z6Xsgdls{yycU zWc=hyN#uY*SFdqYmT&>BfGVnPtJC%slfXC5g;6B0?X$X7oWcJl(M%#j_vob8+Ma@# z_wgxdEdIlyT|?ItM+VLf>>1TNIySZ@;l%Kh>q#G*n}Ymf3>&!m0W8? z%hyPBj1PKq(RXVew0h+~p(|dvY!ETNy5rI2v7_hubY;8XcEoX|^_qR}v0o{cw_GCz zB8vZX@VC;EeZS@XX`g@+l`_4KO)}l56`Jfn3^+@*-Le~6?I?>m`O#`QQ7Ri5o}>jd1Fh*%~ux>XdOh$RX&(Zr!8HKb1Qw zCOpJ(oyMMvENG>-U%au&z;Lgl%X)2{=gBt~)qHAtvAS^7!R7qhizTTyhHX{qCcw$8 zf|qz{{el=G-pVP1x@)&(_KnBdHR<%dOI4ljmW_mGVp^1TZ9*#K=$p05pDS~a5d%ra zz15;_F>)gfyDtVAsYl7utHk$r4M&OOl;&83+#am#=DvPdI#}VOvCpCnPg-8QXDTEn zzv||xa)`4$amrAbcZq^uMN3+Z#;EY&y8>3IAKYtUv- z!^O<_u1BIF$O#JW`>66>4egPXU>)4YA+M>tLB|cx2yr1z&))G(91%DHGxYSjyFaQ$ z!eeRb{s?0lPul03ynGt5mvwNNHy1mdTV)f@b0R zf08$z@_>0Rxi5YSRvxf8vXWN&Fzu$)ndG)!74K8Yw;z1sQ?oS4)4Pzr2Ax<(4qmFm zyaIm_7Aw4aO`Lyeirb^d-6Pxt2}!XYr~cqW0sPWJrcJs74TzPk>21RO%RST$MDOUW zsd|4`KB~4*!S`$Xx|}zo+&KZqq}M-PBDxoG4JWc!3cT)WIH7M`KWUJ9i&0CcIuY*p zXod32^~%%2!fwRt?g4+uJyH-lsn^Q6%Cux8ls5z3c5h=?|U^G>+J z&Zr#x&3g))_0zQER;~2ifeLm#m6_JAkn(!Des7dkAWxX%uw!P5n8@2a0S~S}M1-xY zC3z1cX@#pT#fJ)S>FWn&KdIe!#|hs1Y2_gwmu(Ubc@1(WB?zap9&`yr=REp6$p|pW z&)aQcx_rn>x@_dQep)HhB~!Xm`15l2TW7TW^H05BBMR3JBSmn>=3q}$$jQDajz!#E zXHdPP*Q}Z-^`t5NO3bwPzDGkE{(kq5tkD%f-b*mSCo77Ks6|zJA%qyu3szh_o#C2t zoUha8DGM9IP%WQb#)pn#lQY>T&GToJ-VT&w~Ty`8)??D zAtw%$9QBzh7yFYtL@4`RzjK!k+)WP8Hw@Qds_uHQ`tVuRg9L@JU3?)4E#ZzAA9aLI zspMR(dVV$8+91SNaM#GUXsV8Cm`%RbrOm9TCgy8vn(xVKGOsm1N++;nyX+BrJIFg+PQ~c%3k{HhRGJE1o(r?I$_r?CVsiB ze97ZtQ^%7xm(BJ#(GA6f@puAG@w6uXYOd$gR$PjpX2Ks~J2Z^>!XCuMe-XZJdeDG} z-|Sqp?BN?}4IS4X)#06YoV_5ZZ0KtywX(V(7U{bGKz^>Vd;+{4zKj3x`i$;(5A*4n zT_W)8{K6uV_=j^k%i?VEFS}^BoUAqapBkhYOYpGRuv-oFUOUKPiOnvRZbql z#eAXB92|onzJ0tRKO$?7HA%UYwWy{0d)3`F?n)Y95 zmAY$}cTd;>UA(U7R%wb{gbVyu%aEK-mf3dj&UlN|@jth2KOU459wd7YYZv{@`w2HO z-oi(>(6KuvW@V^rd)SkqzI|QQ@*=MVoSDP?xRjLV9`p*J&i|>}n39YGa2}rYk)&FAex~GNNO+s#)(uP+amj1In%g3oC z!AbWt;o&GYB?x)E27bq`_;};`1o4~{gNv4^(d5@&ata2aF-rO8JSO%|=o~Rkb5&U3 z?yqx0MLT;?MncL-J9bPyo< z)dm|^z>cga{FuH*G5}ZYt*+J>Vuj4x=SN>@D5i}s-1}!>bvnH--0Qjz{1q;fIr=v{|vCYSkmSURTzg!vXaWP`>QZo#b=@?K2C>jyr~x!6Lndtw{mZ4gH`Aj=}q64Ng$?< zNkt;VMy}Lv<5O3Ec-uc>YH>>oNl2~n=s86DRgH>9W^u-`&gn%7ZXKueqDSXP#iT zPA{yFwE09YxpjN+t>k;;7*gm8$rY~^PZ<{bC*FPG<-gQGz$up`zV;eg)Cy6N-5n{8 z?^|uLXJ@fGFY8o%gk$YkwP~t4`ul4EgDl6k4XEp~UV%kZl}Tu;mA&*OCgyjl_?_At z3=KR5EB4hdJ6EKq>YhG|q`UKyjob9MrlzH(x;{&FJrOwVqTQT*5$4!Wi6P z?tco$mOy|)T3I+qV73JCP9-c}sN z71L^)Uea{aLPs;FXLNT#gXj5diKp_XP2TlNRuBoRgEZ|n(=|Rx?a%G-*7Qm?H|-Ya z<3Sc6h3@rBl=?od@pxJ3?_k8t@OZNAvHZ}s*kq@e=#^-_%N6@xZCLdsvTaGjB6ynW zSmy0m-93BhKI*B{#tFzI^!n6hg*W0&tgpeRz72U9LBa^S`lHntW1CE&rb9?H;td74LFq&7|$$>3c3Ci?WR$ zoXIRi7FM4z+p$h%_a9j`5;2dDmkM27(s*{4O+yfcJFz#;%?>@1eek;3ST(|K%qOMx z-iBvA>%?oNDs;P>{OJvcMZ?`Xg5Q1VP)X0%-0zd?w|OmQU2X0@eZqQXjvJ_*CkpWtSYXXpv$B=64lm#uavsU3qfGj2uuEYv=yVN##4w^?iy%9^6MQ zj^*qKF6!$#^wc5l#ZrxUZ127Rv6f&7NlX7%8tG0#UM~XbvkQkSPm0BxxEx&0w?{8i z%=mSY0F@a$o@o;D$XC~~NiMfi#Z~WyY2!tNU|O~vUtEm$;aJ_ElNYm>)f_u$!MJwi zj*z7Kvsb>-@Jgb`Tgfi*kc4qxRm-j6LMtXdE)L}SGsW=XK|yljje~WUmgMhQQF8Ou z&7KyONK06=kG8H&yT+kj!zO{VD640^tF&6UKiE|@o%q@?c1kQ_x3$x`leu+9VTbXu zj%yy}t|l-VJ5Cv{H{1DqsfTo~KGwf)yK}&YMYl)tbjqEdO$^6*=DaC$U1cz$z9;&W z1#7wd?Ll|ux&W~=ciiK8LRL#{?x|5X^C48>#e?XZBWze!86KG>Hdi$dT`{XN%DrUG zTo;z#lk*g@Y{1F&;H#i}eIvCAil4udeB^g+J!ZDX(dCWyj@-&GOLLD2z8uUnOKMSa z8f!S~b@KL;hPAgh_HZ^NgU#fOAKtMoQ}_%0Bdw6nPS<6vT5 z2iKIZ)aNo`0fzB|;_GYEyt8_BV@Pl}+8Nzs?_28~zw)7FMh+Y66gg<#IJ=@KUpKA0 zt0PYdhrXp@n_onVEJLTRk$zGbx`^U-P>B~k7FrR& z7|Zt?9338fKQ1wD#FZow-H>E1oAl|#7x8qa{l*8q(G}ziEDy|NfBwaxSBP>e-PhYi zCj%;y9=zU3@VUB{%b;4B%kN0i9omG&w}|n<&zchBUAcp2mVHmC2%IMD|9-mSPFUb} zG^tc<+kwFvKerE41w&apgWpZZ?(Q8^@_U6I>%YHlyoh&qPkB)8;03GR+B?>Reeg{~ z`OMDJ;lTk5k@ALe3$MV6Ho9nck!T6kpC~#RT=9A0;n3$5gLfFO`MC1w0j&h{x2+z!$3@+JmZx4MShmNWsL`VyC~fW z5?+&sWye1k3@WYhX0(Z9j+pj=|Q955~=mkhdX;r zI>n!jYY^I_7K=3LUb9}|SIsLFCaHjw4;136=ZzC-zbPRminCQ!^4nszxfPwMw45iS zlCNRw_{ry;m+PVuyswm>*@!%Vl05uHN#CzD&abo9&v9L5BA@X&yIb;WvcIti=2!R7 z8c!?K>p#zses-;`nR=M(*~B~RkI(b;&bhol(3d8D#q)^cRi7jB7Oym22obFHe%_gt zB|L6$Kc`ET7F^6#on(odhwhQaKLnsIR2Wx3A2Q~(gZrPScD+5%ld^dGTHd-dSK=#p z;}g;Rb@!Av73zFRmLZ!U$jhsrZoWkl51wFITyC+A&fRyQ`-AJbOIEZq8Q7bvH|1Y1 z$Gx=h7W2#Ey@ZO5UliT0^ZdE2Oebr@GId$L3YHank93dNgWh-9KGNaVm_eEI$=3A}Z6X z61jqx_iL?LH}`t_%@P0Hqcfj?1O~=e(uA zDYd}-hpVO7JK?iSE#|(Njz$0dflKvJsfGWiqui2hs{Z{unzfd1+T~l7vR%Iak5z}Y z;o!t2MrVKD@TJ@*f4no_N;!#sop$6msa1P@$=kiyYQKJbo1dAT)ytYM@J7jbhYmrWPTAOQmQVMUxym$GiGz1ZAmwx8=RxMHQknIqvU0KELttl2%lF>^Z zqS&14aEI+0lF&Xo6n|wDyoaAr)@_L}&}p&TRmw{{Qj2_R z7#q)@nDbMG)jv)pmOVHOTw4Z_0B$N{f6Nezogo%GLoD_$VrN={>+Sz5n%N`#MDq_T z4w(0k(+uuxgK)<{(gZ$cIDna<3o}C(W`+Zp84h4(IDnbq0I0NMGil~2*qNu`W}dQO z8DKy9Cw`!zr$cA0{J&rbI6v^enoMvR9_arw_MGL8|EQwQObhz2X_@)J&CJe%=L5}_ zgP%xZn-|UrVRFy0H{8g78yvtCw#9*r3}hX2X|_yr*kAI(=JA}Dm1 zO#r2zvEtO)`C)~@6A=V(O(F(hg(Z*?z`Niu2;Bb-D+dnXpzigU-Hb54cj6x$o!Tx`?#Xqdxyy&BVaFRi@ zVIp@~Gn~}u?)J{s%vpkl`6Vq-ptAs4AW38655oY}0p|~+Q3$Xi{$HYn$)F&C&v8XU z+@FUE{c4h+&~2fFLBy~fCUgQF>bIT2EPbD)Lh~Vv1}WJr3-K%R7U+6GO+Fb1C;|~b z88XiJdAhoP(AF)?Yh!@&KyDj26d-~`FkfU4;LIzC_y=iOm{Z4qRVfs90Q{jt@E}^q z|DP$ra;`t-X~Tix#)0e@2%oWdNWeg%Y+fdOVRNEEbEf{V-@o#s?8Vvd8v!6r5awY0 zI}^5m7pw3Ha1iC+TepRg0Wtwh`rlCUUql305P{)gXfmJyh6T~VynN2WDDiS}Myl!U z0tDI7?A^fu_Vi!IH|%u7@6QWZr?T+}Nf{9I!muD#n3vC47%faU8geHH@u&<9YYHI5 z`IIF$gVi~Ruh8IE!o0Nl zrwIRVGYOoFb9Sn00X!!`_~9@!A8fpF5YF>)bG9@l#huAS*fXqcovG}VFAM2F_k`Qh z;WRkai{k8VONBGts1%wVjRI#u$#5Er=0SzqQrW95R|;4*!s&K!Cf$X~asZ1l&~N5D zI7mkU`%Xr4th(4q%HJmwgNC9iG#U>Dy%=Dqz~&vAFi&IqThk)V&h>y~kPFD_!|_BC zihw6#QJiEf%#mXVcpzB>pcr6vj3xnXCjeT0cWwWlWQ)B2DH{KNh{N9<~Tq8k2>vr+Xkh+?`#Kwp13#Ln2l8s>4C_ zPllt3cp_RF`byQE0qp@nm)tODZcs*Z4~^wa)=w8t~GN<_c62 zM4m`An)oZX#NO-reI(ID95mGsZXm*e;!Y4v&&Q}N5X*%*Pl%Nm0t%Qw0v1~9gEaKK zLdpM0K7&*=9HhV;L7p8BWRJ>lW6+qu=us)|P^1M12|&&SHEv-`UBE;r&Vn+&Ai`qD zS^s#(m%XR?`;$+Gvb=Z_q$ZH9B?5H^nXdWF{yf#4L#4@_Hc1r-H;4+rrm#KO0-o6u z2D{4OubtN3ba#+?1-{0a3IVv0&{YI88zAO0yD~`AeP%FMcW39B|4?8rW-pEENoD9W zsCHCnGndK)J11H+Tk0M<-Py*P!QRZ}W|^z%jvIKboI$tq=cmIIx!RYP4v1BJq=pnw-ap@`tFBmx=ysH0FA6bgkWg3q6R zC!oQ1s?ZM@M3q5>7G8fn0%}SGeW30{5bm-chXKvNR=ce`h5Dl%4(bjK0ot@pT3}G%Y2aQ40vv_kh(c|IrtZi7 zzZc*qHwvVmxWSYD`Ufftz_vWh4mJZLV+l}w0`w2&`WFoR4sG+zhG79p956D_mtVAl zN^EBLC19X3g;_8%4ov`3H46r!IB1V^HjE6NxiT9@#^C@3vtcMQ5fZ~$ebK;w0b4K& zMh4+KXJ60`itc8$L*s~ePCCdqu*<^H4y5+LEgG}?lJOX*q+{0e$aoL~&Vdn7ob;0M zpu%cSJ2GJo4+v=dTo@KQ3}p7Wpk52uZVng;1yo@+j0kG2=D;v>c~1lt9dp_d=FA6~ zNSfP@gqquqgn>fN*|-4TKW7XQ0b2ENv?D>Or`a$v3aT;SfMK8#0}dGO7ce||&KP78 zCl7!TKyl6`fPoNr_80($1GO)+#sx4u6qfvq3xE-!&1nu83A##cHVkCQIPe2GK+GH+ zB;!a>QOVDJL8_2QQA;DW;ef;pZCV9=d# zv&O*TFhov0!r_3XbGF0d=HLt&G)`RrFz8gJS!3Yw__;91zH+uBfL(kJ7y*pS$v*&_ z!xsXcz&US_U7ur{2w)7(=Mk|asIY4m&O|JkQ-*+l0r_6eb|6K;$x|YZgqrg_0%4Bb zB@zg8$0d;F%n=ckbj`t;2)1)Ma3SH)P=U`ZIsgocbU9$yIXordh;!^I2?sn4M_+Jd z(VRIVV~8luG04F6%&`a9xx6Q1@lY~;7Jk4tLZwmwWEXWp}+o6gPFs&qSWykM0Fq&WU{&{PD7cbrUEX1 zBB-jPu^KqEhQ>DTzi$H7zNpbDst#0&6Vu%VhEyh@$Y_ExhNMQs;8n58z!4B}pfUxc hu8LAtC6J)dh{>{Mu-IV&pn?RhbK0;$T~CAi{{R Date: Sun, 9 Feb 2020 00:53:42 -0500 Subject: [PATCH 0613/1069] Update SECURITY.md to fix security bulletin links. It must be past my bedtime. Sigh. ;-) --- SECURITY.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/SECURITY.md b/SECURITY.md index bee2b246a..18d510ad9 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -46,5 +46,5 @@ Eventually, we would like to have BugCrowd handle this, but that's still a ways There are some ESAPI security bulletins published in the "documentation" directory on GitHub. For details see: -* (Security Bulletin #1 - MAC Bypass in ESAPI Symmetric Encryption)[documentation/ESAPI-security-bulletin1.pdf], which covers CVE-2013-5679 and CVE-2013-5960 -* (Security Bulletin #2 - How Does CVE-2019-17571 Impact ESAPI?)[documentation/ESAPI-security-bulletin2.pdf], which covers the Log4J 1 deserialization CVE. +* [Security Bulletin #1 - MAC Bypass in ESAPI Symmetric Encryption](documentation/ESAPI-security-bulletin1.pdf), which covers CVE-2013-5679 and CVE-2013-5960 +* [Security Bulletin #2 - How Does CVE-2019-17571 Impact ESAPI?](documentation/ESAPI-security-bulletin2.pdf), which covers the Log4J 1 deserialization CVE. From 5ed5f253e3e9a5135a627942e17f9c5791b09f07 Mon Sep 17 00:00:00 2001 From: Wiiitek Date: Sun, 9 Feb 2020 21:53:35 +0100 Subject: [PATCH 0614/1069] upgrade for convergence --- pom.xml | 29 +++++++++-------------------- 1 file changed, 9 insertions(+), 20 deletions(-) diff --git a/pom.xml b/pom.xml index 6a34d2cc0..1997e5a00 100644 --- a/pom.xml +++ b/pom.xml @@ -364,7 +364,7 @@ org.powermock powermock-reflect - 2.0.0 + 2.0.2 test @@ -526,36 +526,25 @@ - enforce-bytecode-version enforce + + + 1.7 + + ESAPI 2.x now uses the JDK1.7 for it's baseline. Please make sure that your + JAVA_HOME environment variable is pointed to a JDK1.7 distribution. + + 1.7 - true - - enforce-jdk-version - - enforce - - - - - 1.7 - - ESAPI 2.x now uses the JDK1.7 for it's baseline. Please make sure that your - JAVA_HOME environment variable is pointed to a JDK1.7 distribution. - - - - - From e179580e133c03044fd6c0adfc93d335afb98549 Mon Sep 17 00:00:00 2001 From: Wiiitek Date: Sun, 9 Feb 2020 22:25:30 +0100 Subject: [PATCH 0615/1069] new way for getting file content from filesystem --- .../reference/ExtensiveEncoderURITest.java | 26 ++++++++++++++----- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/src/test/java/org/owasp/esapi/reference/ExtensiveEncoderURITest.java b/src/test/java/org/owasp/esapi/reference/ExtensiveEncoderURITest.java index 37e9ea213..36bd7c116 100644 --- a/src/test/java/org/owasp/esapi/reference/ExtensiveEncoderURITest.java +++ b/src/test/java/org/owasp/esapi/reference/ExtensiveEncoderURITest.java @@ -2,10 +2,12 @@ import static org.junit.Assert.assertEquals; -import java.io.File; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; import java.net.URL; import java.nio.charset.StandardCharsets; -import java.nio.file.Files; import java.util.ArrayList; import java.util.Collection; import java.util.List; @@ -34,14 +36,24 @@ public ExtensiveEncoderURITest(String uri){ @Parameters public static Collection getMyUris() throws Exception{ URL url = ExtensiveEncoderURITest.class.getResource("/urisForTest.txt"); - String fileName = url.getFile(); - File urisForText = new File(fileName); - - inputs = Files.readAllLines(urisForText.toPath(), StandardCharsets.UTF_8); + try( InputStream is = url.openStream() ) { + InputStreamReader isr = new InputStreamReader(is, StandardCharsets.UTF_8); + BufferedReader br = new BufferedReader(isr); + inputs = readAllLines(br); + } return inputs; } - + + private static List readAllLines(BufferedReader br) throws IOException { + List lines = new ArrayList<>(); + String line; + while ((line = br.readLine()) != null) { + lines.add(line); + } + return lines; + } + @Test public void testUrlsFromFile() throws Exception{ assertEquals(this.expected, v.isValidURI("URL", uri, false)); From c951b1cd05112bf9760d3cd3d08c842c418c443a Mon Sep 17 00:00:00 2001 From: Wiiitek Date: Sun, 9 Feb 2020 23:32:58 +0100 Subject: [PATCH 0616/1069] update for reading configuration from file path with spaces --- .../DefaultSecurityConfiguration.java | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/src/main/java/org/owasp/esapi/reference/DefaultSecurityConfiguration.java b/src/main/java/org/owasp/esapi/reference/DefaultSecurityConfiguration.java index a9fd89cc9..d578850ba 100644 --- a/src/main/java/org/owasp/esapi/reference/DefaultSecurityConfiguration.java +++ b/src/main/java/org/owasp/esapi/reference/DefaultSecurityConfiguration.java @@ -20,6 +20,7 @@ import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; +import java.net.URISyntaxException; import java.net.URL; import java.util.ArrayList; import java.util.Arrays; @@ -581,13 +582,17 @@ public File getResourceFile(String filename) { } if (fileUrl != null) { - String fileLocation = fileUrl.getFile(); - f = new File(fileLocation); - if (f.exists()) { - logSpecial("Found in SystemResource Directory/resourceDirectory: " + f.getAbsolutePath()); - return f; - } else { - logSpecial("Not found in SystemResource Directory/resourceDirectory (this should never happen): " + f.getAbsolutePath()); + try { + String fileLocation = fileUrl.toURI().getPath(); + f = new File(fileLocation); + if (f.exists()) { + logSpecial("Found in SystemResource Directory/resourceDirectory: " + f.getAbsolutePath()); + return f; + } else { + logSpecial("Not found in SystemResource Directory/resourceDirectory (this should never happen): " + f.getAbsolutePath()); + } + } catch (URISyntaxException e) { + logSpecial("Error while converting URL " + fileUrl + " to file path: " + e.getMessage()); } } else { logSpecial("Not found in SystemResource Directory/resourceDirectory: " + resourceDirectory + File.separator + filename); From fd1650d4fd8da129843a1346811f26dc43466de7 Mon Sep 17 00:00:00 2001 From: "Kevin W. Wall" Date: Mon, 17 Feb 2020 14:14:54 -0500 Subject: [PATCH 0617/1069] Update pom.xml SNAPSHOT version Changed from 2.3.0.0-SNAPSHOT to 2.2.1.0-SNAPSHOT. --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 1997e5a00..881b59c5e 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 org.owasp.esapi esapi - 2.3.0.0-SNAPSHOT + 2.2.1.0-SNAPSHOT jar From f8180f59c636e90a9677deb9c29afae2f1f7c665 Mon Sep 17 00:00:00 2001 From: kwwall Date: Mon, 17 Feb 2020 20:42:13 -0500 Subject: [PATCH 0618/1069] Drop 'failBuildOnCVSS' from 5.9 to 5.0. Corrected spelling of 'suppressionFiles'. (Old value still worked.) --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 881b59c5e..132075ffd 100644 --- a/pom.xml +++ b/pom.xml @@ -689,8 +689,8 @@ dependency-check-maven 5.0.0 - 5.9 - ./suppressions.xml + 5.0 + ./suppressions.xml From 448c8f3a1feb517ba07880e709ea4ed37ab447ee Mon Sep 17 00:00:00 2001 From: kwwall Date: Mon, 17 Feb 2020 20:45:04 -0500 Subject: [PATCH 0619/1069] Added suppression of CVE-2019-17571; deleted suppression of CVE-2016-1000031. --- suppressions.xml | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/suppressions.xml b/suppressions.xml index 181b75909..ebedb9648 100644 --- a/suppressions.xml +++ b/suppressions.xml @@ -1,10 +1,22 @@ - + + - .*\bcommons-fileupload-1.3.2.jar - CVE-2016-1000031 + ^log4j:log4j:1\.2\.17$ + cpe:/a:apache:log4j + CVE-2019-17571 - \ No newline at end of file + From 7aafaeeb3476c86886756699501f09963b8657f8 Mon Sep 17 00:00:00 2001 From: Wiiitek Date: Sun, 23 Feb 2020 18:12:58 +0100 Subject: [PATCH 0620/1069] additional time for windows to always sleep more than given seconds --- src/test/java/org/owasp/esapi/crypto/CryptoTokenTest.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/test/java/org/owasp/esapi/crypto/CryptoTokenTest.java b/src/test/java/org/owasp/esapi/crypto/CryptoTokenTest.java index 8f43d3b1b..0d2b8a519 100644 --- a/src/test/java/org/owasp/esapi/crypto/CryptoTokenTest.java +++ b/src/test/java/org/owasp/esapi/crypto/CryptoTokenTest.java @@ -391,7 +391,9 @@ public final void testAddandGetAttributes() { private static void nap(int n) { try { System.out.println("Sleeping " + n + " seconds..."); - Thread.sleep( n * 1000 ); + // adds additional time to make sure we sleep more than n seconds + int additionalTimeToSleep = 100; + Thread.sleep( n * 1000 + additionalTimeToSleep ); } catch (InterruptedException e) { ; // Ignore } From b479d5908d10100a32355c9b9cde05964d4075f0 Mon Sep 17 00:00:00 2001 From: Wiiitek Date: Sun, 23 Feb 2020 19:16:36 +0100 Subject: [PATCH 0621/1069] inline reader for try-with-resource --- .../org/owasp/esapi/reference/ExtensiveEncoderURITest.java | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/test/java/org/owasp/esapi/reference/ExtensiveEncoderURITest.java b/src/test/java/org/owasp/esapi/reference/ExtensiveEncoderURITest.java index 36bd7c116..0283492c7 100644 --- a/src/test/java/org/owasp/esapi/reference/ExtensiveEncoderURITest.java +++ b/src/test/java/org/owasp/esapi/reference/ExtensiveEncoderURITest.java @@ -36,10 +36,8 @@ public ExtensiveEncoderURITest(String uri){ @Parameters public static Collection getMyUris() throws Exception{ URL url = ExtensiveEncoderURITest.class.getResource("/urisForTest.txt"); - - try( InputStream is = url.openStream() ) { - InputStreamReader isr = new InputStreamReader(is, StandardCharsets.UTF_8); - BufferedReader br = new BufferedReader(isr); + + try( BufferedReader br = new BufferedReader(new InputStreamReader(url.openStream(), StandardCharsets.UTF_8)) ) { inputs = readAllLines(br); } return inputs; From 048fc0c40549cd86b28d0ac5ef89cbfdc4cbb289 Mon Sep 17 00:00:00 2001 From: Wiiitek Date: Sun, 23 Feb 2020 19:17:42 +0100 Subject: [PATCH 0622/1069] removing not needed code --- .../org/owasp/esapi/reference/ExtensiveEncoderURITest.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/test/java/org/owasp/esapi/reference/ExtensiveEncoderURITest.java b/src/test/java/org/owasp/esapi/reference/ExtensiveEncoderURITest.java index 0283492c7..18d88c79e 100644 --- a/src/test/java/org/owasp/esapi/reference/ExtensiveEncoderURITest.java +++ b/src/test/java/org/owasp/esapi/reference/ExtensiveEncoderURITest.java @@ -4,7 +4,6 @@ import java.io.BufferedReader; import java.io.IOException; -import java.io.InputStream; import java.io.InputStreamReader; import java.net.URL; import java.nio.charset.StandardCharsets; @@ -22,7 +21,7 @@ @RunWith(Parameterized.class) public class ExtensiveEncoderURITest { - static List inputs = new ArrayList(); + static List inputs = new ArrayList<>(); Validator v = ESAPI.validator(); String uri; boolean expected; @@ -53,7 +52,7 @@ private static List readAllLines(BufferedReader br) throws IOException { } @Test - public void testUrlsFromFile() throws Exception{ + public void testUrlsFromFile() { assertEquals(this.expected, v.isValidURI("URL", uri, false)); } From 7003acc1da17eb68bc92c97a2b7d47a093021385 Mon Sep 17 00:00:00 2001 From: "Kevin W. Wall" Date: Sun, 23 Feb 2020 17:17:02 -0500 Subject: [PATCH 0623/1069] Updated README.md to refer to config jar Updated 2.2.0.0 release on GitHub and added jar and signature for ESAPI default config files. This README.md update just references that. --- README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.md b/README.md index f7c38d99d..fd3e2d5bf 100644 --- a/README.md +++ b/README.md @@ -23,6 +23,12 @@ The default branch for ESAPI legacy is now the 'develop' branch (rather than the # Where can I find ESAPI 3.x? https://github.com/ESAPI/esapi-java +# Locating ESAPI Jar files +The latest ESAPI 2.2.0.0 default configuration jar and its GPG signature can be found at [esapi-2.2.0.0-configuration.jar](https://github.com/ESAPI/esapi-java-legacy/releases/download/esapi-2.2.0.0/esapi-2.2.0.0-configuration.jar) and [esapi-2.2.0.0-configuration.jar.asc](https://github.com/ESAPI/esapi-java-legacy/releases/download/esapi-2.2.0.0/esapi-2.2.0.0-configuration.jar.asc) respectively. + +The latest regular ESAPI jars can are available from Maven Central. + + # ESAPI Deprecation Policy Unless we unintentionally screw-up, our intent is to keep classes, methods, and/or fields whihc have been annotated as "@deprecated" for a minimum of two (2) years or until the next major release number (e.g., 3.x as of now), which ever comes first, before we remove them. Note that this policy does not apply to classes under the **org.owasp.esapi.reference** package. You are not expected to be using such classes directly in your code. From e007ec83f5169a7f75ba63354a63c697af685fe2 Mon Sep 17 00:00:00 2001 From: "Kevin W. Wall" Date: Tue, 25 Feb 2020 22:22:57 -0500 Subject: [PATCH 0624/1069] Update to include link to the latest release. --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index fd3e2d5bf..68037a5d2 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ OWASP ESAPI (The OWASP Enterprise Security API) is a free, open source, web appl # What does Legacy mean? -

    This is the legacy branch of ESAPI which means it is an actively maintained branch of the project, however feature development for this branch will not be done. Features that have already been scheduled for the 2.x branch will move forward, but the main focus will be working on the ESAPI 3.x branch. +

    This is the legacy branch of ESAPI which means it is an actively maintained branch of the project, however feature development for this branch will not be done. Features that have already been scheduled for the 2.x branch will move forward, but the main focus will be working on the ESAPI 3.x branch. IMPORTANT NOTE: The default branch for ESAPI legacy is now the 'develop' branch (rather than the 'master' branch), where future development, bug fixes, etc. will now be done. The 'master' branch is now marked as "protected"; it reflects the latest stable ESAPI release (2.1.0.1 as of this date). Note that this change of making the 'develop' branch the default may affect any pull requests that you were intending to make. @@ -24,7 +24,7 @@ The default branch for ESAPI legacy is now the 'develop' branch (rather than the https://github.com/ESAPI/esapi-java # Locating ESAPI Jar files -The latest ESAPI 2.2.0.0 default configuration jar and its GPG signature can be found at [esapi-2.2.0.0-configuration.jar](https://github.com/ESAPI/esapi-java-legacy/releases/download/esapi-2.2.0.0/esapi-2.2.0.0-configuration.jar) and [esapi-2.2.0.0-configuration.jar.asc](https://github.com/ESAPI/esapi-java-legacy/releases/download/esapi-2.2.0.0/esapi-2.2.0.0-configuration.jar.asc) respectively. +The [latest ESAPI release](https://github.com/ESAPI/esapi-java-legacy/releases/latest) is 2.2.0.0. The default configuration jar and its GPG signature can be found at [esapi-2.2.0.0-configuration.jar](https://github.com/ESAPI/esapi-java-legacy/releases/download/esapi-2.2.0.0/esapi-2.2.0.0-configuration.jar) and [esapi-2.2.0.0-configuration.jar.asc](https://github.com/ESAPI/esapi-java-legacy/releases/download/esapi-2.2.0.0/esapi-2.2.0.0-configuration.jar.asc) respectively. The latest regular ESAPI jars can are available from Maven Central. From d26b98f11d0d1bacc66df36d7c659c822bb3fba7 Mon Sep 17 00:00:00 2001 From: Bill Sempf Date: Tue, 19 May 2020 00:01:59 -0400 Subject: [PATCH 0625/1069] Release notes for 2.2.1.0 (#543) I have no idea how to associate this with Issue 542 but if I figure it out I will do it. --- .../esapi4java-core-2.2.1.0-release-notes.txt | 115 ++++++++++++++++++ 1 file changed, 115 insertions(+) create mode 100644 documentation/esapi4java-core-2.2.1.0-release-notes.txt diff --git a/documentation/esapi4java-core-2.2.1.0-release-notes.txt b/documentation/esapi4java-core-2.2.1.0-release-notes.txt new file mode 100644 index 000000000..7f91e07a5 --- /dev/null +++ b/documentation/esapi4java-core-2.2.1.0-release-notes.txt @@ -0,0 +1,115 @@ +Release notes for ESAPI 2.2.1.0 + Release date: 2020-May-12 + Project leaders: + -Kevin W. Wall + -Matt Seil + +Previous release: ESAPI 2.2.0.0, 2019-June-24 + + +Executive Summary: Important Things to Note for this Release +------------------------------------------------------------ + + TBD + +================================================================================================================= + +Basic ESAPI facts + +ESAPI 2.2.0.0 release: + 194 Java source files + 4150 JUnit tests in 118 Java source files + +ESAPI 2.2.1.0 release: + TBD + +GitHub Issues fixed in this release + +Issue # GitHub Issue Title +---------------------------------------------------------------------------------------------- + +143 Enchance encodeForOS to auto-detect the underling OS +226 Javadoc Inaccuracy in getRandomInteger() and getRandomReal() +245 KeyDerivationFunction::computeDerivedKey - possible security level mismatch +256 White space clean up +382 Build Fails on path with space +494 Encoder's encodeForCSS doesn't handle RGB Triplets +503 Bug on on referrer header when value contains `§ion` like `www.asdf.com?a=1§ion=2` +509 HTMLValidationRule.getValid(String,String) does not follow documented specifications +511 Add missing documentation to Validator.addRule() and Validator.getRule() +512 Update Apache Commons Bean Utils to 1.9.4 +515 Adding tests for getCookies (also 516) +519 Issue 494 CSSCodec RGB Triplets +530 Log Bridge Tests +536 Various fixes +538 Addressing log4j 1.x CVE-2019-17571 + +----------------------------------------------------------------------------- + + Changes requiring special attention + +----------------------------------------------------------------------------- + +TBD + +----------------------------------------------------------------------------- + + Other changes in this release, some of which not tracked via GitHub issues + +----------------------------------------------------------------------------- + +Documentation updates for locating Jar files +Unneeded code removed from ExtensiveEncoder +Inline reader added to ExtensiveEncoder +Additional time for windows to always sleep more than given seconds in CryptoTokenTest +Change required by tweak to CipherText.toString() method +Removed call to deprecated CryptoHelper.computeDerivedKey() method +New JUnit tests for org.owasp.esapi.crypto.KeyDerivationFunction class +Use existing toString method rather than a StringBuilder +Documentation and tests +JavaLogger move +Splitting user infor from Client Supplier + +----------------------------------------------------------------------------- + +Developer Activity Report (Changes between release 2.2.0.0 and 2.2.1.0, i.e., between 2019-06-25 and 2020-05-12) +Generated manually (this time) + +Developer Total commits Total Number + of Files Changed +===================================================== +jeremiahjstacey 11 68 +kwwall 15 26 +wiitek 3 6 +xeno6696 8 9 +Michael-Ziluck 2 3 +===================================================== + +----------------------------------------------------------------------------- + +53 Closed PRs since 2.2.0.0 release +=================================== +504 New scripts to suppress noise for 'mvn test' +510 Resolve #509 - Properly throw exception when HTML fails +513 Close issue #512 by updating to 1.9.4 of Commons Beans Util.\ +519 Issue 494 CSSCodec RGB Triplets +520 OS Name DefaultExecutorTests #143 +540 Issue 382: Build Fails on path with space +596 Closes Issue 245 + +----------------------------------------------------------------------------- + +Notice: + + Release notes written by Bill Sempf (bill.sempf@owasp.org) please direct any communication to me. + +Project co-leaders + Kevin W. Wall (kwwall) + Matt Seil (xeno6696) + +Special shout-outs to: + Jeremiah Stacey (jeremiahjstacey) -- All around ESAPI support and JUnit test case developer extraordinaire + Dave Wichers (davewichers) - for Maven Central / Sonatype help + +Thanks you all for your time and effort to ESAPI and making it a better project. And if I've missed any, my apologies; let me know and I will correct it. + From 482194884f5db00b8d22bfde6e5a8823374dd128 Mon Sep 17 00:00:00 2001 From: "Kevin W. Wall" Date: Tue, 19 May 2020 00:03:59 -0400 Subject: [PATCH 0626/1069] Update release date to 2020-TBD Need to wait until other PR is merged. --- documentation/esapi4java-core-2.2.1.0-release-notes.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/documentation/esapi4java-core-2.2.1.0-release-notes.txt b/documentation/esapi4java-core-2.2.1.0-release-notes.txt index 7f91e07a5..bb46556b8 100644 --- a/documentation/esapi4java-core-2.2.1.0-release-notes.txt +++ b/documentation/esapi4java-core-2.2.1.0-release-notes.txt @@ -1,5 +1,5 @@ Release notes for ESAPI 2.2.1.0 - Release date: 2020-May-12 + Release date: 2020-TBD Project leaders: -Kevin W. Wall -Matt Seil From 90d3d0d9ba48c6b372980cdded869faf31fd59e7 Mon Sep 17 00:00:00 2001 From: kwwall Date: Sat, 6 Jun 2020 17:25:09 -0400 Subject: [PATCH 0627/1069] Fix Javadoc --- src/main/java/org/owasp/esapi/codecs/HashTrie.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main/java/org/owasp/esapi/codecs/HashTrie.java b/src/main/java/org/owasp/esapi/codecs/HashTrie.java index 4c8d41910..fef14aee7 100644 --- a/src/main/java/org/owasp/esapi/codecs/HashTrie.java +++ b/src/main/java/org/owasp/esapi/codecs/HashTrie.java @@ -252,8 +252,7 @@ Entry getLongestMatch(CharSequence key, int pos) /** * Recursively lookup the longest key match. * @param keyIn Where to read the key from - * @param pos The position in the key that is being - * looked up at this level. + * @param key The key that is being looked up at this level. * @return The Entry associated with the longest key * match or null if none exists. */ From 9be40d4055c0bc186845e8679c72cf4b476ad710 Mon Sep 17 00:00:00 2001 From: kwwall Date: Sat, 6 Jun 2020 17:27:51 -0400 Subject: [PATCH 0628/1069] Fix Javadoc --- src/main/java/org/owasp/esapi/crypto/CipherText.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/owasp/esapi/crypto/CipherText.java b/src/main/java/org/owasp/esapi/crypto/CipherText.java index 8e57f03e7..1047116fa 100644 --- a/src/main/java/org/owasp/esapi/crypto/CipherText.java +++ b/src/main/java/org/owasp/esapi/crypto/CipherText.java @@ -804,7 +804,8 @@ protected boolean canEqual(Object other) { * proved in 1996 [see http://pssic.free.fr/Extra%20Reading/SEC+/SEC+/hmac-cb.pdf] that * HMAC security doesn’t require that the underlying hash function be collision resistant, * but only that it acts as a pseudo-random function, which SHA1 satisfies. - * @param ciphertext The ciphertext value for which the MAC is computed. + * @param authKey The {@Code SecretKey} used with the computed HMAC-SHA1 + * to ensure authenticity. * @return The value for the MAC. */ private byte[] computeMAC(SecretKey authKey) { From 23608fc130683b1ec1b3efea9331be92a815272b Mon Sep 17 00:00:00 2001 From: kwwall Date: Sat, 6 Jun 2020 17:59:19 -0400 Subject: [PATCH 0629/1069] Converted String array explicity to string, which is probably what was intended in exception messages. Otherwise end up with something that looks like '[Ljava.lang.String;@7825ed4c' which is not very helpful. --- .../esapi/reference/accesscontrol/DelegatingACR.java | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/main/java/org/owasp/esapi/reference/accesscontrol/DelegatingACR.java b/src/main/java/org/owasp/esapi/reference/accesscontrol/DelegatingACR.java index 07af135d7..65cb0ebb9 100644 --- a/src/main/java/org/owasp/esapi/reference/accesscontrol/DelegatingACR.java +++ b/src/main/java/org/owasp/esapi/reference/accesscontrol/DelegatingACR.java @@ -4,6 +4,7 @@ import java.lang.reflect.Modifier; import java.util.Iterator; import java.util.Vector; +import java.util.Arrays; import org.apache.commons.collections4.iterators.ArrayListIterator; @@ -25,12 +26,12 @@ public void setPolicyParameters(DynaBeanACRParameter policyParameter) { } catch (SecurityException e) { throw new IllegalArgumentException(e.getMessage() + " delegateClass.delegateMethod(parameterClasses): \"" + - delegateClassName + "." + methodName + "(" + parameterClassNames + + delegateClassName + "." + methodName + "(" + Arrays.toString(parameterClassNames) + ")\" must be public.", e); } catch (NoSuchMethodException e) { throw new IllegalArgumentException(e.getMessage() + " delegateClass.delegateMethod(parameterClasses): \"" + - delegateClassName + "." + methodName + "(" + parameterClassNames + + delegateClassName + "." + methodName + "(" + Arrays.toString(parameterClassNames) + ")\" does not exist.", e); } @@ -42,14 +43,14 @@ public void setPolicyParameters(DynaBeanACRParameter policyParameter) { throw new IllegalArgumentException( " Delegate class \"" + delegateClassName + "\" must be concrete, because method " + - delegateClassName + "." + methodName + "(" + parameterClassNames + + delegateClassName + "." + methodName + "(" + Arrays.toString(parameterClassNames) + ") is not static.", ex); } catch (IllegalAccessException ex) { new IllegalArgumentException( " Delegate class \"" + delegateClassName + "\" must must have a zero-argument constructor, because " + "method delegateClass.delegateMethod(parameterClasses): \"" + - delegateClassName + "." + methodName + "(" + parameterClassNames + + delegateClassName + "." + methodName + "(" + Arrays.toString(parameterClassNames) + ")\" is not static.", ex); } } else { From 1531303c2d5f0904740124031043f552d77c351e Mon Sep 17 00:00:00 2001 From: kwwall Date: Sat, 6 Jun 2020 18:09:03 -0400 Subject: [PATCH 0630/1069] Add missing space in exception message. --- src/main/java/org/owasp/esapi/util/ObjFactory.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/owasp/esapi/util/ObjFactory.java b/src/main/java/org/owasp/esapi/util/ObjFactory.java index 2c23e5f00..7853ef593 100644 --- a/src/main/java/org/owasp/esapi/util/ObjFactory.java +++ b/src/main/java/org/owasp/esapi/util/ObjFactory.java @@ -92,7 +92,7 @@ public static T make(String className, String typeName) throws Configuration // The class is meant to be singleton, however, the SecurityManager restricts us from calling the // getInstance method on the class, thus this is a configuration issue and a ConfigurationException // is thrown - throw new ConfigurationException( "The SecurityManager has restricted the object factory from getting a reference to the singleton implementation" + + throw new ConfigurationException( "The SecurityManager has restricted the object factory from getting a reference to the singleton implementation " + "of the class [" + className + "]", e ); } From b8c48a9c22317eff3ac52003de9b6eaecce0f7de Mon Sep 17 00:00:00 2001 From: kwwall Date: Sat, 6 Jun 2020 18:10:40 -0400 Subject: [PATCH 0631/1069] Make conversion from String array to String explicit for exception message. --- src/main/java/org/owasp/esapi/reference/DefaultValidator.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/owasp/esapi/reference/DefaultValidator.java b/src/main/java/org/owasp/esapi/reference/DefaultValidator.java index abe6c93a5..530e2efa8 100644 --- a/src/main/java/org/owasp/esapi/reference/DefaultValidator.java +++ b/src/main/java/org/owasp/esapi/reference/DefaultValidator.java @@ -23,6 +23,7 @@ import java.net.URI; import java.net.URISyntaxException; import java.text.DateFormat; +import java.util.Arrays; import java.util.ArrayList; import java.util.Date; import java.util.HashMap; @@ -764,7 +765,7 @@ public boolean isValidFileContent(String context, byte[] input, int maxBytes, bo public byte[] getValidFileContent(String context, byte[] input, int maxBytes, boolean allowNull) throws ValidationException, IntrusionException { if (isEmpty(input)) { if (allowNull) return null; - throw new ValidationException( context + ": Input required", "Input required: context=" + context + ", input=" + input, context ); + throw new ValidationException( context + ": Input required", "Input required: context=" + context + ", input=" + Arrays.toString(input), context ); } long esapiMaxBytes = ESAPI.securityConfiguration().getAllowedFileUploadSize(); From 120ff02fe530ac2933d36253316cf1e208d13023 Mon Sep 17 00:00:00 2001 From: kwwall Date: Sun, 7 Jun 2020 13:27:52 -0400 Subject: [PATCH 0632/1069] Remove references to WAF aliases. Feature not fullyed developed. Issue identifed by LGTM. Checked with Arshan and he confirmed it should be removed. --- configuration/esapi/waf-policy.xsd | Bin 20582 -> 19482 bytes .../AppGuardianConfiguration.java | 10 -------- .../configuration/ConfigurationParser.java | 22 +----------------- 3 files changed, 1 insertion(+), 31 deletions(-) diff --git a/configuration/esapi/waf-policy.xsd b/configuration/esapi/waf-policy.xsd index 4287e792b508ce0b8010b83473965465fca10b2d..1ddc99039e59d5d626245497c0e53a33e74f38f1 100644 GIT binary patch delta 28 kcmaF1fN|Cg#toHBn-!U*94FVyv2A|oUdzG4z{|h|0GodZ(EtDd delta 128 zcmbO=gYnq{#toHBoQVuM44Djx48@Z_u#57-c?_vQkpzd}&HhX-j!26B$<1bmDw_O( yMU)+;Sx?M)vOaGTOm33A_~iA@Rg+mw%_iR#u>jH)lPk^kP5v*=v3a(0H3tBCASYA+ diff --git a/src/main/java/org/owasp/esapi/waf/configuration/AppGuardianConfiguration.java b/src/main/java/org/owasp/esapi/waf/configuration/AppGuardianConfiguration.java index 36f5239fe..53e6c3404 100644 --- a/src/main/java/org/owasp/esapi/waf/configuration/AppGuardianConfiguration.java +++ b/src/main/java/org/owasp/esapi/waf/configuration/AppGuardianConfiguration.java @@ -77,11 +77,6 @@ public class AppGuardianConfiguration { public static final String JAVASCRIPT_TARGET_TOKEN = "##1##"; public static final String JAVASCRIPT_REDIRECT = ""; - /* - * The aliases declared in the beginning of the config file. - */ - private HashMap aliases; - /* * Fail response settings. */ @@ -114,8 +109,6 @@ public AppGuardianConfiguration() { afterBodyRules = new ArrayList(); beforeResponseRules = new ArrayList(); cookieRules = new ArrayList(); - - aliases = new HashMap(); } /* @@ -160,9 +153,6 @@ public void setDefaultResponseCode(int defaultResponseCode) { this.defaultResponseCode = defaultResponseCode; } - public void addAlias(String key, Object obj) { - aliases.put(key, obj); - } public List getBeforeBodyRules() { return beforeBodyRules; diff --git a/src/main/java/org/owasp/esapi/waf/configuration/ConfigurationParser.java b/src/main/java/org/owasp/esapi/waf/configuration/ConfigurationParser.java index 4531ed058..cc429d182 100644 --- a/src/main/java/org/owasp/esapi/waf/configuration/ConfigurationParser.java +++ b/src/main/java/org/owasp/esapi/waf/configuration/ConfigurationParser.java @@ -94,7 +94,6 @@ public static AppGuardianConfiguration readConfigurationFile(InputStream stream, doc = parser.build(stream); root = doc.getRootElement(); - Element aliasesRoot = root.getFirstChildElement("aliases"); Element settingsRoot = root.getFirstChildElement("settings"); Element authNRoot = root.getFirstChildElement("authentication-rules"); Element authZRoot = root.getFirstChildElement("authorization-rules"); @@ -106,30 +105,11 @@ public static AppGuardianConfiguration readConfigurationFile(InputStream stream, Element beanShellRoot = root.getFirstChildElement("bean-shell-rules"); - /** - * Parse the 'aliases' section. - */ - if ( aliasesRoot != null ) { - Elements aliases = aliasesRoot.getChildElements("alias"); - - for(int i=0;i section is required"); + throw new ConfigurationException("", "The section is required"); } else if ( settingsRoot != null ) { From 9fb1ff81b9ced9108e64ec79661d958d28dd293a Mon Sep 17 00:00:00 2001 From: kwwall Date: Sun, 7 Jun 2020 16:53:10 -0400 Subject: [PATCH 0633/1069] Address LGTM alerts about failure to use 'secure' cookies. --- .../esapi/filters/SecurityWrapperRequest.java | 92 +++++++++---------- 1 file changed, 42 insertions(+), 50 deletions(-) diff --git a/src/main/java/org/owasp/esapi/filters/SecurityWrapperRequest.java b/src/main/java/org/owasp/esapi/filters/SecurityWrapperRequest.java index 910733d99..939f572b6 100644 --- a/src/main/java/org/owasp/esapi/filters/SecurityWrapperRequest.java +++ b/src/main/java/org/owasp/esapi/filters/SecurityWrapperRequest.java @@ -64,11 +64,11 @@ public class SecurityWrapperRequest extends HttpServletRequestWrapper implements * @param request The {@code HttpServletRequest} we are wrapping. */ public SecurityWrapperRequest(HttpServletRequest request) { - super( request ); + super( request ); } private HttpServletRequest getHttpServletRequest() { - return (HttpServletRequest)super.getRequest(); + return (HttpServletRequest)super.getRequest(); } /** @@ -128,8 +128,8 @@ public String getContentType() { public String getContextPath() { String path = getHttpServletRequest().getContextPath(); SecurityConfiguration sc = ESAPI.securityConfiguration(); - //Return empty String for the ROOT context - if (path == null || "".equals(path.trim())) return ""; + //Return empty String for the ROOT context + if (path == null || "".equals(path.trim())) return ""; String clean = ""; try { @@ -159,7 +159,7 @@ public Cookie[] getCookies() { int maxAge = c.getMaxAge(); String domain = c.getDomain(); String path = c.getPath(); - + Cookie n = new Cookie(name, value); n.setMaxAge(maxAge); @@ -347,7 +347,7 @@ public String getParameter(String name) { * @return The "scrubbed" parameter value. */ public String getParameter(String name, boolean allowNull) { - SecurityConfiguration sc = ESAPI.securityConfiguration(); + SecurityConfiguration sc = ESAPI.securityConfiguration(); return getParameter(name, allowNull, sc.getIntProp("HttpUtilities.httpQueryParamValueLength"), "HTTPParameterValue"); } @@ -423,7 +423,7 @@ public Enumeration getParameterNames() { Enumeration en = getHttpServletRequest().getParameterNames(); while (en.hasMoreElements()) { try { - SecurityConfiguration sc = ESAPI.securityConfiguration(); + SecurityConfiguration sc = ESAPI.securityConfiguration(); String name = (String) en.nextElement(); String clean = ESAPI.validator().getValidInput("HTTP parameter name: " + name, name, "HTTPParameterName", sc.getIntProp("HttpUtilities.httpQueryParamNameLength"), true); v.add(clean); @@ -446,8 +446,8 @@ public String[] getParameterValues(String name) { String[] values = getHttpServletRequest().getParameterValues(name); List newValues; - if(values == null) - return null; + if(values == null) + return null; newValues = new ArrayList(); SecurityConfiguration sc = ESAPI.securityConfiguration(); for (String value : values) { @@ -469,7 +469,7 @@ public String[] getParameterValues(String name) { */ public String getPathInfo() { String path = getHttpServletRequest().getPathInfo(); - if (path == null) return null; + if (path == null) return null; String clean = ""; SecurityConfiguration sc = ESAPI.securityConfiguration(); try { @@ -681,15 +681,15 @@ public String getServerName() { * HttpServletRequest after parsing and checking the range 0-65536. * @return The local server port */ - public int getServerPort() { - int port = getHttpServletRequest().getServerPort(); - if ( port < 0 || port > 0xFFFF ) { - logger.warning( Logger.SECURITY_FAILURE, "HTTP server port out of range: " + port ); - port = 0; - } - return port; - } - + public int getServerPort() { + int port = getHttpServletRequest().getServerPort(); + if ( port < 0 || port > 0xFFFF ) { + logger.warning( Logger.SECURITY_FAILURE, "HTTP server port out of range: " + port ); + port = 0; + } + return port; + } + /** * Returns the server path from the HttpServletRequest after canonicalizing @@ -710,52 +710,44 @@ public String getServletPath() { /** * Returns a session, creating it if necessary, and sets the HttpOnly flag - * on the Session ID cookie. + * on the Session ID cookie. The 'secure' flag is also set if the property + * {@Code HttpUtilities.ForceSecureCookies} is set to {@Code true} in the ESAPI.properties file. * @return The current session */ public HttpSession getSession() { - HttpSession session = getHttpServletRequest().getSession(); - - // send a new cookie header with HttpOnly on first and second responses - if (ESAPI.securityConfiguration().getBooleanProp("HttpUtilities.ForceHttpOnlySession")) { - if (session.getAttribute("HTTP_ONLY") == null) { - session.setAttribute("HTTP_ONLY", "set"); - Cookie cookie = new Cookie(ESAPI.securityConfiguration().getStringProp("HttpUtilities.HttpSessionIdName"), session.getId()); - cookie.setPath( getHttpServletRequest().getContextPath() ); - cookie.setMaxAge(-1); // session cookie - HttpServletResponse response = ESAPI.currentResponse(); - if (response != null) { - ESAPI.currentResponse().addCookie(cookie); - } - } - } - return session; + return getSession(true); } /** - * Returns a session, creating it if necessary, and sets the HttpOnly flag - * on the Session ID cookie. - * @param create Create a new session if one doesn't exist + * Returns the current session associated with this request or, if there is no current session and + * {@Code create} is {@Code true}, returns a new session and sets the HttpOnly flag on the session ID cookie. + * The 'secure' flag is also set if the property {@Code HttpUtilities.ForceSecureCookies} is set to + * {@Code true} in the ESAPI.properties file. + * @param create If set to {@Code true}, create a new session if one doesn't exist, otherwise return {@Code null} * @return The current session */ public HttpSession getSession(boolean create) { HttpSession session = getHttpServletRequest().getSession(create); + if (session == null) { return null; } + SecurityConfiguration sc = ESAPI.securityConfiguration(); + // send a new cookie header with HttpOnly on first and second responses - if (ESAPI.securityConfiguration().getBooleanProp("HttpUtilities.ForceHttpOnlySession")) { - if (session.getAttribute("HTTP_ONLY") == null) { - session.setAttribute("HTTP_ONLY", "set"); - Cookie cookie = new Cookie(ESAPI.securityConfiguration().getStringProp("HttpUtilities.HttpSessionIdName"), session.getId()); - cookie.setMaxAge(-1); // session cookie - cookie.setPath( getHttpServletRequest().getContextPath() ); - HttpServletResponse response = ESAPI.currentResponse(); - if (response != null) { - ESAPI.currentResponse().addCookie(cookie); - } - } + if ( sc.getBooleanProp("HttpUtilities.ForceHttpOnlySession") ) { + if (session.getAttribute("HTTP_ONLY") == null) { + session.setAttribute("HTTP_ONLY", "set"); + Cookie cookie = new Cookie( sc.getStringProp("HttpUtilities.HttpSessionIdName"), session.getId() ); + cookie.setMaxAge(-1); // session cookie + cookie.setPath( getHttpServletRequest().getContextPath() ); + cookie.setSecure( sc.getBooleanProp("HttpUtilities.ForceSecureCookies") ); + HttpServletResponse response = ESAPI.currentResponse(); + if (response != null) { + ESAPI.currentResponse().addCookie(cookie); + } + } } return session; } From e700d20f5d3b3bdb7f4323b3174ca64468385c23 Mon Sep 17 00:00:00 2001 From: kwwall Date: Sun, 7 Jun 2020 20:31:38 -0400 Subject: [PATCH 0634/1069] Reorganized one of the tests and added some new test cases. --- .../owasp/esapi/crypto/CryptoTokenTest.java | 36 +++++++++++-------- 1 file changed, 22 insertions(+), 14 deletions(-) diff --git a/src/test/java/org/owasp/esapi/crypto/CryptoTokenTest.java b/src/test/java/org/owasp/esapi/crypto/CryptoTokenTest.java index 0d2b8a519..52c60c6ee 100644 --- a/src/test/java/org/owasp/esapi/crypto/CryptoTokenTest.java +++ b/src/test/java/org/owasp/esapi/crypto/CryptoTokenTest.java @@ -288,34 +288,42 @@ public final void testSetAndGetAttribute() { // where attribute values contains each of the values that will // be quoted, namely: '\', '=', and ';' try { + String[] attrValues = { "\\", ";", "=", "foo=", "bar\\=", "foobar=\"" }; String complexValue = "kwwall;1291183520293;abc=x=yx;xyz=;efg=a;a;;bbb=quotes\\tuff"; - - ctok.setAttribute("..--__", ""); // Ugly, but legal attr name; empty is legal value. - ctok.setAttribute("attr1", "\\"); - ctok.setAttribute("attr2", ";"); - ctok.setAttribute("attr3", "="); + ctok.setAttribute("complexAttr", complexValue); + ctok.setAttribute("..--__", ""); // Ugly weird but legal attr name; empty is legal value. + + for ( int i = 0; i < attrValues.length; i++ ) { + String attrName = "attr" + i; + String attrVal = attrValues[i]; + + ctok.setAttribute( attrName, attrVal ); + } + String tokenVal = ctok.getToken(); assertNotNull("tokenVal should not be null", tokenVal); CryptoToken ctok2 = new CryptoToken(tokenVal); + String weirdAttr = ctok2.getAttribute("..--__"); assertTrue("Expecting empty string for value of weird attr, but got: " + weirdAttr, weirdAttr.equals("")); - String attr1 = ctok2.getAttribute("attr1"); - assertTrue("attr1 has unexpected value of " + attr1, attr1.equals("\\") ); - - String attr2 = ctok2.getAttribute("attr2"); - assertTrue("attr2 has unexpected value of " + attr2, attr2.equals(";") ); - - String attr3 = ctok2.getAttribute("attr3"); - assertTrue("attr3 has unexpected value of " + attr3, attr3.equals("=") ); - String complexAttr = ctok2.getAttribute("complexAttr"); assertNotNull(complexAttr); assertTrue("complexAttr has unexpected value of " + complexAttr, complexAttr.equals(complexValue) ); + for ( int i = 0; i < attrValues.length; i++ ) { + String attrName = "attr" + i; + String attrVal = attrValues[i]; + String retrieved = ctok2.getAttribute( attrName ); + String assertMsg = "Exptected attribute '" + attrName + "' to have value of '" + attrVal + "', but had value of '" + retrieved; + + assertTrue( assertMsg, attrVal.equals( attrVal ) ); + ctok.setAttribute( attrName, attrVal ); + } + } catch (ValidationException e) { fail("Caught unexpected ValidationException: " + e); } catch (Exception e) { From e1b634e81606b1a2c7cd60b1251af13ee1831511 Mon Sep 17 00:00:00 2001 From: kwwall Date: Sun, 21 Jun 2020 17:08:52 -0400 Subject: [PATCH 0635/1069] Add registered trademark to first 'OWASP' referene. --- README.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 68037a5d2..155838fae 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ Enterprise Security API for Java (Legacy)
    -OWASP ESAPI (The OWASP Enterprise Security API) is a free, open source, web application security control library that makes it easier for programmers to write lower-risk applications. The ESAPI for Java library is designed to make it easier for programmers to retrofit security into existing applications. ESAPI for Java also serves as a solid foundation for new development. +OWASP® ESAPI (The OWASP Enterprise Security API) is a free, open source, web application security control library that makes it easier for programmers to write lower-risk applications. The ESAPI for Java library is designed to make it easier for programmers to retrofit security into existing applications. ESAPI for Java also serves as a solid foundation for new development.
    @@ -85,3 +85,6 @@ Old archives for the old Mailman mailing lists for ESAPI-Users and ESAPI-Dev are For a general overview of Google Groups and its web interface, see https://groups.google.com/forum/#!overview For assistance subscribing and unsubscribing to Google Groups, see https://webapps.stackexchange.com/questions/13508/how-can-i-subscribe-to-a-google-mailing-list-with-a-non-google-e-mail-address/15593#15593 + +---------- +OWASP is a registered trademark of The OWASP Foundation. From 8eb9d0fa5528aadbdf474908f18cb4b27ee70df4 Mon Sep 17 00:00:00 2001 From: kwwall Date: Sun, 21 Jun 2020 17:10:55 -0400 Subject: [PATCH 0636/1069] Update Jaavdoc. --- src/main/java/org/owasp/esapi/ESAPI.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/java/org/owasp/esapi/ESAPI.java b/src/main/java/org/owasp/esapi/ESAPI.java index 33eca3fcc..e11239c75 100644 --- a/src/main/java/org/owasp/esapi/ESAPI.java +++ b/src/main/java/org/owasp/esapi/ESAPI.java @@ -93,6 +93,8 @@ public static Authenticator authenticator() { } /** + * The ESAPI Encoder is primarilly used to provide output encoding to + * prevent Cross-Site Scripting (XSS). * @return the current ESAPI Encoder object being used to encode and decode data for this application. */ public static Encoder encoder() { From 56b4aa68c6dafa6b85f6d187756bad1f4da75074 Mon Sep 17 00:00:00 2001 From: kwwall Date: Sun, 21 Jun 2020 17:11:18 -0400 Subject: [PATCH 0637/1069] Update Javadoc. --- src/main/java/org/owasp/esapi/Encoder.java | 106 +++++++++++++++++---- 1 file changed, 90 insertions(+), 16 deletions(-) diff --git a/src/main/java/org/owasp/esapi/Encoder.java b/src/main/java/org/owasp/esapi/Encoder.java index ca82238be..b89c85e82 100644 --- a/src/main/java/org/owasp/esapi/Encoder.java +++ b/src/main/java/org/owasp/esapi/Encoder.java @@ -23,28 +23,102 @@ /** - * The Encoder interface contains a number of methods for decoding input and encoding output - * so that it will be safe for a variety of interpreters. To prevent - * double-encoding, callers should make sure input does not already contain encoded characters - * by calling canonicalize. Validator implementations should call canonicalize on user input - * before validating to prevent encoded attacks. + * The {@code Encoder} interface contains a number of methods for decoding input and encoding output + * so that it will be safe for a variety of interpreters. Its primary use is to + * provide output encoding to prevent XSS. *

    - * All of the methods must use a "whitelist" or "positive" security model. - * For the encoding methods, this means that all characters should be encoded, except for a specific list of - * "immune" characters that are known to be safe. - *

    - * The Encoder performs two key functions, encoding and decoding. These functions rely + * To prevent double-encoding, callers should make sure input does not already contain encoded characters + * by calling one of the {@code canonicalize()} methods. Validator implementations should call + * {@code canonicalize()} on user input before validating to prevent encoded attacks. + *

    + * All of the methods must use an "allow list" or "positive" security model rather + * than a "deny list" or "negative" security model. For the encoding methods, this means that + * all characters should be encoded, except for a specific list of "immune" characters that are + * known to be safe. + *

    + * The {@code Encoder} performs two key functions, encoding and decoding. These functions rely * on a set of codecs that can be found in the org.owasp.esapi.codecs package. These include: - *

    • CSS Escaping
    • + *
        + *
      • CSS Escaping
      • *
      • HTMLEntity Encoding
      • *
      • JavaScript Escaping
      • - *
      • MySQL Escaping
      • - *
      • Oracle Escaping
      • + *
      • MySQL Database Escaping
      • + *
      • Oracle Database Escaping
      • *
      • Percent Encoding (aka URL Encoding)
      • - *
      • Unix Escaping
      • + *
      • Unix Shell Escaping
      • *
      • VBScript Escaping
      • - *
      • Windows Encoding
      - *

      + *

    • Windows Cmd Escaping
    • + *
    • LDAP Escaping
    • + *
    • XML and XML Attribute Encoding
    • + *
    • XPath Escaping
    • + *
    • Base64 Encoding
    • + *
    + *

    + * Note that in addition to these encoder methods, ESAPI also provides a JSP Tag + * Library ({@code META-INF/esapi.tld}) in the ESAPI jar. This allows one to use + * the more convenient JSP tags in JSPs. These * tags are simply wrappers for the + * various "encodeForXXYZ()" methods. + *

    + * Some important final words: + *

      + *
    • Where to output encode: + * Knowing where to place the output encoding in your code + * is just as important as knowing which context (HTML, HTML attribute, CSS, + * JavaScript, or URL) to use for the output encoding and surprisingly the two + * are often related. In general, output encoding should be done just prior to the + * output being rendered because that is what determines what the appropriate + * context is for the output encoding. In fact, doing output encoding on + * untrusted data that is stored and to be used later--whether stored in an HTTP + * session or in a database--is almost always considered an anti-pattern. An + * example of this is one gathers and stores some untrusted data item such as an + * email address from a user. A developer thinks "let's output encode this and + * store the encoded data in the database, thus making the untrusted data safe + * to use, thus saving us all the encoding troubles later on". On the surface, + * that sounds like a reasonable approach. The problem is how to know what + * output encoding to use, not only for now, but for all possible future + * uses? It might be that the current application code base is only using it in + * an HTML contexxt that is displayed in an HTML report or shown in an HTML + * context in the user's profile. But what it it is later used in a mailto: URL? + * Then instead of HTML encoding, it would need to have URL encoding. Similarly, + * what if there is a later switch made to use AJAX and the untrusted email + * address gets used in a JavaScript context? The complication is that even if + * you know with certainty today all the ways that an untrusted data item is + * used in your application, it is genrally impossible to predict all the + * contexts that it may be used in the future, not only in your application, but + * in other applications that could access that data in the database. + *
    • + *
    • Avoiding multiple nested contexts: + * A really tricky situation to get correct is hen there are multiple nested + * encoding contexts. But far, the most common place this seems to come up is + * untrusted URLs used in JavaScript. How should you handle that? Well, to be + * honest, the best way is to rewrite your code to avoid it. An example of + * this that is well worth reading may be found at + * ESAPI-DEV mailing list archives: + * URL encoding within JavaScript. Be sure to read the entire thread. + * The question itself is too nuanced to be answered in Javadoc, but now, + * hopefully you are at least aware of the potential pitfalls. + *
    • + *
    * * @author Jeff Williams (jeff.williams .at. aspectsecurity.com) Aspect Security From eae125386565b5b89ed4c11565eac041a4b50f57 Mon Sep 17 00:00:00 2001 From: kwwall Date: Sun, 21 Jun 2020 17:12:08 -0400 Subject: [PATCH 0638/1069] Change cookie path from "//" to "/". --- .../java/org/owasp/esapi/reference/DefaultHTTPUtilities.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/owasp/esapi/reference/DefaultHTTPUtilities.java b/src/main/java/org/owasp/esapi/reference/DefaultHTTPUtilities.java index 149937579..f690e26bd 100644 --- a/src/main/java/org/owasp/esapi/reference/DefaultHTTPUtilities.java +++ b/src/main/java/org/owasp/esapi/reference/DefaultHTTPUtilities.java @@ -715,7 +715,7 @@ public void killAllCookies(HttpServletRequest request, HttpServletResponse respo * @param name */ public void killCookie(HttpServletRequest request, HttpServletResponse response, String name) { - String path = "//"; + String path = "/"; String domain=""; Cookie cookie = getFirstCookie(request, name); if ( cookie != null ) { From 195f07c6553e9c62ea520468c3c34faf2a4aaccd Mon Sep 17 00:00:00 2001 From: kwwall Date: Fri, 26 Jun 2020 20:40:09 -0400 Subject: [PATCH 0639/1069] Minor typo fix; add Inc. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 155838fae..01095ed7d 100644 --- a/README.md +++ b/README.md @@ -87,4 +87,4 @@ For a general overview of Google Groups and its web interface, see https://group For assistance subscribing and unsubscribing to Google Groups, see https://webapps.stackexchange.com/questions/13508/how-can-i-subscribe-to-a-google-mailing-list-with-a-non-google-e-mail-address/15593#15593 ---------- -OWASP is a registered trademark of The OWASP Foundation. +OWASP is a registered trademark of the OWASP Foundation, Inc. From 980cce5b2e20770088c7a68641a95706864e32d5 Mon Sep 17 00:00:00 2001 From: kwwall Date: Fri, 26 Jun 2020 20:47:26 -0400 Subject: [PATCH 0640/1069] Update pom.xml to use latest versions of AntiSamy, slf4j, & batik-css. Antisamy: 1.5.8 -> 1.5.10 slf4j: 1.7.26 -> 1.7.30 batik: 1.11 -> 1.13 (addresses CVE-2019-17566) --- pom.xml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/pom.xml b/pom.xml index 132075ffd..a57922307 100644 --- a/pom.xml +++ b/pom.xml @@ -228,7 +228,7 @@ org.owasp.antisamy antisamy - 1.5.8 + 1.5.10 xml-apis @@ -243,7 +243,7 @@ org.slf4j slf4j-api - 1.7.26 + 1.7.30 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From ecf4a202c46c910b3b98559fd635b44b683dfa7a Mon Sep 17 00:00:00 2001 From: kwwall Date: Thu, 2 Jul 2020 16:30:30 -0400 Subject: [PATCH 0649/1069] Update wiki link to new OWASP URL --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 01095ed7d..baddc52bb 100644 --- a/README.md +++ b/README.md @@ -61,7 +61,7 @@ More detail is available in the file '[SECURITY.md](https://raw.githubuserconten ## Where to Find More Information on ESAPI -*Wiki:* https://www.owasp.org/index.php/Category:OWASP_Enterprise_Security_API +*Wiki:* https://owasp.org/www-project-enterprise-security-api/ *Nightly Build:* Travis CI - https://travis-ci.org/bkimminich/esapi-java-legacy From 7abf4a5cfe296a04003ca5222cfb5a53f3bca935 Mon Sep 17 00:00:00 2001 From: kwwall Date: Fri, 3 Jul 2020 00:20:56 -0400 Subject: [PATCH 0650/1069] Fix typo. s/parse/parser/ --- src/main/java/org/owasp/esapi/Encoder.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/owasp/esapi/Encoder.java b/src/main/java/org/owasp/esapi/Encoder.java index 48b1e9cf4..b9d42dea5 100644 --- a/src/main/java/org/owasp/esapi/Encoder.java +++ b/src/main/java/org/owasp/esapi/Encoder.java @@ -468,7 +468,7 @@ public interface Encoder { *

    * The use of a real XML parser is strongly encouraged. However, in the * hopefully rare case that you need to make sure that data is safe for - * inclusion in an XML document and cannot use a parse, this method provides + * inclusion in an XML document and cannot use a parser, this method provides * a safe mechanism to do so. * * @see Character Encoding in Entities From 807adf71fa3c1efd7a3b09a7a190b87c4c18f228 Mon Sep 17 00:00:00 2001 From: davewichers Date: Fri, 3 Jul 2020 16:35:08 -0400 Subject: [PATCH 0651/1069] Upgrade some dependencies and most plugins. Add FindSecBugs plugin to SpotBugs. Add animal sniffer plugin to try to detect use of Java APIs not in Java 7. Plugin runs but doesn't detect use of APIs currently causing compilation errors with Java 7. --- pom.xml | 204 ++++++++++++++++++++++++++++++++------------------------ 1 file changed, 118 insertions(+), 86 deletions(-) diff --git a/pom.xml b/pom.xml index a9dcd8a5e..3235520cd 100644 --- a/pom.xml +++ b/pom.xml @@ -49,7 +49,7 @@ The Open Web Application Security Project (OWASP) - http://www.owasp.org/index.php + https://owasp.org @@ -90,7 +90,7 @@ Jeff Williams - Aspect Security + Contrast Security Project Inventor @@ -121,7 +121,6 @@ Dave Wichers - Aspect Security Jim Manico @@ -133,12 +132,17 @@ UTF-8 + 1.23 + 2.0.7 + 4.0.4 + 3.0.0-M5 javax.servlet javax.servlet-api + 3.0.1 provided @@ -253,6 +257,7 @@ commons-io commons-io + 2.6 @@ -292,24 +297,38 @@ 1.4.01 + + + com.github.spotbugs + spotbugs-annotations + ${version.spotbugs} + true + + + net.jcip + jcip-annotations + 1.0 + true + + junit junit - 4.12 + 4.13 test org.bouncycastle bcprov-jdk15on - 1.61 + 1.65.01 test org.powermock powermock-api-mockito2 - 2.0.2 + ${version.powermock} test @@ -319,31 +338,13 @@ org.javassist javassist - - - org.mockito - mockito-core - - - org.powermock - powermock-reflect - - - net.bytebuddy - byte-buddy - - - net.bytebuddy - byte-buddy-agent - - + org.javassist javassist 3.25.0-GA @@ -352,19 +353,26 @@ org.mockito mockito-core - 2.27.0 + + 2.28.2 test org.powermock powermock-module-junit4 - 2.0.2 + ${version.powermock} test + + + junit + junit + + org.powermock powermock-reflect - 2.0.2 + ${version.powermock} test @@ -384,13 +392,13 @@ org.openjdk.jmh jmh-core - 1.21 + ${version.jmh} test org.openjdk.jmh jmh-generator-annprocess - 1.21 + ${version.jmh} test @@ -401,51 +409,22 @@ org.apache.maven.plugins maven-assembly-plugin - 3.1.1 + 3.3.0 org.apache.maven.plugins maven-dependency-plugin - 3.1.1 + 3.1.2 org.apache.maven.plugins maven-release-plugin - 2.5.3 + 3.0.0-M1 - - - com.github.spotbugs - spotbugs-maven-plugin - 3.1.11 - - - - com.github.spotbugs - spotbugs - 3.1.12 - - - - - - ${PhaseIfJava8plus} - - - - true - true - true - Low - Max - false - - - + net.sourceforge.maven-taglib maven-taglib-plugin @@ -467,7 +446,7 @@ org.apache.maven.plugins maven-compiler-plugin - 3.8.0 + 3.8.1 1.7 1.7 @@ -501,7 +480,7 @@ org.apache.maven.plugins maven-deploy-plugin - 2.8.2 + 3.0.0-M1 @@ -516,33 +495,60 @@ org.apache.maven.plugins maven-enforcer-plugin - 3.0.0-M2 + 3.0.0-M3 org.codehaus.mojo extra-enforcer-rules 1.2 + + org.codehaus.mojo + animal-sniffer-enforcer-rule + + 1.17 + - - enforce - + check-java-versions + compile + enforce 1.7 - + ESAPI 2.x now uses the JDK1.7 for its baseline. Please make sure that your JAVA_HOME environment variable is pointed to a JDK1.7 or later distribution. - + 1.7 + true + + Dependencies shouldn't require Java 8+ + true + + + + check-java7API-signatures + compile + enforce + + + + + org.codehaus.mojo.signature + + java17 + 1.0 + + + @@ -564,13 +570,13 @@ org.apache.maven.plugins maven-install-plugin - 2.5.2 + 3.0.0-M1 org.apache.maven.plugins maven-jar-plugin - 3.1.0 + 3.2.0 @@ -584,7 +590,7 @@ org.apache.maven.plugins maven-javadoc-plugin - 3.1.0 + 3.2.0 7 none @@ -608,13 +614,13 @@ org.apache.maven.plugins maven-pmd-plugin - 3.11.0 + 3.13.0 org.apache.maven.plugins maven-project-info-reports-plugin - 3.0.0 + 3.1.0 @@ -626,13 +632,13 @@ org.apache.maven.plugins maven-site-plugin - 3.7.1 + 3.9.1 org.apache.maven.plugins maven-source-plugin - 3.0.1 + 3.2.1 attach-sources @@ -644,14 +650,14 @@ org.apache.maven.plugins - maven-surefire-report-plugin - 2.22.1 + maven-surefire-plugin + ${version.surefire} org.apache.maven.plugins - maven-surefire-plugin - 2.22.1 + maven-surefire-report-plugin + ${version.surefire} @@ -687,7 +693,7 @@ org.owasp dependency-check-maven - 5.0.0 + 5.3.2 5.0 ./suppressions.xml @@ -767,11 +773,11 @@ org.apache.maven.plugins - maven-surefire-report-plugin + maven-surefire-plugin org.apache.maven.plugins - maven-surefire-plugin + maven-surefire-report-plugin + org.codehaus.mojo versions-maven-plugin @@ -799,12 +805,38 @@ + Java8plus [1.8,) site + + + + + + + com.github.spotbugs + spotbugs-maven-plugin + ${version.spotbugs} + + + + com.h3xstream.findsecbugs + findsecbugs-plugin + 1.10.1 + + + Max + false + + + + + From cceffa6aab0887e379be1c7117d2e620b36cfdee Mon Sep 17 00:00:00 2001 From: davewichers Date: Fri, 3 Jul 2020 17:56:33 -0400 Subject: [PATCH 0652/1069] Fix 1 link to the OWASP web site in pom. --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 3235520cd..8623ac2e6 100644 --- a/pom.xml +++ b/pom.xml @@ -37,7 +37,7 @@ ESAPI - https://www.owasp.org/index.php/Category:OWASP_Enterprise_Security_API + https://owasp.org/www-project-enterprise-security-api/ The Enterprise Security API (ESAPI) project is an OWASP project to create simple strong security controls for every web platform. Security controls are not simple to build. You can read about the From f94ca94e15237c0f0887913d5d33c697fb97594a Mon Sep 17 00:00:00 2001 From: "Kevin W. Wall" Date: Fri, 3 Jul 2020 18:48:59 -0400 Subject: [PATCH 0653/1069] Fix for GitHub Issue 552 (#553) * Fix #552 * Undesired commit, but 'git checkout -- documentation/esapi4java-core-2.2.1.0-release-notes.txt' has no effect. Sigh. * Updated from 'misc-cleanup' branch. --- .../esapi4java-core-2.2.1.0-release-notes.txt | 248 ++++++++++-------- .../logging/appender/ClientInfoSupplier.java | 8 +- .../appender/EventTypeLogSupplier.java | 8 +- .../logging/appender/ServerInfoSupplier.java | 8 +- .../logging/appender/UserInfoSupplier.java | 8 +- 5 files changed, 153 insertions(+), 127 deletions(-) diff --git a/documentation/esapi4java-core-2.2.1.0-release-notes.txt b/documentation/esapi4java-core-2.2.1.0-release-notes.txt index bb46556b8..46f8bab9d 100644 --- a/documentation/esapi4java-core-2.2.1.0-release-notes.txt +++ b/documentation/esapi4java-core-2.2.1.0-release-notes.txt @@ -1,115 +1,133 @@ -Release notes for ESAPI 2.2.1.0 - Release date: 2020-TBD - Project leaders: - -Kevin W. Wall - -Matt Seil - -Previous release: ESAPI 2.2.0.0, 2019-June-24 - - -Executive Summary: Important Things to Note for this Release ------------------------------------------------------------- - - TBD - -================================================================================================================= - -Basic ESAPI facts - -ESAPI 2.2.0.0 release: - 194 Java source files - 4150 JUnit tests in 118 Java source files - -ESAPI 2.2.1.0 release: - TBD - -GitHub Issues fixed in this release - -Issue # GitHub Issue Title ----------------------------------------------------------------------------------------------- - -143 Enchance encodeForOS to auto-detect the underling OS -226 Javadoc Inaccuracy in getRandomInteger() and getRandomReal() -245 KeyDerivationFunction::computeDerivedKey - possible security level mismatch -256 White space clean up -382 Build Fails on path with space -494 Encoder's encodeForCSS doesn't handle RGB Triplets -503 Bug on on referrer header when value contains `§ion` like `www.asdf.com?a=1§ion=2` -509 HTMLValidationRule.getValid(String,String) does not follow documented specifications -511 Add missing documentation to Validator.addRule() and Validator.getRule() -512 Update Apache Commons Bean Utils to 1.9.4 -515 Adding tests for getCookies (also 516) -519 Issue 494 CSSCodec RGB Triplets -530 Log Bridge Tests -536 Various fixes -538 Addressing log4j 1.x CVE-2019-17571 - ------------------------------------------------------------------------------ - - Changes requiring special attention - ------------------------------------------------------------------------------ - -TBD - ------------------------------------------------------------------------------ - - Other changes in this release, some of which not tracked via GitHub issues - ------------------------------------------------------------------------------ - -Documentation updates for locating Jar files -Unneeded code removed from ExtensiveEncoder -Inline reader added to ExtensiveEncoder -Additional time for windows to always sleep more than given seconds in CryptoTokenTest -Change required by tweak to CipherText.toString() method -Removed call to deprecated CryptoHelper.computeDerivedKey() method -New JUnit tests for org.owasp.esapi.crypto.KeyDerivationFunction class -Use existing toString method rather than a StringBuilder -Documentation and tests -JavaLogger move -Splitting user infor from Client Supplier - ------------------------------------------------------------------------------ - -Developer Activity Report (Changes between release 2.2.0.0 and 2.2.1.0, i.e., between 2019-06-25 and 2020-05-12) -Generated manually (this time) - -Developer Total commits Total Number - of Files Changed -===================================================== -jeremiahjstacey 11 68 -kwwall 15 26 -wiitek 3 6 -xeno6696 8 9 -Michael-Ziluck 2 3 -===================================================== - ------------------------------------------------------------------------------ - -53 Closed PRs since 2.2.0.0 release -=================================== -504 New scripts to suppress noise for 'mvn test' -510 Resolve #509 - Properly throw exception when HTML fails -513 Close issue #512 by updating to 1.9.4 of Commons Beans Util.\ -519 Issue 494 CSSCodec RGB Triplets -520 OS Name DefaultExecutorTests #143 -540 Issue 382: Build Fails on path with space -596 Closes Issue 245 - ------------------------------------------------------------------------------ - -Notice: - - Release notes written by Bill Sempf (bill.sempf@owasp.org) please direct any communication to me. - -Project co-leaders - Kevin W. Wall (kwwall) - Matt Seil (xeno6696) - -Special shout-outs to: - Jeremiah Stacey (jeremiahjstacey) -- All around ESAPI support and JUnit test case developer extraordinaire - Dave Wichers (davewichers) - for Maven Central / Sonatype help - -Thanks you all for your time and effort to ESAPI and making it a better project. And if I've missed any, my apologies; let me know and I will correct it. - +Release notes for ESAPI 2.2.1.0 + Release date: 2020-July-?? + Project leaders: + -Kevin W. Wall + -Matt Seil + +Previous release: ESAPI 2.2.0.0, 2019-June-24 + + +Executive Summary: Important Things to Note for this Release +------------------------------------------------------------ + +This is a minor release. It's main purpose was to update dependencies to eliminate potential vulnerabilities arising from dependencies with known CVEs. See the section "Changes requiring special attention" below for additional details. + +Also special props to Bill Sempf for stepping up and volunteering to prepare the initial cut of these release notes. Had he not done so, this release either would not have release notes or it would have been delayed another 6 months while I procrastinated further with various distractions. (Squirrel!) + +================================================================================================================= + +Basic ESAPI facts +----------------- + +ESAPI 2.2.0.0 release: + 194 Java source files + 4150 JUnit tests in 118 Java source files + +ESAPI 2.2.1.0 release: + 211 Java source files + 4309 JUnit tests in 134 Java source files + +GitHub Issues fixed in this release + +Issue # GitHub Issue Title +---------------------------------------------------------------------------------------------- + +143 Enchance encodeForOS to auto-detect the underling OS +226 Javadoc Inaccuracy in getRandomInteger() and getRandomReal() +245 KeyDerivationFunction::computeDerivedKey - possible security level mismatch +256 White space clean up +382 Build Fails on path with space +494 Encoder's encodeForCSS doesn't handle RGB Triplets +503 Bug on on referrer header when value contains `§ion` like `www.asdf.com?a=1§ion=2` +509 HTMLValidationRule.getValid(String,String) does not follow documented specifications +511 Add missing documentation to Validator.addRule() and Validator.getRule() +512 Update Apache Commons Bean Utils to 1.9.4 +515 Adding tests for getCookies (also 516) +519 Issue 494 CSSCodec RGB Triplets +522 javadoc corrections for Encoder.canonicalize() +530 Log Bridge Tests +536 Various fixes +538 Addressing log4j 1.x CVE-2019-17571 +552 Rewrite implementation of some ESAPI classes to remove Java 8 dependencies + +----------------------------------------------------------------------------- + + Changes requiring special attention + +----------------------------------------------------------------------------- +The new default ESAPI logger is JUL (java.util.logging packages) and we have deprecated the use of Log4j 1.x as it is way past the end-of-life and we now support SLF4J. We did not want to make SLF4J the default logger (at least not yet) as we did not want to have the default ESAPI use require additional dependencies. However, SLF4J is likely to be the future choice, at least once we start on EsAPI 3.0. A special shout-out to Jeremiah Stacey for making this possible by re-factoring much of the ESAPI logger code. Note, the straw that broke the proverbial camel's back was the announcement of CVE-2019-17571 (rated Critical), for which there is no fix available and likely will never be. + +Related to that CVE and how it affects ESAPI, be sure to read + https://github.com/ESAPI/esapi-java-legacy/blob/develop/documentation/ESAPI-security-bulletin2.pdf +which describes CVE-2019-17571, a deserialization vulnerability in Log4j 1.2.17. ESAPI is not affected by this (even if you chose to use Log4j 1 as you default ESAPI logger). This security bulletin describes why this CVE is not exploitable as used by ESAPI. + +Notable dependency updates (excludes those only used with JUnit tests): + antiSamy 1.5.8 -> 1.5.10 + batik-css 1.11 -> 1.13 + commons-beansutil 1.9.3 -> 1.9.4 + slf4j-api 1.7.26 -> 1.7.30 + +Finally, while ESAPI still supports JDK 7 (even though that too is way past end-of-life), the next ESAPI release will move to JDK 8 as the minimal baseline. (We already use Java 8 for development but still to Java 7 source and runtime compatiblity.) + +----------------------------------------------------------------------------- + + Other changes in this release, some of which not tracked via GitHub issues + +----------------------------------------------------------------------------- + +Documentation updates for locating Jar files +Unneeded code removed from ExtensiveEncoder +Inline reader added to ExtensiveEncoder +Additional time for windows to always sleep more than given seconds in CryptoTokenTest +Change required by tweak to CipherText.toString() method +Removed call to deprecated CryptoHelper.computeDerivedKey() method +New JUnit tests for org.owasp.esapi.crypto.KeyDerivationFunction class +Use existing toString method rather than a StringBuilder +Documentation and tests +JavaLogger moved +Splitting user info from Client Supplier + +----------------------------------------------------------------------------- + +Developer Activity Report (Changes between release 2.2.0.0 and 2.2.1.0, i.e., between 2019-06-25 and 2020-05-12) +Generated manually (this time) + +Developer Total Total Number +(GitHub ID) commits of Files Changed +===================================================== +jeremiahjstacey 11 68 +kwwall 16 26 +wiitek 3 6 +xeno6696 8 9 +Michael-Ziluck 2 3 +sempf 1 1 +===================================================== + +----------------------------------------------------------------------------- + +53 Closed PRs since 2.2.0.0 release (those rejected not listed) +=============================================================== +504 New scripts to suppress noise for 'mvn test' +510 Resolve #509 - Properly throw exception when HTML fails +513 Close issue #512 by updating to 1.9.4 of Commons Beans Util.\ +519 Issue 494 CSSCodec RGB Triplets +520 OS Name DefaultExecutorTests #143 +540 Issue 382: Build Fails on path with space +596 Closes Issue 245 + +----------------------------------------------------------------------------- + +Notice: + + Release notes written by Bill Sempf (bill.sempf@owasp.org), but please direct any communication to the project leaders. + +Project co-leaders + Kevin W. Wall (kwwall) + Matt Seil (xeno6696) + +Special shout-outs to: + Jeremiah Stacey (jeremiahjstacey) -- All around ESAPI support and JUnit test case developer extraordinaire + Dave Wichers (davewichers) - for pom.xml improvements + Bill Sempf -- for these release notes. Awesome job, Bill. I owe you a brew. + +Thanks you all for your time and effort to ESAPI and making it a better project. And if I've missed any, my apologies; let me know and I will correct it. diff --git a/src/main/java/org/owasp/esapi/logging/appender/ClientInfoSupplier.java b/src/main/java/org/owasp/esapi/logging/appender/ClientInfoSupplier.java index 2ff07a19a..5a8524340 100644 --- a/src/main/java/org/owasp/esapi/logging/appender/ClientInfoSupplier.java +++ b/src/main/java/org/owasp/esapi/logging/appender/ClientInfoSupplier.java @@ -15,7 +15,8 @@ package org.owasp.esapi.logging.appender; -import java.util.function.Supplier; +// Uncomment and use once ESAPI supports Java 8 as the minimal baseline. +// import java.util.function.Supplier; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpSession; @@ -27,7 +28,8 @@ * Supplier which can provide a String representing the client-side connection * information. */ -public class ClientInfoSupplier implements Supplier { +public class ClientInfoSupplier // implements Supplier +{ /** Default Last Host string if the Authenticated user is null.*/ private static final String DEFAULT_LAST_HOST = "#UNKNOWN_HOST#"; /** Session Attribute containing the ESAPI Session id. */ @@ -47,7 +49,7 @@ public class ClientInfoSupplier implements Supplier { /** Whether to log the user info from this instance. */ private boolean logClientInfo = true; - @Override + // @Override -- Uncomment when we switch to Java 8 as minimal baseline. public String get() { String clientInfo = ""; diff --git a/src/main/java/org/owasp/esapi/logging/appender/EventTypeLogSupplier.java b/src/main/java/org/owasp/esapi/logging/appender/EventTypeLogSupplier.java index 2f857f128..4af1bd50b 100644 --- a/src/main/java/org/owasp/esapi/logging/appender/EventTypeLogSupplier.java +++ b/src/main/java/org/owasp/esapi/logging/appender/EventTypeLogSupplier.java @@ -15,7 +15,8 @@ package org.owasp.esapi.logging.appender; -import java.util.function.Supplier; +// Uncomment and use once ESAPI supports Java 8 as the minimal baseline. +// import java.util.function.Supplier; import org.owasp.esapi.Logger; import org.owasp.esapi.Logger.EventType; @@ -25,7 +26,8 @@ * an EventType for logging * */ -public class EventTypeLogSupplier implements Supplier { +public class EventTypeLogSupplier // implements Supplier +{ /** EventType reference to supply log representation of. */ private final EventType eventType; @@ -38,7 +40,7 @@ public EventTypeLogSupplier(EventType evtyp) { this.eventType = evtyp == null ? Logger.EVENT_UNSPECIFIED : evtyp; } - @Override + // @Override -- Uncomment when we switch to Java 8 as minimal baseline. public String get() { return eventType.toString(); } diff --git a/src/main/java/org/owasp/esapi/logging/appender/ServerInfoSupplier.java b/src/main/java/org/owasp/esapi/logging/appender/ServerInfoSupplier.java index ca9b4bbd8..c9e0d8e02 100644 --- a/src/main/java/org/owasp/esapi/logging/appender/ServerInfoSupplier.java +++ b/src/main/java/org/owasp/esapi/logging/appender/ServerInfoSupplier.java @@ -15,7 +15,8 @@ package org.owasp.esapi.logging.appender; -import java.util.function.Supplier; +// Uncomment and use once ESAPI supports Java 8 as the minimal baseline. +// import java.util.function.Supplier; import javax.servlet.http.HttpServletRequest; @@ -25,7 +26,8 @@ * Supplier which can provide a String representing the server-side connection * information. */ -public class ServerInfoSupplier implements Supplier { +public class ServerInfoSupplier // implements Supplier +{ /** Whether to log the server connection info. */ private boolean logServerIP = true; /** Whether to log the application name. */ @@ -45,7 +47,7 @@ public ServerInfoSupplier(String logName) { this.logName = logName; } - @Override + // @Override -- Uncomment when we switch to Java 8 as minimal baseline. public String get() { // log server, port, app name, module name -- server:80/app/module StringBuilder appInfo = new StringBuilder(); diff --git a/src/main/java/org/owasp/esapi/logging/appender/UserInfoSupplier.java b/src/main/java/org/owasp/esapi/logging/appender/UserInfoSupplier.java index e9c34e31a..6065ed366 100644 --- a/src/main/java/org/owasp/esapi/logging/appender/UserInfoSupplier.java +++ b/src/main/java/org/owasp/esapi/logging/appender/UserInfoSupplier.java @@ -15,7 +15,8 @@ package org.owasp.esapi.logging.appender; -import java.util.function.Supplier; +// Uncomment and use once ESAPI supports Java 8 as the minimal baseline. +// import java.util.function.Supplier; import org.owasp.esapi.ESAPI; import org.owasp.esapi.User; @@ -24,14 +25,15 @@ * Supplier which can provide a String representing the client-side connection * information. */ -public class UserInfoSupplier implements Supplier { +public class UserInfoSupplier // implements Supplier +{ /** Default UserName string if the Authenticated user is null.*/ private static final String DEFAULT_USERNAME = "#ANONYMOUS#"; /** Whether to log the user info from this instance. */ private boolean logUserInfo = true; - @Override + // @Override -- Uncomment when we switch to Java 8 as minimal baseline. public String get() { // log user information - username:session@ipaddr User user = ESAPI.authenticator().getCurrentUser(); From e8e613fefb24e263a1676c042261e388f16c4569 Mon Sep 17 00:00:00 2001 From: "Kevin W. Wall" Date: Sun, 12 Jul 2020 21:59:43 -0400 Subject: [PATCH 0654/1069] Prep 2.2.1.0 (#557) * Delete the release notes that prevents me from doing a 'git pull origin' from my fork. * Add note referring to minimal Java 7 baseline. * Close #542 - final release notes for 2.2.1.0. * Close #556 - Add some final additional 'see also' refs. * Close #554 * Close #555 * Added issue 521 which should have been closed per PR 535. Issue now closed. * Close #558 * Update to reflect fix to issue #558 * Find/fix dependency causing java.lang.OutOfMemoryError: PermGen space that only occurs on Mac with Java 7. Apparently it was the surefire plugin. Co-authored-by: davewichers --- README.md | 4 +- .../esapi4java-core-2.2.1.0-release-notes.txt | 253 ++++++++++++++---- pom.xml | 5 +- src/main/java/org/owasp/esapi/Encoder.java | 3 + .../org/owasp/esapi/crypto/CryptoHelper.java | 10 +- .../owasp/esapi/crypto/CryptoHelperTest.java | 10 +- .../esapi/reference/AccessControllerTest.java | 2 +- .../owasp/esapi/reference/ValidatorTest.java | 8 +- .../esapi/fbac-policies/DataAccessRules.txt | 4 +- 9 files changed, 234 insertions(+), 65 deletions(-) diff --git a/README.md b/README.md index baddc52bb..dc59e7a2a 100644 --- a/README.md +++ b/README.md @@ -17,9 +17,11 @@ OWASP® ESAPI (The OWASP Enterprise Security API) is a free, open source, web ap # What does Legacy mean?

    This is the legacy branch of ESAPI which means it is an actively maintained branch of the project, however feature development for this branch will not be done. Features that have already been scheduled for the 2.x branch will move forward, but the main focus will be working on the ESAPI 3.x branch. -IMPORTANT NOTE: +IMPORTANT NOTES: The default branch for ESAPI legacy is now the 'develop' branch (rather than the 'master' branch), where future development, bug fixes, etc. will now be done. The 'master' branch is now marked as "protected"; it reflects the latest stable ESAPI release (2.1.0.1 as of this date). Note that this change of making the 'develop' branch the default may affect any pull requests that you were intending to make. +Also, the minimal baseline Java version to use ESAPI is Java 7. (This was changed from Java 6 during the 2.2.0.0 release.) + # Where can I find ESAPI 3.x? https://github.com/ESAPI/esapi-java diff --git a/documentation/esapi4java-core-2.2.1.0-release-notes.txt b/documentation/esapi4java-core-2.2.1.0-release-notes.txt index 94da6b95d..ee3caf5ea 100644 --- a/documentation/esapi4java-core-2.2.1.0-release-notes.txt +++ b/documentation/esapi4java-core-2.2.1.0-release-notes.txt @@ -27,40 +27,61 @@ ESAPI 2.2.1.0 release: 211 Java source files 4309 JUnit tests in 134 Java source files -GitHub Issues fixed in this release +39 GitHub Issues closed in this release Issue # GitHub Issue Title ---------------------------------------------------------------------------------------------- -143 Enchance encodeForOS to auto-detect the underling OS -226 Javadoc Inaccuracy in getRandomInteger() and getRandomReal() -245 KeyDerivationFunction::computeDerivedKey - possible security level mismatch -256 White space clean up -382 Build Fails on path with space -494 Encoder's encodeForCSS doesn't handle RGB Triplets -503 Bug on on referrer header when value contains `§ion` like `www.asdf.com?a=1§ion=2` -509 HTMLValidationRule.getValid(String,String) does not follow documented specifications -511 Add missing documentation to Validator.addRule() and Validator.getRule() -512 Update Apache Commons Bean Utils to 1.9.4 -515 Adding tests for getCookies (also 516) -519 Issue 494 CSSCodec RGB Triplets -522 javadoc corrections for Encoder.canonicalize() -530 Log Bridge Tests -536 Various fixes -538 Addressing log4j 1.x CVE-2019-17571 -552 Rewrite implementation of some ESAPI classes to remove Java 8 dependencies - +143 - Enchance encodeForOS to auto-detect the underling OS +173 - DOMConfigurator is being used inappropriately in the ESAPIWebApplicationFirewallFilter +226 - Javadoc Inaccuracy in getRandomInteger() and getRandomReal() +232 - SecurityWrapperResponse.createCookieHeader modification request (closed; marked 'wontfix') +235 - exception is java.lang.NoClassDefFoundError: org.owasp.esapi.codecs.Codec +245 - KeyDerivationFunction::computeDerivedKey - possible security level mismatch +256 - Whitespace in JavaEncryptor +263 - I am getting validation exception while validating a paramter coming from http request +268 - SecurityWrapperResponse setStatus should not always set SC_OK +269 - org.owasp.esapi.reference.DefaultValidator reports ValidationException with IE 9 +271 - Add Constructor to DefaultSecurityConfiguration to accept a properties file (1.4) +276 - Patch for /branches/2.1/src/main/java/org/owasp/esapi/reference/DefaultExecutor.java +310 - Make HTMLValidationRule to look for antisamy-esapi.xml in classpaths enhancement +382 - Build Fails on path with space +465 - Update both ESAPI.properties files to show comment for ESAPI logger support for SLF4J +488 - Missed a legal input case in DefaultSecurityConfiguration.java +494 - Encoder's encodeForCSS doesn't handle RGB Triplets +495 - Maven Install Requires GPG Key +499 - ValidatorTest.isValidDirectoryPath() has tests that fail under Windows if ESAPI tests run from different drive where Windows installed +500 - Suppress noise from ESAPI searching for properties and stop ignoring important IOExceptions +503 - Bug on on referrer header when value contains `§ion` like `www.asdf.com?a=1§ion=2` +509 - HTMLValidationRule.getValid(String,String) does not follow documented specifications +511 - Add missing documentation to Validator.addRule() and Validator.getRule() +512 - Update Apache Commons Bean Utils to 1.9.4 +515 - NullPointerException can result from line 163 of SecurityWrapperRequest.java: +521 - JUnit test ValidatorTest.testIsValidSafeHTML() now failing +522 - javadoc corrections for Encoder.canonicalize() +523 - Links in README to users list and dev list are reversed +527 - Configuration flag for disabling Logger User and App Information +530 - Apply default logging content to SLF4J messages +532 - Update JUL and Log4J code structure and workflow to match SLF4J +536 - SecurityWrapperResponse setHeader error message is unclear +538 - Addressing log4j 1.x CVE-2019-17571 +542 - Write up ESAPI release notes for planned 2.2.1.0 release +552 - Rewrite implementation of some ESAPI classes to remove Java 8 dependencies +554 - CryptoHelper.arrayCompare() fails with NPE under Java 7 when one of the arguments is null +555 - JUnit test org.owasp.esapi.reference.AccessControllerTest.testIsAuthorizedForData fails when run against Java 7 on Linux +556 - Major overhaul to ESAPI Encoder Javadoc based on ESAPI Encoder Usability Study +558 - ValidatorTest.testIsValidDirectoryPath() JUnit test fails under MacOS ----------------------------------------------------------------------------- - Changes requiring special attention + Changes Requiring Special Attention ----------------------------------------------------------------------------- -The new default ESAPI logger is JUL (java.util.logging packages) and we have deprecated the use of Log4j 1.x as it is way past the end-of-life and we now support SLF4J. We did not want to make SLF4J the default logger (at least not yet) as we did not want to have the default ESAPI use require additional dependencies. However, SLF4J is likely to be the future choice, at least once we start on EsAPI 3.0. A special shout-out to Jeremiah Stacey for making this possible by re-factoring much of the ESAPI logger code. Note, the straw that broke the proverbial camel's back was the announcement of CVE-2019-17571 (rated Critical), for which there is no fix available and likely will never be. +The new default ESAPI logger is JUL (java.util.logging packages) and we have deprecated the use of Log4J 1.x because we now support SLF4J and Log4J 1.x is way past its end-of-life. We did not want to make SLF4J the default logger (at least not yet) as we did not want to have the default ESAPI use require additional dependencies. However, SLF4J is likely to be the future choice, at least once we start on EsAPI 3.0. A special shout-out to Jeremiah Stacey for making this possible by re-factoring much of the ESAPI logger code. Note, the straw that broke the proverbial camel's back was the announcement of CVE-2019-17571 (rated Critical), for which there is no fix available and likely will never be. Related to that CVE and how it affects ESAPI, be sure to read https://github.com/ESAPI/esapi-java-legacy/blob/develop/documentation/ESAPI-security-bulletin2.pdf -which describes CVE-2019-17571, a deserialization vulnerability in Log4j 1.2.17. ESAPI is not affected by this (even if you chose to use Log4j 1 as you default ESAPI logger). This security bulletin describes why this CVE is not exploitable as used by ESAPI. +which describes CVE-2019-17571, a deserialization vulnerability in Log4J 1.2.17. ESAPI is not affected by this (even if you chose to use Log4J 1 as you default ESAPI logger). This security bulletin describes why this CVE is not exploitable as used by ESAPI. Notable dependency updates (excludes those only used with JUnit tests): antiSamy 1.5.8 -> 1.5.10 @@ -70,6 +91,32 @@ Notable dependency updates (excludes those only used with JUnit tests): Finally, while ESAPI still supports JDK 7 (even though that too is way past end-of-life), the next ESAPI release will move to JDK 8 as the minimal baseline. (We already use Java 8 for development but still to Java 7 source and runtime compatiblity.) +----------------------------------------------------------------------------- + + Known Issues / Problems + +----------------------------------------------------------------------------- +If you use Java 7 (the minimal Java baseline supported by ESAPI) and try to run 'mvn test' there is one test that fails. This test passes with Java 8. The failing test is: + + [ERROR] Tests run: 5, Failures: 1, Errors: 0, Skipped: 0, Time elapsed: 0.203 s + <<< FAILURE! - in org.owasp.esapi.crypto.SecurityProviderLoaderTest + [ERROR] org.owasp.esapi.crypto.SecurityProviderLoaderTest.testWithBouncyCastle + Time elapsed: 0.116 s <<< FAILURE! + java.lang.AssertionError: Encryption w/ Bouncy Castle failed with + EncryptionException for preferred cipher transformation; exception was: + org.owasp.esapi.errors.EncryptionException: Encryption failure (unavailable + cipher requested) + at + org.owasp.esapi.crypto.SecurityProviderLoaderTest.testWithBouncyCastle(Security + ProviderLoaderTest.java:133) + +I will spare you all the details and tell you that this has to do with Java 7 not being able to correctly parse the signed Bouncy Castle JCE provider jar. More details are available at: + https://www.bouncycastle.org/latest_releases.html +and + https://github.com/bcgit/bc-java/issues/477 +I am sure that there are ways of making Bouncy Castle work with Java 7, but since ESAPI does not rely on Bouncy Castle (it can use any compliant JCE provider), this should not be a problem. (It works fine with the default SunJCE provider.) If it is important to get the BC provider working with the ESAPI Encryptor and Java 7, then open a GitHub issue and we will take a deeper look at it and see if we can suggest something. + + ----------------------------------------------------------------------------- Other changes in this release, some of which not tracked via GitHub issues @@ -77,60 +124,154 @@ Finally, while ESAPI still supports JDK 7 (even though that too is way past end- ----------------------------------------------------------------------------- Documentation updates for locating Jar files -Unneeded code removed from ExtensiveEncoder +Unneeded code removed from ExtensiveEncoderURI test Inline reader added to ExtensiveEncoder Additional time for windows to always sleep more than given seconds in CryptoTokenTest Change required by tweak to CipherText.toString() method Removed call to deprecated CryptoHelper.computeDerivedKey() method New JUnit tests for org.owasp.esapi.crypto.KeyDerivationFunction class -Use existing toString method rather than a StringBuilder -Documentation and tests -JavaLogger moved -Splitting user info from Client Supplier +Miscellaneous documentation and tests +JavaLogger moved to new package +Log4J 1.x no longer ESAPI's default logger ----------------------------------------------------------------------------- -Developer Activity Report (Changes between release 2.2.0.0 and 2.2.1.0, i.e., between 2019-06-25 and 2020-05-12) -Generated manually (this time) +Developer Activity Report (Changes between release 2.2.0.0 and 2.2.1.0, i.e., between 2019-06-25 and 2020-07-11) +Generated manually (this time) -- all errors are the fault of kwwall and his inability to do simple arithmetic. + +Developer Total Total Number # Merged +(GitHub ID) commits of Files Changed PRs +======================================================== +HJW8472 11 8 1 +jeremiahjstacey 78 70 6 +kwwall 65 64 8 +Michael-Ziluck 3 2 2 +sempf 1 1 1 +wiitek 6 4 2 +xeno6696 3 5 1 +======================================================== + Total: 21 -Developer Total Total Number -(GitHub ID) commits of Files Changed -===================================================== -jeremiahjstacey 11 68 -kwwall 16 26 -wiitek 3 6 -xeno6696 8 9 -Michael-Ziluck 2 3 -sempf 1 1 -===================================================== ----------------------------------------------------------------------------- -53 Closed PRs since 2.2.0.0 release (those rejected not listed) -=============================================================== +21 Closed PRs merged since 2.2.0.0 release (those rejected not listed) +====================================================================== +PR# GitHub ID Description +---------------------------------------------------------------------- +504 -- kwwall -- New scripts to suppress noise for 'mvn test' +505 -- kwwall -- Close issue #256. White-space clean up. +506 -- kwwall -- Closes Issue 245 +508 -- Michael-Ziluck -- Resolves #226 - Corrected docs for the bounded, numeric, random methods +510 -- Michael-Ziluck -- Resolve #509 - Properly throw exception when HTML fails +513 -- kwwall -- Close issue #512 by updating to 1.9.4 of Commons Beans Util. +514 -- xeno6696 -- Fixed issues #503 by writing a new addReferer method, also temporaril… +516 -- jeremiahjstacey -- Issue 515 +518 -- jeremiahjstacey -- Issue #511 Copying Docs from DefaultValidator +519 -- jeremiahjstacey -- Issue 494 CSSCodec RGB Triplets +520 -- jeremiahjstacey -- OS Name DefaultExecutorTests #143 +533 -- jeremiahjstacey -- #532 JUL and Log4J match SLF4J class structure and Workflow +535 -- kwwall -- Issue 521 +537 -- jeremiahjstacey -- Issue 536 +539 -- wiiitek -- upgrade for convergence +540 -- wiiitek -- Issue 382: Build Fails on path with space +541 -- HJW8472 -- Fixed issue #310 +543 -- sempf -- Release notes for 2.2.1.0 +551 -- kwwall -- Misc cleanup +553 -- kwwall -- Fix for GitHub Issue 552 +557 -- kwwall -- Final prep for 2.2.1.0 release + + +CHANGELOG: Create your own. May I suggest: + + git log --since=2019-06-25 --reverse --pretty=medium + + which will show all the commits since just after the last (2.2.0.0) release. + +----------------------------------------------------------------------------- -504 New scripts to suppress noise for 'mvn test' -510 Resolve #509 - Properly throw exception when HTML fails -513 Close issue #512 by updating to 1.9.4 of Commons Beans Util.\ -519 Issue 494 CSSCodec RGB Triplets -520 OS Name DefaultExecutorTests #143 -540 Issue 382: Build Fails on path with space -596 Closes Issue 245 +Direct and Transitive Runtime and Test Dependencies: + + $ mvn dependency:tree + [INFO] Scanning for projects... + [INFO] + [INFO] -----------------------< org.owasp.esapi:esapi >------------------------ + [INFO] Building ESAPI 2.2.1.0 + [INFO] --------------------------------[ jar ]--------------------------------- + [INFO] + [INFO] --- maven-dependency-plugin:3.1.2:tree (default-cli) @ esapi --- + [INFO] org.owasp.esapi:esapi:jar:2.2.1.0-RC1 + [INFO] +- javax.servlet:javax.servlet-api:jar:3.0.1:provided + [INFO] +- javax.servlet.jsp:javax.servlet.jsp-api:jar:2.3.3:provided + [INFO] +- com.io7m.xom:xom:jar:1.2.10:compile + [INFO] +- commons-beanutils:commons-beanutils:jar:1.9.4:compile + [INFO] | +- commons-logging:commons-logging:jar:1.2:compile + [INFO] | \- commons-collections:commons-collections:jar:3.2.2:compile + [INFO] +- commons-configuration:commons-configuration:jar:1.10:compile + [INFO] +- commons-lang:commons-lang:jar:2.6:compile + [INFO] +- commons-fileupload:commons-fileupload:jar:1.3.3:compile + [INFO] +- log4j:log4j:jar:1.2.17:compile + [INFO] +- org.apache.commons:commons-collections4:jar:4.2:compile + [INFO] +- org.apache-extras.beanshell:bsh:jar:2.0b6:compile + [INFO] +- org.owasp.antisamy:antisamy:jar:1.5.10:compile + [INFO] | +- net.sourceforge.nekohtml:nekohtml:jar:1.9.22:compile + [INFO] | +- org.apache.httpcomponents:httpclient:jar:4.5.12:compile + [INFO] | | \- org.apache.httpcomponents:httpcore:jar:4.4.13:compile + [INFO] | \- commons-codec:commons-codec:jar:1.14:compile + [INFO] +- org.slf4j:slf4j-api:jar:1.7.30:compile + [INFO] +- commons-io:commons-io:jar:2.6:compile + [INFO] +- org.apache.xmlgraphics:batik-css:jar:1.13:compile + [INFO] | +- org.apache.xmlgraphics:batik-shared-resources:jar:1.13:compile + [INFO] | +- org.apache.xmlgraphics:batik-util:jar:1.13:compile + [INFO] | | +- org.apache.xmlgraphics:batik-constants:jar:1.13:compile + [INFO] | | \- org.apache.xmlgraphics:batik-i18n:jar:1.13:compile + [INFO] | +- org.apache.xmlgraphics:xmlgraphics-commons:jar:2.4:compile + [INFO] | \- xml-apis:xml-apis-ext:jar:1.3.04:compile + [INFO] +- xalan:xalan:jar:2.7.2:compile + [INFO] | \- xalan:serializer:jar:2.7.2:compile + [INFO] +- xerces:xercesImpl:jar:2.12.0:compile + [INFO] +- xml-apis:xml-apis:jar:1.4.01:compile + [INFO] +- com.github.spotbugs:spotbugs-annotations:jar:4.0.4:compile (optional) + [INFO] | \- com.google.code.findbugs:jsr305:jar:3.0.2:compile (optional) + [INFO] +- net.jcip:jcip-annotations:jar:1.0:compile (optional) + [INFO] +- junit:junit:jar:4.13:test + [INFO] | \- org.hamcrest:hamcrest-core:jar:1.3:test + [INFO] +- org.bouncycastle:bcprov-jdk15on:jar:1.65.01:test + [INFO] +- org.powermock:powermock-api-mockito2:jar:2.0.7:test + [INFO] | \- org.powermock:powermock-api-support:jar:2.0.7:test + [INFO] | \- org.powermock:powermock-core:jar:2.0.7:test + [INFO] +- org.javassist:javassist:jar:3.25.0-GA:test + [INFO] +- org.mockito:mockito-core:jar:2.28.2:test + [INFO] | +- net.bytebuddy:byte-buddy:jar:1.9.10:test + [INFO] | +- net.bytebuddy:byte-buddy-agent:jar:1.9.10:test + [INFO] | \- org.objenesis:objenesis:jar:2.6:test + [INFO] +- org.powermock:powermock-module-junit4:jar:2.0.7:test + [INFO] | \- org.powermock:powermock-module-junit4-common:jar:2.0.7:test + [INFO] +- org.powermock:powermock-reflect:jar:2.0.7:test + [INFO] +- org.openjdk.jmh:jmh-core:jar:1.23:test + [INFO] | +- net.sf.jopt-simple:jopt-simple:jar:4.6:test + [INFO] | \- org.apache.commons:commons-math3:jar:3.2:test + [INFO] \- org.openjdk.jmh:jmh-generator-annprocess:jar:1.23:test + [INFO] ------------------------------------------------------------------------ + [INFO] BUILD SUCCESS + [INFO] ------------------------------------------------------------------------ ----------------------------------------------------------------------------- -Notice: +Ackknowledgements: Release notes written by Bill Sempf (bill.sempf@owasp.org), but please direct any communication to the project leaders. -Project co-leaders - Kevin W. Wall (kwwall) - Matt Seil (xeno6696) - Special shout-outs to: - Jeremiah Stacey (jeremiahjstacey) -- All around ESAPI support and JUnit test case developer extraordinaire - Dave Wichers (davewichers) - for pom.xml improvements - Bill Sempf -- for these release notes. Awesome job, Bill. I owe you a brew. + Jeremiah Stacey (jeremiahjstacey) -- All around ESAPI support and JUnit test case developer extraordinaire and for refactoring ESAPI loggers. + Dave Wichers (davewichers) - for several extremely useful pom.xml improvements. + Bill Sempf (sempf) -- for these release notes. Awesome job, Bill. I owe you a brew. + Chamila Wijayarathna and Nalin A. G. Arachchilage for their authorship and subsequent extensive discussion of their paper "Fighting Against XSS Attacks: A Usability Evaluation of OWASP ESAPI Output Encoding" (https://scholarspace.manoa.hawaii.edu/bitstream/10125/60167/0727.pdf). Their paper and their willingness to engage with me to discuss it was what led to the (hopefully) improved Javadoc for the ESAPI Encoder interface. + And lastly a special thanks to first-time contributors Michael-Ziluck, wiiitek, and HJW8472. Thanks you all for your time and effort to ESAPI and making it a better project. And if I've missed any, my apologies; let me know and I will correct it. + +A special thanks to the ESAPI community from the ESAPI project co-leaders: + Kevin W. Wall (kwwall) <== The irresponsible party for these release notes! + Matt Seil (xeno6696) diff --git a/pom.xml b/pom.xml index 8623ac2e6..8a383d6f2 100644 --- a/pom.xml +++ b/pom.xml @@ -135,7 +135,10 @@ 1.23 2.0.7 4.0.4 - 3.0.0-M5 + + 3.0.0-M2 diff --git a/src/main/java/org/owasp/esapi/Encoder.java b/src/main/java/org/owasp/esapi/Encoder.java index b9d42dea5..a8b949d71 100644 --- a/src/main/java/org/owasp/esapi/Encoder.java +++ b/src/main/java/org/owasp/esapi/Encoder.java @@ -149,6 +149,9 @@ * * * + * @see OWASP Cross-Site Scripting Prevention Cheat Sheet. + * @see OWASP Proactive Controls: C4: Encode and Escape Data + * @see Properly encoding and escaping for the web. * @author Jeff Williams (jeff.williams .at. aspectsecurity.com) Aspect Security * @since June 1, 2007 diff --git a/src/main/java/org/owasp/esapi/crypto/CryptoHelper.java b/src/main/java/org/owasp/esapi/crypto/CryptoHelper.java index 11a6bcb9f..31f276ac4 100644 --- a/src/main/java/org/owasp/esapi/crypto/CryptoHelper.java +++ b/src/main/java/org/owasp/esapi/crypto/CryptoHelper.java @@ -352,7 +352,15 @@ public static void copyByteArray(final byte[] src, byte[] dest) */ @Deprecated public static boolean arrayCompare(byte[] b1, byte[] b2) { - // Note: See GitHub issue #246 + // Note: See GitHub issue #246 and #554. + // If we make Java 8 the minimal ESAPI baseline before we remove this + // method, we can at least remove these next 6 lines. (Issue 554.) + if ( b1 == null && b2 == null ) { // Must test this first! + return true; // Prevent NPE; compatibility with Java 8 and later. + } + if ( b1 == null || b2 == null ) { + return false; // Prevent NPE; compatibility with Java 8 and later. + } return java.security.MessageDigest.isEqual(b1, b2); } diff --git a/src/test/java/org/owasp/esapi/crypto/CryptoHelperTest.java b/src/test/java/org/owasp/esapi/crypto/CryptoHelperTest.java index 678b0acf8..aa7ae0cf3 100644 --- a/src/test/java/org/owasp/esapi/crypto/CryptoHelperTest.java +++ b/src/test/java/org/owasp/esapi/crypto/CryptoHelperTest.java @@ -131,7 +131,13 @@ public final void testArrayCompare() { // stop = System.nanoTime(); // diff = stop - start; // System.out.println("diff: " + diff + " nanosec"); - + +// start = System.nanoTime(); + assertFalse(CryptoHelper.arrayCompare(null, ba1)); +// stop = System.nanoTime(); +// diff = stop - start; +// System.out.println("diff: " + diff + " nanosec"); + ba2 = ba1; // start = System.nanoTime(); assertTrue(CryptoHelper.arrayCompare(ba1, ba2)); @@ -186,4 +192,4 @@ private boolean checkByteArray(byte[] ba, byte b) { public static junit.framework.Test suite() { return new JUnit4TestAdapter(CryptoHelperTest.class); } -} \ No newline at end of file +} diff --git a/src/test/java/org/owasp/esapi/reference/AccessControllerTest.java b/src/test/java/org/owasp/esapi/reference/AccessControllerTest.java index ad53d9ea3..107c1da71 100644 --- a/src/test/java/org/owasp/esapi/reference/AccessControllerTest.java +++ b/src/test/java/org/owasp/esapi/reference/AccessControllerTest.java @@ -226,7 +226,7 @@ public void testIsAuthorizedForData() { userRW = Class.forName("java.lang.String"); anyR = Class.forName("java.io.BufferedReader"); userAdminR = Class.forName("java.util.Random"); - userAdminRW = Class.forName("java.awt.event.MouseWheelEvent"); + userAdminRW = Class.forName("javax.crypto.Cipher"); undefined = Class.forName("java.io.FileWriter"); }catch(ClassNotFoundException cnf){ diff --git a/src/test/java/org/owasp/esapi/reference/ValidatorTest.java b/src/test/java/org/owasp/esapi/reference/ValidatorTest.java index b0fd24789..fc0dc7e06 100644 --- a/src/test/java/org/owasp/esapi/reference/ValidatorTest.java +++ b/src/test/java/org/owasp/esapi/reference/ValidatorTest.java @@ -350,7 +350,13 @@ public void testIsValidDirectoryPath() throws IOException { // Unix specific paths should pass assertTrue(instance.isValidDirectoryPath("test", "/", parent, false)); // Root directory - assertTrue(instance.isValidDirectoryPath("test", "/etc", parent, false)); // Always exist directory + // Unfortunately, on MacOS both "/etc" and "/var" are symlinks + // to "/private/etc" and "/private/var" respectively, and "/sbin" + // and "/bin" sometimes are symlinks on certain *nix OSs, so we need + // to special case MacOS here. + boolean isMac = System.getProperty("os.name").toLowerCase().contains("mac"); + String testDirNotSymLink = isMac ? "/private" : "/etc"; + assertTrue(instance.isValidDirectoryPath("test", testDirNotSymLink, parent, false)); // Always exist directory // Unix specific paths that should not exist or work assertFalse(instance.isValidDirectoryPath("test", "/bin/sh", parent, false)); // Standard shell, not dir diff --git a/src/test/resources/esapi/fbac-policies/DataAccessRules.txt b/src/test/resources/esapi/fbac-policies/DataAccessRules.txt index f21e8c869..0341aa56f 100644 --- a/src/test/resources/esapi/fbac-policies/DataAccessRules.txt +++ b/src/test/resources/esapi/fbac-policies/DataAccessRules.txt @@ -4,6 +4,6 @@ java.io.BufferedReader | any | read | default deny java.lang.String | User | read, write | java.lang.Math | Admin | read, write | java.util.ArrayList | Admin | read | -java.awt.event.MouseWheelEvent | Admin, User | write, read | +javax.crypto.Cipher | Admin, User | write, read | java.util.Date | User | write | -java.util.Random | User, Admin | read | \ No newline at end of file +java.util.Random | User, Admin | read | From 6bc2889e370acfdee6d8ffd711fc9a354506893d Mon Sep 17 00:00:00 2001 From: kwwall Date: Sun, 12 Jul 2020 22:40:52 -0400 Subject: [PATCH 0655/1069] Change release from 2.2.1.0-RC1 to 2.2.1.0. Also add additional comment about surefire plug-in. --- pom.xml | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/pom.xml b/pom.xml index 8a383d6f2..33af4721a 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 org.owasp.esapi esapi - 2.2.1.0-RC1 + 2.2.1.0 jar @@ -136,8 +136,11 @@ 2.0.7 4.0.4 + org.owasp.esapi.reference.DefaultValidatorInputStringAPITest.getValidInputNullAllowedPassthrough Time elapsed: 2.057 s <<< ERROR! + java.lang.OutOfMemoryError: PermGen space + + when running tests with Java 7 on Mac OS X. No problems observed on Linux. + --> 3.0.0-M2 From fefe6033d8e21963ab5f84b1649b8708366cd524 Mon Sep 17 00:00:00 2001 From: kwwall Date: Sun, 12 Jul 2020 22:42:38 -0400 Subject: [PATCH 0656/1069] Correct # of commits. --- documentation/esapi4java-core-2.2.1.0-release-notes.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/documentation/esapi4java-core-2.2.1.0-release-notes.txt b/documentation/esapi4java-core-2.2.1.0-release-notes.txt index ee3caf5ea..f91f82332 100644 --- a/documentation/esapi4java-core-2.2.1.0-release-notes.txt +++ b/documentation/esapi4java-core-2.2.1.0-release-notes.txt @@ -142,9 +142,10 @@ Generated manually (this time) -- all errors are the fault of kwwall and his ina Developer Total Total Number # Merged (GitHub ID) commits of Files Changed PRs ======================================================== +davewichers 2 1 0 HJW8472 11 8 1 jeremiahjstacey 78 70 6 -kwwall 65 64 8 +kwwall 67 64 8 Michael-Ziluck 3 2 2 sempf 1 1 1 wiitek 6 4 2 From fd009ec4cb166f8ecd72e4cb0fa303109558372e Mon Sep 17 00:00:00 2001 From: kwwall Date: Sun, 12 Jul 2020 22:57:35 -0400 Subject: [PATCH 0657/1069] Prep for next development release and change version from 2.2.1.0 to 2.3.0.0-SNAPSHOT. --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 33af4721a..6356f1973 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 org.owasp.esapi esapi - 2.2.1.0 + 2.3.0.0-SNAPSHOT jar From 5ddb8399e60d947dd454007976910e3f51c37f87 Mon Sep 17 00:00:00 2001 From: kwwall Date: Mon, 13 Jul 2020 23:23:44 -0400 Subject: [PATCH 0658/1069] Miscellaneous minor clean-up of release notes. 1) Add release date. 2) Fix multiple spelling errors 3) Document new 'Known Issue' about running 'mvn test' from Windows 10 'cmd' prompt. --- .../esapi4java-core-2.2.1.0-release-notes.txt | 21 ++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/documentation/esapi4java-core-2.2.1.0-release-notes.txt b/documentation/esapi4java-core-2.2.1.0-release-notes.txt index f91f82332..7d6c54ef7 100644 --- a/documentation/esapi4java-core-2.2.1.0-release-notes.txt +++ b/documentation/esapi4java-core-2.2.1.0-release-notes.txt @@ -1,5 +1,5 @@ Release notes for ESAPI 2.2.1.0 - Release date: 2020-July-?? + Release date: 2020-July-12 Project leaders: -Kevin W. Wall -Matt Seil @@ -32,14 +32,14 @@ ESAPI 2.2.1.0 release: Issue # GitHub Issue Title ---------------------------------------------------------------------------------------------- -143 - Enchance encodeForOS to auto-detect the underling OS +143 - Enhance encodeForOS to auto-detect the underling OS 173 - DOMConfigurator is being used inappropriately in the ESAPIWebApplicationFirewallFilter 226 - Javadoc Inaccuracy in getRandomInteger() and getRandomReal() 232 - SecurityWrapperResponse.createCookieHeader modification request (closed; marked 'wontfix') 235 - exception is java.lang.NoClassDefFoundError: org.owasp.esapi.codecs.Codec 245 - KeyDerivationFunction::computeDerivedKey - possible security level mismatch 256 - Whitespace in JavaEncryptor -263 - I am getting validation exception while validating a paramter coming from http request +263 - I am getting validation exception while validating a parameter coming from http request 268 - SecurityWrapperResponse setStatus should not always set SC_OK 269 - org.owasp.esapi.reference.DefaultValidator reports ValidationException with IE 9 271 - Add Constructor to DefaultSecurityConfiguration to accept a properties file (1.4) @@ -77,7 +77,7 @@ Issue # GitHub Issue Title Changes Requiring Special Attention ----------------------------------------------------------------------------- -The new default ESAPI logger is JUL (java.util.logging packages) and we have deprecated the use of Log4J 1.x because we now support SLF4J and Log4J 1.x is way past its end-of-life. We did not want to make SLF4J the default logger (at least not yet) as we did not want to have the default ESAPI use require additional dependencies. However, SLF4J is likely to be the future choice, at least once we start on EsAPI 3.0. A special shout-out to Jeremiah Stacey for making this possible by re-factoring much of the ESAPI logger code. Note, the straw that broke the proverbial camel's back was the announcement of CVE-2019-17571 (rated Critical), for which there is no fix available and likely will never be. +The new default ESAPI logger is JUL (java.util.logging packages) and we have deprecated the use of Log4J 1.x because we now support SLF4J and Log4J 1.x is way past its end-of-life. We did not want to make SLF4J the default logger (at least not yet) as we did not want to have the default ESAPI use require additional dependencies. However, SLF4J is likely to be the future choice, at least once we start on ESAPI 3.0. A special shout-out to Jeremiah Stacey for making this possible by re-factoring much of the ESAPI logger code. Note, the straw that broke the proverbial camel's back was the announcement of CVE-2019-17571 (rated Critical), for which there is no fix available and likely will never be. Related to that CVE and how it affects ESAPI, be sure to read https://github.com/ESAPI/esapi-java-legacy/blob/develop/documentation/ESAPI-security-bulletin2.pdf @@ -89,7 +89,7 @@ Notable dependency updates (excludes those only used with JUnit tests): commons-beansutil 1.9.3 -> 1.9.4 slf4j-api 1.7.26 -> 1.7.30 -Finally, while ESAPI still supports JDK 7 (even though that too is way past end-of-life), the next ESAPI release will move to JDK 8 as the minimal baseline. (We already use Java 8 for development but still to Java 7 source and runtime compatiblity.) +Finally, while ESAPI still supports JDK 7 (even though that too is way past end-of-life), the next ESAPI release will move to JDK 8 as the minimal baseline. (We already use Java 8 for development but still to Java 7 source and runtime compatibility.) ----------------------------------------------------------------------------- @@ -117,6 +117,13 @@ and I am sure that there are ways of making Bouncy Castle work with Java 7, but since ESAPI does not rely on Bouncy Castle (it can use any compliant JCE provider), this should not be a problem. (It works fine with the default SunJCE provider.) If it is important to get the BC provider working with the ESAPI Encryptor and Java 7, then open a GitHub issue and we will take a deeper look at it and see if we can suggest something. + +Another problem is if you run 'mvn test' from the 'cmd' prompt (and possibly PowerShell as well), you will get intermittent failures (generally between 10-25% of the time) at arbitrary spots. If you run it again without any changes it will work fine without any failures. We have discovered that it doesn't seem to fail if you run the tests from an IDE like Eclipse or if you redirect both stdout and stderr to a file; e.g., + + C:\code\esapi-java-legacy> mvn test >testoutput.txt 2>&1 + +We do not know the reason for these failures, but only that we have observed them on Windows 10. If you see this error, please do NOT report it as a GitHub issue unless you know a fix for it. + ----------------------------------------------------------------------------- Other changes in this release, some of which not tracked via GitHub issues @@ -167,7 +174,7 @@ PR# GitHub ID Description 508 -- Michael-Ziluck -- Resolves #226 - Corrected docs for the bounded, numeric, random methods 510 -- Michael-Ziluck -- Resolve #509 - Properly throw exception when HTML fails 513 -- kwwall -- Close issue #512 by updating to 1.9.4 of Commons Beans Util. -514 -- xeno6696 -- Fixed issues #503 by writing a new addReferer method, also temporaril… +514 -- xeno6696 -- Fixed issues #503 by writing a new addReferer method, also temporarily… 516 -- jeremiahjstacey -- Issue 515 518 -- jeremiahjstacey -- Issue #511 Copying Docs from DefaultValidator 519 -- jeremiahjstacey -- Issue 494 CSSCodec RGB Triplets @@ -260,7 +267,7 @@ Direct and Transitive Runtime and Test Dependencies: ----------------------------------------------------------------------------- -Ackknowledgements: +Acknowledgments: Release notes written by Bill Sempf (bill.sempf@owasp.org), but please direct any communication to the project leaders. From 31f0f988d45de4ed08e515dc3a82842fad4f5bd4 Mon Sep 17 00:00:00 2001 From: kwwall Date: Fri, 17 Jul 2020 21:28:21 -0400 Subject: [PATCH 0659/1069] Rephrase potentially confusing or misleading statements about ESAPI 3. Thanks to Timo Pagel for bringing this to our awareness. --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index dc59e7a2a..85874bed9 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ OWASP® ESAPI (The OWASP Enterprise Security API) is a free, open source, web ap # What does Legacy mean? -

    This is the legacy branch of ESAPI which means it is an actively maintained branch of the project, however feature development for this branch will not be done. Features that have already been scheduled for the 2.x branch will move forward, but the main focus will be working on the ESAPI 3.x branch. +

    This is the legacy branch of ESAPI which means it is an actively maintained branch of the project, however significan _new_ feature development for this branch will _not_ be done. Features that have already been scheduled for the 2.x branch will move forward. IMPORTANT NOTES: The default branch for ESAPI legacy is now the 'develop' branch (rather than the 'master' branch), where future development, bug fixes, etc. will now be done. The 'master' branch is now marked as "protected"; it reflects the latest stable ESAPI release (2.1.0.1 as of this date). Note that this change of making the 'develop' branch the default may affect any pull requests that you were intending to make. @@ -25,6 +25,8 @@ Also, the minimal baseline Java version to use ESAPI is Java 7. (This was # Where can I find ESAPI 3.x? https://github.com/ESAPI/esapi-java +Note however that work on ESAPI 3 has not yet become in earnest and is only in its earliest planning stages. Even the code that is presently there will likely change. + # Locating ESAPI Jar files The [latest ESAPI release](https://github.com/ESAPI/esapi-java-legacy/releases/latest) is 2.2.0.0. The default configuration jar and its GPG signature can be found at [esapi-2.2.0.0-configuration.jar](https://github.com/ESAPI/esapi-java-legacy/releases/download/esapi-2.2.0.0/esapi-2.2.0.0-configuration.jar) and [esapi-2.2.0.0-configuration.jar.asc](https://github.com/ESAPI/esapi-java-legacy/releases/download/esapi-2.2.0.0/esapi-2.2.0.0-configuration.jar.asc) respectively. From 92ae8e11b2d16e47e85c61db0e47f503da6162d4 Mon Sep 17 00:00:00 2001 From: kwwall Date: Sun, 19 Jul 2020 22:56:06 -0400 Subject: [PATCH 0660/1069] Fix latest release #s. Fix some MarkDown. Add link to 'Should I use ESAPI?'. --- README.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 85874bed9..3f7970602 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ OWASP® ESAPI (The OWASP Enterprise Security API) is a free, open source, web ap # What does Legacy mean? -

    This is the legacy branch of ESAPI which means it is an actively maintained branch of the project, however significan _new_ feature development for this branch will _not_ be done. Features that have already been scheduled for the 2.x branch will move forward. +

    This is the legacy branch of ESAPI which means it is an actively maintained branch of the project, however significan *new* feature development for this branch will *not* be done. Features that have already been scheduled for the 2.x branch will move forward. IMPORTANT NOTES: The default branch for ESAPI legacy is now the 'develop' branch (rather than the 'master' branch), where future development, bug fixes, etc. will now be done. The 'master' branch is now marked as "protected"; it reflects the latest stable ESAPI release (2.1.0.1 as of this date). Note that this change of making the 'develop' branch the default may affect any pull requests that you were intending to make. @@ -28,10 +28,11 @@ https://github.com/ESAPI/esapi-java Note however that work on ESAPI 3 has not yet become in earnest and is only in its earliest planning stages. Even the code that is presently there will likely change. # Locating ESAPI Jar files -The [latest ESAPI release](https://github.com/ESAPI/esapi-java-legacy/releases/latest) is 2.2.0.0. The default configuration jar and its GPG signature can be found at [esapi-2.2.0.0-configuration.jar](https://github.com/ESAPI/esapi-java-legacy/releases/download/esapi-2.2.0.0/esapi-2.2.0.0-configuration.jar) and [esapi-2.2.0.0-configuration.jar.asc](https://github.com/ESAPI/esapi-java-legacy/releases/download/esapi-2.2.0.0/esapi-2.2.0.0-configuration.jar.asc) respectively. +The [latest ESAPI release](https://github.com/ESAPI/esapi-java-legacy/releases/latest) is 2.2.1.0. The default configuration jar and its GPG signature can be found at [esapi-2.2.1.0-configuration.jar](https://github.com/ESAPI/esapi-java-legacy/releases/download/esapi-2.2.1.0/esapi-2.2.1.0-configuration.jar) and [esapi-2.2.1.0-configuration.jar.asc](https://github.com/ESAPI/esapi-java-legacy/releases/download/esapi-2.2.1.0/esapi-2.2.1.0-configuration.jar.asc) respectively. The latest regular ESAPI jars can are available from Maven Central. +However, before you start a *new* project using ESAPI, but sure to read "[Should I use ESAPI?](https://owasp.org/www-project-enterprise-security-api/#div-shouldiuseesapi)". # ESAPI Deprecation Policy Unless we unintentionally screw-up, our intent is to keep classes, methods, and/or fields whihc have been annotated as "@deprecated" for a minimum of two (2) years or until the next major release number (e.g., 3.x as of now), which ever comes first, before we remove them. @@ -82,7 +83,7 @@ Webchat http://webchat.freenode.net/ *Mailing lists:* As of 2019-03-25, ESAPI's 2 mailing lists were officially moved OFF of their Mailman mailing lists to a new home on Google Groups. -The names of the 2 Google Groups are "[esapi-project-users](mailto:esapi-project-users@owasp.org)" and "[esapi-project-dev](mailto:esapi-project-dev@owasp.org)", which you may POST to _after_ you subscribe to them via "[Subscribe to ESAPI Users list](https://groups.google.com/a/owasp.org/forum/#!forum/esapi-project-users/join)" and "[Subscribe to ESAPI Developers list](https://groups.google.com/a/owasp.org/forum/#!forum/esapi-project-dev/join)" respectively. +The names of the 2 Google Groups are "[esapi-project-users](mailto:esapi-project-users@owasp.org)" and "[esapi-project-dev](mailto:esapi-project-dev@owasp.org)", which you may POST to *after* you subscribe to them via "[Subscribe to ESAPI Users list](https://groups.google.com/a/owasp.org/forum/#!forum/esapi-project-users/join)" and "[Subscribe to ESAPI Developers list](https://groups.google.com/a/owasp.org/forum/#!forum/esapi-project-dev/join)" respectively. Old archives for the old Mailman mailing lists for ESAPI-Users and ESAPI-Dev are still available at https://lists.owasp.org/pipermail/esapi-users/ and https://lists.owasp.org/pipermail/esapi-dev/ respectively. From 073193e4498c83a325c1caec6bd42f3912438ee2 Mon Sep 17 00:00:00 2001 From: jeremiahjstacey Date: Fri, 24 Jul 2020 18:46:39 -0500 Subject: [PATCH 0661/1069] Issue #560 JUL fixes (#562) * JUL Property Resource Logic fix Adjusting the loading behavior of the esapi-java-logging.properties file to account for a possible null stream resolution. Updating the error handling from system error output to throwing ConfigurationExceptions. Tests updated accordingly * JUL Default Logging configuration Adding a default version of esapi-java-logging.properties to the configuration directory. * JUL Documentation updates Noting the property file requirement and resource location in the class java doc. * Property Comment Updates Supplying a more accurate description of the effect of Logger.ClientInfo * Property Comment Updates Supplying a more accurate description of the effect of Logger.ClientInfo --- configuration/esapi/ESAPI.properties | 2 +- .../esapi/esapi-java-logging.properties | 6 +++ .../esapi/logging/java/JavaLogFactory.java | 10 ++++- .../logging/java/JavaLogFactoryTest.java | 44 +++++++------------ src/test/resources/esapi/ESAPI.properties | 2 +- 5 files changed, 32 insertions(+), 32 deletions(-) create mode 100644 configuration/esapi/esapi-java-logging.properties diff --git a/configuration/esapi/ESAPI.properties b/configuration/esapi/ESAPI.properties index bafe1e38c..ccf146114 100644 --- a/configuration/esapi/ESAPI.properties +++ b/configuration/esapi/ESAPI.properties @@ -394,7 +394,7 @@ Logger.LogFileName=ESAPI_logging_file Logger.MaxLogFileSize=10000000 # Determines whether ESAPI should log the user info. Logger.UserInfo=true -# Determines whether ESAPI should log the app info. +# Determines whether ESAPI should log the session id and client IP. Logger.ClientInfo=true #=========================================================================== diff --git a/configuration/esapi/esapi-java-logging.properties b/configuration/esapi/esapi-java-logging.properties new file mode 100644 index 000000000..71011acc5 --- /dev/null +++ b/configuration/esapi/esapi-java-logging.properties @@ -0,0 +1,6 @@ +handlers= java.util.logging.ConsoleHandler +.level= INFO +java.util.logging.ConsoleHandler.level = INFO +java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter +java.util.logging.SimpleFormatter.format=[%1$tF %1$tT] [%3$-7s] %5$s %n +#https://www.logicbig.com/tutorials/core-java-tutorial/logging/customizing-default-format.html \ No newline at end of file diff --git a/src/main/java/org/owasp/esapi/logging/java/JavaLogFactory.java b/src/main/java/org/owasp/esapi/logging/java/JavaLogFactory.java index 93230c8e5..601d0da2a 100644 --- a/src/main/java/org/owasp/esapi/logging/java/JavaLogFactory.java +++ b/src/main/java/org/owasp/esapi/logging/java/JavaLogFactory.java @@ -26,6 +26,7 @@ import org.owasp.esapi.LogFactory; import org.owasp.esapi.Logger; import org.owasp.esapi.codecs.HTMLEntityCodec; +import org.owasp.esapi.errors.ConfigurationException; import org.owasp.esapi.logging.appender.LogAppender; import org.owasp.esapi.logging.appender.LogPrefixAppender; import org.owasp.esapi.logging.cleaning.CodecLogScrubber; @@ -35,6 +36,10 @@ import org.owasp.esapi.reference.DefaultSecurityConfiguration; /** * LogFactory implementation which creates JAVA supporting Loggers. + * + * This implementation requires that a file named 'esapi-java-logging.properties' exists on the classpath. + *
    + * A default file implementation is available in the configuration jar on GitHub under the 'Releases' * */ public class JavaLogFactory implements LogFactory { @@ -86,9 +91,12 @@ public class JavaLogFactory implements LogFactory { */ try (InputStream stream = JavaLogFactory.class.getClassLoader(). getResourceAsStream("esapi-java-logging.properties")) { + if (stream == null) { + throw new ConfigurationException("Unable to locate resource: esapi-java-logging.properties"); + } logManager.readConfiguration(stream); } catch (IOException ioe) { - System.err.print(new IOException("Failed to load esapi-java-logging.properties.", ioe)); + throw new ConfigurationException("Failed to load esapi-java-logging.properties.", ioe); } } diff --git a/src/test/java/org/owasp/esapi/logging/java/JavaLogFactoryTest.java b/src/test/java/org/owasp/esapi/logging/java/JavaLogFactoryTest.java index c0713f239..465083da3 100644 --- a/src/test/java/org/owasp/esapi/logging/java/JavaLogFactoryTest.java +++ b/src/test/java/org/owasp/esapi/logging/java/JavaLogFactoryTest.java @@ -14,24 +14,21 @@ */ package org.owasp.esapi.logging.java; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; - import java.io.IOException; import java.io.InputStream; -import java.io.OutputStream; -import java.io.PrintStream; import java.util.List; import java.util.logging.LogManager; +import org.hamcrest.CustomMatcher; import org.junit.Assert; import org.junit.Rule; import org.junit.Test; +import org.junit.rules.ExpectedException; import org.junit.rules.TestName; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; -import org.mockito.Mockito; import org.owasp.esapi.Logger; +import org.owasp.esapi.errors.ConfigurationException; import org.owasp.esapi.logging.appender.LogAppender; import org.owasp.esapi.logging.appender.LogPrefixAppender; import org.owasp.esapi.logging.cleaning.CodecLogScrubber; @@ -47,9 +44,12 @@ public class JavaLogFactoryTest { @Rule public TestName testName = new TestName(); + + @Rule + public ExpectedException exEx = ExpectedException.none(); @Test - public void testIOExceptionOnMissingConfiguration() throws Exception { + public void testConfigurationExceptionOnMissingConfiguration() throws Exception { final IOException originException = new IOException(testName.getMethodName()); LogManager testLogManager = new LogManager() { @@ -59,31 +59,17 @@ public void readConfiguration(InputStream ins) throws IOException, SecurityExcep } }; - OutputStream nullOutputStream = new OutputStream() { + exEx.expectMessage("Failed to load esapi-java-logging.properties"); + exEx.expect(ConfigurationException.class); + + exEx.expectCause(new CustomMatcher("Check for IOException") { @Override - public void write(int b) throws IOException { - //No Op + public boolean matches(Object item) { + return item instanceof IOException; } - }; - - ArgumentCaptor stdErrOut = ArgumentCaptor.forClass(Object.class); - PrintStream orig = System.err; - try (PrintStream errPrinter = new PrintStream(nullOutputStream)) { - PrintStream spyPrinter = PowerMockito.spy(errPrinter); - Mockito.doCallRealMethod().when(spyPrinter).print(stdErrOut.capture()); - System.setErr(spyPrinter); - - JavaLogFactory.readLoggerConfiguration(testLogManager); - - Object writeData = stdErrOut.getValue(); - assertTrue(writeData instanceof IOException); - IOException actual = (IOException) writeData; - assertEquals(originException, actual.getCause()); - assertEquals("Failed to load esapi-java-logging.properties.", actual.getMessage()); - } finally { - System.setErr(orig); - } + }); + JavaLogFactory.readLoggerConfiguration(testLogManager); } @Test diff --git a/src/test/resources/esapi/ESAPI.properties b/src/test/resources/esapi/ESAPI.properties index 14b47d32f..6df0acc93 100644 --- a/src/test/resources/esapi/ESAPI.properties +++ b/src/test/resources/esapi/ESAPI.properties @@ -426,7 +426,7 @@ Logger.LogFileName=ESAPI_logging_file Logger.MaxLogFileSize=10000000 # Determines whether ESAPI should log the user info. Logger.UserInfo=true -# Determines whether ESAPI should log the app info. +# Determines whether ESAPI should log the session id and client IP. Logger.ClientInfo=true #=========================================================================== From 4b97073075a26facadf2931de792eddf0f8141b4 Mon Sep 17 00:00:00 2001 From: Snyk bot Date: Mon, 27 Jul 2020 03:29:35 +0100 Subject: [PATCH 0662/1069] fix: upgrade com.github.spotbugs:spotbugs-annotations from 4.0.4 to 4.0.5 (#559) Snyk has created this PR to upgrade com.github.spotbugs:spotbugs-annotations from 4.0.4 to 4.0.5. See this package in NPM: https://www.npmjs.com/package/com.github.spotbugs:spotbugs-annotations See this project in Snyk: https://app.snyk.io/org/planetlevel/project/f53b118f-f068-49f2-98e2-0b5681787cd7?utm_source=github&utm_medium=upgrade-pr --- pom.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pom.xml b/pom.xml index 6356f1973..5c574aca3 100644 --- a/pom.xml +++ b/pom.xml @@ -134,7 +134,7 @@ UTF-8 1.23 2.0.7 - 4.0.4 + 4.0.5 + Dependencies shouldn't require Java 8+ From b7a7043963b09acf2c2401a4b565298b90c32308 Mon Sep 17 00:00:00 2001 From: kwwall Date: Sun, 26 Jul 2020 22:59:44 -0400 Subject: [PATCH 0663/1069] Revert "fix: upgrade com.github.spotbugs:spotbugs-annotations from 4.0.4 to 4.0.5 (#559)" This reverts commit 4b97073075a26facadf2931de792eddf0f8141b4. Synk-bot PR #559 results in errors when running 'mvn site' about the '4.0.5' version of com.github.spotbugs:spotbugs-annotations not being found (in Maven Central). I should have tested it first. :-( Lesson learned. --- pom.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pom.xml b/pom.xml index 5c574aca3..6356f1973 100644 --- a/pom.xml +++ b/pom.xml @@ -134,7 +134,7 @@ UTF-8 1.23 2.0.7 - 4.0.5 + 4.0.4 + Dependencies shouldn't require Java 8+ From ba79395ecbddcfb0896f565cf54ba26936268668 Mon Sep 17 00:00:00 2001 From: kwwall Date: Sun, 26 Jul 2020 23:20:58 -0400 Subject: [PATCH 0664/1069] Update pom to reflect new 2.2.1.1 patch release. --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 6356f1973..4a60e515e 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 org.owasp.esapi esapi - 2.3.0.0-SNAPSHOT + 2.2.1.1 jar From 3200f669ce3ae1fe7685a592d1e2f32eb987b892 Mon Sep 17 00:00:00 2001 From: kwwall Date: Sun, 26 Jul 2020 23:28:22 -0400 Subject: [PATCH 0665/1069] Javadoc fix-ups. --- src/main/java/org/owasp/esapi/Encoder.java | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/main/java/org/owasp/esapi/Encoder.java b/src/main/java/org/owasp/esapi/Encoder.java index a8b949d71..4c831d281 100644 --- a/src/main/java/org/owasp/esapi/Encoder.java +++ b/src/main/java/org/owasp/esapi/Encoder.java @@ -149,11 +149,10 @@ * * * - * @see OWASP Cross-Site Scripting Prevention Cheat Sheet. + * @see OWASP Cross-Site Scripting Prevention Cheat Sheet * @see OWASP Proactive Controls: C4: Encode and Escape Data - * @see Properly encoding and escaping for the web. - * @author Jeff Williams (jeff.williams .at. aspectsecurity.com) Aspect Security + * @see Properly encoding and escaping for the web + * @author Jeff Williams (jeff.williams .at. owasp.org) * @since June 1, 2007 */ public interface Encoder { @@ -167,7 +166,7 @@ public interface Encoder { * Encoder.AllowMixedEncoding=false * * - * @see Encoder#canonicalize(String, boolean, boolean) canonicalize + * @see #canonicalize(String, boolean, boolean) * @see W3C specifications * * @param input the text to canonicalize @@ -178,7 +177,7 @@ public interface Encoder { /** * This method is the equivalent to calling {@code Encoder.canonicalize(input, strict, strict);}. * - * @see Encoder#canonicalize(String, boolean, boolean) canonicalize + * @see #canonicalize(String, boolean, boolean) * @see W3C specifications * * @param input From 5b95e700c8ef812b01a4d788daad9cf30cf35e29 Mon Sep 17 00:00:00 2001 From: kwwall Date: Sun, 26 Jul 2020 23:36:37 -0400 Subject: [PATCH 0666/1069] Added 'IMPORTANT WORKAROUND for 2.2.1.0 ESAPI Logging' section. --- .../esapi4java-core-2.2.1.0-release-notes.txt | 33 ++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/documentation/esapi4java-core-2.2.1.0-release-notes.txt b/documentation/esapi4java-core-2.2.1.0-release-notes.txt index 7d6c54ef7..6119a5c4c 100644 --- a/documentation/esapi4java-core-2.2.1.0-release-notes.txt +++ b/documentation/esapi4java-core-2.2.1.0-release-notes.txt @@ -124,6 +124,37 @@ Another problem is if you run 'mvn test' from the 'cmd' prompt (and possibly Pow We do not know the reason for these failures, but only that we have observed them on Windows 10. If you see this error, please do NOT report it as a GitHub issue unless you know a fix for it. + + *** IMPORTANT WORKAROUND for 2.2.1.0 ESAPI Logging *** + +Lastly, if you try to use the new ESAPI 2.2.1.0 logging, you will notice that you need to change ESAPI.Logger and also possibly provide some other logging properties as well. This is because the logger packages were reorganized to improve maintainability, but we failed to mention it. To use ESAPI logging in ESAPI 2.2.1.0 (and later), you MUST set the ESAPI.Logger property to one of: + + org.owasp.esapi.logging.java.JavaLogFactory - To use the new default, java.util.logging (JUL) + org.owasp.esapi.logging.log4j.Log4JLogFactory - To use the end-of-life Log4J 1.x logger + org.owasp.esapi.logging.slf4j.Slf4JLogFactory - To use the new (to release 2.2.0.0) SLF4J logger + +In addition, if you wish to use JUL for logging, you *must* supply an "esapi-java-logging.properties" file in your classpath. Unfortunately, we failed to drop add that to the ESAPI configuration jar under the GitHub 'Releases', so this file has been added explicitly to the 2.2.1.0 release 'Assets' for this release (for details, see https://github.com/ESAPI/esapi-java-legacy/releases/esapi-2.2.1.0). Even worse, there was a logic error in the static initializer of JavaLogFactory (now fixed in the 2.2.1.1 patch release) that causes a NullPointerException to be thrown so that the message about the missing "esapi-java-logging.properties" file was never seen. + +If you are using JavaLogFactory or Slf4JLogFactory, you will also want to ensure that you have the following ESAPI logging properties set to get the logs to appear what you are used to with Log4J 1.x logging: + # Set the application name if these logs are combined with other applications + Logger.ApplicationName=ExampleApplication + # If you use an HTML log viewer that does not properly HTML escape log data, you can set LogEncodingRequired to true + Logger.LogEncodingRequired=false + # Determines whether ESAPI should log the application name. This might be clutter in some single-server/single-app environments. + Logger.LogApplicationName=true + # Determines whether ESAPI should log the server IP and port. This might be clutter in some single-server environments. + Logger.LogServerIP=true + # LogFileName, the name of the logging file. Provide a full directory path (e.g., C:\\ESAPI\\ESAPI_logging_file) if you + # want to place it in a specific directory. + Logger.LogFileName=ESAPI_logging_file + # MaxLogFileSize, the max size (in bytes) of a single log file before it cuts over to a new one (default is 10,000,000) + Logger.MaxLogFileSize=10000000 + # Determines whether ESAPI should log the user info. + Logger.UserInfo=true + # Determines whether ESAPI should log the session id and client IP. + Logger.ClientInfo=true + +See GitHub issue #560 for additional details. ----------------------------------------------------------------------------- Other changes in this release, some of which not tracked via GitHub issues @@ -209,7 +240,7 @@ Direct and Transitive Runtime and Test Dependencies: [INFO] --------------------------------[ jar ]--------------------------------- [INFO] [INFO] --- maven-dependency-plugin:3.1.2:tree (default-cli) @ esapi --- - [INFO] org.owasp.esapi:esapi:jar:2.2.1.0-RC1 + [INFO] org.owasp.esapi:esapi:jar:2.2.1.0 [INFO] +- javax.servlet:javax.servlet-api:jar:3.0.1:provided [INFO] +- javax.servlet.jsp:javax.servlet.jsp-api:jar:2.3.3:provided [INFO] +- com.io7m.xom:xom:jar:1.2.10:compile From 156b98cd8aaa7a21d483bded315f3509663576b2 Mon Sep 17 00:00:00 2001 From: kwwall Date: Sun, 26 Jul 2020 23:51:12 -0400 Subject: [PATCH 0667/1069] New release notes for 2.2.1.1 patch release. --- .../esapi4java-core-2.2.1.1-release-notes.txt | 237 ++++++++++++++++++ 1 file changed, 237 insertions(+) create mode 100644 documentation/esapi4java-core-2.2.1.1-release-notes.txt diff --git a/documentation/esapi4java-core-2.2.1.1-release-notes.txt b/documentation/esapi4java-core-2.2.1.1-release-notes.txt new file mode 100644 index 000000000..7fa3ffc35 --- /dev/null +++ b/documentation/esapi4java-core-2.2.1.1-release-notes.txt @@ -0,0 +1,237 @@ +Release notes for ESAPI 2.2.1.1 + Release date: 2020-July-26 + Project leaders: + -Kevin W. Wall + -Matt Seil + +Previous release: ESAPI 2.2.1.0, 2020-July-12 + + +Executive Summary: Important Things to Note for this Release +------------------------------------------------------------ + +This is a patch release to address GitHub issue #560. See that GitHub issue and + +Also special props to Bill Sempf for stepping up and volunteering to prepare the initial cut of these release notes. Had he not done so, this release either would not have release notes or it would have been delayed another 6 months while I procrastinated further with various distractions. (Squirrel!) + +================================================================================================================= + +Basic ESAPI facts +----------------- + +ESAPI 2.2.1.0 release: + 211 Java source files + 4309 JUnit tests in 134 Java source files + +ESAPI 2.2.1.1 release: + 211 Java source files + 4312 JUnit tests in 134 Java source files + +39 GitHub Issues closed in this release + +Issue # GitHub Issue Title +---------------------------------------------------------------------------------------------- + +560 - Could not initialize class org.owasp.esapi.logging.java.JavaLogFactory (ESAPI 2.2.1.0) +561 - Update ESAPI-release-steps.odt to note how to do 'Release' on GitHub +564 - Create release notes for 2.2.1.1 patch release + +----------------------------------------------------------------------------- + + Changes Requiring Special Attention + +----------------------------------------------------------------------------- +As of ESAPI 2.2.1.0 (the previous release), the new default ESAPI logger is JUL (java.util.logging packages) and we have deprecated the use of Log4J 1.x because we now support SLF4J and Log4J 1.x is way past its end-of-life. We did not want to make SLF4J the default logger (at least not yet) as we did not want to have the default ESAPI use require additional dependencies. However, SLF4J is likely to be the future choice, at least once we start on ESAPI 3.0. A special shout-out to Jeremiah Stacey for making this possible by re-factoring much of the ESAPI logger code. Note, the straw that broke the proverbial camel's back was the announcement of CVE-2019-17571 (rated Critical), for which there is no fix available and likely will never be. + +However, if you try to juse the new ESAPI 2.2.1.0 logging you will notice that you need to change ESAPI.Logger and also possibly provide some other properties as well to get the logging behavior that you desire. + +To use ESAPI logging in ESAPI 2.2.1.0 (and later), you will need to set the ESAPI.Logger property to + + org.owasp.esapi.logging.java.JavaLogFactory - To use the new default, java.util.logging (JUL) + org.owasp.esapi.logging.log4j.Log4JLogFactory - To use the end-of-life Log4J 1.x logger + org.owasp.esapi.logging.slf4j.Slf4JLogFactory - To use the new (to release 2.2.0.0) SLF4J logger + +In addition, if you wish to use JUL for logging, you *MUST* supply an "esapi-java-logging.properties" file in your classpath. This file is included in the 'esapi-2.2.1.1-configuration.jar' file provided under the 'Assets' section of the GitHub Release at + https://github.com/ESAPI/esapi-java-legacy/releases/esapi-2.2.1.1 + +Unfortunately, there was a logic error in the static initializer of JavaLogFactory (now fixed in this release) that caused a NullPointerException to be thrown so that the message about the missing "esapi-java-logging.properties" file was never seen. + +If you are using JavaLogFactory, you will also want to ensure that you have the following ESAPI logging properties set: + # Set the application name if these logs are combined with other applications + Logger.ApplicationName=ExampleApplication + # If you use an HTML log viewer that does not properly HTML escape log data, you can set LogEncodingRequired to true + Logger.LogEncodingRequired=false + # Determines whether ESAPI should log the application name. This might be clutter in some single-server/single-app environments. + Logger.LogApplicationName=true + # Determines whether ESAPI should log the server IP and port. This might be clutter in some single-server environments. + Logger.LogServerIP=true + # LogFileName, the name of the logging file. Provide a full directory path (e.g., C:\\ESAPI\\ESAPI_logging_file) if you + # want to place it in a specific directory. + Logger.LogFileName=ESAPI_logging_file + # MaxLogFileSize, the max size (in bytes) of a single log file before it cuts over to a new one (default is 10,000,000) + Logger.MaxLogFileSize=10000000 + # Determines whether ESAPI should log the user info. + Logger.UserInfo=true + # Determines whether ESAPI should log the session id and client IP. + Logger.ClientInfo=true + +See GitHub issue #560 for additional details. + + +Related to that aforemented Log4J 1.x CVE and how it affects ESAPI, be sure to read + https://github.com/ESAPI/esapi-java-legacy/blob/develop/documentation/ESAPI-security-bulletin2.pdf +which describes CVE-2019-17571, a deserialization vulnerability in Log4J 1.2.17. ESAPI is *NOT* affected by this (even if you chose to use Log4J 1 as you default ESAPI logger). This security bulletin describes why this CVE is not exploitable as used by ESAPI. + + +Finally, while ESAPI still supports JDK 7 (even though that too is way past end-of-life), the next ESAPI release will move to JDK 8 as the minimal baseline. (We already use Java 8 for development but still to Java 7 source and runtime compatibility.) + +----------------------------------------------------------------------------- + + Known Issues / Problems + +----------------------------------------------------------------------------- +If you use Java 7 (the minimal Java baseline supported by ESAPI) and try to run 'mvn test' there is one test that fails. This test passes with Java 8. The failing test is: + + [ERROR] Tests run: 5, Failures: 1, Errors: 0, Skipped: 0, Time elapsed: 0.203 s + <<< FAILURE! - in org.owasp.esapi.crypto.SecurityProviderLoaderTest + [ERROR] org.owasp.esapi.crypto.SecurityProviderLoaderTest.testWithBouncyCastle + Time elapsed: 0.116 s <<< FAILURE! + java.lang.AssertionError: Encryption w/ Bouncy Castle failed with + EncryptionException for preferred cipher transformation; exception was: + org.owasp.esapi.errors.EncryptionException: Encryption failure (unavailable + cipher requested) + at + org.owasp.esapi.crypto.SecurityProviderLoaderTest.testWithBouncyCastle(Security + ProviderLoaderTest.java:133) + +I will spare you all the details and tell you that this has to do with Java 7 not being able to correctly parse the signed Bouncy Castle JCE provider jar. More details are available at: + https://www.bouncycastle.org/latest_releases.html +and + https://github.com/bcgit/bc-java/issues/477 +I am sure that there are ways of making Bouncy Castle work with Java 7, but since ESAPI does not rely on Bouncy Castle (it can use any compliant JCE provider), this should not be a problem. (It works fine with the default SunJCE provider.) If it is important to get the BC provider working with the ESAPI Encryptor and Java 7, then open a GitHub issue and we will take a deeper look at it and see if we can suggest something. + + + +Another problem is if you run 'mvn test' from the 'cmd' prompt (and possibly PowerShell as well), you will get intermittent failures (generally between 10-25% of the time) at arbitrary spots. If you run it again without any changes it will work fine without any failures. We have discovered that it doesn't seem to fail if you run the tests from an IDE like Eclipse or if you redirect both stdout and stderr to a file; e.g., + + C:\code\esapi-java-legacy> mvn test >testoutput.txt 2>&1 + +We do not know the reason for these failures, but only that we have observed them on Windows 10. If you see this error, please do NOT report it as a GitHub issue unless you know a fix for it. + +----------------------------------------------------------------------------- + + Other changes in this release, some of which not tracked via GitHub issues + +----------------------------------------------------------------------------- + +* Updates to README.md fileg +* Minor Javadoc fixes to org.owasp.esapi.Encoder +* Fixes / cleanup to 2.2.1.0 release notes (documentation/esapi4java-core-2.2.1.0-release-notes.txt) + +----------------------------------------------------------------------------- + +Developer Activity Report (Changes between release 2.2.1.0 and 2.2.1.1, i.e., between 2020-07-12 and 2020-07-26) +Generated manually (this time) -- all errors are the fault of kwwall and his inability to do simple arithmetic. + +Developer Total Total Number # Merged +(GitHub ID) commits of Files Changed PRs +======================================================== +jeremiahjstacey 5 5 1 +kwwall 67 64 8 +======================================================== + Total: 21 + + +----------------------------------------------------------------------------- + + +2 Closed PRs merged since 2.2.1.0 release (those rejected not listed) +====================================================================== +PR# GitHub ID Description +---------------------------------------------------------------------- +559 -- synk-bot -- Upgrade com.github.spotbugs:spotbugs-annotations from 4.0.4 to 4.0.5 +562 -- jeremiahjstacey -- Issue #560 JUL fixes + +CHANGELOG: Create your own. May I suggest: + + git log --since=2020-07-13 --reverse --pretty=medium + + which will show all the commits since just after the last (2.2.1.0) release. + +----------------------------------------------------------------------------- + +Direct and Transitive Runtime and Test Dependencies: + + $ mvn dependency:tree + [INFO] Scanning for projects... + [INFO] + [INFO] -----------------------< org.owasp.esapi:esapi >------------------------ + [INFO] Building ESAPI 2.2.1.1 + [INFO] --------------------------------[ jar ]--------------------------------- + [INFO] + [INFO] --- maven-dependency-plugin:3.1.2:tree (default-cli) @ esapi --- + [INFO] org.owasp.esapi:esapi:jar:2.2.1.1 + [INFO] +- javax.servlet:javax.servlet-api:jar:3.0.1:provided + [INFO] +- javax.servlet.jsp:javax.servlet.jsp-api:jar:2.3.3:provided + [INFO] +- com.io7m.xom:xom:jar:1.2.10:compile + [INFO] +- commons-beanutils:commons-beanutils:jar:1.9.4:compile + [INFO] | +- commons-logging:commons-logging:jar:1.2:compile + [INFO] | \- commons-collections:commons-collections:jar:3.2.2:compile + [INFO] +- commons-configuration:commons-configuration:jar:1.10:compile + [INFO] +- commons-lang:commons-lang:jar:2.6:compile + [INFO] +- commons-fileupload:commons-fileupload:jar:1.3.3:compile + [INFO] +- log4j:log4j:jar:1.2.17:compile + [INFO] +- org.apache.commons:commons-collections4:jar:4.2:compile + [INFO] +- org.apache-extras.beanshell:bsh:jar:2.0b6:compile + [INFO] +- org.owasp.antisamy:antisamy:jar:1.5.10:compile + [INFO] | +- net.sourceforge.nekohtml:nekohtml:jar:1.9.22:compile + [INFO] | +- org.apache.httpcomponents:httpclient:jar:4.5.12:compile + [INFO] | | \- org.apache.httpcomponents:httpcore:jar:4.4.13:compile + [INFO] | \- commons-codec:commons-codec:jar:1.14:compile + [INFO] +- org.slf4j:slf4j-api:jar:1.7.30:compile + [INFO] +- commons-io:commons-io:jar:2.6:compile + [INFO] +- org.apache.xmlgraphics:batik-css:jar:1.13:compile + [INFO] | +- org.apache.xmlgraphics:batik-shared-resources:jar:1.13:compile + [INFO] | +- org.apache.xmlgraphics:batik-util:jar:1.13:compile + [INFO] | | +- org.apache.xmlgraphics:batik-constants:jar:1.13:compile + [INFO] | | \- org.apache.xmlgraphics:batik-i18n:jar:1.13:compile + [INFO] | +- org.apache.xmlgraphics:xmlgraphics-commons:jar:2.4:compile + [INFO] | \- xml-apis:xml-apis-ext:jar:1.3.04:compile + [INFO] +- xalan:xalan:jar:2.7.2:compile + [INFO] | \- xalan:serializer:jar:2.7.2:compile + [INFO] +- xerces:xercesImpl:jar:2.12.0:compile + [INFO] +- xml-apis:xml-apis:jar:1.4.01:compile + [INFO] +- com.github.spotbugs:spotbugs-annotations:jar:4.0.5:compile (optional) + [INFO] | \- com.google.code.findbugs:jsr305:jar:3.0.2:compile (optional) + [INFO] +- net.jcip:jcip-annotations:jar:1.0:compile (optional) + [INFO] +- junit:junit:jar:4.13:test + [INFO] | \- org.hamcrest:hamcrest-core:jar:1.3:test + [INFO] +- org.bouncycastle:bcprov-jdk15on:jar:1.65.01:test + [INFO] +- org.powermock:powermock-api-mockito2:jar:2.0.7:test + [INFO] | \- org.powermock:powermock-api-support:jar:2.0.7:test + [INFO] | \- org.powermock:powermock-core:jar:2.0.7:test + [INFO] +- org.javassist:javassist:jar:3.25.0-GA:test + [INFO] +- org.mockito:mockito-core:jar:2.28.2:test + [INFO] | +- net.bytebuddy:byte-buddy:jar:1.9.10:test + [INFO] | +- net.bytebuddy:byte-buddy-agent:jar:1.9.10:test + [INFO] | \- org.objenesis:objenesis:jar:2.6:test + [INFO] +- org.powermock:powermock-module-junit4:jar:2.0.7:test + [INFO] | \- org.powermock:powermock-module-junit4-common:jar:2.0.7:test + [INFO] +- org.powermock:powermock-reflect:jar:2.0.7:test + [INFO] +- org.openjdk.jmh:jmh-core:jar:1.23:test + [INFO] | +- net.sf.jopt-simple:jopt-simple:jar:4.6:test + [INFO] | \- org.apache.commons:commons-math3:jar:3.2:test + [INFO] \- org.openjdk.jmh:jmh-generator-annprocess:jar:1.23:test + [INFO] ------------------------------------------------------------------------ + [INFO] BUILD SUCCESS + [INFO] ------------------------------------------------------------------------ + +----------------------------------------------------------------------------- + +Acknowledgments: + +elangoravi for bringing GitHub issue #560 to our attention. This is one where we thought the workaround instructions was harder than just trying to fix it and thus we were encouraged to release a patch. + +A special thanks to the ESAPI community from the ESAPI project co-leaders: + Kevin W. Wall (kwwall) <== The irresponsible party for these release notes! + Matt Seil (xeno6696) From 74fc4ba1fa9d356efa5e7052286482c689aafa09 Mon Sep 17 00:00:00 2001 From: kwwall Date: Mon, 27 Jul 2020 00:01:15 -0400 Subject: [PATCH 0668/1069] Close #561 plus other major changes and cleanup. --- documentation/ESAPI-release-steps.odt | Bin 273380 -> 165319 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/documentation/ESAPI-release-steps.odt b/documentation/ESAPI-release-steps.odt index bf188f05f965c9d5051d1f4fc60a4ff4df2e1b23..33e7a367d25f75d5535c1da7d9ec4f8ee7888f6c 100644 GIT binary patch delta 58060 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

    + * The primary use of ESAPI {@code Encoder} is to prevent XSS vulnerabilities by + * providing output encoding using the various "encodeForXYZ()" methods, + * where XYZ is one of CSS, HTML, HTMLAttribute, JavaScript, or URL. When + * using the ESAPI output encoders, it is important that you use the one for the + * appropriate context where the output will be rendered. For example, it + * the output appears in an JavaScript context, you should use {@code encodeForJavaScript} + * (note this includes all of the DOM JavaScript event handler attributes such as + * 'onfocus', 'onclick', 'onload', etc.). If the output would be rendered in an HTML + * attribute context (with the exception of the aforementioned 'onevent' type event + * handler attributes), you would use {@code encodeForHTMLAttribute}. If you are + * encoding anywhere a URL is expected (e.g., a 'href' attribute for for <a> or + * a 'src' attribute on a <img> tag, etc.), then you should use use {@code encodeForURL}. + * If encoding CSS, then use {@code encodeForCSS}. Etc. This is because there are + * different escaping requirements for these different contexts. Developers who are + * new to ESAPI or to defending against XSS vulnerabilities are highly encouraged to + * first read the + * + * OWASP Cross-Site Scripting Prevention Cheat Sheet. + *