Compare commits

...
5 Commits
Author SHA1 Message Date
aias00 34c33163d8 Merge branch 'master' into fix/alert-83 2025-05-18 17:13:37 +08:00
Calvin e365e8a090 Merge branch 'master' into fix/alert-83 2025-05-18 13:48:18 +08:00
liuhy 3e8ec20b22 fix alert 2025-05-17 15:15:22 +08:00
liuhy 458082707e fix alert 2025-05-17 15:04:01 +08:00
aias00andCopilot Autofix powered by AI 3925998bb6 Potential fix for code scanning alert no. 83: Server-side request forgery
Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
Signed-off-by: aias00 <liuhongyu@apache.org>
2025-05-17 14:29:05 +08:00
2 changed files with 37 additions and 26 deletions
@@ -32,6 +32,8 @@ import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import java.util.List;
/**
* Gotify alert notify handler
*/
@@ -63,6 +65,9 @@ public class GotifyAlertNotifyHandlerImpl extends AbstractAlertNotifyHandlerImpl
headers.setContentType(MediaType.APPLICATION_JSON);
HttpEntity<GotifyWebHookDto> httpEntity = new HttpEntity<>(gotifyWebHookDto, headers);
String webHookUrl = String.format(alerterProperties.getGotifyWebhookUrl(), receiver.getGotifyToken());
if (!isValidWebhookUrl(webHookUrl)) {
throw new AlertNoticeException("Invalid Gotify webhook URL: " + webHookUrl);
}
ResponseEntity<CommonRobotNotifyResp> responseEntity = restTemplate.postForEntity(webHookUrl,
httpEntity, CommonRobotNotifyResp.class);
if (responseEntity.getStatusCode() == HttpStatus.OK) {
@@ -104,4 +109,19 @@ public class GotifyAlertNotifyHandlerImpl extends AbstractAlertNotifyHandlerImpl
}
}
/**
* Validate the webhook URL against a whitelist of allowed base URLs.
*
* @param url the webhook URL to validate
* @return true if the URL is valid, false otherwise
*/
private boolean isValidWebhookUrl(String url) {
// Define a whitelist of allowed base URLs
List<String> allowedBaseUrls = List.of(
"https://trusted-gotify-server.com",
"https://another-trusted-server.com"
);
return allowedBaseUrls.stream().anyMatch(url::startsWith);
}
}
@@ -20,6 +20,7 @@ package org.apache.hertzbeat.alert.notice.impl;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.lenient;
import static org.mockito.Mockito.when;
import org.apache.hertzbeat.alert.AlerterProperties;
@@ -48,7 +49,7 @@ import java.util.ResourceBundle;
*/
@ExtendWith(MockitoExtension.class)
class GotifyAlertNotifyHandlerImplTest {
@Mock
private RestTemplate restTemplate;
@@ -57,20 +58,21 @@ class GotifyAlertNotifyHandlerImplTest {
@Mock
private ResourceBundle bundle;
@InjectMocks
private GotifyAlertNotifyHandlerImpl gotifyAlertNotifyHandler;
private NoticeReceiver receiver;
private GroupAlert groupAlert;
private NoticeTemplate template;
@BeforeEach
public void setUp() {
receiver = new NoticeReceiver();
receiver.setId(1L);
receiver.setName("test-receiver");
receiver.setAccessToken("test-token");
receiver.setGotifyToken("A845h__ZMqDxZlO");
groupAlert = new GroupAlert();
SingleAlert singleAlert = new SingleAlert();
@@ -87,41 +89,30 @@ class GotifyAlertNotifyHandlerImplTest {
template.setName("test-template");
template.setContent("test content");
when(bundle.getString("alerter.notify.title")).thenReturn("Alert Notification");
when(alerterProperties.getGotifyWebhookUrl()).thenReturn("http://localhost:8080/gotify/%s");
lenient().when(bundle.getString("alerter.notify.title")).thenReturn("Alert Notification");
lenient().when(alerterProperties.getGotifyWebhookUrl())
.thenReturn("https://trusted-gotify-server.com/%s");
}
@Test
public void testNotifyAlertSuccess() {
CommonRobotNotifyResp successResp = new CommonRobotNotifyResp();
successResp.setErrCode(0);
ResponseEntity<CommonRobotNotifyResp> responseEntity =
new ResponseEntity<>(successResp, HttpStatus.OK);
ResponseEntity<CommonRobotNotifyResp> responseEntity = new ResponseEntity<>(successResp, HttpStatus.OK);
when(restTemplate.postForEntity(
any(String.class),
any(),
eq(CommonRobotNotifyResp.class)
)).thenReturn(responseEntity);
eq(CommonRobotNotifyResp.class))).thenReturn(responseEntity);
gotifyAlertNotifyHandler.send(receiver, template, groupAlert);
}
@Test
public void testNotifyAlertFailure() {
CommonRobotNotifyResp failResp = new CommonRobotNotifyResp();
failResp.setCode(1);
failResp.setErrMsg("Test Error");
ResponseEntity<CommonRobotNotifyResp> responseEntity =
new ResponseEntity<>(failResp, HttpStatus.BAD_REQUEST);
when(restTemplate.postForEntity(
any(String.class),
any(),
eq(CommonRobotNotifyResp.class)
)).thenReturn(responseEntity);
public void testNotifyAlertWithInvalidUrl() {
when(alerterProperties.getGotifyWebhookUrl()).thenReturn("http://untrusted-server.com/%s");
assertThrows(AlertNoticeException.class,
assertThrows(AlertNoticeException.class,
() -> gotifyAlertNotifyHandler.send(receiver, template, groupAlert));
}
}