Skip to content

Commit 6e7f129

Browse files
committed
feat(appsec): add blocking fallback for GlassFish/Payara multipart instrumentation
TomcatServerInstrumentation is muzzled out for Payara's response type, so BlockResponseFunction is never registered. Add GlassFishBlockingHelper that commits the blocking response directly via Servlet API, extracting the HttpServletResponse from Multipart.request (private field) via reflection. The setAccessible call works because the advice is inlined into Multipart.getParts() — the same module as the field owner. Verified against system-tests APPSEC_BLOCKING with spring-boot-payara: - Test_Blocking_request_body_filenames: passes (was missing_feature) - Test_Blocking_request_body_files_content: passes (was missing_feature)
1 parent bf02571 commit 6e7f129

2 files changed

Lines changed: 91 additions & 1 deletion

File tree

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
package datadog.trace.instrumentation.tomcat7;
2+
3+
import datadog.appsec.api.blocking.BlockingContentType;
4+
import datadog.trace.api.gateway.Flow;
5+
import datadog.trace.bootstrap.blocking.BlockingActionHelper;
6+
import java.io.OutputStream;
7+
import java.util.Map;
8+
import javax.servlet.http.HttpServletRequest;
9+
import javax.servlet.http.HttpServletResponse;
10+
11+
public final class GlassFishBlockingHelper {
12+
13+
public static boolean commitBlocking(
14+
HttpServletRequest request,
15+
HttpServletResponse response,
16+
Flow.Action.RequestBlockingAction rba) {
17+
if (response == null) {
18+
return false;
19+
}
20+
try {
21+
if (response.isCommitted()) {
22+
return false;
23+
}
24+
response.reset();
25+
response.setStatus(BlockingActionHelper.getHttpCode(rba.getStatusCode()));
26+
for (Map.Entry<String, String> e : rba.getExtraHeaders().entrySet()) {
27+
response.setHeader(e.getKey(), e.getValue());
28+
}
29+
if (rba.getBlockingContentType() != BlockingContentType.NONE) {
30+
String accept = request != null ? request.getHeader("Accept") : null;
31+
BlockingActionHelper.TemplateType type =
32+
BlockingActionHelper.determineTemplateType(rba.getBlockingContentType(), accept);
33+
byte[] body = BlockingActionHelper.getTemplate(type, rba.getSecurityResponseId());
34+
if (body != null) {
35+
response.setHeader("Content-Type", BlockingActionHelper.getContentType(type));
36+
response.setHeader("Content-Length", Integer.toString(body.length));
37+
OutputStream os = response.getOutputStream();
38+
os.write(body);
39+
os.close();
40+
}
41+
}
42+
response.flushBuffer();
43+
return true;
44+
} catch (Exception e) {
45+
return false;
46+
}
47+
}
48+
}

dd-java-agent/instrumentation/tomcat/tomcat-appsec/tomcat-appsec-7.0/src/main/java/datadog/trace/instrumentation/tomcat7/GlassFishMultipartInstrumentation.java

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,14 @@
1919
import datadog.trace.bootstrap.instrumentation.api.AgentSpan;
2020
import datadog.trace.bootstrap.instrumentation.api.AgentTracer;
2121
import java.io.InputStream;
22+
import java.lang.reflect.Field;
23+
import java.lang.reflect.Method;
2224
import java.util.ArrayList;
2325
import java.util.Collection;
2426
import java.util.List;
2527
import java.util.function.BiFunction;
28+
import javax.servlet.http.HttpServletRequest;
29+
import javax.servlet.http.HttpServletResponse;
2630
import javax.servlet.http.Part;
2731
import net.bytebuddy.asm.Advice;
2832

@@ -57,6 +61,13 @@ public String instrumentedType() {
5761
return "org.apache.catalina.fileupload.Multipart";
5862
}
5963

64+
@Override
65+
public String[] helperClassNames() {
66+
return new String[] {
67+
"datadog.trace.instrumentation.tomcat7.GlassFishBlockingHelper",
68+
};
69+
}
70+
6071
@Override
6172
public void methodAdvice(MethodTransformer transformer) {
6273
transformer.applyAdvice(
@@ -68,7 +79,9 @@ public static class GetPartsAdvice {
6879

6980
@Advice.OnMethodExit(suppress = Throwable.class, onThrowable = Throwable.class)
7081
static void after(
71-
@Advice.Return Collection<?> parts, @Advice.Thrown(readOnly = false) Throwable t) {
82+
@Advice.This Object thisMultipart,
83+
@Advice.Return Collection<?> parts,
84+
@Advice.Thrown(readOnly = false) Throwable t) {
7285
if (t != null || parts == null || parts.isEmpty()) {
7386
return;
7487
}
@@ -91,6 +104,29 @@ static void after(
91104
return;
92105
}
93106

107+
// Extract servlet request/response for fallback blocking when no BlockResponseFunction is
108+
// registered (Payara: TomcatServerInstrumentation is muzzled out for Payara's response type).
109+
// setAccessible works here because this code is inlined into Multipart.getParts() —
110+
// the same module as the private field's owner class.
111+
HttpServletRequest fallbackReq = null;
112+
HttpServletResponse fallbackResp = null;
113+
try {
114+
Field f = thisMultipart.getClass().getDeclaredField("request");
115+
f.setAccessible(true);
116+
Object catReq = f.get(thisMultipart);
117+
if (catReq instanceof HttpServletRequest) {
118+
fallbackReq = (HttpServletRequest) catReq;
119+
}
120+
if (catReq != null) {
121+
Method m = catReq.getClass().getMethod("getResponse");
122+
Object catResp = m.invoke(catReq);
123+
if (catResp instanceof HttpServletResponse) {
124+
fallbackResp = (HttpServletResponse) catResp;
125+
}
126+
}
127+
} catch (Exception ignored) {
128+
}
129+
94130
int maxFiles = Config.get().getAppSecMaxFileContentCount();
95131
int maxBytes = Config.get().getAppSecMaxFileContentBytes();
96132

@@ -142,6 +178,9 @@ static void after(
142178
brf.tryCommitBlockingResponse(reqCtx.getTraceSegment(), rba);
143179
t = new BlockingException("Blocked request (GlassFish multipart file upload)");
144180
reqCtx.getTraceSegment().effectivelyBlocked();
181+
} else if (GlassFishBlockingHelper.commitBlocking(fallbackReq, fallbackResp, rba)) {
182+
t = new BlockingException("Blocked request (GlassFish multipart file upload)");
183+
reqCtx.getTraceSegment().effectivelyBlocked();
145184
}
146185
}
147186
}
@@ -156,6 +195,9 @@ static void after(
156195
brf.tryCommitBlockingResponse(reqCtx.getTraceSegment(), rba);
157196
t = new BlockingException("Blocked request (GlassFish multipart file upload content)");
158197
reqCtx.getTraceSegment().effectivelyBlocked();
198+
} else if (GlassFishBlockingHelper.commitBlocking(fallbackReq, fallbackResp, rba)) {
199+
t = new BlockingException("Blocked request (GlassFish multipart file upload content)");
200+
reqCtx.getTraceSegment().effectivelyBlocked();
159201
}
160202
}
161203
}

0 commit comments

Comments
 (0)