Skip to content

Commit 8ee423b

Browse files
authored
Merge pull request #475 from lostsnow/fix/request-response-body-fetch
Fix/request response body fetch
2 parents 7635c91 + d9cd7bc commit 8ee423b

File tree

15 files changed

+283
-27
lines changed

15 files changed

+283
-27
lines changed

dongtai-common/src/main/java/io/dongtai/iast/common/scope/Scope.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ public enum Scope {
44
HTTP_REQUEST(1),
55
HTTP_ENTRY(2),
66
SERVLET_INPUT_STREAM_READ(3),
7-
SERVLET_OUTPUT_STREAM_WRITE(4),
7+
SERVLET_OUTPUT_WRITE(4),
88
;
99

1010
private final int id;

dongtai-common/src/main/java/io/dongtai/iast/common/scope/ScopeTracker.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ public GeneralScope getScope(Scope scope) {
1414
return this.get().getHttpEntryScope();
1515
case SERVLET_INPUT_STREAM_READ:
1616
return this.get().getServletInputStreamReadScope();
17-
case SERVLET_OUTPUT_STREAM_WRITE:
17+
case SERVLET_OUTPUT_WRITE:
1818
return this.get().getServletOutputStreamWriteScope();
1919
default:
2020
return null;

dongtai-core/src/main/java/io/dongtai/iast/core/bytecode/enhance/asm/AsmMethods.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,18 @@ static Method getAsmMethod(final Class<?> clazz,
120120
int.class
121121
);
122122

123+
Method SPY$onPrintWriterWrite = InnerHelper.getAsmMethod(
124+
SpyDispatcher.class,
125+
"onPrintWriterWrite",
126+
String.class,
127+
Object.class,
128+
int.class,
129+
String.class,
130+
char[].class,
131+
int.class,
132+
int.class
133+
);
134+
123135
Method SPY$enterSource = InnerHelper.getAsmMethod(
124136
SpyDispatcher.class,
125137
"enterSource"

dongtai-core/src/main/java/io/dongtai/iast/core/bytecode/enhance/plugin/PluginRegister.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,9 @@ public ClassVisitor initial(ClassVisitor classVisitor, ClassContext context, Pol
5050
if (pluginVisitor != classVisitor) {
5151
classVisitor = pluginVisitor;
5252
// TODO: need transform multiple times?
53-
break;
53+
if (!context.getClassName().equals(DispatchJ2ee.APACHE_COYOTE_WRITER)) {
54+
break;
55+
}
5456
}
5557
}
5658
return classVisitor;

dongtai-core/src/main/java/io/dongtai/iast/core/bytecode/enhance/plugin/framework/j2ee/dispatch/DispatchJ2ee.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ public class DispatchJ2ee implements DispatchPlugin {
2222
public static final String JAKARTA_SERVLET_INPUT_STREAM = " jakarta.servlet.ServletInputStream".substring(1);
2323
public static final String JAVAX_SERVLET_OUTPUT_STREAM = " javax.servlet.ServletOutputStream".substring(1);
2424
public static final String JAKARTA_SERVLET_OUTPUT_STREAM = " jakarta.servlet.ServletOutputStream".substring(1);
25-
25+
public static final String APACHE_COYOTE_WRITER = " org.apache.catalina.connector.CoyoteWriter".substring(1);
2626

2727
@Override
2828
public ClassVisitor dispatch(ClassVisitor classVisitor, ClassContext context, Policy policy) {
@@ -41,6 +41,8 @@ public ClassVisitor dispatch(ClassVisitor classVisitor, ClassContext context, Po
4141
classVisitor = new ServletOutputStreamAdapter(classVisitor, context);
4242
} else if (ancestors.contains(JAKARTA_SERVLET_OUTPUT_STREAM)) {
4343
classVisitor = new ServletOutputStreamAdapter(classVisitor, context);
44+
} else if (APACHE_COYOTE_WRITER.equals(className)) {
45+
classVisitor = new PrintWriterAdapter(classVisitor, context);
4446
}
4547
return classVisitor;
4648
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
package io.dongtai.iast.core.bytecode.enhance.plugin.framework.j2ee.dispatch;
2+
3+
import io.dongtai.iast.core.bytecode.enhance.ClassContext;
4+
import io.dongtai.iast.core.bytecode.enhance.plugin.AbstractClassVisitor;
5+
import io.dongtai.iast.core.utils.AsmUtils;
6+
import io.dongtai.log.DongTaiLog;
7+
import org.objectweb.asm.*;
8+
9+
import java.util.*;
10+
11+
public class PrintWriterAdapter extends AbstractClassVisitor {
12+
private static final Set<String> WRITE_DESC = new HashSet<>(Arrays.asList("(I)V", "([CII)V", "(Ljava/lang/String;II)V"));
13+
14+
PrintWriterAdapter(ClassVisitor classVisitor, ClassContext context) {
15+
super(classVisitor, context);
16+
}
17+
18+
@Override
19+
public MethodVisitor visitMethod(final int access, final String name, final String desc, final String signature, final String[] exceptions) {
20+
MethodVisitor mv = super.visitMethod(access, name, desc, signature, exceptions);
21+
Type[] typeOfArgs = Type.getArgumentTypes(desc);
22+
String signCode = AsmUtils.buildSignature(context.getClassName(), name, desc);
23+
if (isWrite(name, desc)) {
24+
DongTaiLog.debug("Adding HTTP response PrintWriter by {}", signCode);
25+
mv = new PrintWriterWriteAdviceAdapter(mv, access, name, desc, signCode, context);
26+
setTransformed();
27+
}
28+
if (hasTransformed()) {
29+
DongTaiLog.trace("rewrite method {}.{} for listener[match={}]", context.getClassName(), name, context.getMatchedClassName());
30+
}
31+
32+
return mv;
33+
}
34+
35+
private boolean isWrite(String name, String desc) {
36+
if ("write".equals(name) && WRITE_DESC.contains(desc)) {
37+
return true;
38+
}
39+
return false;
40+
}
41+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
package io.dongtai.iast.core.bytecode.enhance.plugin.framework.j2ee.dispatch;
2+
3+
import io.dongtai.iast.common.scope.Scope;
4+
import io.dongtai.iast.core.bytecode.enhance.ClassContext;
5+
import io.dongtai.iast.core.bytecode.enhance.plugin.AbstractAdviceAdapter;
6+
import org.objectweb.asm.Label;
7+
import org.objectweb.asm.MethodVisitor;
8+
9+
public class PrintWriterWriteAdviceAdapter extends AbstractAdviceAdapter {
10+
public PrintWriterWriteAdviceAdapter(MethodVisitor mv, int access, String name, String desc, String signature,
11+
ClassContext context) {
12+
super(mv, access, name, desc, context, "j2ee", signature);
13+
}
14+
15+
@Override
16+
protected void before() {
17+
mark(tryLabel);
18+
enterScope(Scope.SERVLET_OUTPUT_WRITE);
19+
20+
Label elseLabel = new Label();
21+
22+
isFirstLevelScope(Scope.SERVLET_OUTPUT_WRITE);
23+
mv.visitJumpInsn(EQ, elseLabel);
24+
25+
onPrintWriterWrite();
26+
27+
mark(elseLabel);
28+
}
29+
30+
@Override
31+
protected void after(final int opcode) {
32+
leaveScope(Scope.SERVLET_OUTPUT_WRITE);
33+
}
34+
35+
private void onPrintWriterWrite() {
36+
if ("(I)V".equals(this.desc)) {
37+
invokeStatic(ASM_TYPE_SPY_HANDLER, SPY_HANDLER$getDispatcher);
38+
push(desc);
39+
loadThis();
40+
loadArg(0);
41+
pushNull();
42+
pushNull();
43+
push(-1);
44+
push(-1);
45+
invokeInterface(ASM_TYPE_SPY_DISPATCHER, SPY$onPrintWriterWrite);
46+
} else if ("([CII)V".equals(this.desc)) {
47+
invokeStatic(ASM_TYPE_SPY_HANDLER, SPY_HANDLER$getDispatcher);
48+
push(desc);
49+
loadThis();
50+
push(-1);
51+
pushNull();
52+
loadArg(0);
53+
loadArg(1);
54+
loadArg(2);
55+
invokeInterface(ASM_TYPE_SPY_DISPATCHER, SPY$onPrintWriterWrite);
56+
} else if ("(Ljava/lang/String;II)V".equals(this.desc)) {
57+
invokeStatic(ASM_TYPE_SPY_HANDLER, SPY_HANDLER$getDispatcher);
58+
push(desc);
59+
loadThis();
60+
push(-1);
61+
loadArg(0);
62+
pushNull();
63+
loadArg(1);
64+
loadArg(2);
65+
invokeInterface(ASM_TYPE_SPY_DISPATCHER, SPY$onPrintWriterWrite);
66+
}
67+
}
68+
}

dongtai-core/src/main/java/io/dongtai/iast/core/bytecode/enhance/plugin/framework/j2ee/dispatch/ServletOutputStreamWriteAdviceAdapter.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,11 @@ public ServletOutputStreamWriteAdviceAdapter(MethodVisitor mv, int access, Strin
1515
@Override
1616
protected void before() {
1717
mark(tryLabel);
18-
enterScope(Scope.SERVLET_OUTPUT_STREAM_WRITE);
18+
enterScope(Scope.SERVLET_OUTPUT_WRITE);
1919

2020
Label elseLabel = new Label();
2121

22-
isFirstLevelScope(Scope.SERVLET_OUTPUT_STREAM_WRITE);
22+
isFirstLevelScope(Scope.SERVLET_OUTPUT_WRITE);
2323
mv.visitJumpInsn(EQ, elseLabel);
2424

2525
onServletOutputStreamWrite();
@@ -29,7 +29,7 @@ protected void before() {
2929

3030
@Override
3131
protected void after(final int opcode) {
32-
leaveScope(Scope.SERVLET_OUTPUT_STREAM_WRITE);
32+
leaveScope(Scope.SERVLET_OUTPUT_WRITE);
3333
}
3434

3535
private void onServletOutputStreamWrite() {

dongtai-core/src/main/java/io/dongtai/iast/core/handler/hookpoint/SpyDispatcherImpl.java

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -245,6 +245,26 @@ public void onServletOutputStreamWrite(String desc, Object stream, int b, byte[]
245245
}
246246
}
247247

248+
@Override
249+
public void onPrintWriterWrite(String desc, Object writer, int b, String s, char[] cs, int offset, int len) {
250+
try {
251+
ScopeManager.SCOPE_TRACKER.getPolicyScope().enterAgent();
252+
if (!EngineManager.isEngineRunning()) {
253+
return;
254+
}
255+
256+
if (!ScopeManager.SCOPE_TRACKER.getScope(Scope.HTTP_ENTRY).in()) {
257+
return;
258+
}
259+
260+
HttpImpl.onPrintWriterWrite(desc, writer, b, s, cs, offset, len);
261+
} catch (Throwable e) {
262+
DongTaiLog.warn(ErrorCode.SPY_COLLECT_HTTP_FAILED, "response body", e);
263+
} finally {
264+
ScopeManager.SCOPE_TRACKER.getPolicyScope().leaveAgent();
265+
}
266+
}
267+
248268
/**
249269
* mark for enter Source Entry Point
250270
*

dongtai-core/src/main/java/io/dongtai/iast/core/handler/hookpoint/controller/impl/HttpImpl.java

Lines changed: 115 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,34 @@ public static void solveHttpRequest(Object obj, Object req, Object resp, Map<Str
9595
} catch (Throwable ignore) {
9696
}
9797

98+
try {
99+
String contentType = ((Map<String, String>) requestMeta.get("headers")).get("Content-Type");
100+
String method = (String) requestMeta.get("method");
101+
if (("POST".equalsIgnoreCase(method) || "PUT".equalsIgnoreCase(method))
102+
&& !isRawBody(contentType)) {
103+
Method getParameterNamesMethod = ReflectUtils.getDeclaredMethodFromSuperClass(req.getClass(),
104+
"getParameterNames", null);
105+
Method getParameterMethod = ReflectUtils.getDeclaredMethodFromSuperClass(req.getClass(),
106+
"getParameter", new Class[]{String.class});
107+
Enumeration<?> parameterNames = (Enumeration<?>) getParameterNamesMethod.invoke(req);
108+
StringBuilder postBody = new StringBuilder();
109+
boolean first = true;
110+
while (parameterNames.hasMoreElements()) {
111+
String key = (String) parameterNames.nextElement();
112+
if (first) {
113+
first = false;
114+
postBody.append(key).append("=").append((String) getParameterMethod.invoke(req, key));
115+
} else {
116+
postBody.append("&").append(key).append("=").append((String) getParameterMethod.invoke(req, key));
117+
}
118+
}
119+
if (postBody.length() > 0) {
120+
requestMeta.put("body", postBody.toString());
121+
}
122+
}
123+
} catch (Throwable ignore) {
124+
}
125+
98126
EngineManager.enterHttpEntry(requestMeta);
99127
DongTaiLog.debug("HTTP Request:{} {} from: {}", requestMeta.get("method"), requestMeta.get("requestURI"),
100128
obj.getClass().getName());
@@ -111,6 +139,9 @@ public static Map<String, String> parseRequestHeaders(Object req, Enumeration<?>
111139
try {
112140
String key = (String) headerNames.nextElement();
113141
String val = (String) getHeaderMethod.invoke(req, key);
142+
if ("content-type".equalsIgnoreCase(key)) {
143+
key = "Content-Type";
144+
}
114145
headers.put(key, val);
115146
} catch (Throwable ignore) {
116147
}
@@ -119,6 +150,13 @@ public static Map<String, String> parseRequestHeaders(Object req, Enumeration<?>
119150
}
120151

121152
public static void onServletInputStreamRead(int ret, String desc, Object stream, byte[] bs, int offset, int len) {
153+
if (EngineManager.REQUEST_CONTEXT.get() != null
154+
&& EngineManager.REQUEST_CONTEXT.get().get("body") != null
155+
&& EngineManager.REQUEST_CONTEXT.get().get("body") != ""
156+
) {
157+
return;
158+
}
159+
122160
if ("()I".equals(desc)) {
123161
if (ret == -1) {
124162
return;
@@ -189,33 +227,92 @@ public static void onServletOutputStreamWrite(String desc, Object stream, int b,
189227
maxLength = 50000;
190228
}
191229

192-
if ("(I)V".equals(desc)) {
193-
if (b == -1) {
194-
return;
195-
}
196-
ByteArrayOutputStream buff = EngineManager.BODY_BUFFER.getResponse();
197-
if (buff.size() < maxLength) {
198-
buff.write(b);
199-
}
200-
} else if ("([B)V".equals(desc)) {
201-
if (bs == null) {
202-
return;
230+
try {
231+
if ("(I)V".equals(desc)) {
232+
if (b == -1) {
233+
return;
234+
}
235+
ByteArrayOutputStream buff = EngineManager.BODY_BUFFER.getResponse();
236+
if (buff.size() < maxLength) {
237+
buff.write(b);
238+
}
239+
} else if ("([B)V".equals(desc)) {
240+
if (bs == null) {
241+
return;
242+
}
243+
onServletOutputStreamWrite("([BII)V", stream, b, bs, 0, bs.length);
244+
} else if ("([BII)V".equals(desc)) {
245+
if (bs == null || offset < 0 || len < 0) {
246+
return;
247+
}
248+
249+
ByteArrayOutputStream buff = EngineManager.BODY_BUFFER.getResponse();
250+
int size = buff.size();
251+
if (size < maxLength) {
252+
buff.write(bs, offset, Math.min(len, maxLength - size));
253+
}
203254
}
204-
onServletOutputStreamWrite("([BII)V", stream, b, bs, 0, bs.length);
205-
} else if ("([BII)V".equals(desc)) {
206-
if (bs == null || offset < 0 || len < 0) {
255+
} catch (Throwable ignore) {
256+
}
257+
}
258+
259+
public static void onPrintWriterWrite(String desc, Object writer, int b, String s, char[] cs, int offset, int len) {
260+
try {
261+
boolean getBody = ((Config<Boolean>) ConfigBuilder.getInstance().getConfig(ConfigKey.REPORT_RESPONSE_BODY)).get();
262+
if (!getBody) {
207263
return;
208264
}
265+
} catch (Throwable ignore) {
266+
return;
267+
}
209268

210-
ByteArrayOutputStream buff = EngineManager.BODY_BUFFER.getResponse();
211-
int size = buff.size();
212-
if (size < maxLength) {
213-
buff.write(bs, offset, Math.min(len, maxLength - size));
269+
Integer maxLength = PropertyUtils.getInstance().getResponseLength();
270+
if (maxLength == 0) {
271+
return;
272+
} else if (maxLength < 0 || maxLength > 50000) {
273+
maxLength = 50000;
274+
}
275+
276+
try {
277+
if ("(I)V".equals(desc)) {
278+
if (b == -1) {
279+
return;
280+
}
281+
ByteArrayOutputStream buff = EngineManager.BODY_BUFFER.getResponse();
282+
if (buff.size() < maxLength) {
283+
buff.write(b);
284+
}
285+
} else if ("([CII)V".equals(desc)) {
286+
if (cs == null || offset < 0 || len < 0) {
287+
return;
288+
}
289+
290+
ByteArrayOutputStream buff = EngineManager.BODY_BUFFER.getResponse();
291+
int size = buff.size();
292+
if (size < maxLength) {
293+
buff.write((new String(cs, offset, Math.min(len, maxLength - size))).getBytes());
294+
}
295+
} else if ("(Ljava/lang/String;II)V".equals(desc)) {
296+
if (StringUtils.isEmpty(s) || offset < 0 || len < 0) {
297+
return;
298+
}
299+
300+
ByteArrayOutputStream buff = EngineManager.BODY_BUFFER.getResponse();
301+
int size = buff.size();
302+
if (size < maxLength) {
303+
buff.write((new String(s.toCharArray(), offset, Math.min(len, maxLength - size))).getBytes());
304+
}
214305
}
306+
} catch (Throwable ignore) {
215307
}
216308
}
217309

218310
public static IastClassLoader getClassLoader() {
219311
return iastClassLoader;
220312
}
313+
314+
public static boolean isRawBody(String contentType) {
315+
return contentType != null
316+
&& (contentType.contains("application/json") || contentType.contains("application/xml"));
317+
}
221318
}

0 commit comments

Comments
 (0)