diff -Nru struts-1.3.10/src/core/pom.xml struts-1.3.10.CVE-2015-0899/src/core/pom.xml --- struts-1.3.10/src/core/pom.xml 2015-03-30 12:13:30.943199760 +0200 +++ struts-1.3.10.CVE-2015-0899/src/core/pom.xml 2015-03-30 12:40:00.302267718 +0200 @@ -184,6 +184,12 @@ <artifactId>oro</artifactId> <version>2.0.8</version> </dependency> + <dependency> + <groupId>log4j</groupId> + <artifactId>log4j</artifactId> + <version>1.2.17</version> + <scope>test</scope> + </dependency> </dependencies> </project> diff -Nru struts-1.3.10/src/core/src/main/java/org/apache/struts/config/ActionConfig.java struts-1.3.10.CVE-2015-0899/src/core/src/main/java/org/apache/struts/config/ActionConfig.java --- struts-1.3.10/src/core/src/main/java/org/apache/struts/config/ActionConfig.java 2008-06-05 00:13:42.000000000 +0200 +++ struts-1.3.10.CVE-2015-0899/src/core/src/main/java/org/apache/struts/config/ActionConfig.java 2015-03-30 12:20:14.202124216 +0200 @@ -861,6 +861,36 @@ } } + // 2014/07/02 - security problem patch. + // Author: NTT DATA Corporation + /** + * Accepted page value for multi-page validation.<br> + * If two or more page values are accepted, then acceptPage is set minimum of them.<br> + * If multi-page validation is not use, acceptPage is not set. Then multi-page validation is disabled. + * @since Struts 1.2.9-sp2 + */ + protected Integer acceptPage = null; + + /** + * Returns accepted page value for multi-page validation. + * + * @return Accepted page value for multi-page validation + * @since Struts 1.2.9-sp2 + */ + public Integer getAcceptPage() { + return acceptPage; + } + + /** + * Set accepted page value for multi-page validation. + * + * @param acceptPage Accepted page value for multi-page validation + * @since Struts 1.2.9-sp2 + */ + public void setAcceptPage(Integer acceptPage) { + this.acceptPage = acceptPage; + } + // --------------------------------------------------------- Public Methods /** @@ -1283,6 +1313,11 @@ sb.append(type); } + // 2014/07/02 - security problem patch. + // Author: NTT DATA Corporation + sb.append(",acceptPage="); + sb.append(acceptPage); + return (sb.toString()); } } diff -Nru struts-1.3.10/src/core/src/main/java/org/apache/struts/util/RequestUtils.java struts-1.3.10.CVE-2015-0899/src/core/src/main/java/org/apache/struts/util/RequestUtils.java --- struts-1.3.10/src/core/src/main/java/org/apache/struts/util/RequestUtils.java 2015-03-30 12:13:31.002196823 +0200 +++ struts-1.3.10.CVE-2015-0899/src/core/src/main/java/org/apache/struts/util/RequestUtils.java 2015-03-30 12:23:40.352806356 +0200 @@ -469,6 +469,14 @@ parameterValue = request.getParameterValues(name); } + // 2014/05/13 - CVE-2014-0114 security problem patch. + // Author: NTT DATA Corporation + if (stripped.startsWith("class.") || stripped.indexOf(".class.") >= 0) { + // this log output is only for detection of invalid parameters and not an integral part of the bug fix + log.info("ignore parameter: paramName=" + stripped); + continue; + } + // Populate parameters, except "standard" struts attributes // such as 'org.apache.struts.action.CANCEL' if (!(stripped.startsWith("org.apache.struts.")) diff -Nru struts-1.3.10/src/core/src/main/java/org/apache/struts/validator/DynaValidatorForm.java struts-1.3.10.CVE-2015-0899/src/core/src/main/java/org/apache/struts/validator/DynaValidatorForm.java --- struts-1.3.10/src/core/src/main/java/org/apache/struts/validator/DynaValidatorForm.java 2008-06-05 00:14:02.000000000 +0200 +++ struts-1.3.10.CVE-2015-0899/src/core/src/main/java/org/apache/struts/validator/DynaValidatorForm.java 2015-03-30 12:22:30.733325776 +0200 @@ -112,9 +112,12 @@ String validationKey = getValidationKey(mapping, request); + // 2014/07/02 - security problem patch. + // Author: NTT DATA Corporation + int validationPage = determinePage(mapping, request); Validator validator = Resources.initValidator(validationKey, this, application, request, - errors, page); + errors, validationPage); try { validatorResults = validator.validate(); @@ -125,6 +128,24 @@ return errors; } + // 2014/07/02 - security problem patch. + // Author: NTT DATA Corporation + /** + * Determine validation page.<br> + * If acceptPage of ActionMapping is null, then returns Integer.MAX_VALUE. + * (multi-page validation is disabled. All validation fields are enabled.)<br> + * If page property is less than acceptPage of ActionMapping, returns acceptPage value.<br> + * If page property is greater than or equal to acceptPage of ActionMapping, returns page property value. + * @param mapping The mapping used to select this instance. + * @param request The servlet request we are processing. + * @return validation page. + * @since Struts 1.2.9-sp2 + */ + protected int determinePage(ActionMapping mapping, HttpServletRequest request) { + Integer acceptPage = mapping.getAcceptPage(); + return acceptPage != null ? Math.max(acceptPage.intValue(), page) : Integer.MAX_VALUE; + } + /** * Returns the Validation key. * diff -Nru struts-1.3.10/src/core/src/main/java/org/apache/struts/validator/ValidatorForm.java struts-1.3.10.CVE-2015-0899/src/core/src/main/java/org/apache/struts/validator/ValidatorForm.java --- struts-1.3.10/src/core/src/main/java/org/apache/struts/validator/ValidatorForm.java 2008-06-05 00:14:02.000000000 +0200 +++ struts-1.3.10.CVE-2015-0899/src/core/src/main/java/org/apache/struts/validator/ValidatorForm.java 2015-03-30 12:36:22.312287599 +0200 @@ -108,9 +108,12 @@ String validationKey = getValidationKey(mapping, request); + // 2014/07/02 - security problem patch. + // Author: NTT DATA Corporation + int validationPage = determinePage(mapping, request); Validator validator = Resources.initValidator(validationKey, this, application, request, - errors, page); + errors, validationPage); try { validatorResults = validator.validate(); @@ -121,6 +124,24 @@ return errors; } + // 2014/07/02 - security problem patch. + // Author: NTT DATA Corporation + /** + * Determine validation page.<br> + * If acceptPage of ActionMapping is null, then returns Integer.MAX_VALUE. + * (multi-page validation is disabled. All validation fields are enabled.)<br> + * If page property is less than acceptPage of ActionMapping, returns acceptPage value.<br> + * If page property is greater than or equal to acceptPage of ActionMapping, returns page property value. + * @param mapping The mapping used to select this instance. + * @param request The servlet request we are processing. + * @return validation page. + * @since Struts 1.2.9-sp2 + */ + protected int determinePage(ActionMapping mapping, HttpServletRequest request) { + Integer acceptPage = mapping.getAcceptPage(); + return acceptPage != null ? Math.max(acceptPage.intValue(), page) : Integer.MAX_VALUE; + } + /** * Returns the Validation key. * diff -Nru struts-1.3.10/src/core/src/test/java/org/apache/struts/util/TestRequestUtilsPopulate.java struts-1.3.10.CVE-2015-0899/src/core/src/test/java/org/apache/struts/util/TestRequestUtilsPopulate.java --- struts-1.3.10/src/core/src/test/java/org/apache/struts/util/TestRequestUtilsPopulate.java 2015-03-30 12:13:30.984197719 +0200 +++ struts-1.3.10.CVE-2015-0899/src/core/src/test/java/org/apache/struts/util/TestRequestUtilsPopulate.java 2015-03-30 12:33:30.592968395 +0200 @@ -21,11 +21,19 @@ package org.apache.struts.util; +import java.io.BufferedReader; +import java.io.StringReader; +import java.io.StringWriter; +import java.util.HashSet; + import javax.servlet.ServletException; import junit.framework.Test; import junit.framework.TestSuite; +import org.apache.log4j.LogManager; +import org.apache.log4j.PatternLayout; +import org.apache.log4j.WriterAppender; import org.apache.struts.action.ActionMapping; import org.apache.struts.util.RequestUtils; import org.apache.struts.Globals; @@ -120,4 +128,247 @@ } + /** + * Ensure that the parameter of HTTP request + * which causes ClassLoader manipulation is ignored. + * + * The purpose of this test is to ensure that security problem + * CVE-2014-0114 is fixed. + * + */ + public void testRequestParameterIgnore1() throws Exception { + + String stringValue = "Test"; + + MockFormBean mockForm = new MockFormBean(); + + // Set up the mock HttpServletRequest + request.setMethod("GET"); + request.setContentType(""); + + request.addParameter("class.xxx.case1", stringValue); + + // logger + StringWriter writer = new StringWriter(); + WriterAppender appender = new WriterAppender(new PatternLayout("%p, %m%n"), writer); + LogManager.getRootLogger().addAppender(appender); + LogManager.getRootLogger().setAdditivity(false); + + // Try to populate + HashSet ignoreSet = new HashSet(); + try { + RequestUtils.populate(mockForm, request); + + String keyword1 = "INFO, "; + String keyword2 = "ignore parameter: paramName="; + String logString = writer.toString(); + StringReader reader = new StringReader(logString); + BufferedReader bufReader = new BufferedReader(reader); + String line = null; + while ((line = bufReader.readLine()) != null) { + if (!line.startsWith(keyword1)) { + continue; + } + int pos = line.indexOf(keyword2); + if (pos >= 0) { + ignoreSet.add(line.substring(pos + keyword2.length())); + } + } + } catch(ServletException se) { + fail("Occur exception."); + } finally { + LogManager.getRootLogger().removeAppender(appender); + LogManager.getRootLogger().setAdditivity(true); + } + + // Check + assertEquals("ignore num no match", 1, ignoreSet.size()); + assertTrue("not exists ignore parameter class.xxx.case1", ignoreSet.contains("class.xxx.case1")); + assertNull("ActionForm property set", mockForm.getStringProperty()); + + } + + /** + * Ensure that the parameter of HTTP request + * which causes ClassLoader manipulation is ignored. + * + * The purpose of this test is to ensure that security problem + * CVE-2014-0114 is fixed. + * + */ + public void testRequestParameterIgnore2() throws Exception { + + String stringValue = "Test"; + + MockFormBean mockForm = new MockFormBean(); + + // Set up the mock HttpServletRequest + request.setMethod("GET"); + request.setContentType(""); + + request.addParameter("xxx.class.case2", stringValue); + + // logger + StringWriter writer = new StringWriter(); + WriterAppender appender = new WriterAppender(new PatternLayout("%p, %m%n"), writer); + LogManager.getRootLogger().addAppender(appender); + LogManager.getRootLogger().setAdditivity(false); + + // Try to populate + HashSet ignoreSet = new HashSet(); + try { + RequestUtils.populate(mockForm, request); + + String keyword1 = "INFO, "; + String keyword2 = "ignore parameter: paramName="; + String logString = writer.toString(); + StringReader reader = new StringReader(logString); + BufferedReader bufReader = new BufferedReader(reader); + String line = null; + while ((line = bufReader.readLine()) != null) { + if (!line.startsWith(keyword1)) { + continue; + } + int pos = line.indexOf(keyword2); + if (pos >= 0) { + ignoreSet.add(line.substring(pos + keyword2.length())); + } + } + } catch(ServletException se) { + fail("Occur exception."); + } finally { + LogManager.getRootLogger().removeAppender(appender); + LogManager.getRootLogger().setAdditivity(true); + } + + // Check + assertEquals("ignore num no match", 1, ignoreSet.size()); + assertTrue("not exists ignore parameter xxx.class.case2", ignoreSet.contains("xxx.class.case2")); + assertNull("ActionForm property set", mockForm.getStringProperty()); + + } + + /** + * Ensure that the parameter of HTTP request + * which causes ClassLoader manipulation is ignored. + * + * The purpose of this test is to ensure that security problem + * CVE-2014-0114 is fixed. + * + */ + public void testRequestParameterIgnore3() throws Exception { + + String stringValue = "Test"; + + MockFormBean mockForm = new MockFormBean(); + + // Set up the mock HttpServletRequest + request.setMethod("GET"); + request.setContentType(""); + + request.addParameter("stringProperty", stringValue); + + // logger + StringWriter writer = new StringWriter(); + WriterAppender appender = new WriterAppender(new PatternLayout("%p, %m%n"), writer); + LogManager.getRootLogger().addAppender(appender); + LogManager.getRootLogger().setAdditivity(false); + + // Try to populate + HashSet ignoreSet = new HashSet(); + try { + RequestUtils.populate(mockForm, request); + + String keyword1 = "INFO, "; + String keyword2 = "ignore parameter: paramName="; + String logString = writer.toString(); + StringReader reader = new StringReader(logString); + BufferedReader bufReader = new BufferedReader(reader); + String line = null; + while ((line = bufReader.readLine()) != null) { + if (!line.startsWith(keyword1)) { + continue; + } + int pos = line.indexOf(keyword2); + if (pos >= 0) { + ignoreSet.add(line.substring(pos + keyword2.length())); + } + } + } catch(ServletException se) { + fail("Occur exception."); + } finally { + LogManager.getRootLogger().removeAppender(appender); + LogManager.getRootLogger().setAdditivity(true); + } + + // Check + assertEquals("ignore num no match", 0, ignoreSet.size()); + assertFalse("exists ignore parameter stringProperty", ignoreSet.contains("stringProperty")); + assertEquals("ActionForm property not equal", stringValue, mockForm.getStringProperty()); + + } + + /** + * Ensure that the parameter of HTTP request + * which causes ClassLoader manipulation is ignored. + * + * The purpose of this test is to ensure that security problem + * CVE-2014-0114 is fixed. + * + */ + public void testRequestParameterIgnore4() throws Exception { + + String stringValue = "Test"; + + MockFormBean mockForm = new MockFormBean(); + + // Set up the mock HttpServletRequest + request.setMethod("GET"); + request.setContentType(""); + + request.addParameter("class.xxx.case4", stringValue); + request.addParameter("xxx.class.case4", stringValue); + request.addParameter("stringProperty", stringValue); + + // logger + StringWriter writer = new StringWriter(); + WriterAppender appender = new WriterAppender(new PatternLayout("%p, %m%n"), writer); + LogManager.getRootLogger().addAppender(appender); + LogManager.getRootLogger().setAdditivity(false); + + // Try to populate + HashSet ignoreSet = new HashSet(); + try { + RequestUtils.populate(mockForm, request); + + String keyword1 = "INFO, "; + String keyword2 = "ignore parameter: paramName="; + String logString = writer.toString(); + StringReader reader = new StringReader(logString); + BufferedReader bufReader = new BufferedReader(reader); + String line = null; + while ((line = bufReader.readLine()) != null) { + if (!line.startsWith(keyword1)) { + continue; + } + int pos = line.indexOf(keyword2); + if (pos >= 0) { + ignoreSet.add(line.substring(pos + keyword2.length())); + } + } + } catch(ServletException se) { + fail("Occur exception."); + } finally { + LogManager.getRootLogger().removeAppender(appender); + LogManager.getRootLogger().setAdditivity(true); + } + + // Check + assertEquals("ignore num no match", 2, ignoreSet.size()); + assertTrue("not exists ignore parameter class.xxx.case4", ignoreSet.contains("class.xxx.case4")); + assertTrue("not exists ignore parameter xxx.class.case4", ignoreSet.contains("xxx.class.case4")); + assertEquals("ActionForm property not equal", stringValue, mockForm.getStringProperty()); + + } + }