Skip to content

Commit 214f794

Browse files
fix: restrict ADK Web cross-origin defaults
1 parent bead3da commit 214f794

4 files changed

Lines changed: 128 additions & 3 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: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,12 @@
1616

1717
package com.google.adk.web.websocket;
1818

19+
import com.google.adk.web.config.AdkWebCorsProperties;
1920
import org.springframework.beans.factory.annotation.Autowired;
2021
import org.springframework.context.annotation.Configuration;
2122
import org.springframework.web.socket.config.annotation.EnableWebSocket;
2223
import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
24+
import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistration;
2325
import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;
2426

2527
/** Configuration class for WebSocket handling. */
@@ -28,14 +30,21 @@
2830
public class WebSocketConfig implements WebSocketConfigurer {
2931

3032
private final LiveWebSocketHandler liveWebSocketHandler;
33+
private final AdkWebCorsProperties corsProperties;
3134

3235
@Autowired
33-
public WebSocketConfig(LiveWebSocketHandler liveWebSocketHandler) {
36+
public WebSocketConfig(
37+
LiveWebSocketHandler liveWebSocketHandler, AdkWebCorsProperties corsProperties) {
3438
this.liveWebSocketHandler = liveWebSocketHandler;
39+
this.corsProperties = corsProperties;
3540
}
3641

3742
@Override
3843
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
39-
registry.addHandler(liveWebSocketHandler, "/run_live").setAllowedOrigins("*");
44+
WebSocketHandlerRegistration registration =
45+
registry.addHandler(liveWebSocketHandler, "/run_live");
46+
if (!corsProperties.origins().isEmpty()) {
47+
registration.setAllowedOrigins(corsProperties.origins().toArray(String[]::new));
48+
}
4049
}
4150
}
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)