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


The following commit(s) were added to refs/heads/rocketmq-studio by this push:
     new ff0547fdd fix(web): rank multi-action ACL permission lists in the risk 
diagnostics (#4899)
ff0547fdd is described below

commit ff0547fdd4fe7c35c9a32161adf0a7283d05bf32
Author: 烤化の初雪 <[email protected]>
AuthorDate: Thu Sep 24 18:20:26 2026 +0800

    fix(web): rank multi-action ACL permission lists in the risk diagnostics 
(#4899)
    
    fix(acl): read the multi-action permission lists the server writes
    
    The rule editor lets one rule carry several actions and
    MybatisPlusAclRepository#joinNormalizedCsv stores them as one comma-joined
    value, which examineBrokerClusterAclConfig hands back verbatim as e.g.
    "order-events=PUB,SUB". normalizePermission only accepted a single exact
    DENY/PUB/SUB/ALL token, so every multi-action entry became UNKNOWN:
    
    - a valid PUB+SUB entry was reported as INVALID_PERMISSION_ENTRY
      ("权限条目格式无法识别"),
    - defaultTopicPerm/defaultGroupPerm of "PUB,SUB" ranked 0, so a 
default-allow
      account was silently absent from defaultAllowAccountCount and from the
      DEFAULT_TOPIC_ALLOW/DEFAULT_GROUP_ALLOW findings,
    - a "*=PUB,SUB" wildcard was downgraded from critical to warning.
    
    Parse the action list and rank the strongest action; PUB+SUB is full access,
    matching *=ALL.
    
    Regression tests: three cases in aclRiskDiagnostics.test.ts (invalid entry,
    default allow, wildcard severity), all failing before the change.
---
 web/src/utils/aclRiskDiagnostics.test.ts | 56 ++++++++++++++++++++++++++++++++
 web/src/utils/aclRiskDiagnostics.ts      | 25 +++++++++-----
 2 files changed, 72 insertions(+), 9 deletions(-)

diff --git a/web/src/utils/aclRiskDiagnostics.test.ts 
b/web/src/utils/aclRiskDiagnostics.test.ts
index c3648ec86..4b5c1c13e 100644
--- a/web/src/utils/aclRiskDiagnostics.test.ts
+++ b/web/src/utils/aclRiskDiagnostics.test.ts
@@ -169,4 +169,60 @@ describe('ACL risk diagnostics', () => {
       ]),
     );
   });
+
+  it('reads the comma-joined action list the server writes for a multi-action 
rule', () => {
+    const diagnostics = analyzeAclRisk(
+      config({
+        accounts: [
+          account({
+            // One rule with PUB and SUB checked is stored with its actions 
joined by a comma
+            // (MybatisPlusAclRepository#joinNormalizedCsv), and 
examineBrokerClusterAclConfig
+            // hands that value back verbatim as "resource=PUB,SUB".
+            topicPerms: ['order-events=PUB,SUB'],
+            groupPerms: ['cg-order=PUB,SUB'],
+          }),
+        ],
+      }),
+    );
+
+    expect(diagnostics.issues.map((item) => 
item.code)).not.toContain('INVALID_PERMISSION_ENTRY');
+    expect(diagnostics.summary.wildcardPermissionAccountCount).toBe(0);
+  });
+
+  it('counts a comma-joined default permission as an allow', () => {
+    const diagnostics = analyzeAclRisk(
+      config({
+        accounts: [account({ defaultTopicPerm: 'PUB,SUB', defaultGroupPerm: 
'DENY' })],
+      }),
+    );
+
+    expect(diagnostics.summary.defaultAllowAccountCount).toBe(1);
+    expect(diagnostics.issues).toEqual(
+      expect.arrayContaining([
+        expect.objectContaining({
+          code: 'DEFAULT_TOPIC_ALLOW',
+          severity: 'critical',
+          evidence: ['defaultTopicPerm=PUB,SUB'],
+        }),
+      ]),
+    );
+  });
+
+  it('ranks a publish+subscribe wildcard as full access', () => {
+    const diagnostics = analyzeAclRisk(
+      config({
+        accounts: [account({ topicPerms: ['*=PUB,SUB'] })],
+      }),
+    );
+
+    expect(diagnostics.issues).toEqual(
+      expect.arrayContaining([
+        expect.objectContaining({
+          code: 'WILDCARD_TOPIC_PERMISSION',
+          severity: 'critical',
+          evidence: ['*=PUB,SUB'],
+        }),
+      ]),
+    );
+  });
 });
diff --git a/web/src/utils/aclRiskDiagnostics.ts 
b/web/src/utils/aclRiskDiagnostics.ts
index 0cbda1159..dfe61fcb0 100644
--- a/web/src/utils/aclRiskDiagnostics.ts
+++ b/web/src/utils/aclRiskDiagnostics.ts
@@ -98,17 +98,24 @@ const PERMISSION_ALLOW_RANK: Record<AclPermission, number> 
= {
 
 const OPEN_WHITELIST_VALUES = new Set(['*', '0.0.0.0/0', '::/0', '0/0']);
 
+/**
+ * The rule editor lets one rule carry several actions, and the repository 
stores them as a
+ * single comma-joined value (MybatisPlusAclRepository#joinNormalizedCsv) that
+ * examineBrokerClusterAclConfig hands back verbatim as e.g. 
"order-events=PUB,SUB".
+ * Rank the strongest action of such a list instead of treating the whole 
value as unknown:
+ * PUB+SUB grants the same access as ALL, while a value with no recognised 
action stays UNKNOWN.
+ */
 const normalizePermission = (value?: string | null): AclPermission => {
-  const normalized = (value ?? '').trim().toUpperCase();
-  if (
-    normalized === 'DENY' ||
-    normalized === 'PUB' ||
-    normalized === 'SUB' ||
-    normalized === 'ALL'
-  ) {
-    return normalized;
+  const actions = (value ?? '')
+    .split(/[,\s;|]+/)
+    .map((action) => action.trim().toUpperCase())
+    .filter(Boolean);
+  if (actions.includes('ALL') || (actions.includes('PUB') && 
actions.includes('SUB'))) {
+    return 'ALL';
   }
-  return 'UNKNOWN';
+  if (actions.includes('PUB')) return 'PUB';
+  if (actions.includes('SUB')) return 'SUB';
+  return actions.includes('DENY') ? 'DENY' : 'UNKNOWN';
 };
 
 const splitWhitelist = (value?: string | null): string[] =>

Reply via email to