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[] =>