This is an automated email from the ASF dual-hosted git repository.

rcordier pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/james-project.git


The following commit(s) were added to refs/heads/master by this push:
     new b2e2070771 [FIX] Prevent sending invalid content type upon downloads
b2e2070771 is described below

commit b2e2070771310b2d27173436a5ba8ebca6a3d54c
Author: Benoit Tellier <[email protected]>
AuthorDate: Wed Mar 15 15:49:49 2023 +0700

    [FIX] Prevent sending invalid content type upon downloads
---
 .../org/apache/james/jmap/http/DownloadRoutes.java | 10 +++++++++-
 .../apache/james/jmap/routes/DownloadRoutes.scala  | 23 ++++++++++++++--------
 2 files changed, 24 insertions(+), 9 deletions(-)

diff --git 
a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/http/DownloadRoutes.java
 
b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/http/DownloadRoutes.java
index d5b09cfe4d..9c55521dd9 100644
--- 
a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/http/DownloadRoutes.java
+++ 
b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/http/DownloadRoutes.java
@@ -67,6 +67,7 @@ import com.google.common.base.CharMatcher;
 import com.google.common.collect.ImmutableList;
 
 import io.netty.buffer.Unpooled;
+import io.netty.handler.codec.http.HttpHeaderValidationUtil;
 import io.netty.handler.codec.http.HttpMethod;
 import reactor.core.publisher.Flux;
 import reactor.core.publisher.Mono;
@@ -226,7 +227,7 @@ public class DownloadRoutes implements JMAPRoutes {
     private Mono<Void> downloadBlob(Optional<String> optionalName, 
HttpServerResponse response, long blobSize, ContentType blobContentType, 
InputStream stream) {
         return addContentDispositionHeader(optionalName, response)
             .header("Content-Length", String.valueOf(blobSize))
-            .header(CONTENT_TYPE, blobContentType.asString())
+            .header(CONTENT_TYPE, 
sanitizeHeaderValue(blobContentType.asString()))
             .status(OK)
             .send(ReactorUtils.toChunks(stream, BUFFER_SIZE)
                 .map(Unpooled::wrappedBuffer)
@@ -234,6 +235,13 @@ public class DownloadRoutes implements JMAPRoutes {
             .then();
     }
 
+    public String sanitizeHeaderValue(String s) {
+        if (HttpHeaderValidationUtil.validateValidHeaderValue(s) == -1) {
+            return s;
+        }
+        return "application/octet-stream";
+    }
+
     private HttpServerResponse addContentDispositionHeader(Optional<String> 
optionalName, HttpServerResponse resp) {
         return optionalName.map(name -> 
addContentDispositionHeaderRegardingEncoding(name, resp))
             .orElse(resp);
diff --git 
a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/routes/DownloadRoutes.scala
 
b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/routes/DownloadRoutes.scala
index 63317105da..8b3446bd55 100644
--- 
a/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/routes/DownloadRoutes.scala
+++ 
b/server/protocols/jmap-rfc-8621/src/main/scala/org/apache/james/jmap/routes/DownloadRoutes.scala
@@ -18,13 +18,19 @@
  ****************************************************************/
 package org.apache.james.jmap.routes
 
+import java.io.InputStream
+import java.nio.charset.StandardCharsets
+import java.util.stream
+import java.util.stream.Stream
+
 import com.google.common.base.CharMatcher
 import eu.timepit.refined.numeric.NonNegative
 import eu.timepit.refined.refineV
 import io.netty.buffer.Unpooled
 import io.netty.handler.codec.http.HttpHeaderNames.{CONTENT_LENGTH, 
CONTENT_TYPE}
 import io.netty.handler.codec.http.HttpResponseStatus._
-import io.netty.handler.codec.http.{HttpMethod, HttpResponseStatus, 
QueryStringDecoder}
+import io.netty.handler.codec.http.{HttpHeaderValidationUtil, HttpMethod, 
HttpResponseStatus, QueryStringDecoder}
+import javax.inject.{Inject, Named}
 import org.apache.commons.io.output.UnsynchronizedByteArrayOutputStream
 import org.apache.james.jmap.HttpConstants.JSON_CONTENT_TYPE
 import org.apache.james.jmap.api.model.Size.{Size, sanitizeSize}
@@ -54,12 +60,6 @@ import reactor.core.publisher.Mono
 import reactor.core.scala.publisher.SMono
 import reactor.core.scheduler.Schedulers
 import reactor.netty.http.server.{HttpServerRequest, HttpServerResponse}
-import java.io.InputStream
-import java.nio.charset.StandardCharsets
-import java.util.stream
-import java.util.stream.Stream
-
-import javax.inject.{Inject, Named}
 
 import scala.compat.java8.FunctionConverters._
 import scala.jdk.CollectionConverters._
@@ -312,7 +312,7 @@ class DownloadRoutes 
@Inject()(@Named(InjectionKeys.RFC_8621) val authenticator:
       (stream: InputStream) => addContentDispositionHeader(optionalName)
         .compose(addContentLengthHeader(blob.size))
         .apply(response)
-        .header(CONTENT_TYPE, blobContentType.asString)
+        .header(CONTENT_TYPE, sanitizeHeaderValue(blobContentType.asString))
         .status(OK)
         .send(ReactorUtils.toChunks(stream, BUFFER_SIZE)
           .map(Unpooled.wrappedBuffer(_))
@@ -325,6 +325,13 @@ class DownloadRoutes 
@Inject()(@Named(InjectionKeys.RFC_8621) val authenticator:
     resp => optionalName.map(addContentDispositionHeaderRegardingEncoding(_, 
resp))
       .getOrElse(resp)
 
+  private def sanitizeHeaderValue(s: String): String =
+    if (HttpHeaderValidationUtil.validateValidHeaderValue(s) == -1) {
+      s
+    } else {
+      "application/octet-stream"
+    }
+
   private def addContentLengthHeader(sizeTry: Try[Size]): HttpServerResponse 
=> HttpServerResponse =
     resp => sizeTry
       .map(size => resp.header("Content-Length", size.value.toString))


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to