Skip to content

Commit baac905

Browse files
committed
Rum injection: filter setContentType headers
1 parent 296ecfb commit baac905

File tree

4 files changed

+92
-10
lines changed

4 files changed

+92
-10
lines changed

dd-java-agent/instrumentation/servlet/jakarta-servlet-5.0/src/main/java/datadog/trace/instrumentation/servlet5/RumHttpServletResponseWrapper.java

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,7 @@ public void setHeader(String name, String value) {
107107
if (isContentLengthHeader(name)) {
108108
return;
109109
}
110+
checkForContentType(name, value);
110111
checkForContentSecurityPolicy(name);
111112
}
112113
super.setHeader(name, value);
@@ -118,6 +119,7 @@ public void addHeader(String name, String value) {
118119
if (isContentLengthHeader(name)) {
119120
return;
120121
}
122+
checkForContentType(name, value);
121123
checkForContentSecurityPolicy(name);
122124
}
123125
super.addHeader(name, value);
@@ -133,6 +135,12 @@ private void checkForContentSecurityPolicy(String name) {
133135
}
134136
}
135137

138+
private void checkForContentType(String name, String value) {
139+
if ("content-type".equalsIgnoreCase(name)) {
140+
handleContentType(value);
141+
}
142+
}
143+
136144
@Override
137145
public void setContentLength(int len) {
138146
// don't set it since we don't know if we will inject
@@ -182,15 +190,20 @@ public void onInjected() {
182190
}
183191
}
184192

185-
@Override
186-
public void setContentType(String type) {
193+
private void handleContentType(String type) {
194+
final boolean wasInjecting = shouldInject;
187195
if (shouldInject) {
188196
shouldInject = type != null && type.contains("text/html");
189197
}
190-
if (!shouldInject) {
198+
if (wasInjecting && !shouldInject) {
191199
commit();
192200
stopFiltering();
193201
}
202+
}
203+
204+
@Override
205+
public void setContentType(String type) {
206+
handleContentType(type);
194207
super.setContentType(type);
195208
}
196209

dd-java-agent/instrumentation/servlet/jakarta-servlet-5.0/src/test/groovy/RumHttpServletResponseWrapperTest.groovy

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,15 +54,36 @@ class RumHttpServletResponseWrapperTest extends InstrumentationSpecification {
5454
1 * mockResponse.getOutputStream()
5555
}
5656

57-
void 'getWriter with non-HTML content reports skipped'() {
58-
setup:
57+
void 'getWriter with non-HTML content reports skipped (setContentType)'() {
58+
when:
5959
wrapper.setContentType("text/plain")
60+
wrapper.getWriter()
61+
62+
then:
63+
1 * mockTelemetryCollector.onInjectionSkipped(SERVLET_VERSION)
64+
1 * mockResponse.setContentType("text/plain")
65+
1 * mockResponse.getWriter()
66+
}
67+
68+
void 'getWriter with non-HTML content reports skipped (setHeader)'() {
69+
when:
70+
wrapper.setHeader("Content-Type", "text/plain")
71+
wrapper.getWriter()
72+
73+
then:
74+
1 * mockTelemetryCollector.onInjectionSkipped(SERVLET_VERSION)
75+
1 * mockResponse.setHeader("Content-Type", "text/plain")
76+
1 * mockResponse.getWriter()
77+
}
6078
79+
void 'getWriter with non-HTML content reports skipped (addHeader)'() {
6180
when:
81+
wrapper.addHeader("Content-Type", "text/plain")
6282
wrapper.getWriter()
6383
6484
then:
6585
1 * mockTelemetryCollector.onInjectionSkipped(SERVLET_VERSION)
86+
1 * mockResponse.addHeader("Content-Type", "text/plain")
6687
1 * mockResponse.getWriter()
6788
}
6889

dd-java-agent/instrumentation/servlet/javax-servlet/javax-servlet-3.0/src/main/java/datadog/trace/instrumentation/servlet3/RumHttpServletResponseWrapper.java

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,7 @@ public void setHeader(String name, String value) {
126126
if (isContentLengthHeader(name)) {
127127
return;
128128
}
129+
checkForContentType(name, value);
129130
checkForContentSecurityPolicy(name);
130131
}
131132
super.setHeader(name, value);
@@ -137,6 +138,7 @@ public void addHeader(String name, String value) {
137138
if (isContentLengthHeader(name)) {
138139
return;
139140
}
141+
checkForContentType(name, value);
140142
checkForContentSecurityPolicy(name);
141143
}
142144
super.addHeader(name, value);
@@ -205,15 +207,26 @@ public void onInjected() {
205207
}
206208
}
207209

208-
@Override
209-
public void setContentType(String type) {
210+
private void handleContentType(String type) {
211+
final boolean wasInjecting = shouldInject;
210212
if (shouldInject) {
211213
shouldInject = type != null && type.contains("text/html");
212214
}
213-
if (!shouldInject) {
215+
if (wasInjecting && !shouldInject) {
214216
commit();
215217
stopFiltering();
216218
}
219+
}
220+
221+
private void checkForContentType(String name, String value) {
222+
if ("content-type".equalsIgnoreCase(name)) {
223+
handleContentType(value);
224+
}
225+
}
226+
227+
@Override
228+
public void setContentType(String type) {
229+
handleContentType(type);
217230
super.setContentType(type);
218231
}
219232

dd-java-agent/instrumentation/servlet/javax-servlet/javax-servlet-3.0/src/test/groovy/RumHttpServletResponseWrapperTest.groovy

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,15 +54,36 @@ class RumHttpServletResponseWrapperTest extends InstrumentationSpecification {
5454
1 * mockResponse.getOutputStream()
5555
}
5656

57-
void 'getWriter with non-HTML content reports skipped'() {
58-
setup:
57+
void 'getWriter with non-HTML content reports skipped (setContentType)'() {
58+
when:
5959
wrapper.setContentType("text/plain")
60+
wrapper.getWriter()
61+
62+
then:
63+
1 * mockTelemetryCollector.onInjectionSkipped(SERVLET_VERSION)
64+
1 * mockResponse.setContentType("text/plain")
65+
1 * mockResponse.getWriter()
66+
}
67+
68+
void 'getWriter with non-HTML content reports skipped (setHeader)'() {
69+
when:
70+
wrapper.setHeader("Content-Type", "text/plain")
71+
wrapper.getWriter()
72+
73+
then:
74+
1 * mockTelemetryCollector.onInjectionSkipped(SERVLET_VERSION)
75+
1 * mockResponse.setHeader("Content-Type", "text/plain")
76+
1 * mockResponse.getWriter()
77+
}
6078
79+
void 'getWriter with non-HTML content reports skipped (addHeader)'() {
6180
when:
81+
wrapper.addHeader("Content-Type", "text/plain")
6282
wrapper.getWriter()
6383
6484
then:
6585
1 * mockTelemetryCollector.onInjectionSkipped(SERVLET_VERSION)
86+
1 * mockResponse.addHeader("Content-Type", "text/plain")
6687
1 * mockResponse.getWriter()
6788
}
6889
@@ -80,6 +101,20 @@ class RumHttpServletResponseWrapperTest extends InstrumentationSpecification {
80101
1 * mockTelemetryCollector.onInjectionFailed(SERVLET_VERSION, null)
81102
}
82103
104+
void 'getOutputStream exception reports failure'() {
105+
setup:
106+
wrapper.setContentType("text/html")
107+
mockResponse.getOutputStream() >> { throw new IOException("stream error") }
108+
109+
when:
110+
try {
111+
wrapper.getOutputStream()
112+
} catch (IOException ignored) {}
113+
114+
then:
115+
1 * mockTelemetryCollector.onInjectionFailed(SERVLET_VERSION, null)
116+
}
117+
83118
void 'getWriter exception reports failure'() {
84119
setup:
85120
wrapper.setContentType("text/html")

0 commit comments

Comments
 (0)