Skip to content

Commit 422ba9f

Browse files
authored
Issue 521 (ESAPI#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 ESAPI#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 ESAPI#521 by kludge to add backward-compatibility flag to restore the old behavior accidentally broken by the changes to address issue ESAPI#509. * Javadoc clarifications to address issue ESAPI#521 for behavior broken by issue ESAPI#509 commits. * New test files for GitHub issue ESAPI#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 ESAPI#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.
1 parent 872f8db commit 422ba9f

File tree

9 files changed

+484
-72
lines changed

9 files changed

+484
-72
lines changed

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,10 @@ The default branch for ESAPI legacy is now the 'develop' branch (rather than the
2323
# Where can I find ESAPI 3.x?
2424
https://github.com/ESAPI/esapi-java
2525

26+
# ESAPI Deprecation Policy
27+
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.
28+
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.
29+
2630
# Contributing to ESAPI legacy
2731
## How can I contribute or help with fix bugs?
2832
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.

configuration/esapi/ESAPI.properties

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,8 +67,9 @@ ESAPI.Executor=org.owasp.esapi.reference.DefaultExecutor
6767
ESAPI.HTTPUtilities=org.owasp.esapi.reference.DefaultHTTPUtilities
6868
ESAPI.IntrusionDetector=org.owasp.esapi.reference.DefaultIntrusionDetector
6969
# Log4JFactory Requires log4j.xml or log4j.properties in classpath - http://www.laliluna.de/log4j-tutorial.html
70-
ESAPI.Logger=org.owasp.esapi.logging.log4j.Log4JLogFactory
71-
#ESAPI.Logger=org.owasp.esapi.reference.JavaLogFactory
70+
# Note that this is now considered deprecated!
71+
#ESAPI.Logger=org.owasp.esapi.logging.log4j.Log4JLogFactory
72+
ESAPI.Logger=org.owasp.esapi.logging.java.JavaLogFactory
7273
# To use the new SLF4J logger in ESAPI (see GitHub issue #129), set
7374
# ESAPI.Logger=org.owasp.esapi.logging.slf4j.Slf4JLogFactory
7475
# 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}$
499500
# Validation of dates. Controls whether or not 'lenient' dates are accepted.
500501
# See DataFormat.setLenient(boolean flag) for further details.
501502
Validator.AcceptLenientDates=false
503+
504+
# ~~~~~ Important Note ~~~~~
505+
# This is a workaround to make sure that a commit to address GitHub issue #509
506+
# doesn't accidentally break someone's production code. So essentially what we
507+
# are doing is to reverting back to the previous possibly buggy (by
508+
# documentation intent at least), but, by now, expected legacy behavior.
509+
# Prior to the code changes for issue #509, if invalid / malicious HTML input was
510+
# observed, AntiSamy would simply attempt to sanitize (cleanse) it and it would
511+
# only be logged. However, the code change made ESAPI comply with its
512+
# documentation, which stated that a ValidationException should be thrown in
513+
# such cases. Unfortunately, changing this behavior--especially when no one is
514+
# 100% certain that the documentation was correct--could break existing code
515+
# using ESAPI so after a lot of debate, issue #521 was created to restore the
516+
# previous behavior, but still allow the documented behavior. (We did this
517+
# because it wasn't really causing an security issues since AntiSamy would clean
518+
# it up anyway and we value backward compatibility as long as it doesn't clearly
519+
# present security vulnerabilities.)
520+
# More defaults about this are written up under GitHub issue #521 and
521+
# the pull request it references. Future major releases of ESAPI (e.g., ESAPI 3.x)
522+
# will not support this previous behavior, but it will remain for ESAPI 2.x.
523+
# Set this to 'throw' if you want the originally intended behavior of throwing
524+
# that was fixed via issue #509. Set to 'clean' if you want want the HTML input
525+
# sanitized instead.
526+
#
527+
# Possible values:
528+
# clean -- Use the legacy behavior where unsafe HTML input is logged and the
529+
# sanitized (i.e., clean) input as determined by AntiSamy and your
530+
# AntiSamy rules is returned. This is the default behavior if this
531+
# new property is not found.
532+
# throw -- The new, presumably correct and originally intended behavior where
533+
# a ValidationException is thrown when unsafe HTML input is
534+
# encountered.
535+
#
536+
#Validator.HtmlValidationAction=clean
537+
Validator.HtmlValidationAction=throw

src/main/java/org/owasp/esapi/ValidationRule.java

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,23 @@ public interface ValidationRule {
1515
* the value to be parsed
1616
* @return a validated value
1717
* @throws ValidationException
18-
* if any validation rules fail
18+
* if any validation rules fail, <i>except</i> if the
19+
* <b>{@code ESAPI.properties}></b> property
20+
* "Validator.ValidationRule.getValid.ignore509Fix" is set to
21+
* {@code true}, which is the default behavior for ESAPI 2.x
22+
* releases. See
23+
* {@link https://github.com/ESAPI/esapi-java-legacy/issues/509}
24+
* and {@link https://github.com/ESAPI/esapi-java-legacy/issues/521}
25+
* for futher details.
26+
*
27+
* @see #getValid(String context, String input, ValidationErrorList errorList)
1928
*/
2029
Object getValid(String context, String input)
2130
throws ValidationException;
2231

2332
/**
24-
* Whether or not a valid valid can be null. getValid will throw an
25-
* Exception and getSafe will return the default value if flag is set to
33+
* Whether or not a valid valid can be null. {@code getValid} will throw an
34+
* Exception and {#code getSafe} will return the default value if flag is set to
2635
* true
2736
*
2837
* @param flag
@@ -59,7 +68,8 @@ Object getValid(String context, String input,
5968
ValidationErrorList errorList) throws ValidationException;
6069

6170
/**
62-
* Try to call get valid, then call sanitize, finally return a default value
71+
* Try to call {@code getvalid}, then call a 'sanitize' method for sanitization (if one exists),
72+
* finally return a default value.
6373
*/
6474
Object getSafe(String context, String input);
6575

@@ -78,4 +88,4 @@ Object getValid(String context, String input,
7888
*/
7989
String whitelist(String input, Set<Character> list);
8090

81-
}
91+
}

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ public static SecurityConfiguration getInstance() {
114114
public static final String DIGITAL_SIGNATURE_ALGORITHM = "Encryptor.DigitalSignatureAlgorithm";
115115
public static final String DIGITAL_SIGNATURE_KEY_LENGTH = "Encryptor.DigitalSignatureKeyLength";
116116
// ==================================//
117-
// New in ESAPI Java 2.0 //
117+
// New in ESAPI Java 2.x //
118118
// ================================= //
119119
public static final String PREFERRED_JCE_PROVIDER = "Encryptor.PreferredJCEProvider";
120120
public static final String CIPHER_TRANSFORMATION_IMPLEMENTATION = "Encryptor.CipherTransformation";
@@ -157,6 +157,7 @@ public static SecurityConfiguration getInstance() {
157157
public static final String VALIDATION_PROPERTIES = "Validator.ConfigurationFile";
158158
public static final String VALIDATION_PROPERTIES_MULTIVALUED = "Validator.ConfigurationFile.MultiValued";
159159
public static final String ACCEPT_LENIENT_DATES = "Validator.AcceptLenientDates";
160+
public static final String VALIDATOR_HTML_VALIDATION_ACTION = "Validator.HtmlValidationAction";
160161

161162
/**
162163
* Special {@code System} property that, if set to {@code true}, will

src/main/java/org/owasp/esapi/reference/validation/HTMLValidationRule.java

Lines changed: 58 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -97,8 +97,60 @@ public String sanitize( String context, String input ) {
9797
return safe;
9898
}
9999

100+
/**
101+
* Check whether we want the legacy behavior ("clean") or the presumably intended
102+
* behavior of "throw" for how to treat unsafe HTML input when AntiSamy is invoked.
103+
* This admittedly is an UGLY hack to ensure that issue 509 and its corresponding
104+
* fix in PR #510 does not break existing developer's existing code. Full
105+
* details are described in GitHub issue 521.
106+
*
107+
* Checks new ESAPI property "Validator.HtmlValidationAction". A value of "clean"
108+
* means to revert to legacy behavior. A value of "throw" means to use the new
109+
* behavior as implemented in GitHub issue 509.
110+
*
111+
* @return false - If "Validator.HtmlValidationAction" is set to "throw". Otherwise {@code true}.
112+
* @since 2.2.1.0
113+
*/
114+
private boolean legacyHtmlValidation() {
115+
boolean legacy = true; // Make legacy support the default behavior for backward compatibility.
116+
String propValue = "clean"; // For legacy support.
117+
try {
118+
// DISCUSS:
119+
// Hindsight: maybe we should have getBooleanProp(), getStringProp(),
120+
// getIntProp() methods that take a default arg as well?
121+
// At least for ESAPI 3.x.
122+
propValue = ESAPI.securityConfiguration().getStringProp(
123+
// Future: This will be moved to a new PropNames class
124+
org.owasp.esapi.reference.DefaultSecurityConfiguration.VALIDATOR_HTML_VALIDATION_ACTION );
125+
switch ( propValue.toLowerCase() ) {
126+
case "throw":
127+
legacy = false; // New, presumably correct behavior, as addressed by GitHub issue 509
128+
break;
129+
case "clean":
130+
legacy = true; // Give the caller that legacy behavior of sanitizing.
131+
break;
132+
default:
133+
LOGGER.warning(Logger.EVENT_FAILURE, "ESAPI property " +
134+
org.owasp.esapi.reference.DefaultSecurityConfiguration.VALIDATOR_HTML_VALIDATION_ACTION +
135+
" was set to \"" + propValue + "\". Must be set to either \"clean\"" +
136+
" (the default for legacy support) or \"throw\"; assuming \"clean\" for legacy behavior.");
137+
legacy = true;
138+
break;
139+
}
140+
} catch( ConfigurationException cex ) {
141+
// OPEN ISSUE: Should we log this? I think so. Convince me otherwise. But maybe
142+
// we should only log it once or every Nth time??
143+
LOGGER.warning(Logger.EVENT_FAILURE, "ESAPI property " +
144+
org.owasp.esapi.reference.DefaultSecurityConfiguration.VALIDATOR_HTML_VALIDATION_ACTION +
145+
" must be set to either \"clean\" (the default for legacy support) or \"throw\"; assuming \"clean\"",
146+
cex);
147+
}
148+
149+
return legacy;
150+
}
151+
100152
private String invokeAntiSamy( String context, String input ) throws ValidationException {
101-
// CHECKME should this allow empty Strings? " " us IsBlank instead?
153+
// CHECKME should this allow empty Strings? " " use IsBlank instead?
102154
if ( StringUtilities.isEmpty(input) ) {
103155
if (allowNull) {
104156
return null;
@@ -114,7 +166,11 @@ private String invokeAntiSamy( String context, String input ) throws ValidationE
114166

115167
List<String> errors = test.getErrorMessages();
116168
if ( !errors.isEmpty() ) {
117-
throw new ValidationException( context + ": Invalid HTML input", "Invalid HTML input does not follow rules in antisamy-esapi.xml: context=" + context + " errors=" + errors.toString());
169+
if ( legacyHtmlValidation() ) { // See GitHub issues 509 and 521
170+
LOGGER.info(Logger.SECURITY_FAILURE, "Cleaned up invalid HTML input: " + errors );
171+
} else {
172+
throw new ValidationException( context + ": Invalid HTML input", "Invalid HTML input does not follow rules in antisamy-esapi.xml: context=" + context + " errors=" + errors.toString());
173+
}
118174
}
119175

120176
return test.getCleanHTML().trim();

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

Lines changed: 4 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -238,39 +238,8 @@ public void testGetValidRedirectLocation() {
238238
// instance.getValidRedirectLocation(String, String, boolean, ValidationErrorList)
239239
}
240240

241-
public void testGetValidSafeHTML() throws Exception {
242-
System.out.println("getValidSafeHTML");
243-
Validator instance = ESAPI.validator();
244-
ValidationErrorList errors = new ValidationErrorList();
245-
246-
// new school test case setup
247-
HTMLValidationRule rule = new HTMLValidationRule("test");
248-
ESAPI.validator().addRule(rule);
249-
250-
assertEquals("Test.", ESAPI.validator().getRule("test").getValid("test", "Test. <script>alert(document.cookie)</script>"));
251-
252-
String test1 = "<b>Jeff</b>";
253-
String result1 = instance.getValidSafeHTML("test", test1, 100, false, errors);
254-
assertEquals(test1, result1);
255-
256-
String test2 = "<a href=\"http://www.aspectsecurity.com\">Aspect Security</a>";
257-
String result2 = instance.getValidSafeHTML("test", test2, 100, false, errors);
258-
assertEquals(test2, result2);
259-
260-
String test3 = "Test. <script>alert(document.cookie)</script>";
261-
assertEquals("Test.", rule.getSafe("test", test3));
262-
263-
assertEquals("Test. &lt;<div>load=alert()</div>", rule.getSafe("test", "Test. <<div on<script></script>load=alert()"));
264-
assertEquals("Test. <div>b</div>", rule.getSafe("test", "Test. <div style={xss:expression(xss)}>b</div>"));
265-
assertEquals("Test. alert(document.cookie)", rule.getSafe("test", "Test. <s%00cript>alert(document.cookie)</script>"));
266-
assertEquals("Test. alert(document.cookie)", rule.getSafe("test", "Test. <s\tcript>alert(document.cookie)</script>"));
267-
assertEquals("Test. alert(document.cookie)", rule.getSafe("test", "Test. <s\tcript>alert(document.cookie)</script>"));
268-
// TODO: ENHANCE waiting for a way to validate text headed for an attribute for scripts
269-
// This would be nice to catch, but just looks like text to AntiSamy
270-
// assertFalse(instance.isValidSafeHTML("test", "\" onload=\"alert(document.cookie)\" "));
271-
// String result4 = instance.getValidSafeHTML("test", test4);
272-
// assertEquals("", result4);
273-
}
241+
// Test split out and moved to HTMLValidationRuleLogsTest.java & HTMLValidationRuleThrowsTest.java
242+
// public void testGetValidSafeHTML() throws Exception {
274243

275244
public void testIsInvalidFilename() {
276245
System.out.println("testIsInvalidFilename");
@@ -881,32 +850,8 @@ public void testIsValidRedirectLocation() {
881850
// isValidRedirectLocation(String, String, boolean)
882851
}
883852

884-
public void testIsValidSafeHTML() {
885-
System.out.println("isValidSafeHTML");
886-
Validator instance = ESAPI.validator();
887-
888-
assertTrue(instance.isValidSafeHTML("test", "<b>Jeff</b>", 100, false));
889-
assertTrue(instance.isValidSafeHTML("test", "<a href=\"http://www.aspectsecurity.com\">Aspect Security</a>", 100, false));
890-
assertTrue(instance.isValidSafeHTML("test", "Test. <script>alert(document.cookie)</script>", 100, false));
891-
assertTrue(instance.isValidSafeHTML("test", "Test. <div style={xss:expression(xss)}>", 100, false));
892-
assertTrue(instance.isValidSafeHTML("test", "Test. <s%00cript>alert(document.cookie)</script>", 100, false));
893-
assertTrue(instance.isValidSafeHTML("test", "Test. <s\tcript>alert(document.cookie)</script>", 100, false));
894-
assertTrue(instance.isValidSafeHTML("test", "Test. <s\r\n\0cript>alert(document.cookie)</script>", 100, false));
895-
896-
// TODO: waiting for a way to validate text headed for an attribute for scripts
897-
// This would be nice to catch, but just looks like text to AntiSamy
898-
// assertFalse(instance.isValidSafeHTML("test", "\" onload=\"alert(document.cookie)\" "));
899-
ValidationErrorList errors = new ValidationErrorList();
900-
assertTrue(instance.isValidSafeHTML("test1", "<b>Jeff</b>", 100, false, errors));
901-
assertTrue(instance.isValidSafeHTML("test2", "<a href=\"http://www.aspectsecurity.com\">Aspect Security</a>", 100, false, errors));
902-
assertTrue(instance.isValidSafeHTML("test3", "Test. <script>alert(document.cookie)</script>", 100, false, errors));
903-
assertTrue(instance.isValidSafeHTML("test4", "Test. <div style={xss:expression(xss)}>", 100, false, errors));
904-
assertTrue(instance.isValidSafeHTML("test5", "Test. <s%00cript>alert(document.cookie)</script>", 100, false, errors));
905-
assertTrue(instance.isValidSafeHTML("test6", "Test. <s\tcript>alert(document.cookie)</script>", 100, false, errors));
906-
assertTrue(instance.isValidSafeHTML("test7", "Test. <s\r\n\0cript>alert(document.cookie)</script>", 100, false, errors));
907-
assertTrue(errors.size() == 0);
908-
909-
}
853+
// Test split out and moved to HTMLValidationRuleLogsTest.java & HTMLValidationRuleThrowsTest.java
854+
// public void testIsValidSafeHTML() {
910855

911856
public void testSafeReadLine() {
912857
System.out.println("safeReadLine");

0 commit comments

Comments
 (0)