Skip to content
Draft
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 @@ -33,8 +33,15 @@ public static RuntimeException collectBodyData(
List<String> filesContent) {
RuntimeException exc = null;
for (InterfaceHttpData data : parts) {
if (attributes != null
&& data.getHttpDataType() == InterfaceHttpData.HttpDataType.Attribute) {
InterfaceHttpData.HttpDataType dataType;
try {
dataType = data.getHttpDataType();
} catch (UnsupportedOperationException ignored) {
// Some framework-specific implementations (e.g. Vert.x NettyFileUpload) do not support
// getHttpDataType() — skip them; their framework's own instrumentation handles them.
continue;
}
if (attributes != null && dataType == InterfaceHttpData.HttpDataType.Attribute) {
String name = data.getName();
List<String> values = attributes.get(name);
if (values == null) {
Expand All @@ -45,7 +52,7 @@ public static RuntimeException collectBodyData(
} catch (IOException e) {
exc = new UndeclaredThrowableException(e);
}
} else if (data.getHttpDataType() == InterfaceHttpData.HttpDataType.FileUpload) {
} else if (dataType == InterfaceHttpData.HttpDataType.FileUpload) {
FileUpload fileUpload = (FileUpload) data;
String filename = fileUpload.getFilename();
if (filenames != null && filename != null && !filename.isEmpty()) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package datadog.trace.instrumentation.vertx_3_4.server;

import static datadog.trace.api.gateway.Events.EVENTS;

import datadog.appsec.api.blocking.BlockingException;
import datadog.trace.advice.ActiveRequestContext;
import datadog.trace.advice.RequiresRequestContext;
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.bootstrap.CallDepthThreadLocalMap;
import datadog.trace.bootstrap.instrumentation.api.AgentTracer;
import io.vertx.ext.web.FileUpload;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.function.BiFunction;
import net.bytebuddy.asm.Advice;

@RequiresRequestContext(RequestContextSlot.APPSEC)
class RoutingContextFilenamesAdvice {

@Advice.OnMethodEnter(suppress = Throwable.class)
static int before() {
return CallDepthThreadLocalMap.incrementCallDepth(FileUpload.class);
}

@Advice.OnMethodExit(suppress = Throwable.class, onThrowable = Throwable.class)
static void after(
@Advice.Enter int depth,
@Advice.Return Set<FileUpload> uploads,
@ActiveRequestContext RequestContext reqCtx,
@Advice.Thrown(readOnly = false) Throwable throwable) {
CallDepthThreadLocalMap.decrementCallDepth(FileUpload.class);
if (depth != 0 || throwable != null || uploads == null || uploads.isEmpty()) {
return;
}

List<String> filenames = new ArrayList<>(uploads.size());
for (FileUpload upload : uploads) {
String name = upload.fileName();
if (name != null && !name.isEmpty()) {
filenames.add(name);
}
}
if (filenames.isEmpty()) {
return;
}

CallbackProvider cbp = AgentTracer.get().getCallbackProvider(RequestContextSlot.APPSEC);
BiFunction<RequestContext, List<String>, Flow<Void>> cb =
cbp.getCallback(EVENTS.requestFilesFilenames());
if (cb == null) {
return;
}

Flow<Void> flow = cb.apply(reqCtx, filenames);
Flow.Action action = flow.getAction();
if (action instanceof Flow.Action.RequestBlockingAction) {
BlockResponseFunction brf = reqCtx.getBlockResponseFunction();
if (brf != null) {
brf.tryCommitBlockingResponse(
reqCtx.getTraceSegment(), (Flow.Action.RequestBlockingAction) action);
if (throwable == null) {
throwable = new BlockingException("Blocked request (multipart file upload)");
}
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,11 @@
public class RoutingContextImplInstrumentation extends InstrumenterModule.AppSec
implements Instrumenter.ForSingleType, Instrumenter.HasMethodAdvice {

private static final Reference FILE_UPLOAD_REF =
new Reference.Builder("io.vertx.ext.web.FileUpload")
.withMethod(new String[0], 0, "fileName", "Ljava/lang/String;")
.build();

public RoutingContextImplInstrumentation() {
super("vertx", "vertx-3.4");
}
Expand All @@ -26,7 +31,7 @@ public String instrumentedType() {

@Override
public Reference[] additionalMuzzleReferences() {
return new Reference[] {PARSABLE_HEADER_VALUE, VIRTUAL_HOST_HANDLER};
return new Reference[] {PARSABLE_HEADER_VALUE, VIRTUAL_HOST_HANDLER, FILE_UPLOAD_REF};
}

@Override
Expand All @@ -37,5 +42,8 @@ public void methodAdvice(MethodTransformer transformer) {
transformer.applyAdvice(
named("setSession").and(takesArgument(0, named("io.vertx.ext.web.Session"))),
packageName + ".RoutingContextSessionAdvice");
transformer.applyAdvice(
named("fileUploads").and(takesArguments(0)),
packageName + ".RoutingContextFilenamesAdvice");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,11 @@ class VertxHttpServerForkedTest extends HttpServerTest<Vertx> {
true
}

@Override
boolean testBodyFilenames() {
true
}

@Override
boolean testBodyJson() {
true
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ public void start(final Future<Void> startFuture) {
String res = convertFormAttributes(ctx);
ctx.response().setStatusCode(BODY_URLENCODED.getStatus()).end(res);
}));
router.route(BODY_MULTIPART.getPath()).handler(BodyHandler.create());
router
.route(BODY_MULTIPART.getPath())
.handler(
Expand All @@ -100,13 +101,9 @@ public void start(final Future<Void> startFuture) {
ctx,
BODY_MULTIPART,
() -> {
ctx.request().setExpectMultipart(true);
ctx.request()
.endHandler(
(_void) -> {
String res = convertFormAttributes(ctx);
ctx.response().setStatusCode(BODY_MULTIPART.getStatus()).end(res);
});
ctx.fileUploads();
String res = convertFormAttributes(ctx);
ctx.response().setStatusCode(BODY_MULTIPART.getStatus()).end(res);
}));
router.route(BODY_JSON.getPath()).handler(BodyHandler.create());
router
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,11 @@ class VertxHttpServerForkedTest extends HttpServerTest<Vertx> {
true
}

@Override
boolean testBodyFilenames() {
true
}

@Override
boolean testBodyJson() {
true
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ public void start(final Promise<Void> startPromise) {
String res = convertFormAttributes(ctx);
ctx.response().setStatusCode(BODY_URLENCODED.getStatus()).end(res);
}));
router.route(BODY_MULTIPART.getPath()).handler(BodyHandler.create());
router
.route(BODY_MULTIPART.getPath())
.handler(
Expand All @@ -100,13 +101,9 @@ public void start(final Promise<Void> startPromise) {
ctx,
BODY_MULTIPART,
() -> {
ctx.request().setExpectMultipart(true);
ctx.request()
.endHandler(
(_void) -> {
String res = convertFormAttributes(ctx);
ctx.response().setStatusCode(BODY_MULTIPART.getStatus()).end(res);
});
ctx.fileUploads();
String res = convertFormAttributes(ctx);
ctx.response().setStatusCode(BODY_MULTIPART.getStatus()).end(res);
}));
router.route(BODY_JSON.getPath()).handler(BodyHandler.create());
router
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package datadog.trace.instrumentation.vertx_4_0.server;

import static datadog.trace.api.gateway.Events.EVENTS;

import datadog.appsec.api.blocking.BlockingException;
import datadog.trace.advice.ActiveRequestContext;
import datadog.trace.advice.RequiresRequestContext;
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.bootstrap.CallDepthThreadLocalMap;
import datadog.trace.bootstrap.instrumentation.api.AgentTracer;
import io.vertx.ext.web.FileUpload;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.function.BiFunction;
import net.bytebuddy.asm.Advice;

@RequiresRequestContext(RequestContextSlot.APPSEC)
class RoutingContextFilenamesAdvice {

@Advice.OnMethodEnter(suppress = Throwable.class)
static int before() {
return CallDepthThreadLocalMap.incrementCallDepth(FileUpload.class);
}

@Advice.OnMethodExit(suppress = Throwable.class, onThrowable = Throwable.class)
static void after(
@Advice.Enter int depth,
@Advice.Return Collection<FileUpload> uploads,
@ActiveRequestContext RequestContext reqCtx,
@Advice.Thrown(readOnly = false) Throwable throwable) {
CallDepthThreadLocalMap.decrementCallDepth(FileUpload.class);
if (depth != 0 || throwable != null || uploads == null || uploads.isEmpty()) {
return;
}

List<String> filenames = new ArrayList<>(uploads.size());
for (FileUpload upload : uploads) {
String name = upload.fileName();
if (name != null && !name.isEmpty()) {
filenames.add(name);
}
}
if (filenames.isEmpty()) {
return;
}

CallbackProvider cbp = AgentTracer.get().getCallbackProvider(RequestContextSlot.APPSEC);
BiFunction<RequestContext, List<String>, Flow<Void>> cb =
cbp.getCallback(EVENTS.requestFilesFilenames());
if (cb == null) {
return;
}

Flow<Void> flow = cb.apply(reqCtx, filenames);
Flow.Action action = flow.getAction();
if (action instanceof Flow.Action.RequestBlockingAction) {
BlockResponseFunction brf = reqCtx.getBlockResponseFunction();
if (brf != null) {
brf.tryCommitBlockingResponse(
reqCtx.getTraceSegment(), (Flow.Action.RequestBlockingAction) action);
if (throwable == null) {
throwable = new BlockingException("Blocked request (multipart file upload)");
}
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,18 @@
public class RoutingContextImplInstrumentation extends InstrumenterModule.AppSec
implements Instrumenter.ForSingleType, Instrumenter.HasMethodAdvice {

private static final Reference FILE_UPLOAD_REF =
new Reference.Builder("io.vertx.ext.web.FileUpload")
.withMethod(new String[0], 0, "fileName", "Ljava/lang/String;")
.build();

public RoutingContextImplInstrumentation() {
super("vertx", "vertx-4.0");
}

@Override
public Reference[] additionalMuzzleReferences() {
return new Reference[] {VertxVersionMatcher.HTTP_1X_SERVER_RESPONSE};
return new Reference[] {VertxVersionMatcher.HTTP_1X_SERVER_RESPONSE, FILE_UPLOAD_REF};
}

@Override
Expand All @@ -43,5 +48,8 @@ public void methodAdvice(MethodTransformer transformer) {
transformer.applyAdvice(
named("setSession").and(takesArgument(0, named("io.vertx.ext.web.Session"))),
packageName + ".RoutingContextSessionAdvice");
transformer.applyAdvice(
named("fileUploads").and(takesArguments(0)),
packageName + ".RoutingContextFilenamesAdvice");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,11 @@ class VertxHttpServerForkedTest extends HttpServerTest<Vertx> {
true
}

@Override
boolean testBodyFilenames() {
true
}

@Override
boolean testBodyJson() {
true
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ public void start(final Promise<Void> startPromise) {
String res = convertFormAttributes(ctx);
ctx.response().setStatusCode(BODY_URLENCODED.getStatus()).end(res);
}));
router.route(BODY_MULTIPART.getPath()).handler(BodyHandler.create());
router
.route(BODY_MULTIPART.getPath())
.handler(
Expand All @@ -109,13 +110,9 @@ public void start(final Promise<Void> startPromise) {
ctx,
BODY_MULTIPART,
() -> {
ctx.request().setExpectMultipart(true);
ctx.request()
.endHandler(
(_void) -> {
String res = convertFormAttributes(ctx);
ctx.response().setStatusCode(BODY_MULTIPART.getStatus()).end(res);
});
ctx.fileUploads();
String res = convertFormAttributes(ctx);
ctx.response().setStatusCode(BODY_MULTIPART.getStatus()).end(res);
}));
router.route(BODY_JSON.getPath()).handler(BodyHandler.create());
router
Expand Down
Loading
Loading