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);
+    }
 }

Reply via email to