Compare commits

...
4 Commits
Author SHA1 Message Date
aias00 a6b834b3d4 Merge branch 'master' into fix/alert-82 2025-05-18 17:07:46 +08:00
Calvin 6ba0873b8f Merge branch 'master' into fix/alert-82 2025-05-18 13:48:30 +08:00
liuhy f1351cb7b9 fix alert 2025-05-17 15:13:23 +08:00
aias00andCopilot Autofix powered by AI 2ed39220be Potential fix for code scanning alert no. 82: 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:30 +08:00
2 changed files with 39 additions and 27 deletions
@@ -54,7 +54,15 @@ final class FlyBookAlertNotifyHandlerImpl extends AbstractAlertNotifyHandlerImpl
String notificationContent = JsonUtil.toJson(renderContent(noticeTemplate, alert));
// todo priority custom the color
String cardMessage = createLarkMessage(receiver.getUserId(), notificationContent, (byte) 1);
String webHookUrl = alerterProperties.getFlyBookWebhookUrl() + receiver.getAccessToken();
String baseUrl = alerterProperties.getFlyBookWebhookUrl();
if (!isValidBaseUrl(baseUrl)) {
throw new AlertNoticeException("Invalid base URL for FlyBook webhook.");
}
String accessToken = receiver.getAccessToken();
if (!isValidAccessToken(accessToken)) {
throw new AlertNoticeException("Invalid access token for FlyBook webhook.");
}
String webHookUrl = baseUrl + accessToken;
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
HttpEntity<String> flyEntity = new HttpEntity<>(cardMessage, headers);
@@ -202,4 +210,14 @@ final class FlyBookAlertNotifyHandlerImpl extends AbstractAlertNotifyHandlerImpl
public byte type() {
return 6;
}
private boolean isValidBaseUrl(String baseUrl) {
// Ensure the base URL is a trusted, fixed URL
return baseUrl != null && baseUrl.startsWith("https://trusted-domain.com/");
}
private boolean isValidAccessToken(String accessToken) {
// Validate the access token format (e.g., alphanumeric, specific length)
return accessToken != null && accessToken.matches("^[a-zA-Z0-9_-]{20,50}$");
}
}
@@ -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;
@@ -51,10 +52,10 @@ class FlyBookAlertNotifyHandlerImplTest {
@Mock
private RestTemplate restTemplate;
@Mock
private AlerterProperties alerterProperties;
@Mock
private ResourceBundle bundle;
@@ -70,28 +71,33 @@ class FlyBookAlertNotifyHandlerImplTest {
receiver = new NoticeReceiver();
receiver.setId(1L);
receiver.setName("test-receiver");
receiver.setAccessToken("a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6");
groupAlert = new GroupAlert();
SingleAlert singleAlert = new SingleAlert();
singleAlert.setLabels(new HashMap<>());
singleAlert.getLabels().put("severity", "critical");
singleAlert.getLabels().put("alertname", "Test Alert");
List<SingleAlert> alerts = new ArrayList<>();
alerts.add(singleAlert);
groupAlert.setAlerts(alerts);
template = new NoticeTemplate();
template.setId(1L);
template.setName("test-template");
template.setContent("test content");
when(bundle.getString("alerter.notify.title")).thenReturn("Alert Notification");
lenient().when(bundle.getString("alerter.notify.title")).thenReturn("Alert Notification");
lenient().when(alerterProperties.getFlyBookWebhookUrl()).thenReturn("https://trusted-domain.com/");
lenient().when(alerterProperties.getConsoleUrl()).thenReturn("https://console.example.com");
}
@Test
public void testNotifyAlertWithInvalidToken() {
assertThrows(AlertNoticeException.class,
public void testNotifyAlertWithInvalidUrl() {
when(alerterProperties.getFlyBookWebhookUrl()).thenReturn("https://untrusted-domain.com/");
assertThrows(AlertNoticeException.class,
() -> flyBookAlertNotifyHandler.send(receiver, template, groupAlert));
}
@@ -99,33 +105,21 @@ class FlyBookAlertNotifyHandlerImplTest {
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);
flyBookAlertNotifyHandler.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.OK);
public void testNotifyAlertWithInvalidToken() {
receiver.setAccessToken("invalid");
when(restTemplate.postForEntity(
any(String.class),
any(),
eq(CommonRobotNotifyResp.class)
)).thenReturn(responseEntity);
assertThrows(AlertNoticeException.class,
assertThrows(AlertNoticeException.class,
() -> flyBookAlertNotifyHandler.send(receiver, template, groupAlert));
}
}