Skip to content

Commit 3ae8291

Browse files
authored
fix(http-client): default PostBuilder to not follow redirects (#1141)
fix(http-client): disable POST redirect following by default Default PostBuilder.followRedirects to false across JDK, Vert.x, and Android clients to prevent credential leakage on cross-origin redirects. Vert.x gains manual RFC 7231/7538 redirect handling; adds shared abstract redirect test suite and OkHttp-backed test client for Android semantics. Signed-off-by: Emmanuel Hugonnet <ehugonne@redhat.com>
1 parent 30e3108 commit 3ae8291

18 files changed

Lines changed: 821 additions & 73 deletions

File tree

‎extras/http-client-android/pom.xml‎

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,12 +37,22 @@
3737
<artifactId>junit-jupiter-api</artifactId>
3838
<scope>test</scope>
3939
</dependency>
40+
<dependency>
41+
<groupId>org.junit.jupiter</groupId>
42+
<artifactId>junit-jupiter-params</artifactId>
43+
<scope>test</scope>
44+
</dependency>
4045

4146
<dependency>
4247
<groupId>org.mock-server</groupId>
4348
<artifactId>mockserver-netty</artifactId>
4449
<scope>test</scope>
4550
</dependency>
51+
<dependency>
52+
<groupId>com.squareup.okhttp3</groupId>
53+
<artifactId>okhttp</artifactId>
54+
<scope>test</scope>
55+
</dependency>
4656
</dependencies>
4757
<build>
4858
<plugins>

‎extras/http-client-android/src/main/java/org/a2aproject/sdk/client/http/android/AndroidA2AHttpClient.java‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -269,7 +269,7 @@ public CompletableFuture<Void> getAsyncSSE(
269269
private static class AndroidPostBuilder extends AndroidBuilder<PostBuilder>
270270
implements PostBuilder {
271271
private String body = "";
272-
private boolean followRedirects = true;
272+
private boolean followRedirects = false;
273273

274274
@Override
275275
public PostBuilder body(String body) {
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
package org.a2aproject.sdk.client.http.android;
2+
3+
import static org.junit.jupiter.api.Assertions.assertEquals;
4+
import static org.mockserver.model.HttpRequest.request;
5+
import static org.mockserver.verify.VerificationTimes.exactly;
6+
7+
import org.a2aproject.sdk.client.http.A2AHttpClient;
8+
import org.a2aproject.sdk.client.http.A2AHttpResponse;
9+
import org.a2aproject.sdk.client.http.AbstractA2AHttpClientRedirectTest;
10+
import org.junit.jupiter.api.Test;
11+
12+
public class AndroidA2AHttpClientRedirectTest extends AbstractA2AHttpClientRedirectTest {
13+
14+
@Override
15+
protected A2AHttpClient createClient() {
16+
return new AndroidA2AHttpClient();
17+
}
18+
19+
@Test
20+
public void synchronousPostFollowsRedirectWhenExplicitlyEnabled() throws Exception {
21+
startRedirectPair();
22+
23+
A2AHttpResponse result = createClient().createPost()
24+
.url("http://127.0.0.1:" + getRedirector().getLocalPort() + "/agent")
25+
.addHeader("X-API-Key", "FULCRUM-SYNTHETIC-EXPLICIT")
26+
.body("{}")
27+
.followRedirects(true)
28+
.post();
29+
30+
assertEquals(200, result.status(),
31+
"Android client must follow redirect when explicitly enabled");
32+
getCollector().verify(request().withPath("/collect"), exactly(1));
33+
}
34+
}
Lines changed: 5 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -1,61 +1,12 @@
11
package org.a2aproject.sdk.client.http.android;
22

3-
import static org.junit.jupiter.api.Assertions.assertNotNull;
4-
import static org.junit.jupiter.api.Assertions.assertSame;
5-
63
import org.a2aproject.sdk.client.http.A2AHttpClient;
7-
import org.junit.jupiter.api.Test;
8-
9-
public class AndroidA2AHttpClientTest {
10-
11-
@Test
12-
public void testNoArgsConstructor() {
13-
AndroidA2AHttpClient client = new AndroidA2AHttpClient();
14-
assertNotNull(client);
15-
}
4+
import org.a2aproject.sdk.client.http.AbstractA2AHttpClientBasicTest;
165

17-
@Test
18-
public void testCreateGet() {
19-
AndroidA2AHttpClient client = new AndroidA2AHttpClient();
20-
A2AHttpClient.GetBuilder builder = client.createGet();
21-
assertNotNull(builder);
22-
}
23-
24-
@Test
25-
public void testCreatePost() {
26-
AndroidA2AHttpClient client = new AndroidA2AHttpClient();
27-
A2AHttpClient.PostBuilder builder = client.createPost();
28-
assertNotNull(builder);
29-
}
30-
31-
@Test
32-
public void testCreateDelete() {
33-
AndroidA2AHttpClient client = new AndroidA2AHttpClient();
34-
A2AHttpClient.DeleteBuilder builder = client.createDelete();
35-
assertNotNull(builder);
36-
}
37-
38-
@Test
39-
public void testBuilderUrlSetting() {
40-
AndroidA2AHttpClient client = new AndroidA2AHttpClient();
41-
A2AHttpClient.GetBuilder builder = client.createGet();
42-
A2AHttpClient.GetBuilder result = builder.url("https://example.com");
43-
assertSame(builder, result, "Builder should return itself for method chaining");
44-
}
45-
46-
@Test
47-
public void testBuilderHeaderSetting() {
48-
AndroidA2AHttpClient client = new AndroidA2AHttpClient();
49-
A2AHttpClient.GetBuilder builder = client.createGet();
50-
A2AHttpClient.GetBuilder result = builder.addHeader("Accept", "application/json");
51-
assertSame(builder, result, "Builder should return itself for method chaining");
52-
}
6+
public class AndroidA2AHttpClientTest extends AbstractA2AHttpClientBasicTest {
537

54-
@Test
55-
public void testPostBuilderBody() {
56-
AndroidA2AHttpClient client = new AndroidA2AHttpClient();
57-
A2AHttpClient.PostBuilder builder = client.createPost();
58-
A2AHttpClient.PostBuilder result = builder.body("{\"key\":\"value\"}");
59-
assertSame(builder, result, "Builder should return itself for method chaining");
8+
@Override
9+
protected A2AHttpClient createClient() {
10+
return new AndroidA2AHttpClient();
6011
}
6112
}

0 commit comments

Comments
 (0)