Skip to content

Commit 9ac8ab1

Browse files
authored
Expand SSRF support in IAST to apache-httpclient-5 and apache-httpasyncclient-4 (#7920)
1 parent 4df0a01 commit 9ac8ab1

File tree

17 files changed

+330
-11
lines changed

17 files changed

+330
-11
lines changed

dd-java-agent/instrumentation/apache-httpasyncclient-4/src/main/java/datadog/trace/instrumentation/apachehttpasyncclient/ApacheHttpAsyncClientDecorator.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,11 @@ protected URI url(final HttpUriRequest request) throws URISyntaxException {
4141
return request.getURI();
4242
}
4343

44+
@Override
45+
protected URI sourceUrl(final HttpUriRequest request) {
46+
return request.getURI();
47+
}
48+
4449
@Override
4550
protected int status(final HttpContext context) {
4651
final Object responseObject = context.getAttribute(HttpCoreContext.HTTP_RESPONSE);

dd-java-agent/instrumentation/apache-httpclient-5/src/main/java/datadog/trace/instrumentation/apachehttpclient5/ApacheHttpClientDecorator.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,11 @@ protected URI url(final HttpRequest request) throws URISyntaxException {
4242
return request.getUri();
4343
}
4444

45+
@Override
46+
protected HttpRequest sourceUrl(final HttpRequest request) {
47+
return request;
48+
}
49+
4550
@Override
4651
protected int status(final HttpResponse httpResponse) {
4752
return httpResponse.getCode();

dd-java-agent/instrumentation/apache-httpclient-5/src/main/java/datadog/trace/instrumentation/apachehttpclient5/HostAndRequestAsHttpUriRequest.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package datadog.trace.instrumentation.apachehttpclient5;
22

3+
import datadog.trace.api.iast.util.PropagationUtils;
34
import java.net.URI;
45
import java.net.URISyntaxException;
56
import org.apache.hc.core5.http.Header;
@@ -15,6 +16,9 @@ public class HostAndRequestAsHttpUriRequest extends BasicClassicHttpRequest {
1516
public HostAndRequestAsHttpUriRequest(final HttpHost httpHost, final HttpRequest httpRequest) {
1617
super(httpRequest.getMethod(), httpHost, httpRequest.getPath());
1718
actualRequest = httpRequest;
19+
// Propagate in case the host or request is tainted
20+
PropagationUtils.taintObjectIfTainted(this, httpHost);
21+
PropagationUtils.taintObjectIfTainted(this, httpRequest);
1822
}
1923

2024
@Override
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
package datadog.trace.instrumentation.apachehttpclient5;
2+
3+
import static net.bytebuddy.matcher.ElementMatchers.isConstructor;
4+
import static net.bytebuddy.matcher.ElementMatchers.takesArguments;
5+
6+
import com.google.auto.service.AutoService;
7+
import datadog.trace.agent.tooling.Instrumenter;
8+
import datadog.trace.agent.tooling.InstrumenterModule;
9+
import datadog.trace.agent.tooling.bytebuddy.iast.TaintableVisitor;
10+
import datadog.trace.api.iast.InstrumentationBridge;
11+
import datadog.trace.api.iast.Propagation;
12+
import datadog.trace.api.iast.propagation.PropagationModule;
13+
import java.net.URI;
14+
import net.bytebuddy.asm.Advice;
15+
import org.apache.hc.client5.http.classic.methods.HttpUriRequestBase;
16+
17+
@AutoService(InstrumenterModule.class)
18+
public class IastHttpUriRequestBaseInstrumentation extends InstrumenterModule.Iast
19+
implements Instrumenter.ForSingleType, Instrumenter.HasTypeAdvice {
20+
21+
public IastHttpUriRequestBaseInstrumentation() {
22+
super("apache-httpclient", "httpclient5");
23+
}
24+
25+
@Override
26+
public String instrumentedType() {
27+
return "org.apache.hc.client5.http.classic.methods.HttpUriRequestBase";
28+
}
29+
30+
@Override
31+
public void typeAdvice(TypeTransformer transformer) {
32+
transformer.applyAdvice(new TaintableVisitor(instrumentedType()));
33+
}
34+
35+
@Override
36+
public void methodAdvice(MethodTransformer transformer) {
37+
transformer.applyAdvice(
38+
isConstructor().and(takesArguments(String.class, URI.class)),
39+
IastHttpUriRequestBaseInstrumentation.class.getName() + "$CtorAdvice");
40+
}
41+
42+
public static class CtorAdvice {
43+
@Advice.OnMethodExit()
44+
@Propagation
45+
public static void afterCtor(
46+
@Advice.This final HttpUriRequestBase self, @Advice.Argument(1) final URI uri) {
47+
final PropagationModule module = InstrumentationBridge.PROPAGATION;
48+
if (module != null) {
49+
module.taintObjectIfTainted(self, uri);
50+
}
51+
}
52+
}
53+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import datadog.trace.agent.test.AgentTestRunner
2+
import datadog.trace.api.iast.InstrumentationBridge
3+
import datadog.trace.api.iast.propagation.PropagationModule
4+
import org.apache.hc.client5.http.classic.methods.HttpUriRequestBase
5+
6+
class IastHttpUriRequestBaseInstrumentationTest extends AgentTestRunner {
7+
8+
@Override
9+
protected void configurePreAgent() {
10+
injectSysConfig('dd.iast.enabled', 'true')
11+
}
12+
13+
void 'test constructor'() {
14+
given:
15+
final module = Mock(PropagationModule)
16+
InstrumentationBridge.registerIastModule(module)
17+
18+
when:
19+
HttpUriRequestBase.newInstance(method, new URI(uri))
20+
21+
then:
22+
1 * module.taintObjectIfTainted(_ as HttpUriRequestBase, _ as URI)
23+
0 * _
24+
25+
where:
26+
method | uri
27+
"GET" | 'http://localhost.com'
28+
}
29+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
muzzle {
2+
pass {
3+
group = "org.apache.httpcomponents.core5"
4+
module = "httpcore5"
5+
versions = "[5.0,)"
6+
assertInverse = true
7+
}
8+
}
9+
10+
apply from: "$rootDir/gradle/java.gradle"
11+
12+
addTestSuiteForDir('latestDepTest', 'test')
13+
14+
dependencies {
15+
compileOnly group: 'org.apache.httpcomponents.core5', name: 'httpcore5', version: '5.0'
16+
17+
testImplementation group: 'org.apache.httpcomponents.core5', name: 'httpcore5', version: '5.0'
18+
19+
latestDepTestImplementation group: 'org.apache.httpcomponents.core5', name: 'httpcore5', version: '+'
20+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
package datadog.trace.instrumentation.apachehttpcore5;
2+
3+
import static net.bytebuddy.matcher.ElementMatchers.isConstructor;
4+
import static net.bytebuddy.matcher.ElementMatchers.takesArguments;
5+
6+
import com.google.auto.service.AutoService;
7+
import datadog.trace.agent.tooling.Instrumenter;
8+
import datadog.trace.agent.tooling.InstrumenterModule;
9+
import datadog.trace.api.iast.InstrumentationBridge;
10+
import datadog.trace.api.iast.Propagation;
11+
import datadog.trace.api.iast.propagation.PropagationModule;
12+
import java.net.InetAddress;
13+
import net.bytebuddy.asm.Advice;
14+
15+
@AutoService(InstrumenterModule.class)
16+
public class IastHttpHostInstrumentation extends InstrumenterModule.Iast
17+
implements Instrumenter.ForSingleType {
18+
19+
public IastHttpHostInstrumentation() {
20+
super("httpcore-5", "apache-httpcore-5", "apache-http-core-5");
21+
}
22+
23+
@Override
24+
public String instrumentedType() {
25+
return "org.apache.hc.core5.http.HttpHost";
26+
}
27+
28+
@Override
29+
public void methodAdvice(MethodTransformer transformer) {
30+
transformer.applyAdvice(
31+
isConstructor()
32+
.and(takesArguments(String.class, InetAddress.class, String.class, int.class)),
33+
IastHttpHostInstrumentation.class.getName() + "$CtorAdvice");
34+
}
35+
36+
public static class CtorAdvice {
37+
@Advice.OnMethodExit(suppress = Throwable.class)
38+
@Propagation
39+
public static void afterCtor(
40+
@Advice.This final Object self, @Advice.Argument(2) final String host) {
41+
final PropagationModule module = InstrumentationBridge.PROPAGATION;
42+
if (module != null) {
43+
module.taintObjectIfTainted(self, host);
44+
}
45+
}
46+
}
47+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
package datadog.trace.instrumentation.apachehttpcore5
2+
3+
import datadog.trace.agent.test.AgentTestRunner
4+
import datadog.trace.api.iast.InstrumentationBridge
5+
import datadog.trace.api.iast.propagation.PropagationModule
6+
import org.apache.hc.core5.http.HttpHost
7+
8+
class IastHttpHostInstrumentationTest extends AgentTestRunner {
9+
10+
@Override
11+
protected void configurePreAgent() {
12+
injectSysConfig('dd.iast.enabled', 'true')
13+
}
14+
15+
void 'test constructor'(){
16+
given:
17+
final module = Mock(PropagationModule)
18+
InstrumentationBridge.registerIastModule(module)
19+
20+
when:
21+
HttpHost.newInstance(*args)
22+
23+
then:
24+
1 * module.taintObjectIfTainted( _ as HttpHost, 'localhost')
25+
26+
where:
27+
args | _
28+
['localhost'] | _
29+
['localhost', 8080] | _
30+
}
31+
}

dd-java-agent/instrumentation/iast-instrumenter/src/main/resources/datadog/trace/instrumentation/iastinstrumenter/iast_exclusion.trie

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,7 @@
198198
1 org.apache.*
199199
#apache httpClient needs URI propagation
200200
0 org.apache.http.client.methods.*
201+
0 org.apache.hc.client5.http.classic.methods.*
201202
# apache compiled jsps
202203
0 org.apache.jsp.*
203204
1 org.apiguardian.*

dd-smoke-tests/iast-util/build.gradle

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,4 +17,6 @@ dependencies {
1717
compileOnly group: 'org.apache.httpcomponents', name: 'httpclient', version: '4.0'
1818
compileOnly group: 'com.squareup.okhttp', name: 'okhttp', version: '2.2.0'
1919
compileOnly group: 'com.squareup.okhttp3', name: 'okhttp', version: '3.0.0'
20+
compileOnly group: 'org.apache.httpcomponents.client5', name: 'httpclient5', version: '5.0'
21+
compileOnly group: 'org.apache.httpcomponents', name: 'httpasyncclient', version: '4.0'
2022
}

0 commit comments

Comments
 (0)