Skip to content

Commit 5edb424

Browse files
committed
fix(console): detect Ollama from host/port only, not URL path
Avoid reclassifying OpenAI-compat endpoints with /ollama in the path as Ollama, which disabled private-IP SSRF blocking. Infer Ollama only from explicit provider, hostname containing "ollama", or port 11434.
1 parent 6ab89c0 commit 5edb424

2 files changed

Lines changed: 55 additions & 7 deletions

File tree

console/backend/toolkit/src/main/java/com/iflytek/astron/console/toolkit/service/model/ModelService.java

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -417,17 +417,28 @@ private static boolean looksLikeOllamaEndpoint(String endpoint) {
417417
return false;
418418
}
419419
String trimmed = endpoint.trim();
420-
String lower = trimmed.toLowerCase(Locale.ROOT);
421-
if (lower.contains("ollama")) {
422-
return true;
423-
}
424420
try {
425421
URL url = new URL(trimmed.contains("://") ? trimmed : "http://" + trimmed);
422+
String host = url.getHost();
423+
if (host != null && host.toLowerCase(Locale.ROOT).contains("ollama")) {
424+
return true;
425+
}
426426
int port = url.getPort();
427-
return port == OLLAMA_DEFAULT_PORT
428-
|| (port == -1 && lower.matches(".*:11434(?:/|$).*"));
427+
if (port == OLLAMA_DEFAULT_PORT) {
428+
return true;
429+
}
430+
if (port == -1) {
431+
String authority = url.getAuthority();
432+
return authority != null && authority.endsWith(":" + OLLAMA_DEFAULT_PORT);
433+
}
434+
return false;
429435
} catch (MalformedURLException e) {
430-
return lower.matches(".*:11434(?:/|$).*");
436+
String authority = trimmed.split("/", 2)[0];
437+
String lowerAuthority = authority.toLowerCase(Locale.ROOT);
438+
if (lowerAuthority.contains("ollama")) {
439+
return true;
440+
}
441+
return lowerAuthority.matches(".*:" + OLLAMA_DEFAULT_PORT + "$");
431442
}
432443
}
433444

console/backend/toolkit/src/test/java/com/iflytek/astron/console/toolkit/service/model/ModelServiceTest.java

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -828,6 +828,43 @@ void buildModelApiUrlRejectsPrivateIpWhenBlacklistIsEmpty() {
828828
"local-model"));
829829
}
830830

831+
@Test
832+
void resolveValidationProvider_doesNotInferOllamaFromPathSegment() {
833+
String provider = ReflectionTestUtils.invokeMethod(
834+
modelService,
835+
"resolveValidationProvider",
836+
"openai",
837+
"http://192.168.60.12/v1/ollama/chat");
838+
assertEquals("openai", provider);
839+
}
840+
841+
@Test
842+
void looksLikeOllamaEndpoint_detectsHostAndPortOnly() {
843+
assertTrue(ReflectionTestUtils.invokeMethod(
844+
ModelService.class, "looksLikeOllamaEndpoint", "http://127.0.0.1:11434"));
845+
assertTrue(ReflectionTestUtils.invokeMethod(
846+
ModelService.class, "looksLikeOllamaEndpoint", "http://ollama.local"));
847+
assertFalse(ReflectionTestUtils.invokeMethod(
848+
ModelService.class, "looksLikeOllamaEndpoint", "http://192.168.60.12/v1/ollama/chat"));
849+
}
850+
851+
@Test
852+
void buildModelApiUrlRejectsPrivateIpWhenPathContainsOllama() {
853+
ConfigInfo emptyConfig = new ConfigInfo();
854+
emptyConfig.setValue("");
855+
when(configInfoMapper.getListByCategory("NETWORK_SEGMENT_BLACK_LIST"))
856+
.thenReturn(List.of(emptyConfig));
857+
when(configInfoMapper.getListByCategory("IP_WHITE_LIST"))
858+
.thenReturn(List.of(emptyConfig));
859+
860+
assertThrows(BusinessException.class, () -> ReflectionTestUtils.invokeMethod(
861+
modelService,
862+
"buildModelApiUrlNew",
863+
"http://192.168.60.12/v1/ollama/chat",
864+
"openai",
865+
"local-model"));
866+
}
867+
831868
@Test
832869
void buildModelApiUrlAllowsPrivateIpForOllamaEndpoint() {
833870
ConfigInfo emptyConfig = new ConfigInfo();

0 commit comments

Comments
 (0)