Skip to content

<fix>[storage]: desensitize ExternalPrimaryStorage config and addonInfo#3536

Open
ZStack-Robot wants to merge 1 commit into5.5.12from
sync/jin.ma/fix/ZSTAC-80664-v2
Open

<fix>[storage]: desensitize ExternalPrimaryStorage config and addonInfo#3536
ZStack-Robot wants to merge 1 commit into5.5.12from
sync/jin.ma/fix/ZSTAC-80664-v2

Conversation

@ZStack-Robot
Copy link
Collaborator

问题

ZSTAC-80664: ZBS主存储 addonInfo.mdsInfos[].passwordconfig.mdsUrls 凭证未脱敏。

方案

1. addonInfo 不再存储密码

  • MdsInfo.passwordtransient,密码只保留在 config.mdsUrls

2. config 通过 desensitize 接口脱敏

  • 新增 ExternalPrimaryStorageConfig 接口(getIdentity() + desensitize()
  • ExternalPrimaryStorageInventory 自动扫描子类,构建 inventory 时调用 desensitize() 脱敏 config
  • ZBS Config 实现接口,将 root:password@host 脱敏为 root:*****@host

3. 对插件开发者

只需 Config 类实现 ExternalPrimaryStorageConfig,ZStack 自动扫描注册,零额外代码。

改动文件

  • ExternalPrimaryStorageConfig.java新增接口
  • ExternalPrimaryStorageInventory.java — 自动扫描 + desensitize 调用,移除 ye.zou 的手动脱敏
  • Config.java — 实现接口
  • MdsInfo.java — password 加 transient + @nologging
  • ZbsPrimaryStorageCase.groovy — 断言移除 password

测试

  • ZbsPrimaryStorageCase 全部通过
  • API: mdsUrls 脱敏,addonInfo 无 password
  • 日志: 零 mds 密码泄露

Resolves: ZSTAC-80664

sync from gitlab !9395

Resolves: ZSTAC-80664

Change-Id: I11e572e7d71d970853302cc33f5843637155a595
@coderabbitai
Copy link

coderabbitai bot commented Mar 19, 2026

概览

新增 ExternalPrimaryStorageConfig 接口以支持外部存储配置的通用化和脱敏处理,ZBS 插件实现该接口,库存类改进为通过配置类注册表调用类型特定的脱敏方法,同时为 MdsInfo 密码字段添加序列化控制。

变更

内聚块 / 文件 摘要
配置接口抽象
header/src/main/java/org/zstack/header/storage/addon/primary/ExternalPrimaryStorageConfig.java
新增公开接口定义,包含 getIdentity()desensitize() 两个必需方法,用于标识存储插件和返回脱敏后的配置副本。
库存类脱敏重构
header/src/main/java/org/zstack/header/storage/addon/primary/ExternalPrimaryStorageInventory.java
引入线程安全的静态配置类注册表(扫描 ExternalPrimaryStorageConfig 子类并按 getIdentity() 建立映射),构造函数改进为优先使用类型化脱敏,删除硬编码的脱敏助手方法。
ZBS 配置实现
plugin/zbs/src/main/java/org/zstack/storage/zbs/Config.java
Config 类实现 ExternalPrimaryStorageConfig 接口,新增 getIdentity() 返回 ZBS 标识,desensitize() 通过正则表达式掩蔽 URL 中的凭证部分。
数据模型序列化
plugin/zbs/src/main/java/org/zstack/storage/zbs/MdsInfo.java
实现 Serializable 接口,密码字段标记为 transient 并添加 @NoLogging 注解以排除序列化和日志输出。
测试用例更新
test/src/test/groovy/org/zstack/test/integration/storage/primary/addon/zbs/ZbsPrimaryStorageCase.groovy
移除测试断言中 mdsInfos 数组内所有条目的 password 字段,以验证脱敏后的配置不包含敏感信息。

代码审查工作量估评

🎯 3 (中等) | ⏱️ ~22 分钟

诗句

🐰 从配置中隐去密码秘密,
接口统一各存储身份,
注册表聪慧地寻觅,
脱敏确保数据清晰,
插件齐心守护隐私~ 🔐

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 22.22% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed 标题清晰描述了主要变更内容(脱敏 ExternalPrimaryStorage 配置和 addonInfo),符合指定的 [scope]: 格式(71 字符),完全相关于变更集。
Description check ✅ Passed 描述详细说明了问题、解决方案、修改文件和测试情况,与变更集完全相关,包含了凭证脱敏、接口设计和自动扫描机制等核心内容。

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch sync/jin.ma/fix/ZSTAC-80664-v2
📝 Coding Plan
  • Generate coding plan for human review comments

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
plugin/zbs/src/main/java/org/zstack/storage/zbs/MdsInfo.java (1)

24-39: ⚠️ Potential issue | 🟡 Minor

equals()/hashCode() 包含 transient 字段 password,可能导致序列化后行为不一致。

由于 password 被标记为 transient,在 JSON 序列化/反序列化后该字段会变为 null。这意味着:

  • 序列化前相等的两个 MdsInfo 对象,反序列化后可能不再相等
  • 如果将 MdsInfo 存入 HashSet 或作为 HashMap 的 key,反序列化后可能无法正确查找

建议从 equals()hashCode() 中移除 password 字段,仅使用 addrportusername 进行比较。

🛠️ 建议修改
     `@Override`
     public boolean equals(Object obj) {
         if (this == obj) return true;
         if (obj == null || getClass() != obj.getClass()) return false;

         MdsInfo other = (MdsInfo) obj;
         return Objects.equals(addr, other.addr) &&
                 Objects.equals(port, other.port) &&
-                Objects.equals(username, other.username) &&
-                Objects.equals(password, other.password);
+                Objects.equals(username, other.username);
     }

     `@Override`
     public int hashCode() {
-        return Objects.hash(addr, port, username, password);
+        return Objects.hash(addr, port, username);
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@plugin/zbs/src/main/java/org/zstack/storage/zbs/MdsInfo.java` around lines 24
- 39, The equals()/hashCode() implementations in class MdsInfo currently include
the transient field password which will be null after JSON deserialize; update
MdsInfo.equals(Object) and MdsInfo.hashCode() to exclude password and only
compare/hash addr, port and username (i.e. remove any reference to password in
both methods) so object equality and hashing remain stable across serialization.
🧹 Nitpick comments (1)
header/src/main/java/org/zstack/header/storage/addon/primary/ExternalPrimaryStorageInventory.java (1)

26-28: Class.newInstance() 已在 Java 9+ 中被弃用。

建议使用 clz.getDeclaredConstructor().newInstance() 替代,以避免弃用警告和潜在的兼容性问题。

♻️ 建议修改
             try {
-                ExternalPrimaryStorageConfig instance = clz.newInstance();
+                ExternalPrimaryStorageConfig instance = clz.getDeclaredConstructor().newInstance();
                 configClassRegistry.put(instance.getIdentity(), clz);
             } catch (Exception e) {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@header/src/main/java/org/zstack/header/storage/addon/primary/ExternalPrimaryStorageInventory.java`
around lines 26 - 28, Replace the deprecated clz.newInstance() call in
ExternalPrimaryStorageInventory with clz.getDeclaredConstructor().newInstance():
instantiate ExternalPrimaryStorageConfig via
clz.getDeclaredConstructor().newInstance(), then continue to register
instance.getIdentity() into configClassRegistry as before; also update the
surrounding exception handling to catch/declare ReflectiveOperationException (or
the specific NoSuchMethodException, InstantiationException,
IllegalAccessException, InvocationTargetException) so compilation and runtime
behavior remain correct.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@plugin/zbs/src/main/java/org/zstack/storage/zbs/Config.java`:
- Line 28: 当前用于掩码 URI 中凭据的正则 URI_CREDENTIAL_PATTERN =
Pattern.compile(":[^:@]*@") 无法处理密码中含 ':' 或 '@' 的情况;请改为不依赖字符排除的实现:在 Config.java
中移除或替换该正则,改用基于 MdsUri 解析逻辑的显式定位——使用 split(":", 2) 找到用户/密码起始位置并用 lastIndexOf("@")
找到凭据结束位置,然后用这两个索引之间的子串替换为掩码,或将正则改为匹配从第一个 ':' 到最后一个 '@'(非贪婪/明确定位)的模式以确保像
user:pass:word@host 和 user:pass@word@host 这类情况都能正确掩码。

---

Outside diff comments:
In `@plugin/zbs/src/main/java/org/zstack/storage/zbs/MdsInfo.java`:
- Around line 24-39: The equals()/hashCode() implementations in class MdsInfo
currently include the transient field password which will be null after JSON
deserialize; update MdsInfo.equals(Object) and MdsInfo.hashCode() to exclude
password and only compare/hash addr, port and username (i.e. remove any
reference to password in both methods) so object equality and hashing remain
stable across serialization.

---

Nitpick comments:
In
`@header/src/main/java/org/zstack/header/storage/addon/primary/ExternalPrimaryStorageInventory.java`:
- Around line 26-28: Replace the deprecated clz.newInstance() call in
ExternalPrimaryStorageInventory with clz.getDeclaredConstructor().newInstance():
instantiate ExternalPrimaryStorageConfig via
clz.getDeclaredConstructor().newInstance(), then continue to register
instance.getIdentity() into configClassRegistry as before; also update the
surrounding exception handling to catch/declare ReflectiveOperationException (or
the specific NoSuchMethodException, InstantiationException,
IllegalAccessException, InvocationTargetException) so compilation and runtime
behavior remain correct.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: http://open.zstack.ai:20001/code-reviews/zstack-cloud.yaml (via .coderabbit.yaml)

Review profile: CHILL

Plan: Pro

Run ID: 7665dddd-7950-434d-84bc-85c029431843

📥 Commits

Reviewing files that changed from the base of the PR and between f15be32 and a4df2a0.

📒 Files selected for processing (5)
  • header/src/main/java/org/zstack/header/storage/addon/primary/ExternalPrimaryStorageConfig.java
  • header/src/main/java/org/zstack/header/storage/addon/primary/ExternalPrimaryStorageInventory.java
  • plugin/zbs/src/main/java/org/zstack/storage/zbs/Config.java
  • plugin/zbs/src/main/java/org/zstack/storage/zbs/MdsInfo.java
  • test/src/test/groovy/org/zstack/test/integration/storage/primary/addon/zbs/ZbsPrimaryStorageCase.groovy

public Pool() {}
}

private static final Pattern URI_CREDENTIAL_PATTERN = Pattern.compile(":[^:@]*@");
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# 查找 MdsUri 类的实现,确认密码中特殊字符的处理方式
ast-grep --pattern $'class MdsUri {
  $$$
}'

# 搜索密码编码相关的处理逻辑
rg -n "encode|decode|URLEncoder|URLDecoder" --type java -g "**/zbs/**"

Repository: MatheMatrix/zstack

Length of output: 44


🏁 Script executed:

# 搜索 MdsUri 相关的所有文件
find . -type f -name "*MdsUri*" -o -name "*Uri*" | grep -i zbs | head -20

Repository: MatheMatrix/zstack

Length of output: 123


🏁 Script executed:

# 搜索包含 mdsUrl 或 URI 的代码
rg -n "mdsUrl|MdsUri" --type java -A 3 -B 3 | head -100

Repository: MatheMatrix/zstack

Length of output: 9709


🏁 Script executed:

# 查看 Config.java 文件的完整内容
find . -type f -path "**/zbs/**/Config.java" | head -5

Repository: MatheMatrix/zstack

Length of output: 123


🏁 Script executed:

# 搜索正则表达式的使用情况
rg -n "URI_CREDENTIAL_PATTERN" --type java

Repository: MatheMatrix/zstack

Length of output: 367


🏁 Script executed:

# 查看 desensitize 方法的调用情况和上下文
rg -n "desensitize" --type java -A 5 -B 5 | head -80

Repository: MatheMatrix/zstack

Length of output: 7898


🏁 Script executed:

# 查看 MdsUri.java 的完整内容,特别是密码处理
cat -n plugin/zbs/src/main/java/org/zstack/storage/zbs/MdsUri.java | head -150

Repository: MatheMatrix/zstack

Length of output: 5924


🏁 Script executed:

# 查看 Config.java 的完整内容
cat -n plugin/zbs/src/main/java/org/zstack/storage/zbs/Config.java

Repository: MatheMatrix/zstack

Length of output: 2872


🏁 Script executed:

# 搜索是否有任何 URLEncoder 或类似的编码处理
rg -n "URLEncoder|URLDecoder|encode|decode" plugin/zbs/src/main/java/org/zstack/storage/zbs/ -A 3 -B 3

Repository: MatheMatrix/zstack

Length of output: 44


正则表达式无法正确处理密码中包含特殊字符的情况。

MdsUri 的解析逻辑支持密码中包含 :@ 字符(通过 split(":", 2)lastIndexOf("@")),但当前正则 :[^:@]*@ 无法匹配这些情况:

  • user:pass:word@host - 密码含 :,正则无法匹配
  • user:pass@word@host - 只匹配 :pass@,留下 word@host 未掩码

应修改正则或采用其他方式来正确掩码含特殊字符的密码。

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@plugin/zbs/src/main/java/org/zstack/storage/zbs/Config.java` at line 28,
当前用于掩码 URI 中凭据的正则 URI_CREDENTIAL_PATTERN = Pattern.compile(":[^:@]*@") 无法处理密码中含
':' 或 '@' 的情况;请改为不依赖字符排除的实现:在 Config.java 中移除或替换该正则,改用基于 MdsUri 解析逻辑的显式定位——使用
split(":", 2) 找到用户/密码起始位置并用 lastIndexOf("@")
找到凭据结束位置,然后用这两个索引之间的子串替换为掩码,或将正则改为匹配从第一个 ':' 到最后一个 '@'(非贪婪/明确定位)的模式以确保像
user:pass:word@host 和 user:pass@word@host 这类情况都能正确掩码。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants