Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,11 @@ abstract class AkkaHttpServerInstrumentationTest extends HttpServerTest<AkkaHttp
true
}

@Override
boolean testBodyFilesContent() {
true
}

@Override
boolean testBodyJson() {
true
Expand Down Expand Up @@ -233,6 +238,11 @@ abstract class AkkaHttpServerInstrumentationSyncTest extends AkkaHttpServerInstr
false
}

@Override
boolean testBodyFilesContent() {
false
}

@Override
boolean testBodyJson() {
false
Expand Down Expand Up @@ -298,6 +308,11 @@ class AkkaHttpServerInstrumentationBindAndHandleTest extends AkkaHttpServerInstr
akkaHttpVersion != '10.0.10'
}

@Override
boolean testBodyFilesContent() {
akkaHttpVersion != '10.0.10'
}

@Override
boolean testBodyUrlencoded() {
akkaHttpVersion != '10.0.10'
Expand Down Expand Up @@ -329,6 +344,11 @@ class AkkaHttpServerInstrumentationBindAndHandleAsyncWithRouteAsyncHandlerTest e
akkaHttpVersion != '10.0.10'
}

@Override
boolean testBodyFilesContent() {
akkaHttpVersion != '10.0.10'
}

@Override
boolean testBodyUrlencoded() {
akkaHttpVersion != '10.0.10'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,11 @@ class AkkaHttp102ServerInstrumentationBindSyncTest extends AkkaHttpServerInstrum
false
}

@Override
boolean testBodyFilesContent() {
false
}

@Override
boolean testBodyJson() {
false
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,13 @@
import akka.japi.JavaPartialFunction;
import akka.stream.Materializer;
import datadog.appsec.api.blocking.BlockingException;
import datadog.trace.api.Config;
import datadog.trace.api.gateway.BlockResponseFunction;
import datadog.trace.api.gateway.CallbackProvider;
import datadog.trace.api.gateway.Flow;
import datadog.trace.api.gateway.RequestContext;
import datadog.trace.api.gateway.RequestContextSlot;
import datadog.trace.api.http.MultipartContentDecoder;
import datadog.trace.bootstrap.instrumentation.api.AgentSpan;
import datadog.trace.bootstrap.instrumentation.api.AgentTracer;
import java.lang.reflect.Field;
Expand All @@ -44,6 +46,8 @@
public class UnmarshallerHelpers {

public static final int MAX_CONVERSION_DEPTH = 10;
static final int MAX_CONTENT_BYTES = Config.get().getAppSecMaxFileContentBytes();
static final int MAX_FILES_TO_INSPECT = Config.get().getAppSecMaxFileContentCount();
private static final Logger log = LoggerFactory.getLogger(UnmarshallerHelpers.class);

private static final MediaType APPLICATION_X_WWW_FORM_URLENCODED;
Expand Down Expand Up @@ -196,21 +200,29 @@ private static void handleMultipartStrictFormData(
cbp.getCallback(EVENTS.requestBodyProcessed());
BiFunction<RequestContext, List<String>, Flow<Void>> filenamesCallback =
cbp.getCallback(EVENTS.requestFilesFilenames());
if (bodyCallback == null && filenamesCallback == null) {
BiFunction<RequestContext, List<String>, Flow<Void>> contentCallback =
cbp.getCallback(EVENTS.requestFilesContent());
if (bodyCallback == null && filenamesCallback == null && contentCallback == null) {
return;
}

java.lang.Iterable<akka.http.javadsl.model.Multipart.FormData.BodyPart.Strict> strictParts =
st.getStrictParts();
Map<String, List<String>> conv = new HashMap<>();
List<String> filenames = filenamesCallback != null ? new ArrayList<>() : null;
List<String> filesContent = contentCallback != null ? new ArrayList<>() : null;
for (akka.http.javadsl.model.Multipart.FormData.BodyPart.Strict part : strictParts) {
Optional<String> 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;
}

Expand All @@ -221,19 +233,30 @@ private static void handleMultipartStrictFormData(

HttpEntity.Strict sentity = (HttpEntity.Strict) entity;

String name = part.getName();
List<String> curStrings = conv.get(name);
if (curStrings == null) {
curStrings = new ArrayList<>();
conv.put(name, curStrings);
if (bodyCallback != null) {
String name = part.getName();
List<String> 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;
Expand All @@ -260,6 +283,18 @@ private static void handleMultipartStrictFormData(
}
}

if (pendingBlock == null && filesContent != null && !filesContent.isEmpty()) {
Flow<Void> 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;
}
Expand Down Expand Up @@ -417,12 +452,20 @@ public static Unmarshaller<HttpEntity, StrictForm> transformStrictFormUnmarshall

private static void handleStrictFormData(StrictForm sf) {
CallbackProvider cbp = AgentTracer.get().getCallbackProvider(RequestContextSlot.APPSEC);
BiFunction<RequestContext, Object, Flow<Void>> bodyCb =
cbp.getCallback(EVENTS.requestBodyProcessed());
BiFunction<RequestContext, List<String>, Flow<Void>> filenamesCb =
cbp.getCallback(EVENTS.requestFilesFilenames());
BiFunction<RequestContext, List<String>, Flow<Void>> contentCb =
cbp.getCallback(EVENTS.requestFilesContent());
if (bodyCb == null && filenamesCb == null && contentCb == null) {
return;
}

Iterator<Tuple2<String, StrictForm.Field>> iterator = sf.fields().iterator();
Map<String, List<String>> conv = new HashMap<>();
List<String> filenames = filenamesCb != null ? new ArrayList<>() : null;
List<String> filesContent = contentCb != null ? new ArrayList<>() : null;
while (iterator.hasNext()) {
Tuple2<String, StrictForm.Field> next = iterator.next();
String fieldName = next._1();
Expand All @@ -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<String> filenameOpt = bodyPart.getFilename();
if (filenameOpt.isPresent() && !filenameOpt.get().isEmpty()) {
filenames.add(filenameOpt.get());
}
Optional<String> 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();
Comment thread
jandro996 marked this conversation as resolved.
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<Void> 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<Void> 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<Void> 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;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,11 @@ class AkkaHttp102ServerInstrumentationBindSyncTest extends AkkaHttpServerInstrum
false
}

@Override
boolean testBodyFilesContent() {
false
}

@Override
boolean testBodyJson() {
false
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,11 @@ abstract class AkkaHttpServerInstrumentationTest extends HttpServerTest<AkkaHttp
true
}

@Override
boolean testBodyFilesContent() {
true
}

@Override
boolean testBodyJson() {
true
Expand Down Expand Up @@ -233,6 +238,11 @@ abstract class AkkaHttpServerInstrumentationSyncTest extends AkkaHttpServerInstr
false
}

@Override
boolean testBodyFilesContent() {
false
}

@Override
boolean testBodyJson() {
false
Expand Down Expand Up @@ -294,6 +304,11 @@ class AkkaHttpServerInstrumentationBindAndHandleTest extends AkkaHttpServerInstr
true
}

@Override
boolean testBodyFilesContent() {
true
}

@Override
boolean testBodyUrlencoded() {
true
Expand Down Expand Up @@ -325,6 +340,11 @@ class AkkaHttpServerInstrumentationBindAndHandleAsyncWithRouteAsyncHandlerTest e
true
}

@Override
boolean testBodyFilesContent() {
true
}

@Override
boolean testBodyUrlencoded() {
true
Expand Down
Loading