Skip to content

Commit dcdd6fc

Browse files
fix: restrict ADK Web cross-origin defaults
1 parent c2b087c commit dcdd6fc

4 files changed

Lines changed: 123 additions & 4 deletions

File tree

dev/src/main/java/com/google/adk/web/config/AdkWebCorsProperties.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ public record AdkWebCorsProperties(
3434

3535
public AdkWebCorsProperties {
3636
mapping = mapping != null ? mapping : "/**";
37-
origins = origins != null && !origins.isEmpty() ? origins : List.of("*");
37+
origins = origins != null ? origins : List.of();
3838
methods =
3939
methods != null && !methods.isEmpty()
4040
? methods

dev/src/main/java/com/google/adk/web/websocket/WebSocketConfig.java

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import org.springframework.context.annotation.Configuration;
2222
import org.springframework.web.socket.config.annotation.EnableWebSocket;
2323
import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
24+
import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistration;
2425
import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;
2526

2627
/** Configuration class for WebSocket handling. */
@@ -40,8 +41,10 @@ public WebSocketConfig(
4041

4142
@Override
4243
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
43-
registry
44-
.addHandler(liveWebSocketHandler, "/run_live")
45-
.setAllowedOrigins(corsProperties.origins().toArray(new String[0]));
44+
WebSocketHandlerRegistration registration =
45+
registry.addHandler(liveWebSocketHandler, "/run_live");
46+
if (!corsProperties.origins().isEmpty()) {
47+
registration.setAllowedOrigins(corsProperties.origins().toArray(String[]::new));
48+
}
4649
}
4750
}
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
/*
2+
* Copyright 2026 Google LLC
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.google.adk.web.config;
18+
19+
import static com.google.common.truth.Truth.assertThat;
20+
21+
import java.util.List;
22+
import org.junit.jupiter.api.Test;
23+
import org.springframework.mock.web.MockHttpServletRequest;
24+
import org.springframework.web.cors.CorsConfiguration;
25+
import org.springframework.web.cors.CorsConfigurationSource;
26+
27+
class AdkWebCorsConfigTest {
28+
29+
private final AdkWebCorsConfig config = new AdkWebCorsConfig();
30+
31+
@Test
32+
void defaultCorsConfigurationDoesNotAllowArbitraryOrigins() {
33+
CorsConfiguration corsConfiguration =
34+
getCorsConfiguration(new AdkWebCorsProperties(null, null, null, null, false, 0));
35+
36+
assertThat(corsConfiguration.checkOrigin("https://attacker.example")).isNull();
37+
}
38+
39+
@Test
40+
void explicitCorsOriginsAreStillAllowed() {
41+
CorsConfiguration corsConfiguration =
42+
getCorsConfiguration(
43+
new AdkWebCorsProperties(null, List.of("http://localhost:3000"), null, null, false, 0));
44+
45+
assertThat(corsConfiguration.checkOrigin("http://localhost:3000"))
46+
.isEqualTo("http://localhost:3000");
47+
assertThat(corsConfiguration.checkOrigin("https://attacker.example")).isNull();
48+
}
49+
50+
private CorsConfiguration getCorsConfiguration(AdkWebCorsProperties properties) {
51+
CorsConfigurationSource source = config.corsConfigurationSource(properties);
52+
CorsConfiguration corsConfiguration =
53+
source.getCorsConfiguration(new MockHttpServletRequest("POST", "/run"));
54+
assertThat(corsConfiguration).isNotNull();
55+
return corsConfiguration;
56+
}
57+
}
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
/*
2+
* Copyright 2026 Google LLC
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.google.adk.web.websocket;
18+
19+
import static org.mockito.Mockito.mock;
20+
import static org.mockito.Mockito.never;
21+
import static org.mockito.Mockito.verify;
22+
import static org.mockito.Mockito.when;
23+
24+
import com.google.adk.web.config.AdkWebCorsProperties;
25+
import java.util.List;
26+
import org.junit.jupiter.api.Test;
27+
import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistration;
28+
import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;
29+
30+
class WebSocketConfigTest {
31+
32+
@Test
33+
void defaultWebSocketConfigurationDoesNotAllowArbitraryOrigins() {
34+
LiveWebSocketHandler handler = mock(LiveWebSocketHandler.class);
35+
WebSocketHandlerRegistry registry = mock(WebSocketHandlerRegistry.class);
36+
WebSocketHandlerRegistration registration = mock(WebSocketHandlerRegistration.class);
37+
when(registry.addHandler(handler, "/run_live")).thenReturn(registration);
38+
39+
new WebSocketConfig(handler, new AdkWebCorsProperties(null, null, null, null, false, 0))
40+
.registerWebSocketHandlers(registry);
41+
42+
verify(registration, never()).setAllowedOrigins("*");
43+
}
44+
45+
@Test
46+
void explicitCorsOriginsAreAppliedToWebSocketEndpoint() {
47+
LiveWebSocketHandler handler = mock(LiveWebSocketHandler.class);
48+
WebSocketHandlerRegistry registry = mock(WebSocketHandlerRegistry.class);
49+
WebSocketHandlerRegistration registration = mock(WebSocketHandlerRegistration.class);
50+
when(registry.addHandler(handler, "/run_live")).thenReturn(registration);
51+
52+
new WebSocketConfig(
53+
handler,
54+
new AdkWebCorsProperties(null, List.of("http://localhost:3000"), null, null, false, 0))
55+
.registerWebSocketHandlers(registry);
56+
57+
verify(registration).setAllowedOrigins("http://localhost:3000");
58+
}
59+
}

0 commit comments

Comments
 (0)