CVE-2017-12624
Description
Apache CXF supports sending and receiving attachments via either the JAX-WS or JAX-RS specifications. It is possible to craft a message attachment header that could lead to a Denial of Service (DoS) attack on a CXF web service provider. Both JAX-WS and JAX-RS services are vulnerable to this attack. From Apache CXF 3.2.1 and 3.1.14, message attachment headers that are greater than 300 characters will be rejected by default. This value is configurable via the property "attachment-max-header-size".
AI Insight
LLM-synthesized narrative grounded in this CVE's description and references.
Apache CXF 3.2.x before 3.2.1 and 3.1.x before 3.1.14 are vulnerable to a denial-of-service attack via crafted message attachment headers.
Vulnerability
Apache CXF, a Java framework for building web services, is vulnerable to a denial-of-service (DoS) attack when processing message attachments via JAX-WS or JAX-RS specifications. An attacker can craft a message attachment header with an oversized value, causing excessive resource consumption. Versions prior to 3.2.1 and 3.1.14 are affected. The fix introduced a default maximum header size of 300 characters, configurable via the property attachment-max-header-size [1][3].
Exploitation
An attacker can send a SOAP or REST message containing an attachment with a header field exceeding the previous unlimited size. No authentication or special network position is required; the attacker only needs to submit a malicious request to a CXF endpoint processing attachments [1][3].
Impact
Successful exploitation leads to denial of service, as the CXF service may hang or crash while parsing the oversized header, disrupting availability for legitimate users [1][3].
Mitigation
Upgrade to Apache CXF 3.2.1 or 3.1.14 (released November 2017), which reject attachment headers larger than 300 characters by default. Administrators can also lower the attachment-max-header-size property. Red Hat products affected, such as Red Hat JBoss Enterprise Application Platform, received fixes via RHSA-2018:2423 and RHSA-2018:2424 [1][2][3].
AI Insight generated on May 22, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
org.apache.cxf:cxf-coreMaven | >= 3.2.0, < 3.2.1 | 3.2.1 |
org.apache.cxf:cxf-coreMaven | >= 3.1.0, < 3.1.14 | 3.1.14 |
org.apache.cxf:cxf-coreMaven | < 3.0.16 | 3.0.16 |
Affected products
2- Apache Software Foundation/Apache CXFv5Range: prior to 3.1.14
Patches
3a2ce435cf0eeCXF-7507 - Put a configurable limit on the size of attachment headers
8 files changed · +169 −6
core/src/main/java/org/apache/cxf/attachment/AttachmentDeserializer.java+24 −0 modified@@ -29,17 +29,20 @@ import java.util.Map; import java.util.Set; import java.util.TreeMap; +import java.util.logging.Logger; import java.util.regex.Matcher; import java.util.regex.Pattern; import javax.activation.DataSource; +import org.apache.cxf.common.logging.LogUtils; import org.apache.cxf.common.util.StringUtils; import org.apache.cxf.helpers.HttpHeaderHelper; import org.apache.cxf.helpers.IOUtils; import org.apache.cxf.io.CachedOutputStream; import org.apache.cxf.message.Attachment; import org.apache.cxf.message.Message; +import org.apache.cxf.message.MessageUtils; public class AttachmentDeserializer { public static final String ATTACHMENT_PART_HEADERS = AttachmentDeserializer.class.getName() + ".headers"; @@ -49,6 +52,12 @@ public class AttachmentDeserializer { public static final String ATTACHMENT_MAX_SIZE = "attachment-max-size"; + /** + * The maximum MIME Header Length. The default is 300. + */ + public static final String ATTACHMENT_MAX_HEADER_SIZE = "attachment-max-header-size"; + public static final int DEFAULT_MAX_HEADER_SIZE = 300; + public static final int THRESHOLD = 1024 * 100; //100K (byte unit) private static final Pattern CONTENT_TYPE_BOUNDARY_PATTERN = Pattern.compile("boundary=\"?([^\";]*)"); @@ -58,6 +67,8 @@ public class AttachmentDeserializer { private static final Pattern INPUT_STREAM_BOUNDARY_PATTERN = Pattern.compile("^--(\\S*)$", Pattern.MULTILINE); + private static final Logger LOG = LogUtils.getL7dLogger(AttachmentDeserializer.class); + private boolean lazyLoading = true; private int pbAmount = 2048; @@ -79,13 +90,19 @@ public class AttachmentDeserializer { private Set<DelegatingInputStream> loaded = new HashSet<DelegatingInputStream>(); private List<String> supportedTypes; + private int maxHeaderLength = DEFAULT_MAX_HEADER_SIZE; + public AttachmentDeserializer(Message message) { this(message, Collections.singletonList("multipart/related")); } public AttachmentDeserializer(Message message, List<String> supportedTypes) { this.message = message; this.supportedTypes = supportedTypes; + + // Get the maximum Header length from configuration + maxHeaderLength = MessageUtils.getContextualInteger(message, ATTACHMENT_MAX_HEADER_SIZE, + DEFAULT_MAX_HEADER_SIZE); } public void initializeAttachments() throws IOException { @@ -286,6 +303,7 @@ private Attachment createAttachment(Map<String, List<String>> headers) throws IO new DelegatingInputStream(new MimeBodyPartInputStream(stream, boundary, pbAmount), this); createCount++; + return AttachmentUtil.createAttachment(partStream, headers); } @@ -334,6 +352,7 @@ private Map<String, List<String>> loadPartHeaders(InputStream in) throws IOExcep List<String> headerLines = new ArrayList<String>(10); StringBuilder buffer = new StringBuilder(128); String line; + // loop until we hit the end or a null line while ((line = readLine(in)) != null) { // lines beginning with white space get special handling @@ -412,6 +431,11 @@ private String readLine(InputStream in) throws IOException { // just add to the buffer buffer.append((char)c); } + + if (buffer.length() > maxHeaderLength) { + LOG.fine("The attachment header size has exceeded the configured parameter: " + maxHeaderLength); + throw new HeaderSizeExceededException(); + } } // no characters found...this was either an eof or a null line.
core/src/main/java/org/apache/cxf/attachment/ContentDisposition.java+1 −1 modified@@ -28,7 +28,7 @@ public class ContentDisposition { private static final String CD_HEADER_PARAMS_EXPRESSION = - "(([\\w-]+( )?\\*?=( )?\"[^\"]+\")|([\\w-]+( )?\\*?=( )?[^;]+))"; + "[\\w-]++( )?\\*?=( )?((\"[^\"]++\")|([^;]+))"; private static final Pattern CD_HEADER_PARAMS_PATTERN = Pattern.compile(CD_HEADER_PARAMS_EXPRESSION);
core/src/main/java/org/apache/cxf/attachment/HeaderSizeExceededException.java+40 −0 added@@ -0,0 +1,40 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.cxf.attachment; + +public class HeaderSizeExceededException extends RuntimeException { + private static final long serialVersionUID = -8976580055837650080L; + + public HeaderSizeExceededException() { + super(); + } + + public HeaderSizeExceededException(String message) { + super(message); + } + + public HeaderSizeExceededException(String message, Throwable cause) { + super(message, cause); + } + + public HeaderSizeExceededException(Throwable cause) { + super(cause); + } +}
core/src/main/java/org/apache/cxf/message/MessageUtils.java+23 −1 modified@@ -19,8 +19,11 @@ package org.apache.cxf.message; +import java.util.logging.Logger; + import org.w3c.dom.Node; +import org.apache.cxf.common.logging.LogUtils; import org.apache.cxf.common.util.PropertyUtils; @@ -29,6 +32,8 @@ */ public final class MessageUtils { + private static final Logger LOG = LogUtils.getL7dLogger(MessageUtils.class); + /** * Prevents instantiation. */ @@ -139,7 +144,24 @@ public static boolean getContextualBoolean(Message m, String key, boolean defaul } return defaultValue; } - + + public static int getContextualInteger(Message m, String key, int defaultValue) { + if (m != null) { + Object o = m.getContextualProperty(key); + if (o instanceof String) { + try { + int i = Integer.parseInt((String)o); + if (i > 0) { + return i; + } + } catch (NumberFormatException ex) { + LOG.warning("Incorrect integer value of " + o + " specified for: " + key); + } + } + } + return defaultValue; + } + public static Object getContextualProperty(Message m, String propPreferred, String propDefault) { Object prop = null; if (m != null) {
rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/MessageContextImpl.java+8 −1 modified@@ -45,6 +45,7 @@ import org.apache.cxf.attachment.AttachmentDeserializer; import org.apache.cxf.attachment.AttachmentImpl; import org.apache.cxf.attachment.AttachmentUtil; +import org.apache.cxf.attachment.HeaderSizeExceededException; import org.apache.cxf.endpoint.Endpoint; import org.apache.cxf.helpers.CastUtils; import org.apache.cxf.interceptor.AttachmentOutInterceptor; @@ -64,6 +65,7 @@ import org.apache.cxf.message.MessageUtils; public class MessageContextImpl implements MessageContext { + private Message m; public MessageContextImpl(Message m) { this.m = m; @@ -78,6 +80,8 @@ public Object get(Object key) { } catch (CacheSizeExceededException e) { m.getExchange().put("cxf.io.cacheinput", Boolean.FALSE); throw new WebApplicationException(e, 413); + } catch (HeaderSizeExceededException e) { + throw new WebApplicationException(e, 413); } } if (keyValue.equals("WRITE-" + Message.ATTACHMENTS)) { @@ -269,7 +273,9 @@ private MultipartBody createAttachments(String propertyName) { m.getExchange().getInMessage().get(AttachmentDeserializer.ATTACHMENT_MEMORY_THRESHOLD)); inMessage.put(AttachmentDeserializer.ATTACHMENT_MAX_SIZE, m.getExchange().getInMessage().get(AttachmentDeserializer.ATTACHMENT_MAX_SIZE)); - inMessage.setContent(InputStream.class, + inMessage.put(AttachmentDeserializer.ATTACHMENT_MAX_HEADER_SIZE, + m.getExchange().getInMessage().get(AttachmentDeserializer.ATTACHMENT_MAX_HEADER_SIZE)); + inMessage.setContent(InputStream.class, m.getExchange().getInMessage().get("org.apache.cxf.multipart.embedded.input")); inMessage.put(Message.CONTENT_TYPE, m.getExchange().getInMessage().get("org.apache.cxf.multipart.embedded.ctype").toString()); @@ -282,6 +288,7 @@ private MultipartBody createAttachments(String propertyName) { try { Map<String, List<String>> headers = CastUtils.cast((Map<?, ?>)inMessage.get(AttachmentDeserializer.ATTACHMENT_PART_HEADERS)); + Attachment first = new Attachment(AttachmentUtil.createAttachment( inMessage.getContent(InputStream.class), headers),
rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/multipart/Attachment.java+8 −3 modified@@ -88,7 +88,7 @@ public Attachment(InputStream is, MultivaluedMap<String, String> headers) { new DataHandler(new InputStreamDataSource(is, headers.getFirst("Content-Type"))), headers); } - + public Attachment(String id, String mediaType, Object object) { this.object = object; headers.putSingle("Content-ID", id); @@ -103,8 +103,8 @@ public Attachment(String id, InputStream is, ContentDisposition cd) { headers.putSingle("Content-ID", id); headers.putSingle("Content-Type", "application/octet-stream"); } - - Attachment(MultivaluedMap<String, String> headers, DataHandler handler, Object object) { + + public Attachment(MultivaluedMap<String, String> headers, DataHandler handler, Object object) { this.headers = headers; this.handler = handler; this.object = object; @@ -121,7 +121,12 @@ public String getContentId() { } public MediaType getContentType() { +<<<<<<< HEAD String value = handler != null ? handler.getContentType() : headers.getFirst("Content-Type"); +======= + String value = handler != null && handler.getContentType() != null ? handler.getContentType() + : headers.getFirst("Content-Type"); +>>>>>>> 896bd961cb... CXF-7507 - Put a configurable limit on the size of attachment headers return value == null ? MediaType.TEXT_PLAIN_TYPE : JAXRSUtils.toMediaType(value); }
systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/JAXRSMultipartTest.java+64 −0 modified@@ -39,6 +39,7 @@ import javax.imageio.ImageIO; import javax.mail.util.ByteArrayDataSource; import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.MultivaluedHashMap; import javax.ws.rs.core.MultivaluedMap; import javax.ws.rs.core.Response; import javax.xml.bind.JAXBContext; @@ -62,6 +63,7 @@ import org.apache.cxf.jaxrs.client.WebClient; import org.apache.cxf.jaxrs.ext.multipart.Attachment; import org.apache.cxf.jaxrs.ext.multipart.ContentDisposition; +import org.apache.cxf.jaxrs.ext.multipart.InputStreamDataSource; import org.apache.cxf.jaxrs.ext.multipart.MultipartBody; import org.apache.cxf.jaxrs.impl.MetadataMap; import org.apache.cxf.jaxrs.provider.json.JSONProvider; @@ -942,6 +944,67 @@ public void testMultipartRequestTooLargeManyParts() throws Exception { } } + // The large Content Disposition header will be rejected here + @Test + public void testLargeHeader() throws Exception { + InputStream is1 = + getClass().getResourceAsStream("/org/apache/cxf/systest/jaxrs/resources/java.jpg"); + String address = "http://localhost:" + PORT + "/bookstore/books/image"; + WebClient client = WebClient.create(address); + client.type("multipart/mixed").accept("multipart/mixed"); + WebClient.getConfig(client).getRequestContext().put("support.type.as.multipart", + "true"); + + StringBuilder sb = new StringBuilder(); + sb.append("form-data;"); + for (int i = 0; i < 10000; i++) { + sb.append("aaaaaaaaaa"); + } + + MultivaluedMap<String, String> headers = new MultivaluedHashMap<>(); + headers.putSingle("Content-ID", "root"); + headers.putSingle("Content-Type", "application/octet-stream"); + headers.putSingle("Content-Disposition", sb.toString()); + DataHandler handler = new DataHandler(new InputStreamDataSource(is1, "application/octet-stream")); + + Attachment att = new Attachment(headers, handler, null); + Response response = client.post(att); + assertEquals(response.getStatus(), 413); + + client.close(); + } + + // The Content Disposition header will be accepted here, even though it is larger than the default, + // as we have configured a larger value on the service side + @Test + public void testLargerThanDefaultHeader() throws Exception { + InputStream is1 = + getClass().getResourceAsStream("/org/apache/cxf/systest/jaxrs/resources/java.jpg"); + String address = "http://localhost:" + PORT + "/bookstore/books/image"; + WebClient client = WebClient.create(address); + client.type("multipart/mixed").accept("multipart/mixed"); + WebClient.getConfig(client).getRequestContext().put("support.type.as.multipart", + "true"); + + StringBuilder sb = new StringBuilder(); + sb.append("form-data;"); + for (int i = 0; i < 35; i++) { + sb.append("aaaaaaaaaa"); + } + + MultivaluedMap<String, String> headers = new MultivaluedHashMap<>(); + headers.putSingle("Content-ID", "root"); + headers.putSingle("Content-Type", "application/octet-stream"); + headers.putSingle("Content-Disposition", sb.toString()); + DataHandler handler = new DataHandler(new InputStreamDataSource(is1, "application/octet-stream")); + + Attachment att = new Attachment(headers, handler, null); + Response response = client.post(att); + assertEquals(response.getStatus(), 200); + + client.close(); + } + private void doAddBook(String address, String resourceName, int status) throws Exception { doAddBook("multipart/related", address, resourceName, status); } @@ -1031,4 +1094,5 @@ private String stripXmlInstructionIfNeeded(String str) { } return str; } + }
systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/MultipartServer.java+1 −0 modified@@ -40,6 +40,7 @@ protected void run() { Map<String, Object> props = new HashMap<String, Object>(); props.put(AttachmentDeserializer.ATTACHMENT_MAX_SIZE, String.valueOf(1024 * 10)); props.put(AttachmentDeserializer.ATTACHMENT_MEMORY_THRESHOLD, String.valueOf(1024 * 5)); + props.put(AttachmentDeserializer.ATTACHMENT_MAX_HEADER_SIZE, String.valueOf(400)); sf.setProperties(props); //default lifecycle is per-request, change it to singleton sf.setResourceProvider(MultipartStore.class,
8bd915bfd773CXF-7507 - Put a configurable limit on the size of attachment headers
8 files changed · +162 −4
core/src/main/java/org/apache/cxf/attachment/AttachmentDeserializer.java+24 −0 modified@@ -29,17 +29,20 @@ import java.util.Map; import java.util.Set; import java.util.TreeMap; +import java.util.logging.Logger; import java.util.regex.Matcher; import java.util.regex.Pattern; import javax.activation.DataSource; +import org.apache.cxf.common.logging.LogUtils; import org.apache.cxf.common.util.StringUtils; import org.apache.cxf.helpers.HttpHeaderHelper; import org.apache.cxf.helpers.IOUtils; import org.apache.cxf.io.CachedOutputStream; import org.apache.cxf.message.Attachment; import org.apache.cxf.message.Message; +import org.apache.cxf.message.MessageUtils; public class AttachmentDeserializer { public static final String ATTACHMENT_PART_HEADERS = AttachmentDeserializer.class.getName() + ".headers"; @@ -49,13 +52,21 @@ public class AttachmentDeserializer { public static final String ATTACHMENT_MAX_SIZE = "attachment-max-size"; + /** + * The maximum MIME Header Length. The default is 300. + */ + public static final String ATTACHMENT_MAX_HEADER_SIZE = "attachment-max-header-size"; + public static final int DEFAULT_MAX_HEADER_SIZE = 300; + public static final int THRESHOLD = 1024 * 100; //100K (byte unit) private static final Pattern CONTENT_TYPE_BOUNDARY_PATTERN = Pattern.compile("boundary=\"?([^\";]*)"); private static final Pattern INPUT_STREAM_BOUNDARY_PATTERN = Pattern.compile("^--(\\S*)$", Pattern.MULTILINE); + private static final Logger LOG = LogUtils.getL7dLogger(AttachmentDeserializer.class); + private boolean lazyLoading = true; private int pbAmount = 2048; @@ -77,13 +88,19 @@ public class AttachmentDeserializer { private Set<DelegatingInputStream> loaded = new HashSet<>(); private List<String> supportedTypes; + private int maxHeaderLength = DEFAULT_MAX_HEADER_SIZE; + public AttachmentDeserializer(Message message) { this(message, Collections.singletonList("multipart/related")); } public AttachmentDeserializer(Message message, List<String> supportedTypes) { this.message = message; this.supportedTypes = supportedTypes; + + // Get the maximum Header length from configuration + maxHeaderLength = MessageUtils.getContextualInteger(message, ATTACHMENT_MAX_HEADER_SIZE, + DEFAULT_MAX_HEADER_SIZE); } public void initializeAttachments() throws IOException { @@ -277,6 +294,7 @@ private Attachment createAttachment(Map<String, List<String>> headers) throws IO new DelegatingInputStream(new MimeBodyPartInputStream(stream, boundary, pbAmount), this); createCount++; + return AttachmentUtil.createAttachment(partStream, headers); } @@ -325,6 +343,7 @@ private Map<String, List<String>> loadPartHeaders(InputStream in) throws IOExcep StringBuilder buffer = new StringBuilder(128); StringBuilder b = new StringBuilder(128); Map<String, List<String>> heads = new TreeMap<String, List<String>>(String.CASE_INSENSITIVE_ORDER); + // loop until we hit the end or a null line while (readLine(in, b)) { // lines beginning with white space get special handling @@ -371,6 +390,11 @@ private boolean readLine(InputStream in, StringBuilder buffer) throws IOExceptio // just add to the buffer buffer.append((char)c); } + + if (buffer.length() > maxHeaderLength) { + LOG.fine("The attachment header size has exceeded the configured parameter: " + maxHeaderLength); + throw new HeaderSizeExceededException(); + } } // no characters found...this was either an eof or a null line.
core/src/main/java/org/apache/cxf/attachment/ContentDisposition.java+1 −1 modified@@ -28,7 +28,7 @@ public class ContentDisposition { private static final String CD_HEADER_PARAMS_EXPRESSION = - "(([\\w-]+( )?\\*?=( )?\"[^\"]+\")|([\\w-]+( )?\\*?=( )?[^;]+))"; + "[\\w-]++( )?\\*?=( )?((\"[^\"]++\")|([^;]+))"; private static final Pattern CD_HEADER_PARAMS_PATTERN = Pattern.compile(CD_HEADER_PARAMS_EXPRESSION);
core/src/main/java/org/apache/cxf/attachment/HeaderSizeExceededException.java+40 −0 added@@ -0,0 +1,40 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.cxf.attachment; + +public class HeaderSizeExceededException extends RuntimeException { + private static final long serialVersionUID = -8976580055837650080L; + + public HeaderSizeExceededException() { + super(); + } + + public HeaderSizeExceededException(String message) { + super(message); + } + + public HeaderSizeExceededException(String message, Throwable cause) { + super(message, cause); + } + + public HeaderSizeExceededException(Throwable cause) { + super(cause); + } +}
core/src/main/java/org/apache/cxf/message/MessageUtils.java+22 −0 modified@@ -19,8 +19,11 @@ package org.apache.cxf.message; +import java.util.logging.Logger; + import org.w3c.dom.Node; +import org.apache.cxf.common.logging.LogUtils; import org.apache.cxf.common.util.PropertyUtils; @@ -29,6 +32,8 @@ */ public final class MessageUtils { + private static final Logger LOG = LogUtils.getL7dLogger(MessageUtils.class); + /** * Prevents instantiation. */ @@ -142,6 +147,23 @@ public static boolean getContextualBoolean(Message m, String key, boolean defaul return defaultValue; } + public static int getContextualInteger(Message m, String key, int defaultValue) { + if (m != null) { + Object o = m.getContextualProperty(key); + if (o instanceof String) { + try { + int i = Integer.parseInt((String)o); + if (i > 0) { + return i; + } + } catch (NumberFormatException ex) { + LOG.warning("Incorrect integer value of " + o + " specified for: " + key); + } + } + } + return defaultValue; + } + public static Object getContextualProperty(Message m, String propPreferred, String propDefault) { Object prop = null; if (m != null) {
rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/MessageContextImpl.java+7 −0 modified@@ -45,6 +45,7 @@ import org.apache.cxf.attachment.AttachmentDeserializer; import org.apache.cxf.attachment.AttachmentImpl; import org.apache.cxf.attachment.AttachmentUtil; +import org.apache.cxf.attachment.HeaderSizeExceededException; import org.apache.cxf.endpoint.Endpoint; import org.apache.cxf.helpers.CastUtils; import org.apache.cxf.interceptor.AttachmentOutInterceptor; @@ -64,6 +65,7 @@ import org.apache.cxf.message.MessageUtils; public class MessageContextImpl implements MessageContext { + private Message m; public MessageContextImpl(Message m) { this.m = m; @@ -78,6 +80,8 @@ public Object get(Object key) { } catch (CacheSizeExceededException e) { m.getExchange().put("cxf.io.cacheinput", Boolean.FALSE); throw new WebApplicationException(e, 413); + } catch (HeaderSizeExceededException e) { + throw new WebApplicationException(e, 413); } } if (keyValue.equals("WRITE-" + Message.ATTACHMENTS)) { @@ -268,6 +272,8 @@ private MultipartBody createAttachments(String propertyName) { m.getExchange().getInMessage().get(AttachmentDeserializer.ATTACHMENT_MEMORY_THRESHOLD)); inMessage.put(AttachmentDeserializer.ATTACHMENT_MAX_SIZE, m.getExchange().getInMessage().get(AttachmentDeserializer.ATTACHMENT_MAX_SIZE)); + inMessage.put(AttachmentDeserializer.ATTACHMENT_MAX_HEADER_SIZE, + m.getExchange().getInMessage().get(AttachmentDeserializer.ATTACHMENT_MAX_HEADER_SIZE)); inMessage.setContent(InputStream.class, m.getExchange().getInMessage().get("org.apache.cxf.multipart.embedded.input")); inMessage.put(Message.CONTENT_TYPE, @@ -281,6 +287,7 @@ private MultipartBody createAttachments(String propertyName) { try { Map<String, List<String>> headers = CastUtils.cast((Map<?, ?>)inMessage.get(AttachmentDeserializer.ATTACHMENT_PART_HEADERS)); + Attachment first = new Attachment(AttachmentUtil.createAttachment( inMessage.getContent(InputStream.class), headers),
rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/multipart/Attachment.java+3 −3 modified@@ -93,7 +93,7 @@ public Attachment(InputStream is, MultivaluedMap<String, String> headers) { public Attachment(String mediaType, Object object) { this(UUID.randomUUID().toString(), mediaType, object); } - + public Attachment(String id, String mediaType, Object object) { this.object = object; if (id != null) { @@ -111,7 +111,7 @@ public Attachment(String id, InputStream is, ContentDisposition cd) { headers.putSingle("Content-Type", "application/octet-stream"); } - Attachment(MultivaluedMap<String, String> headers, DataHandler handler, Object object) { + public Attachment(MultivaluedMap<String, String> headers, DataHandler handler, Object object) { this.headers = headers; this.handler = handler; this.object = object; @@ -128,7 +128,7 @@ public String getContentId() { } public MediaType getContentType() { - String value = handler != null && handler.getContentType() != null ? handler.getContentType() + String value = handler != null && handler.getContentType() != null ? handler.getContentType() : headers.getFirst("Content-Type"); return value == null ? MediaType.TEXT_PLAIN_TYPE : JAXRSUtils.toMediaType(value); }
systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/JAXRSMultipartTest.java+64 −0 modified@@ -39,6 +39,7 @@ import javax.imageio.ImageIO; import javax.mail.util.ByteArrayDataSource; import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.MultivaluedHashMap; import javax.ws.rs.core.MultivaluedMap; import javax.ws.rs.core.Response; import javax.xml.bind.JAXBContext; @@ -53,6 +54,7 @@ import org.apache.cxf.jaxrs.client.WebClient; import org.apache.cxf.jaxrs.ext.multipart.Attachment; import org.apache.cxf.jaxrs.ext.multipart.ContentDisposition; +import org.apache.cxf.jaxrs.ext.multipart.InputStreamDataSource; import org.apache.cxf.jaxrs.ext.multipart.MultipartBody; import org.apache.cxf.jaxrs.impl.MetadataMap; import org.apache.cxf.jaxrs.provider.json.JSONProvider; @@ -937,6 +939,67 @@ public void testMultipartRequestTooLargeManyParts() throws Exception { } } + // The large Content Disposition header will be rejected here + @Test + public void testLargeHeader() throws Exception { + InputStream is1 = + getClass().getResourceAsStream("/org/apache/cxf/systest/jaxrs/resources/java.jpg"); + String address = "http://localhost:" + PORT + "/bookstore/books/image"; + WebClient client = WebClient.create(address); + client.type("multipart/mixed").accept("multipart/mixed"); + WebClient.getConfig(client).getRequestContext().put("support.type.as.multipart", + "true"); + + StringBuilder sb = new StringBuilder(); + sb.append("form-data;"); + for (int i = 0; i < 10000; i++) { + sb.append("aaaaaaaaaa"); + } + + MultivaluedMap<String, String> headers = new MultivaluedHashMap<>(); + headers.putSingle("Content-ID", "root"); + headers.putSingle("Content-Type", "application/octet-stream"); + headers.putSingle("Content-Disposition", sb.toString()); + DataHandler handler = new DataHandler(new InputStreamDataSource(is1, "application/octet-stream")); + + Attachment att = new Attachment(headers, handler, null); + Response response = client.post(att); + assertEquals(response.getStatus(), 413); + + client.close(); + } + + // The Content Disposition header will be accepted here, even though it is larger than the default, + // as we have configured a larger value on the service side + @Test + public void testLargerThanDefaultHeader() throws Exception { + InputStream is1 = + getClass().getResourceAsStream("/org/apache/cxf/systest/jaxrs/resources/java.jpg"); + String address = "http://localhost:" + PORT + "/bookstore/books/image"; + WebClient client = WebClient.create(address); + client.type("multipart/mixed").accept("multipart/mixed"); + WebClient.getConfig(client).getRequestContext().put("support.type.as.multipart", + "true"); + + StringBuilder sb = new StringBuilder(); + sb.append("form-data;"); + for (int i = 0; i < 35; i++) { + sb.append("aaaaaaaaaa"); + } + + MultivaluedMap<String, String> headers = new MultivaluedHashMap<>(); + headers.putSingle("Content-ID", "root"); + headers.putSingle("Content-Type", "application/octet-stream"); + headers.putSingle("Content-Disposition", sb.toString()); + DataHandler handler = new DataHandler(new InputStreamDataSource(is1, "application/octet-stream")); + + Attachment att = new Attachment(headers, handler, null); + Response response = client.post(att); + assertEquals(response.getStatus(), 200); + + client.close(); + } + private void doAddBook(String address, String resourceName, int status) throws Exception { doAddBook("multipart/related", address, resourceName, status); } @@ -1018,4 +1081,5 @@ private String stripXmlInstructionIfNeeded(String str) { } return str; } + }
systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/MultipartServer.java+1 −0 modified@@ -40,6 +40,7 @@ protected void run() { Map<String, Object> props = new HashMap<>(); props.put(AttachmentDeserializer.ATTACHMENT_MAX_SIZE, String.valueOf(1024 * 10)); props.put(AttachmentDeserializer.ATTACHMENT_MEMORY_THRESHOLD, String.valueOf(1024 * 5)); + props.put(AttachmentDeserializer.ATTACHMENT_MAX_HEADER_SIZE, String.valueOf(400)); sf.setProperties(props); //default lifecycle is per-request, change it to singleton sf.setResourceProvider(MultipartStore.class,
896bd961cbbbCXF-7507 - Put a configurable limit on the size of attachment headers
8 files changed · +165 −7
core/src/main/java/org/apache/cxf/attachment/AttachmentDeserializer.java+24 −0 modified@@ -29,17 +29,20 @@ import java.util.Map; import java.util.Set; import java.util.TreeMap; +import java.util.logging.Logger; import java.util.regex.Matcher; import java.util.regex.Pattern; import javax.activation.DataSource; +import org.apache.cxf.common.logging.LogUtils; import org.apache.cxf.common.util.StringUtils; import org.apache.cxf.helpers.HttpHeaderHelper; import org.apache.cxf.helpers.IOUtils; import org.apache.cxf.io.CachedOutputStream; import org.apache.cxf.message.Attachment; import org.apache.cxf.message.Message; +import org.apache.cxf.message.MessageUtils; public class AttachmentDeserializer { public static final String ATTACHMENT_PART_HEADERS = AttachmentDeserializer.class.getName() + ".headers"; @@ -49,6 +52,12 @@ public class AttachmentDeserializer { public static final String ATTACHMENT_MAX_SIZE = "attachment-max-size"; + /** + * The maximum MIME Header Length. The default is 300. + */ + public static final String ATTACHMENT_MAX_HEADER_SIZE = "attachment-max-header-size"; + public static final int DEFAULT_MAX_HEADER_SIZE = 300; + public static final int THRESHOLD = 1024 * 100; //100K (byte unit) private static final Pattern CONTENT_TYPE_BOUNDARY_PATTERN = Pattern.compile("boundary=\"?([^\";]*)"); @@ -58,6 +67,8 @@ public class AttachmentDeserializer { private static final Pattern INPUT_STREAM_BOUNDARY_PATTERN = Pattern.compile("^--(\\S*)$", Pattern.MULTILINE); + private static final Logger LOG = LogUtils.getL7dLogger(AttachmentDeserializer.class); + private boolean lazyLoading = true; private int pbAmount = 2048; @@ -79,13 +90,19 @@ public class AttachmentDeserializer { private Set<DelegatingInputStream> loaded = new HashSet<DelegatingInputStream>(); private List<String> supportedTypes; + private int maxHeaderLength = DEFAULT_MAX_HEADER_SIZE; + public AttachmentDeserializer(Message message) { this(message, Collections.singletonList("multipart/related")); } public AttachmentDeserializer(Message message, List<String> supportedTypes) { this.message = message; this.supportedTypes = supportedTypes; + + // Get the maximum Header length from configuration + maxHeaderLength = MessageUtils.getContextualInteger(message, ATTACHMENT_MAX_HEADER_SIZE, + DEFAULT_MAX_HEADER_SIZE); } public void initializeAttachments() throws IOException { @@ -279,6 +296,7 @@ private Attachment createAttachment(Map<String, List<String>> headers) throws IO new DelegatingInputStream(new MimeBodyPartInputStream(stream, boundary, pbAmount), this); createCount++; + return AttachmentUtil.createAttachment(partStream, headers); } @@ -327,6 +345,7 @@ private Map<String, List<String>> loadPartHeaders(InputStream in) throws IOExcep StringBuilder buffer = new StringBuilder(128); StringBuilder b = new StringBuilder(128); Map<String, List<String>> heads = new TreeMap<String, List<String>>(String.CASE_INSENSITIVE_ORDER); + // loop until we hit the end or a null line while (readLine(in, b)) { // lines beginning with white space get special handling @@ -373,6 +392,11 @@ private boolean readLine(InputStream in, StringBuilder buffer) throws IOExceptio // just add to the buffer buffer.append((char)c); } + + if (buffer.length() > maxHeaderLength) { + LOG.fine("The attachment header size has exceeded the configured parameter: " + maxHeaderLength); + throw new HeaderSizeExceededException(); + } } // no characters found...this was either an eof or a null line.
core/src/main/java/org/apache/cxf/attachment/ContentDisposition.java+1 −1 modified@@ -28,7 +28,7 @@ public class ContentDisposition { private static final String CD_HEADER_PARAMS_EXPRESSION = - "(([\\w-]+( )?\\*?=( )?\"[^\"]+\")|([\\w-]+( )?\\*?=( )?[^;]+))"; + "[\\w-]++( )?\\*?=( )?((\"[^\"]++\")|([^;]+))"; private static final Pattern CD_HEADER_PARAMS_PATTERN = Pattern.compile(CD_HEADER_PARAMS_EXPRESSION);
core/src/main/java/org/apache/cxf/attachment/HeaderSizeExceededException.java+40 −0 added@@ -0,0 +1,40 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.cxf.attachment; + +public class HeaderSizeExceededException extends RuntimeException { + private static final long serialVersionUID = -8976580055837650080L; + + public HeaderSizeExceededException() { + super(); + } + + public HeaderSizeExceededException(String message) { + super(message); + } + + public HeaderSizeExceededException(String message, Throwable cause) { + super(message, cause); + } + + public HeaderSizeExceededException(Throwable cause) { + super(cause); + } +}
core/src/main/java/org/apache/cxf/message/MessageUtils.java+23 −1 modified@@ -19,8 +19,11 @@ package org.apache.cxf.message; +import java.util.logging.Logger; + import org.w3c.dom.Node; +import org.apache.cxf.common.logging.LogUtils; import org.apache.cxf.common.util.PropertyUtils; @@ -29,6 +32,8 @@ */ public final class MessageUtils { + private static final Logger LOG = LogUtils.getL7dLogger(MessageUtils.class); + /** * Prevents instantiation. */ @@ -139,7 +144,24 @@ public static boolean getContextualBoolean(Message m, String key, boolean defaul } return defaultValue; } - + + public static int getContextualInteger(Message m, String key, int defaultValue) { + if (m != null) { + Object o = m.getContextualProperty(key); + if (o instanceof String) { + try { + int i = Integer.parseInt((String)o); + if (i > 0) { + return i; + } + } catch (NumberFormatException ex) { + LOG.warning("Incorrect integer value of " + o + " specified for: " + key); + } + } + } + return defaultValue; + } + public static Object getContextualProperty(Message m, String propPreferred, String propDefault) { Object prop = null; if (m != null) {
rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/MessageContextImpl.java+8 −1 modified@@ -45,6 +45,7 @@ import org.apache.cxf.attachment.AttachmentDeserializer; import org.apache.cxf.attachment.AttachmentImpl; import org.apache.cxf.attachment.AttachmentUtil; +import org.apache.cxf.attachment.HeaderSizeExceededException; import org.apache.cxf.endpoint.Endpoint; import org.apache.cxf.helpers.CastUtils; import org.apache.cxf.interceptor.AttachmentOutInterceptor; @@ -64,6 +65,7 @@ import org.apache.cxf.message.MessageUtils; public class MessageContextImpl implements MessageContext { + private Message m; public MessageContextImpl(Message m) { this.m = m; @@ -78,6 +80,8 @@ public Object get(Object key) { } catch (CacheSizeExceededException e) { m.getExchange().put("cxf.io.cacheinput", Boolean.FALSE); throw new WebApplicationException(e, 413); + } catch (HeaderSizeExceededException e) { + throw new WebApplicationException(e, 413); } } if (keyValue.equals("WRITE-" + Message.ATTACHMENTS)) { @@ -269,7 +273,9 @@ private MultipartBody createAttachments(String propertyName) { m.getExchange().getInMessage().get(AttachmentDeserializer.ATTACHMENT_MEMORY_THRESHOLD)); inMessage.put(AttachmentDeserializer.ATTACHMENT_MAX_SIZE, m.getExchange().getInMessage().get(AttachmentDeserializer.ATTACHMENT_MAX_SIZE)); - inMessage.setContent(InputStream.class, + inMessage.put(AttachmentDeserializer.ATTACHMENT_MAX_HEADER_SIZE, + m.getExchange().getInMessage().get(AttachmentDeserializer.ATTACHMENT_MAX_HEADER_SIZE)); + inMessage.setContent(InputStream.class, m.getExchange().getInMessage().get("org.apache.cxf.multipart.embedded.input")); inMessage.put(Message.CONTENT_TYPE, m.getExchange().getInMessage().get("org.apache.cxf.multipart.embedded.ctype").toString()); @@ -282,6 +288,7 @@ private MultipartBody createAttachments(String propertyName) { try { Map<String, List<String>> headers = CastUtils.cast((Map<?, ?>)inMessage.get(AttachmentDeserializer.ATTACHMENT_PART_HEADERS)); + Attachment first = new Attachment(AttachmentUtil.createAttachment( inMessage.getContent(InputStream.class), headers),
rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/ext/multipart/Attachment.java+4 −4 modified@@ -93,7 +93,7 @@ public Attachment(InputStream is, MultivaluedMap<String, String> headers) { public Attachment(String mediaType, Object object) { this(UUID.randomUUID().toString(), mediaType, object); } - + public Attachment(String id, String mediaType, Object object) { this.object = object; if (id != null) { @@ -110,8 +110,8 @@ public Attachment(String id, InputStream is, ContentDisposition cd) { headers.putSingle("Content-ID", id); headers.putSingle("Content-Type", "application/octet-stream"); } - - Attachment(MultivaluedMap<String, String> headers, DataHandler handler, Object object) { + + public Attachment(MultivaluedMap<String, String> headers, DataHandler handler, Object object) { this.headers = headers; this.handler = handler; this.object = object; @@ -128,7 +128,7 @@ public String getContentId() { } public MediaType getContentType() { - String value = handler != null && handler.getContentType() != null ? handler.getContentType() + String value = handler != null && handler.getContentType() != null ? handler.getContentType() : headers.getFirst("Content-Type"); return value == null ? MediaType.TEXT_PLAIN_TYPE : JAXRSUtils.toMediaType(value); }
systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/JAXRSMultipartTest.java+64 −0 modified@@ -39,6 +39,7 @@ import javax.imageio.ImageIO; import javax.mail.util.ByteArrayDataSource; import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.MultivaluedHashMap; import javax.ws.rs.core.MultivaluedMap; import javax.ws.rs.core.Response; import javax.xml.bind.JAXBContext; @@ -53,6 +54,7 @@ import org.apache.cxf.jaxrs.client.WebClient; import org.apache.cxf.jaxrs.ext.multipart.Attachment; import org.apache.cxf.jaxrs.ext.multipart.ContentDisposition; +import org.apache.cxf.jaxrs.ext.multipart.InputStreamDataSource; import org.apache.cxf.jaxrs.ext.multipart.MultipartBody; import org.apache.cxf.jaxrs.impl.MetadataMap; import org.apache.cxf.jaxrs.provider.json.JSONProvider; @@ -937,6 +939,67 @@ public void testMultipartRequestTooLargeManyParts() throws Exception { } } + // The large Content Disposition header will be rejected here + @Test + public void testLargeHeader() throws Exception { + InputStream is1 = + getClass().getResourceAsStream("/org/apache/cxf/systest/jaxrs/resources/java.jpg"); + String address = "http://localhost:" + PORT + "/bookstore/books/image"; + WebClient client = WebClient.create(address); + client.type("multipart/mixed").accept("multipart/mixed"); + WebClient.getConfig(client).getRequestContext().put("support.type.as.multipart", + "true"); + + StringBuilder sb = new StringBuilder(); + sb.append("form-data;"); + for (int i = 0; i < 10000; i++) { + sb.append("aaaaaaaaaa"); + } + + MultivaluedMap<String, String> headers = new MultivaluedHashMap<>(); + headers.putSingle("Content-ID", "root"); + headers.putSingle("Content-Type", "application/octet-stream"); + headers.putSingle("Content-Disposition", sb.toString()); + DataHandler handler = new DataHandler(new InputStreamDataSource(is1, "application/octet-stream")); + + Attachment att = new Attachment(headers, handler, null); + Response response = client.post(att); + assertEquals(response.getStatus(), 413); + + client.close(); + } + + // The Content Disposition header will be accepted here, even though it is larger than the default, + // as we have configured a larger value on the service side + @Test + public void testLargerThanDefaultHeader() throws Exception { + InputStream is1 = + getClass().getResourceAsStream("/org/apache/cxf/systest/jaxrs/resources/java.jpg"); + String address = "http://localhost:" + PORT + "/bookstore/books/image"; + WebClient client = WebClient.create(address); + client.type("multipart/mixed").accept("multipart/mixed"); + WebClient.getConfig(client).getRequestContext().put("support.type.as.multipart", + "true"); + + StringBuilder sb = new StringBuilder(); + sb.append("form-data;"); + for (int i = 0; i < 35; i++) { + sb.append("aaaaaaaaaa"); + } + + MultivaluedMap<String, String> headers = new MultivaluedHashMap<>(); + headers.putSingle("Content-ID", "root"); + headers.putSingle("Content-Type", "application/octet-stream"); + headers.putSingle("Content-Disposition", sb.toString()); + DataHandler handler = new DataHandler(new InputStreamDataSource(is1, "application/octet-stream")); + + Attachment att = new Attachment(headers, handler, null); + Response response = client.post(att); + assertEquals(response.getStatus(), 200); + + client.close(); + } + private void doAddBook(String address, String resourceName, int status) throws Exception { doAddBook("multipart/related", address, resourceName, status); } @@ -1018,4 +1081,5 @@ private String stripXmlInstructionIfNeeded(String str) { } return str; } + }
systests/jaxrs/src/test/java/org/apache/cxf/systest/jaxrs/MultipartServer.java+1 −0 modified@@ -40,6 +40,7 @@ protected void run() { Map<String, Object> props = new HashMap<String, Object>(); props.put(AttachmentDeserializer.ATTACHMENT_MAX_SIZE, String.valueOf(1024 * 10)); props.put(AttachmentDeserializer.ATTACHMENT_MEMORY_THRESHOLD, String.valueOf(1024 * 5)); + props.put(AttachmentDeserializer.ATTACHMENT_MAX_HEADER_SIZE, String.valueOf(400)); sf.setProperties(props); //default lifecycle is per-request, change it to singleton sf.setResourceProvider(MultipartStore.class,
Vulnerability mechanics
Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
25- cxf.apache.org/security-advisories.data/CVE-2017-12624.txt.ascnvdIssue TrackingVendor AdvisoryWEB
- www.securityfocus.com/bid/101859nvdThird Party AdvisoryVDB Entry
- www.securitytracker.com/id/1040486nvdThird Party AdvisoryVDB Entry
- github.com/advisories/GHSA-7vgj-8mw4-hg8rghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2017-12624ghsaADVISORY
- access.redhat.com/errata/RHSA-2018:2423nvdWEB
- access.redhat.com/errata/RHSA-2018:2424nvdWEB
- access.redhat.com/errata/RHSA-2018:2425nvdWEB
- access.redhat.com/errata/RHSA-2018:2428nvdWEB
- github.com/apache/cxf/commit/896bd961cbbb6b8569700e5b70229f78f94ad9dghsaWEB
- github.com/apache/cxf/commit/8bd915bfd7735c248ad660059c6b6ad26cdbcdf6ghsaWEB
- github.com/apache/cxf/commit/a2ce435cf0eedc8158d118d6d275114408d2a376ghsaWEB
- issues.apache.org/jira/browse/CXF-7507ghsaWEB
- lists.apache.org/thread.html/r36e44ffc1a9b365327df62cdfaabe85b9a5637de102cea07d79b2dbf@%3Ccommits.cxf.apache.org%3EghsaWEB
- lists.apache.org/thread.html/rc774278135816e7afc943dc9fc78eb0764f2c84a2b96470a0187315c@%3Ccommits.cxf.apache.org%3EghsaWEB
- lists.apache.org/thread.html/rd49aabd984ed540c8ff7916d4d79405f3fa311d2fdbcf9ed307839a6@%3Ccommits.cxf.apache.org%3EghsaWEB
- lists.apache.org/thread.html/rec7160382badd3ef4ad017a22f64a266c7188b9ba71394f0d321e2d4@%3Ccommits.cxf.apache.org%3EghsaWEB
- lists.apache.org/thread.html/rfb87e0bf3995e7d560afeed750fac9329ff5f1ad49da365129b7f89e@%3Ccommits.cxf.apache.org%3EghsaWEB
- lists.apache.org/thread.html/rff42cfa5e7d75b7c1af0e37589140a8f1999e578a75738740b244bd4@%3Ccommits.cxf.apache.org%3EghsaWEB
- lists.apache.org/thread.html/r36e44ffc1a9b365327df62cdfaabe85b9a5637de102cea07d79b2dbf%40%3Ccommits.cxf.apache.org%3Envd
- lists.apache.org/thread.html/rc774278135816e7afc943dc9fc78eb0764f2c84a2b96470a0187315c%40%3Ccommits.cxf.apache.org%3Envd
- lists.apache.org/thread.html/rd49aabd984ed540c8ff7916d4d79405f3fa311d2fdbcf9ed307839a6%40%3Ccommits.cxf.apache.org%3Envd
- lists.apache.org/thread.html/rec7160382badd3ef4ad017a22f64a266c7188b9ba71394f0d321e2d4%40%3Ccommits.cxf.apache.org%3Envd
- lists.apache.org/thread.html/rfb87e0bf3995e7d560afeed750fac9329ff5f1ad49da365129b7f89e%40%3Ccommits.cxf.apache.org%3Envd
- lists.apache.org/thread.html/rff42cfa5e7d75b7c1af0e37589140a8f1999e578a75738740b244bd4%40%3Ccommits.cxf.apache.org%3Envd
News mentions
0No linked articles in our index yet.