This is an automated email from the ASF dual-hosted git repository. lizhimins pushed a commit to branch rocketmq-studio in repository https://gitbox.apache.org/repos/asf/rocketmq-dashboard.git
commit 65801f4bd42bc5140033e1f2aa1b87ab0bdd230d Author: btlqql <[email protected]> AuthorDate: Fri Jul 17 16:40:10 2026 +0800 fix: validate audit query and cleanup parameters (#442) --- .../rocketmq/studio/ops/audit/AuditService.java | 19 +++++++++- .../studio/ops/audit/AuditServiceTest.java | 43 ++++++++++++++++++++++ 2 files changed, 60 insertions(+), 2 deletions(-) diff --git a/server/src/main/java/com/rocketmq/studio/ops/audit/AuditService.java b/server/src/main/java/com/rocketmq/studio/ops/audit/AuditService.java index f5968b8c..e1d98b89 100644 --- a/server/src/main/java/com/rocketmq/studio/ops/audit/AuditService.java +++ b/server/src/main/java/com/rocketmq/studio/ops/audit/AuditService.java @@ -17,6 +17,7 @@ package com.rocketmq.studio.ops.audit; import com.rocketmq.studio.common.domain.PageResult; +import com.rocketmq.studio.common.exception.BusinessException; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; @@ -38,6 +39,7 @@ public class AuditService { public PageResult<AuditRecordVO> queryLogs(int page, int pageSize, String search, String operationType, String startDate, String endDate, String result) { + validatePagination(page, pageSize); log.info("Querying audit logs, page={}, pageSize={}, search={}, operationType={}, result={}", page, pageSize, search, operationType, result); @@ -47,8 +49,9 @@ public class AuditService { List<AuditRecordVO> allRecords = auditRepository.findAll(search, operationType, start, end, result); long total = allRecords.size(); - int fromIndex = Math.min((page - 1) * pageSize, allRecords.size()); - int toIndex = Math.min(fromIndex + pageSize, allRecords.size()); + long offset = (long) (page - 1) * pageSize; + int fromIndex = (int) Math.min(offset, allRecords.size()); + int toIndex = (int) Math.min((long) fromIndex + pageSize, allRecords.size()); List<AuditRecordVO> pageRecords = allRecords.subList(fromIndex, toIndex); return PageResult.of(pageRecords, total, page, pageSize); @@ -56,11 +59,23 @@ public class AuditService { public int cleanupLogs(int beforeDays) { + if (beforeDays <= 0) { + throw new BusinessException(400, "beforeDays must be greater than 0"); + } log.info("Cleaning up audit logs older than {} days", beforeDays); LocalDateTime cutoff = LocalDateTime.now().minusDays(beforeDays); return auditRepository.deleteBefore(cutoff); } + private void validatePagination(int page, int pageSize) { + if (page <= 0) { + throw new BusinessException(400, "page must be greater than 0"); + } + if (pageSize <= 0) { + throw new BusinessException(400, "pageSize must be greater than 0"); + } + } + private LocalDateTime parseDate(String dateStr, boolean startOfDay) { if (dateStr == null || dateStr.isEmpty()) { return null; diff --git a/server/src/test/java/com/rocketmq/studio/ops/audit/AuditServiceTest.java b/server/src/test/java/com/rocketmq/studio/ops/audit/AuditServiceTest.java index e0446328..2abcb631 100644 --- a/server/src/test/java/com/rocketmq/studio/ops/audit/AuditServiceTest.java +++ b/server/src/test/java/com/rocketmq/studio/ops/audit/AuditServiceTest.java @@ -17,6 +17,7 @@ package com.rocketmq.studio.ops.audit; import com.rocketmq.studio.common.domain.PageResult; +import com.rocketmq.studio.common.exception.BusinessException; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.ArgumentCaptor; @@ -30,6 +31,7 @@ import java.util.Collections; import java.util.List; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.isNull; @@ -107,6 +109,35 @@ class AuditServiceTest { assertThat(result.getTotal()).isEqualTo(1); } + @Test + void queryLogsShouldRejectNonPositivePage() { + assertThatThrownBy(() -> auditService.queryLogs(0, 10, null, null, null, null, null)) + .isInstanceOf(BusinessException.class) + .hasMessage("page must be greater than 0") + .satisfies(ex -> assertThat(((BusinessException) ex).getCode()).isEqualTo(400)); + } + + @Test + void queryLogsShouldRejectNonPositivePageSize() { + assertThatThrownBy(() -> auditService.queryLogs(1, 0, null, null, null, null, null)) + .isInstanceOf(BusinessException.class) + .hasMessage("pageSize must be greater than 0") + .satisfies(ex -> assertThat(((BusinessException) ex).getCode()).isEqualTo(400)); + } + + @Test + void queryLogsShouldAvoidOffsetOverflow() { + AuditRecordVO record = AuditRecordVO.builder().operationType("CREATE").build(); + when(auditRepository.findAll(isNull(), isNull(), isNull(), isNull(), isNull())) + .thenReturn(List.of(record)); + + PageResult<AuditRecordVO> result = auditService.queryLogs( + Integer.MAX_VALUE, Integer.MAX_VALUE, null, null, null, null, null); + + assertThat(result.getItems()).isEmpty(); + assertThat(result.getTotal()).isEqualTo(1); + } + @Test void queryLogsShouldPassSearchFilterToRepository() { when(auditRepository.findAll(eq("topic-a"), isNull(), isNull(), isNull(), isNull())) @@ -191,6 +222,18 @@ class AuditServiceTest { assertThat(result).isZero(); } + @Test + void cleanupLogsShouldRejectNonPositiveRetention() { + assertThatThrownBy(() -> auditService.cleanupLogs(0)) + .isInstanceOf(BusinessException.class) + .hasMessage("beforeDays must be greater than 0") + .satisfies(ex -> assertThat(((BusinessException) ex).getCode()).isEqualTo(400)); + + assertThatThrownBy(() -> auditService.cleanupLogs(-1)) + .isInstanceOf(BusinessException.class) + .hasMessage("beforeDays must be greater than 0"); + } + @Test void queryLogsShouldHandleAllFiltersTogether() { when(auditRepository.findAll(eq("admin"), eq("DELETE"), any(LocalDateTime.class),
