Skip to content

fix[storage]: desensitize addonInfo password fields in ExternalPrimaryStorageInventory#4120

Closed
zstack-robot-2 wants to merge 1 commit into
5.5.22from
sync/songtao.liu/fix/ZSTAC-80822-addon-password-mask-v3
Closed

fix[storage]: desensitize addonInfo password fields in ExternalPrimaryStorageInventory#4120
zstack-robot-2 wants to merge 1 commit into
5.5.22from
sync/songtao.liu/fix/ZSTAC-80822-addon-password-mask-v3

Conversation

@zstack-robot-2
Copy link
Copy Markdown
Collaborator

Problem

ExternalPrimaryStorageInventory constructor was not desensitizing password fields in the addonInfo field. When ZBS primary storage addonInfo contained mdsInfos entries with fields like password, sshPassword, or sshUsername, these were exposed in plaintext via API responses.

Root Cause

In ExternalPrimaryStorageInventory.java:65-66, the constructor deserialized addonInfo but never called desensitization:

// Before fix:
addonInfo = JSONObjectUtil.toObject(lvo.getAddonInfo(), LinkedHashMap.class);
// Missing: desensitizeAddonInfo(addonInfo);

Compare with config which already had desensitization applied.

Fix

Added desensitizeAddonInfo(addonInfo) call at line 66 and refactored to share a common desensitizeMdsInfos helper that masks password, sshPassword, and sshUsername within mdsInfos arrays. Both config and addonInfo now pass through the same masking logic for their mdsInfos entries.

Files changed (2)

  1. header/.../ExternalPrimaryStorageInventory.java — 1-line fix + extract common helper
  2. test/.../ExternalPrimaryStorageInventoryTest.java — 8 unit tests with init helper

Test Evidence

Running org.zstack.test.header.storage.addon.ExternalPrimaryStorageInventoryTest
Tests run: 8, Failures: 0, Errors: 0, Skipped: 0
BUILD SUCCESS

testAddonInfoPasswordDesensitized       PASS  (masks password in addonInfo.mdsInfos)
testAddonInfoSshPasswordDesensitized    PASS  (masks sshPassword + sshUsername)
testAddonInfoNullSafe                   PASS  (null addonInfo handled)
testAddonInfoNoMdsInfos                 PASS  (addonInfo without mdsInfos key)
testConfigMdsUrlsDesensitized           PASS  (masks user:pass in config.mdsUrls)
testConfigWithoutMdsInfosDoesNotLeak    PASS  (config mdsInfos also desensitized)
testConfigNullSafe                      PASS  (null config handled)
testConfigNoMdsUrls                     PASS  (config without mdsUrls key)

Command: mvn test -f test/pom.xml -o -Dtest=ExternalPrimaryStorageInventoryTest

🤖 Generated with Claude Code

sync from gitlab !10019

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 27, 2026

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

在 ExternalPrimaryStorageInventory 中新增对 addonInfo 的递归脱敏(基于敏感后缀/包含子串掩码、嵌套 Map/List 递归、以及按 URL 规则脱敏包含凭证的字符串值),并新增单元测试覆盖多种敏感/非敏感命名模式、嵌套与边界场景。

改动详情

敏感字段脱敏与测试验证

层级 / 文件 概述
敏感字段脱敏实现
header/src/main/java/org/zstack/header/storage/addon/primary/ExternalPrimaryStorageInventory.java
添加对 addonInfo 的脱敏调用:引入集合依赖与 MASK 常量,新增敏感 key 判定 (isSensitiveKey)、desensitizeAddonInfodesensitizeMapdesensitizeList(以及针对包含 :// 的字符串的 URL 脱敏);在构造器反序列化 addonInfo 后调用以递归脱敏敏感键并将 config 的脱敏改为复用该递归逻辑。
脱敏功能单元测试
test/src/test/java/org/zstack/test/header/storage/addon/ExternalPrimaryStorageInventoryTest.java
新增测试类并扩展用例,验证 addonInfoconfig 的多项行为:null/缺失容错、后缀/包含/大小写变体的敏感键掩码、嵌套 Map 的递归掩码、URL 中凭证的 ***@ /掩码表现、以及 mdsUrls/mdsInfos 的回退与空值场景。

🎯 代码审查工作量评估

🎯 4 (Complex) | ⏱️ ~45 分钟

诗句

🐰 我为密钥缝上六星布,
把密码藏在柔软处;
嵌套映射我轻轻掩,
URL 凭证换作星;
测试为灯,夜不孤。


Important

Pre-merge checks failed

Please resolve all errors before merging. Addressing warnings is optional.

❌ Failed checks (1 error, 1 warning)

Check name Status Explanation Resolution
Title check ❌ Error 标题超过72字符限制(86字符),不符合规范。 将标题缩短至72字符以内,例如:'fix[storage]: desensitize addonInfo passwords in ExternalPrimaryStorageInventory' 或更简洁的表述。
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Description check ✅ Passed 描述清晰地说明了问题、根本原因、修复方案和测试证据,与代码变更高度相关。
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch sync/songtao.liu/fix/ZSTAC-80822-addon-password-mask-v3

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

@MatheMatrix MatheMatrix force-pushed the sync/songtao.liu/fix/ZSTAC-80822-addon-password-mask-v3 branch from d3b2567 to 77eae8f Compare May 27, 2026 08:03
Copy link
Copy Markdown

@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.

🧹 Nitpick comments (1)
test/src/test/java/org/zstack/test/header/storage/addon/ExternalPrimaryStorageInventoryTest.java (1)

136-143: ⚡ Quick win

断言强度不足,建议使用 assertEquals 验证掩码值

当前使用 assertNotEquals 只验证了值不是原始密码,但没有验证它确实是预期的掩码值 "******"。如果脱敏逻辑出现意外行为(如返回 null 或其他字符串),测试仍可能通过。

♻️ 建议的修改
         Object mdsInfos = config.get("mdsInfos");
         if (mdsInfos instanceof List) {
             for (Object item : (List) mdsInfos) {
                 if (item instanceof Map) {
-                    Assert.assertNotEquals("shouldBeMasked", ((Map) item).get("sshPassword"));
+                    Assert.assertEquals("******", ((Map) item).get("sshPassword"));
                 }
             }
         }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@test/src/test/java/org/zstack/test/header/storage/addon/ExternalPrimaryStorageInventoryTest.java`
around lines 136 - 143, Replace the weak negative assertion with a strict
equality check so the test verifies the password was actually masked: in
ExternalPrimaryStorageInventoryTest where you iterate mdsInfos from config and
inspect ((Map) item).get("sshPassword"), change the Assert.assertNotEquals check
to Assert.assertEquals("******", ((Map) item).get("sshPassword")) (optionally
add an Assert.assertNotNull prior to equality if you want a clearer failure when
the key is missing).
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In
`@test/src/test/java/org/zstack/test/header/storage/addon/ExternalPrimaryStorageInventoryTest.java`:
- Around line 136-143: Replace the weak negative assertion with a strict
equality check so the test verifies the password was actually masked: in
ExternalPrimaryStorageInventoryTest where you iterate mdsInfos from config and
inspect ((Map) item).get("sshPassword"), change the Assert.assertNotEquals check
to Assert.assertEquals("******", ((Map) item).get("sshPassword")) (optionally
add an Assert.assertNotNull prior to equality if you want a clearer failure when
the key is missing).

ℹ️ 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: c0f91138-cad2-4169-a6a3-193a03c71291

📥 Commits

Reviewing files that changed from the base of the PR and between a621cf2 and d3b2567.

📒 Files selected for processing (2)
  • header/src/main/java/org/zstack/header/storage/addon/primary/ExternalPrimaryStorageInventory.java
  • test/src/test/java/org/zstack/test/header/storage/addon/ExternalPrimaryStorageInventoryTest.java

@MatheMatrix MatheMatrix force-pushed the sync/songtao.liu/fix/ZSTAC-80822-addon-password-mask-v3 branch from 77eae8f to 34611d8 Compare May 27, 2026 08:14
Copy link
Copy Markdown

@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

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@header/src/main/java/org/zstack/header/storage/addon/primary/ExternalPrimaryStorageInventory.java`:
- Line 79: 在 ExternalPrimaryStorageInventory 中补充 SENSITIVE_KEYS 集合,加入
"sshUsername"(当前只有 "password" 与 "sshPassword"),以确保 mdsInfos 等字段中的 sshUsername
不会明文返回;修改 SENSITIVE_KEYS 的初始化(保留现有项)添加 "sshUsername" 并确认序列化/脱敏逻辑仍使用该常量集合(引用
SENSITIVE_KEYS 的方法无需变更)。
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ 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: 0a8d9d23-7f00-4eb0-b5d4-ecf052ff5c19

📥 Commits

Reviewing files that changed from the base of the PR and between d3b2567 and 34611d8.

📒 Files selected for processing (2)
  • header/src/main/java/org/zstack/header/storage/addon/primary/ExternalPrimaryStorageInventory.java
  • test/src/test/java/org/zstack/test/header/storage/addon/ExternalPrimaryStorageInventoryTest.java
🚧 Files skipped from review as they are similar to previous changes (1)
  • test/src/test/java/org/zstack/test/header/storage/addon/ExternalPrimaryStorageInventoryTest.java

}

private static final String MASK = "******";
private static final Set<String> SENSITIVE_KEYS = new HashSet<>(Arrays.asList("password", "sshPassword"));
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

补全敏感键集合,避免 sshUsername 明文返回。

当前仅覆盖 passwordsshPasswordmdsInfos 中的 sshUsername 仍会透出,和本次修复目标不一致,存在敏感信息泄露风险。

🔧 建议修改
-    private static final Set<String> SENSITIVE_KEYS = new HashSet<>(Arrays.asList("password", "sshPassword"));
+    private static final Set<String> SENSITIVE_KEYS = new HashSet<>(Arrays.asList("password", "sshPassword", "sshUsername"));
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
private static final Set<String> SENSITIVE_KEYS = new HashSet<>(Arrays.asList("password", "sshPassword"));
private static final Set<String> SENSITIVE_KEYS = new HashSet<>(Arrays.asList("password", "sshPassword", "sshUsername"));
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@header/src/main/java/org/zstack/header/storage/addon/primary/ExternalPrimaryStorageInventory.java`
at line 79, 在 ExternalPrimaryStorageInventory 中补充 SENSITIVE_KEYS 集合,加入
"sshUsername"(当前只有 "password" 与 "sshPassword"),以确保 mdsInfos 等字段中的 sshUsername
不会明文返回;修改 SENSITIVE_KEYS 的初始化(保留现有项)添加 "sshUsername" 并确认序列化/脱敏逻辑仍使用该常量集合(引用
SENSITIVE_KEYS 的方法无需变更)。

@MatheMatrix MatheMatrix force-pushed the sync/songtao.liu/fix/ZSTAC-80822-addon-password-mask-v3 branch 6 times, most recently from f07ed64 to 27ba92b Compare May 27, 2026 09:19
Copy link
Copy Markdown

@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

🧹 Nitpick comments (1)
test/src/test/java/org/zstack/test/header/storage/addon/ExternalPrimaryStorageInventoryTest.java (1)

311-336: ⚡ Quick win

若实现补充 privateKey/secretKey 脱敏,此测试需同步更新。

此测试明确验证 secretKeyprivateKey 不被脱敏。如果采纳实现层的修复建议(将 "privatekey""secretkey" 加入敏感后缀集合),则需将这两个字段的断言修改为期望 "******"

-        Assert.assertEquals("sk-abc123", item.get("secretKey"));
-        Assert.assertEquals("-----BEGIN RSA-----", item.get("privateKey"));
+        Assert.assertEquals("******", item.get("secretKey"));
+        Assert.assertEquals("******", item.get("privateKey"));
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@test/src/test/java/org/zstack/test/header/storage/addon/ExternalPrimaryStorageInventoryTest.java`
around lines 311 - 336, The test testBareKeySuffixNotMasked assumes
secretKey/privateKey remain unmasked, but if the implementation now treats
"privatekey"/"secretkey" as sensitive suffixes the expected values should be
updated; modify the assertions in
ExternalPrimaryStorageInventoryTest.testBareKeySuffixNotMasked to expect the
masked value ("******") for item.get("secretKey") and item.get("privateKey")
(while keeping other assertions unchanged), ensuring the test aligns with
ExternalPrimaryStorageInventory.valueOf's masking behavior and the sensitive
suffix collection used by the code that masks addonInfo fields.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@header/src/main/java/org/zstack/header/storage/addon/primary/ExternalPrimaryStorageInventory.java`:
- Around line 79-90: The current suffix-based detection in
SENSITIVE_KEY_SUFFIXES and isSensitiveKey misses keys like "privateKey" and
"secretKey"; update the logic by either adding "privatekey" (lowercase) to
SENSITIVE_KEY_SUFFIXES and/or introduce a new exact-match set (e.g.,
SENSITIVE_KEY_EXACTS) containing names like "secretkey" and "privatekey", then
modify isSensitiveKey to check the exact-match set first (using
String.valueOf(key).toLowerCase()) and fall back to suffix checks; ensure you
reference and update the existing SENSITIVE_KEY_SUFFIXES and isSensitiveKey
symbols so both "privateKey" and "secretKey" are masked.

---

Nitpick comments:
In
`@test/src/test/java/org/zstack/test/header/storage/addon/ExternalPrimaryStorageInventoryTest.java`:
- Around line 311-336: The test testBareKeySuffixNotMasked assumes
secretKey/privateKey remain unmasked, but if the implementation now treats
"privatekey"/"secretkey" as sensitive suffixes the expected values should be
updated; modify the assertions in
ExternalPrimaryStorageInventoryTest.testBareKeySuffixNotMasked to expect the
masked value ("******") for item.get("secretKey") and item.get("privateKey")
(while keeping other assertions unchanged), ensuring the test aligns with
ExternalPrimaryStorageInventory.valueOf's masking behavior and the sensitive
suffix collection used by the code that masks addonInfo fields.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ 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: 2cf72a50-b8f2-4a38-93c4-33b0f384984c

📥 Commits

Reviewing files that changed from the base of the PR and between 34611d8 and 27ba92b.

📒 Files selected for processing (2)
  • header/src/main/java/org/zstack/header/storage/addon/primary/ExternalPrimaryStorageInventory.java
  • test/src/test/java/org/zstack/test/header/storage/addon/ExternalPrimaryStorageInventoryTest.java

@MatheMatrix MatheMatrix force-pushed the sync/songtao.liu/fix/ZSTAC-80822-addon-password-mask-v3 branch 6 times, most recently from 39bca1a to d7dbbbb Compare May 27, 2026 10:12
Copy link
Copy Markdown

@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

🧹 Nitpick comments (1)
test/src/test/java/org/zstack/test/header/storage/addon/ExternalPrimaryStorageInventoryTest.java (1)

306-320: ⚡ Quick win

建议补一条“无凭证但包含 @”的 URL 回归用例。

当前只覆盖了带凭证 URL 的脱敏,建议增加 http://10.0.0.1/path@tag 这类样例并断言原值不变,以防后续再次引入误脱敏。

可参考新增用例
+    `@Test`
+    public void testUrlWithoutUserInfoShouldNotBeMasked() {
+        ExternalPrimaryStorageVO vo = createVO();
+        vo.setAddonInfo("{"
+                + "\"endpoint\": \"http://10.0.0.1/path@tag\","
+                + "\"clusterName\": \"prod\""
+                + "}");
+
+        ExternalPrimaryStorageInventory inv = ExternalPrimaryStorageInventory.valueOf(vo);
+        Map addonInfo = inv.getAddonInfo();
+        Assert.assertEquals("http://10.0.0.1/path@tag", addonInfo.get("endpoint"));
+    }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@test/src/test/java/org/zstack/test/header/storage/addon/ExternalPrimaryStorageInventoryTest.java`
around lines 306 - 320, Add a new unit test that covers URLs containing "@" but
without credentials to ensure no desensitization; create an
ExternalPrimaryStorageVO, call vo.setAddonInfo with an endpoint like
"http://10.0.0.1/path@tag" (and other addon fields), convert it via
ExternalPrimaryStorageInventory.valueOf(vo), then inspect inv.getAddonInfo() and
assert the endpoint still contains the original "`@tag`" (and does not contain
"***@") and other fields (e.g., "clusterName") remain unchanged; place this
alongside testUrlInMapValueDesensitized and reuse the same helper createVO()
pattern.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@header/src/main/java/org/zstack/header/storage/addon/primary/ExternalPrimaryStorageInventory.java`:
- Around line 119-120: The current logic in ExternalPrimaryStorageInventory
calls desensitizeUrl for any string containing "://", which incorrectly trims
strings like "http://host/path@tag" that have '@' in the path, not as credential
user-info; update the desensitization so it only strips credentials when
user-info is present: modify the desensitizeUrl implementation (or the call
site) to parse the string as a URI (using java.net.URI), check uri.getUserInfo()
(or uri.getRawAuthority()) and only remove the user-info portion when
getUserInfo() != null; keep other strings intact. Ensure the change targets
ExternalPrimaryStorageInventory and desensitizeUrl so only URLs with actual
authority user-info are altered.

---

Nitpick comments:
In
`@test/src/test/java/org/zstack/test/header/storage/addon/ExternalPrimaryStorageInventoryTest.java`:
- Around line 306-320: Add a new unit test that covers URLs containing "@" but
without credentials to ensure no desensitization; create an
ExternalPrimaryStorageVO, call vo.setAddonInfo with an endpoint like
"http://10.0.0.1/path@tag" (and other addon fields), convert it via
ExternalPrimaryStorageInventory.valueOf(vo), then inspect inv.getAddonInfo() and
assert the endpoint still contains the original "`@tag`" (and does not contain
"***@") and other fields (e.g., "clusterName") remain unchanged; place this
alongside testUrlInMapValueDesensitized and reuse the same helper createVO()
pattern.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ 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: 5bb4a22d-e377-4271-ad3a-307c833dac49

📥 Commits

Reviewing files that changed from the base of the PR and between 27ba92b and d7dbbbb.

📒 Files selected for processing (2)
  • header/src/main/java/org/zstack/header/storage/addon/primary/ExternalPrimaryStorageInventory.java
  • test/src/test/java/org/zstack/test/header/storage/addon/ExternalPrimaryStorageInventoryTest.java

@MatheMatrix MatheMatrix force-pushed the sync/songtao.liu/fix/ZSTAC-80822-addon-password-mask-v3 branch 2 times, most recently from 0a3ff3d to c2904c1 Compare May 28, 2026 02:04
…yStorageInventory

Use exact whitelist (case-insensitive) for sensitive key detection:
password/sshPassword/privateKey/token/secret/accessKey/apiSecret/credential.
URL credential masking in config.mdsUrls/mdsInfos with colon-before-at guard.
No suffix/contains heuristic, no exclude lists.

Resolves: ZSTAC-80822
Change-Id: I80822-addon-password-desensitize
@MatheMatrix MatheMatrix force-pushed the sync/songtao.liu/fix/ZSTAC-80822-addon-password-mask-v3 branch from 8080e47 to 30e5a46 Compare May 28, 2026 03:07
@zstack-robot-1 zstack-robot-1 deleted the sync/songtao.liu/fix/ZSTAC-80822-addon-password-mask-v3 branch May 28, 2026 07:25
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.

1 participant