This is an automated email from the ASF dual-hosted git repository.
Aias00 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/shenyu.git
The following commit(s) were added to refs/heads/master by this push:
new 75d4241f8f fix(admin): reject cross-namespace updates of namespace
plugin relations (#7526)
75d4241f8f is described below
commit 75d4241f8f8129347fa337b41219e2854d248c99
Author: Sean-Walker0 <[email protected]>
AuthorDate: Sat Oct 10 09:22:45 2026 +0800
fix(admin): reject cross-namespace updates of namespace plugin relations
(#7526)
update() loaded the relation by id and wrote the DTO over it without
comparing namespaces, so a request carrying another namespace's
namespaceId could rewrite the relation (config, enabled, sort) and even
move it to a different namespace via the rebuilt rel row.
Assert the loaded relation exists and belongs to the request
namespace before any write. Regression tests cover the
cross-namespace rejection, the unknown id rejection (both red on the
previous code), and the same-namespace success path.
Co-authored-by: Sean-Walker0
<[email protected]>
---
.../service/impl/NamespacePluginServiceImpl.java | 4 ++
.../admin/service/NamespacePluginServiceTest.java | 45 ++++++++++++++++++++++
2 files changed, 49 insertions(+)
diff --git
a/shenyu-admin/src/main/java/org/apache/shenyu/admin/service/impl/NamespacePluginServiceImpl.java
b/shenyu-admin/src/main/java/org/apache/shenyu/admin/service/impl/NamespacePluginServiceImpl.java
index f4f6f9c264..3298e06399 100644
---
a/shenyu-admin/src/main/java/org/apache/shenyu/admin/service/impl/NamespacePluginServiceImpl.java
+++
b/shenyu-admin/src/main/java/org/apache/shenyu/admin/service/impl/NamespacePluginServiceImpl.java
@@ -24,6 +24,7 @@ import com.google.common.collect.Lists;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.shenyu.admin.exception.ShenyuAdminException;
+import org.apache.shenyu.admin.utils.Assert;
import org.apache.shenyu.admin.mapper.NamespacePluginRelMapper;
import org.apache.shenyu.admin.mapper.PluginHandleMapper;
import org.apache.shenyu.admin.mapper.PluginMapper;
@@ -113,6 +114,9 @@ public class NamespacePluginServiceImpl implements
NamespacePluginService {
@Transactional(rollbackFor = Exception.class)
public String update(final NamespacePluginDTO namespacePluginDTO) {
final NamespacePluginVO before =
namespacePluginRelMapper.selectById(namespacePluginDTO.getId());
+ Assert.notNull(before, "Namespace plugin relation does not exist: " +
namespacePluginDTO.getId());
+ Assert.isTrue(Objects.equals(before.getNamespaceId(),
namespacePluginDTO.getNamespaceId()),
+ "Namespace plugin relation does not belong to namespace: " +
namespacePluginDTO.getNamespaceId());
NamespacePluginRelDO namespacePluginRelDO =
NamespacePluginRelDO.buildNamespacePluginRelDO(namespacePluginDTO);
if (namespacePluginRelMapper.updateSelective(namespacePluginRelDO) >
0) {
final NamespacePluginVO now =
namespacePluginRelMapper.selectById(namespacePluginDTO.getId());
diff --git
a/shenyu-admin/src/test/java/org/apache/shenyu/admin/service/NamespacePluginServiceTest.java
b/shenyu-admin/src/test/java/org/apache/shenyu/admin/service/NamespacePluginServiceTest.java
index 4fcdc9a9f9..64c90907ae 100644
---
a/shenyu-admin/src/test/java/org/apache/shenyu/admin/service/NamespacePluginServiceTest.java
+++
b/shenyu-admin/src/test/java/org/apache/shenyu/admin/service/NamespacePluginServiceTest.java
@@ -25,6 +25,9 @@ import org.apache.shenyu.admin.model.entity.PluginDO;
import org.apache.shenyu.admin.model.entity.PluginHandleDO;
import org.apache.shenyu.admin.model.entity.SelectorDO;
import org.apache.shenyu.admin.model.vo.NamespacePluginVO;
+import org.apache.shenyu.admin.utils.ShenyuResultMessage;
+import org.apache.shenyu.admin.model.dto.NamespacePluginDTO;
+import org.apache.shenyu.admin.exception.ShenyuAdminException;
import org.apache.shenyu.admin.model.vo.PluginSnapshotVO;
import org.apache.shenyu.admin.service.impl.NamespacePluginServiceImpl;
import org.apache.shenyu.admin.service.publish.NamespacePluginEventPublisher;
@@ -168,4 +171,46 @@ public final class NamespacePluginServiceTest {
// verify
Assertions.assertEquals(0, result.size());
}
+
+ @Test
+ public void testUpdateRejectsRelationFromAnotherNamespace() {
+ NamespacePluginDTO dto = new NamespacePluginDTO();
+ dto.setId("rel-1");
+ dto.setNamespaceId("namespace-a");
+ dto.setPluginId("pluginId");
+ NamespacePluginVO before = Mockito.mock(NamespacePluginVO.class);
+ Mockito.when(before.getNamespaceId()).thenReturn("namespace-b");
+
Mockito.when(namespacePluginRelMapper.selectById("rel-1")).thenReturn(before);
+
+ Assertions.assertThrows(ShenyuAdminException.class, () ->
namespacePluginService.update(dto));
+ Mockito.verify(namespacePluginRelMapper,
Mockito.never()).updateSelective(Mockito.any());
+ }
+
+ @Test
+ public void testUpdateRejectsUnknownRelation() {
+ NamespacePluginDTO dto = new NamespacePluginDTO();
+ dto.setId("missing-rel");
+ dto.setNamespaceId("namespace-a");
+ dto.setPluginId("pluginId");
+
Mockito.when(namespacePluginRelMapper.selectById("missing-rel")).thenReturn(null);
+
+ Assertions.assertThrows(ShenyuAdminException.class, () ->
namespacePluginService.update(dto));
+ Mockito.verify(namespacePluginRelMapper,
Mockito.never()).updateSelective(Mockito.any());
+ }
+
+ @Test
+ public void testUpdateAcceptsRelationFromSameNamespace() {
+ NamespacePluginDTO dto = new NamespacePluginDTO();
+ dto.setId("rel-1");
+ dto.setNamespaceId("namespace-a");
+ dto.setPluginId("pluginId");
+ NamespacePluginVO before = Mockito.mock(NamespacePluginVO.class);
+ Mockito.when(before.getNamespaceId()).thenReturn("namespace-a");
+ NamespacePluginVO after = Mockito.mock(NamespacePluginVO.class);
+
Mockito.when(namespacePluginRelMapper.selectById("rel-1")).thenReturn(before,
after);
+
Mockito.when(namespacePluginRelMapper.updateSelective(Mockito.any())).thenReturn(1);
+
+ Assertions.assertEquals(ShenyuResultMessage.UPDATE_SUCCESS,
namespacePluginService.update(dto));
+ Mockito.verify(namespacePluginEventPublisher).onUpdated(after, before);
+ }
}