Moderate severityNVD Advisory· Published Mar 20, 2014· Updated May 6, 2026
CVE-2014-1904
CVE-2014-1904
Description
Cross-site scripting (XSS) vulnerability in web/servlet/tags/form/FormTag.java in Spring MVC in Spring Framework 3.0.0 before 3.2.8 and 4.0.0 before 4.0.2 allows remote attackers to inject arbitrary web script or HTML via the requested URI in a default action.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
org.springframework:spring-webmvcMaven | >= 3.0.0, < 3.2.8.RELEASE | 3.2.8.RELEASE |
org.springframework:spring-webmvcMaven | >= 4.0.0, < 4.0.2.RELEASE | 4.0.2.RELEASE |
Affected products
1- cpe:2.3:a:pivotal_software:spring_framework:*:*:*:*:*:*:*:*Range: >=3.0.0,<3.2.8
Patches
275e08695a049Mixed polishing along with recent changes
5 files changed · +50 −29
spring-context/src/main/java/org/springframework/cache/CacheManager.java+7 −6 modified@@ -1,5 +1,5 @@ /* - * Copyright 2002-2011 the original author or authors. + * Copyright 2002-2014 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,7 +19,8 @@ import java.util.Collection; /** - * A manager for a set of {@link Cache}s. + * Spring's central cache manager SPI. + * Allows for retrieving named {@link Cache} regions. * * @author Costin Leau * @since 3.1 @@ -28,14 +29,14 @@ public interface CacheManager { /** * Return the cache associated with the given name. - * @param name cache identifier (must not be {@code null}) - * @return associated cache, or {@code null} if none is found + * @param name the cache identifier (must not be {@code null}) + * @return the associated cache, or {@code null} if none found */ Cache getCache(String name); /** - * Return a collection of the caches known by this cache manager. - * @return names of caches known by the cache manager. + * Return a collection of the cache names known by this manager. + * @return the names of all caches known by the cache manager */ Collection<String> getCacheNames();
spring-expression/src/test/java/org/springframework/expression/spel/MapAccessTests.java+13 −3 modified@@ -81,9 +81,19 @@ public void testGetValue(){ Object bean = new TestBean("name1", new TestBean("name2", null, "Description 2", 15, props1), "description 1", 6, props1); ExpressionParser parser = new SpelExpressionParser(); - Expression exp = parser.parseExpression("testBean.properties['key2']"); - String key = (String) exp.getValue(bean); - assertNotNull(key); + Expression expr = parser.parseExpression("testBean.properties['key2']"); + assertEquals("value2", expr.getValue(bean)); + } + + @Test + public void testGetValueFromRootMap() { + Map<String, String> map = new HashMap<String, String>(); + map.put("key", "value"); + EvaluationContext context = new StandardEvaluationContext(map); + + ExpressionParser spelExpressionParser = new SpelExpressionParser(); + Expression expr = spelExpressionParser.parseExpression("#root['key']"); + assertEquals("value", expr.getValue(map)); }
spring-tx/src/main/java/org/springframework/transaction/interceptor/RollbackRuleAttribute.java+8 −8 modified@@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2014 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,10 +16,10 @@ package org.springframework.transaction.interceptor; -import org.springframework.util.Assert; - import java.io.Serializable; +import org.springframework.util.Assert; + /** * Rule determining whether or not a given exception (and any subclasses) * should cause a rollback. @@ -59,8 +59,8 @@ public class RollbackRuleAttribute implements Serializable{ * @throws IllegalArgumentException if the supplied {@code clazz} is * not a {@code Throwable} type or is {@code null} */ - public RollbackRuleAttribute(Class clazz) { - Assert.notNull(clazz, "'clazz' cannot be null."); + public RollbackRuleAttribute(Class<?> clazz) { + Assert.notNull(clazz, "'clazz' cannot be null"); if (!Throwable.class.isAssignableFrom(clazz)) { throw new IllegalArgumentException( "Cannot construct rollback rule from [" + clazz.getName() + "]: it's not a Throwable"); @@ -87,7 +87,7 @@ public RollbackRuleAttribute(Class clazz) { * {@code exceptionName} is {@code null} or empty */ public RollbackRuleAttribute(String exceptionName) { - Assert.hasText(exceptionName, "'exceptionName' cannot be null or empty."); + Assert.hasText(exceptionName, "'exceptionName' cannot be null or empty"); this.exceptionName = exceptionName; } @@ -110,8 +110,8 @@ public int getDepth(Throwable ex) { } - private int getDepth(Class exceptionClass, int depth) { - if (exceptionClass.getName().indexOf(this.exceptionName) != -1) { + private int getDepth(Class<?> exceptionClass, int depth) { + if (exceptionClass.getName().contains(this.exceptionName)) { // Found it! return depth; }
spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/ServletRequestMethodArgumentResolver.java+4 −5 modified@@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2014 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,7 +19,6 @@ import java.io.IOException; import java.io.InputStream; import java.io.Reader; -import java.lang.reflect.Method; import java.security.Principal; import java.util.Locale; @@ -102,9 +101,9 @@ else if (Reader.class.isAssignableFrom(paramType)) { return request.getReader(); } else { - // should never happen.. - Method method = parameter.getMethod(); - throw new UnsupportedOperationException("Unknown parameter type: " + paramType + " in method: " + method); + // should never happen... + throw new UnsupportedOperationException( + "Unknown parameter type: " + paramType + " in method: " + parameter.getMethod()); } }
spring-webmvc/src/main/java/org/springframework/web/servlet/tags/form/FormTag.java+18 −7 modified@@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2014 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,8 +16,8 @@ package org.springframework.web.servlet.tags.form; +import java.io.UnsupportedEncodingException; import java.util.Map; - import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; @@ -32,6 +32,7 @@ import org.springframework.util.StringUtils; import org.springframework.web.servlet.support.RequestDataValueProcessor; import org.springframework.web.util.HtmlUtils; +import org.springframework.web.util.UriUtils; /** * Databinding-aware JSP tag for rendering an HTML '{@code form}' whose @@ -352,8 +353,7 @@ protected int writeTagContent(TagWriter tagWriter) throws JspException { tagWriter.startTag(FORM_TAG); writeDefaultAttributes(tagWriter); tagWriter.writeAttribute(ACTION_ATTRIBUTE, resolveAction()); - writeOptionalAttribute(tagWriter, METHOD_ATTRIBUTE, - isMethodBrowserSupported(getMethod()) ? getMethod() : DEFAULT_METHOD); + writeOptionalAttribute(tagWriter, METHOD_ATTRIBUTE, getHttpMethod()); writeOptionalAttribute(tagWriter, TARGET_ATTRIBUTE, getTarget()); writeOptionalAttribute(tagWriter, ENCTYPE_ATTRIBUTE, getEnctype()); writeOptionalAttribute(tagWriter, ACCEPT_CHARSET_ATTRIBUTE, getAcceptCharset()); @@ -389,6 +389,10 @@ protected int writeTagContent(TagWriter tagWriter) throws JspException { return EVAL_BODY_INCLUDE; } + private String getHttpMethod() { + return (isMethodBrowserSupported(getMethod()) ? getMethod() : DEFAULT_METHOD); + } + private void assertHttpMethod(String method) { for (HttpMethod httpMethod : HttpMethod.values()) { if (httpMethod.name().equalsIgnoreCase(method)) { @@ -426,7 +430,6 @@ protected String resolveModelAttribute() throws JspException { * with the context and servlet paths, and the result is used. Otherwise, the * {@link org.springframework.web.servlet.support.RequestContext#getRequestUri() * originating URI} is used. - * * @return the value that is to be used for the '{@code action}' attribute */ protected String resolveAction() throws JspException { @@ -438,14 +441,22 @@ protected String resolveAction() throws JspException { } else if (StringUtils.hasText(servletRelativeAction)) { String pathToServlet = getRequestContext().getPathToServlet(); - if (servletRelativeAction.startsWith("/") && !servletRelativeAction.startsWith(getRequestContext().getContextPath())) { + if (servletRelativeAction.startsWith("/") && + !servletRelativeAction.startsWith(getRequestContext().getContextPath())) { servletRelativeAction = pathToServlet + servletRelativeAction; } servletRelativeAction = getDisplayString(evaluate(ACTION_ATTRIBUTE, servletRelativeAction)); return processAction(servletRelativeAction); } else { String requestUri = getRequestContext().getRequestUri(); + String encoding = this.pageContext.getResponse().getCharacterEncoding(); + try { + requestUri = UriUtils.encodePath(requestUri, encoding); + } + catch (UnsupportedEncodingException ex) { + // shouldn't happen - if it does, proceed with requestUri as-is + } ServletResponse response = this.pageContext.getResponse(); if (response instanceof HttpServletResponse) { requestUri = ((HttpServletResponse) response).encodeURL(requestUri); @@ -471,7 +482,7 @@ else if (StringUtils.hasText(servletRelativeAction)) { private String processAction(String action) { RequestDataValueProcessor processor = getRequestContext().getRequestDataValueProcessor(); ServletRequest request = this.pageContext.getRequest(); - if ((processor != null) && (request instanceof HttpServletRequest)) { + if (processor != null && request instanceof HttpServletRequest) { action = processor.processAction((HttpServletRequest) request, action); } return action;
741b4b229ae0Add encoding for the default action in FormTag
2 files changed · +26 −2
spring-webmvc/src/main/java/org/springframework/web/servlet/tags/form/FormTag.java+10 −1 modified@@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2014 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,6 +16,7 @@ package org.springframework.web.servlet.tags.form; +import java.io.UnsupportedEncodingException; import java.util.Map; import javax.servlet.ServletRequest; @@ -32,6 +33,7 @@ import org.springframework.util.StringUtils; import org.springframework.web.servlet.support.RequestDataValueProcessor; import org.springframework.web.util.HtmlUtils; +import org.springframework.web.util.UriUtils; /** * Databinding-aware JSP tag for rendering an HTML '{@code form}' whose @@ -442,6 +444,13 @@ else if (StringUtils.hasText(servletRelativeAction)) { } else { String requestUri = getRequestContext().getRequestUri(); + String encoding = pageContext.getResponse().getCharacterEncoding(); + try { + requestUri = UriUtils.encodePath(requestUri, encoding); + } + catch (UnsupportedEncodingException e) { + throw new JspException(e); + } ServletResponse response = this.pageContext.getResponse(); if (response instanceof HttpServletResponse) { requestUri = ((HttpServletResponse) response).encodeURL(requestUri);
spring-webmvc/src/test/java/org/springframework/web/servlet/tags/form/FormTagTests.java+16 −1 modified@@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2014 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -340,6 +340,21 @@ public void testRequestDataValueProcessorHooks() throws Exception { assertFormTagClosed(output); } + public void testDefaultActionEncoded() throws Exception { + + this.request.setRequestURI("/a b c"); + request.setQueryString(""); + + this.tag.doStartTag(); + this.tag.doEndTag(); + this.tag.doFinally(); + + String output = getOutput(); + String formOutput = getFormTag(output); + + assertContainsAttribute(formOutput, "action", "/a%20b%20c"); + } + private String getFormTag(String output) { int inputStart = output.indexOf("<", 1); int inputEnd = output.lastIndexOf(">", output.length() - 2);
Vulnerability mechanics
Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
12- www.gopivotal.com/security/cve-2014-1904nvdPatchVendor Advisory
- github.com/spring-projects/spring-framework/commit/741b4b229ae032bd17175b46f98673ce0bd2d485nvdPatchThird Party AdvisoryWEB
- docs.spring.io/spring/docs/3.2.8.RELEASE/changelog.txtnvdVendor AdvisoryWEB
- rhn.redhat.com/errata/RHSA-2014-0400.htmlnvdThird Party AdvisoryWEB
- seclists.org/fulldisclosure/2014/Mar/101nvdMailing ListThird Party AdvisoryWEB
- www.securityfocus.com/bid/66137nvdThird Party AdvisoryVDB Entry
- github.com/advisories/GHSA-ff7p-jqjm-v66hghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2014-1904ghsaADVISORY
- secunia.com/advisories/57915nvdPermissions RequiredWEB
- www.securityfocus.com/archive/1/531422/100/0/threadednvdBroken Link
- github.com/spring-projects/spring-framework/commit/75e08695a04980dbceae6789364717e9d8764d58ghsaWEB
- jira.springsource.org/browse/SPR-11426nvdPermissions RequiredWEB
News mentions
0No linked articles in our index yet.