diff --git a/dd-java-agent/instrumentation/akka/akka-http/akka-http-10.0/src/baseTest/groovy/AkkaHttpServerInstrumentationTest.groovy b/dd-java-agent/instrumentation/akka/akka-http/akka-http-10.0/src/baseTest/groovy/AkkaHttpServerInstrumentationTest.groovy index 65ed26909e7..a1aa9510a93 100644 --- a/dd-java-agent/instrumentation/akka/akka-http/akka-http-10.0/src/baseTest/groovy/AkkaHttpServerInstrumentationTest.groovy +++ b/dd-java-agent/instrumentation/akka/akka-http/akka-http-10.0/src/baseTest/groovy/AkkaHttpServerInstrumentationTest.groovy @@ -79,6 +79,11 @@ abstract class AkkaHttpServerInstrumentationTest extends HttpServerTest, Flow> filenamesCallback = cbp.getCallback(EVENTS.requestFilesFilenames()); - if (bodyCallback == null && filenamesCallback == null) { + BiFunction, Flow> contentCallback = + cbp.getCallback(EVENTS.requestFilesContent()); + if (bodyCallback == null && filenamesCallback == null && contentCallback == null) { return; } @@ -204,13 +210,19 @@ private static void handleMultipartStrictFormData( st.getStrictParts(); Map> conv = new HashMap<>(); List filenames = filenamesCallback != null ? new ArrayList<>() : null; + List filesContent = contentCallback != null ? new ArrayList<>() : null; for (akka.http.javadsl.model.Multipart.FormData.BodyPart.Strict part : strictParts) { Optional filenameOpt = part.getFilename(); if (filenames != null && filenameOpt.isPresent() && !filenameOpt.get().isEmpty()) { filenames.add(filenameOpt.get()); } - if (bodyCallback == null) { + boolean needsEntity = + bodyCallback != null + || (filesContent != null + && filenameOpt.isPresent() + && filesContent.size() < MAX_FILES_TO_INSPECT); + if (!needsEntity) { continue; } @@ -221,19 +233,30 @@ private static void handleMultipartStrictFormData( HttpEntity.Strict sentity = (HttpEntity.Strict) entity; - String name = part.getName(); - List curStrings = conv.get(name); - if (curStrings == null) { - curStrings = new ArrayList<>(); - conv.put(name, curStrings); + if (bodyCallback != null) { + String name = part.getName(); + List curStrings = conv.get(name); + if (curStrings == null) { + curStrings = new ArrayList<>(); + conv.put(name, curStrings); + } + + String s = + sentity + .getData() + .decodeString( + Unmarshaller$.MODULE$.bestUnmarshallingCharsetFor(sentity).nioCharset()); + curStrings.add(s); } - String s = - sentity - .getData() - .decodeString( - Unmarshaller$.MODULE$.bestUnmarshallingCharsetFor(sentity).nioCharset()); - curStrings.add(s); + if (filesContent != null + && filenameOpt.isPresent() + && filesContent.size() < MAX_FILES_TO_INSPECT) { + byte[] bytes = sentity.getData().take(MAX_CONTENT_BYTES).toArray(); + filesContent.add( + MultipartContentDecoder.decodeBytes( + bytes, bytes.length, entity.getContentType().toString())); + } } BlockingException pendingBlock = null; @@ -260,6 +283,18 @@ private static void handleMultipartStrictFormData( } } + if (pendingBlock == null && filesContent != null && !filesContent.isEmpty()) { + Flow flow = contentCallback.apply(reqCtx, filesContent); + Flow.Action action = flow.getAction(); + if (action instanceof Flow.Action.RequestBlockingAction) { + pendingBlock = + tryBlock( + reqCtx, + (Flow.Action.RequestBlockingAction) action, + "multipart file upload content"); + } + } + if (pendingBlock != null) { throw pendingBlock; } @@ -417,12 +452,20 @@ public static Unmarshaller transformStrictFormUnmarshall private static void handleStrictFormData(StrictForm sf) { CallbackProvider cbp = AgentTracer.get().getCallbackProvider(RequestContextSlot.APPSEC); + BiFunction> bodyCb = + cbp.getCallback(EVENTS.requestBodyProcessed()); BiFunction, Flow> filenamesCb = cbp.getCallback(EVENTS.requestFilesFilenames()); + BiFunction, Flow> contentCb = + cbp.getCallback(EVENTS.requestFilesContent()); + if (bodyCb == null && filenamesCb == null && contentCb == null) { + return; + } Iterator> iterator = sf.fields().iterator(); Map> conv = new HashMap<>(); List filenames = filenamesCb != null ? new ArrayList<>() : null; + List filesContent = contentCb != null ? new ArrayList<>() : null; while (iterator.hasNext()) { Tuple2 next = iterator.next(); String fieldName = next._1(); @@ -449,47 +492,71 @@ private static void handleStrictFormData(StrictForm sf) { instanceof akka.http.scaladsl.model.Multipart$FormData$BodyPart$Strict) { akka.http.scaladsl.model.Multipart$FormData$BodyPart$Strict bodyPart = (akka.http.scaladsl.model.Multipart$FormData$BodyPart$Strict) strictFieldValue; - if (filenames != null) { - Optional filenameOpt = bodyPart.getFilename(); - if (filenameOpt.isPresent() && !filenameOpt.get().isEmpty()) { - filenames.add(filenameOpt.get()); - } + Optional filenameOpt = bodyPart.getFilename(); + if (filenames != null && filenameOpt.isPresent() && !filenameOpt.get().isEmpty()) { + filenames.add(filenameOpt.get()); } HttpEntity.Strict sentity = bodyPart.entity(); - String s = - sentity - .getData() - .decodeString( - Unmarshaller$.MODULE$.bestUnmarshallingCharsetFor(sentity).nioCharset()); - strings.add(s); + if (filesContent != null + && filenameOpt.isPresent() + && filesContent.size() < MAX_FILES_TO_INSPECT) { + byte[] bytes = sentity.getData().take(MAX_CONTENT_BYTES).toArray(); + filesContent.add( + MultipartContentDecoder.decodeBytes( + bytes, bytes.length, sentity.contentType().toString())); + } + if (bodyCb != null) { + String s = + sentity + .getData() + .decodeString( + Unmarshaller$.MODULE$.bestUnmarshallingCharsetFor(sentity).nioCharset()); + strings.add(s); + } } } BlockingException pendingBlock = null; - try { - handleArbitraryPostData(conv, "HttpEntity -> StrictForm unmarshaller"); - } catch (BlockingException e) { - pendingBlock = e; - } - - if (filenamesCb != null && filenames != null && !filenames.isEmpty()) { - AgentSpan span = activeSpan(); - RequestContext reqCtx; - if (span != null - && (reqCtx = span.getRequestContext()) != null - && reqCtx.getData(RequestContextSlot.APPSEC) != null) { - Flow flow = filenamesCb.apply(reqCtx, filenames); - if (pendingBlock == null) { - Flow.Action action = flow.getAction(); - if (action instanceof Flow.Action.RequestBlockingAction) { - pendingBlock = - tryBlock( - reqCtx, (Flow.Action.RequestBlockingAction) action, "multipart file upload"); - } + if (bodyCb != null) { + try { + handleArbitraryPostData(conv, "HttpEntity -> StrictForm unmarshaller"); + } catch (BlockingException e) { + pendingBlock = e; + } + } + + AgentSpan span = activeSpan(); + RequestContext reqCtx = null; + if (span != null) { + RequestContext ctx = span.getRequestContext(); + if (ctx != null && ctx.getData(RequestContextSlot.APPSEC) != null) { + reqCtx = ctx; + } + } + + if (reqCtx != null && filenamesCb != null && filenames != null && !filenames.isEmpty()) { + Flow flow = filenamesCb.apply(reqCtx, filenames); + if (pendingBlock == null) { + Flow.Action action = flow.getAction(); + if (action instanceof Flow.Action.RequestBlockingAction) { + pendingBlock = + tryBlock(reqCtx, (Flow.Action.RequestBlockingAction) action, "multipart file upload"); } } } + if (pendingBlock == null && reqCtx != null && contentCb != null && !filesContent.isEmpty()) { + Flow flow = contentCb.apply(reqCtx, filesContent); + Flow.Action action = flow.getAction(); + if (action instanceof Flow.Action.RequestBlockingAction) { + pendingBlock = + tryBlock( + reqCtx, + (Flow.Action.RequestBlockingAction) action, + "multipart file upload content"); + } + } + if (pendingBlock != null) { throw pendingBlock; } diff --git a/dd-java-agent/instrumentation/akka/akka-http/akka-http-10.6/src/test/groovy/AkkaHttp102ServerInstrumentationTests.groovy b/dd-java-agent/instrumentation/akka/akka-http/akka-http-10.6/src/test/groovy/AkkaHttp102ServerInstrumentationTests.groovy index 6e517914b17..711bf874fa5 100644 --- a/dd-java-agent/instrumentation/akka/akka-http/akka-http-10.6/src/test/groovy/AkkaHttp102ServerInstrumentationTests.groovy +++ b/dd-java-agent/instrumentation/akka/akka-http/akka-http-10.6/src/test/groovy/AkkaHttp102ServerInstrumentationTests.groovy @@ -41,6 +41,11 @@ class AkkaHttp102ServerInstrumentationBindSyncTest extends AkkaHttpServerInstrum false } + @Override + boolean testBodyFilesContent() { + false + } + @Override boolean testBodyJson() { false diff --git a/dd-java-agent/instrumentation/akka/akka-http/akka-http-10.6/src/test/groovy/AkkaHttpServerInstrumentationTest.groovy b/dd-java-agent/instrumentation/akka/akka-http/akka-http-10.6/src/test/groovy/AkkaHttpServerInstrumentationTest.groovy index 1fe0b3a3be0..9a4815ed382 100644 --- a/dd-java-agent/instrumentation/akka/akka-http/akka-http-10.6/src/test/groovy/AkkaHttpServerInstrumentationTest.groovy +++ b/dd-java-agent/instrumentation/akka/akka-http/akka-http-10.6/src/test/groovy/AkkaHttpServerInstrumentationTest.groovy @@ -80,6 +80,11 @@ abstract class AkkaHttpServerInstrumentationTest extends HttpServerTest