From b705ba91d326427ffefe5fb72a9bc452af60059c Mon Sep 17 00:00:00 2001 From: "ye.zou" Date: Mon, 22 Jun 2026 11:09:01 +0800 Subject: [PATCH] [compute]: mask invalid userdata tag Malformed userdata can break the sensitive-output masking path when Rest.maskSensitiveInfo is enabled. The handler now falls back to replacing the whole userdata token if YAML parsing fails, preserving query success without exposing plaintext. Constraint: ZStack 4.8.38 verification must run inside PR docker instead of host Maven Rejected: Return original tag on YAML parse failure | would keep plaintext userdata visible when masking is enabled Confidence: high Scope-risk: narrow Tested: Docker verify-case: mvn -pl compute,testlib -am -DskipTests -DskipJacoco=true install; mvn test -Dtest=org.zstack.test.userdata.TestUserdataTagOutputHandler -Dsurefire.useFile=false -DskipJacoco=true Not-tested: Full CI pipeline Resolves: ZSTAC-65483 Change-Id: Icd82dcc2f8ec8972e6b238f9e9a63186facfe1a3 --- .../org/zstack/compute/vm/VmSystemTags.java | 8 +++- .../TestUserdataTagOutputHandler.java | 44 +++++++++++++++++++ 2 files changed, 51 insertions(+), 1 deletion(-) create mode 100644 test/src/test/java/org/zstack/test/userdata/TestUserdataTagOutputHandler.java diff --git a/compute/src/main/java/org/zstack/compute/vm/VmSystemTags.java b/compute/src/main/java/org/zstack/compute/vm/VmSystemTags.java index efb4f5e0ff8..70ff1fe1652 100755 --- a/compute/src/main/java/org/zstack/compute/vm/VmSystemTags.java +++ b/compute/src/main/java/org/zstack/compute/vm/VmSystemTags.java @@ -251,7 +251,13 @@ public String desensitizeTag(SystemTag systemTag, String tag) { } Yaml yaml = new Yaml(); - Object obj = yaml.load(userdata); + Object obj; + try { + obj = yaml.load(userdata); + } catch (RuntimeException e) { + tokens.put(t, "*****"); + continue; + } if (!(obj instanceof LinkedHashMap)) { return tag; } diff --git a/test/src/test/java/org/zstack/test/userdata/TestUserdataTagOutputHandler.java b/test/src/test/java/org/zstack/test/userdata/TestUserdataTagOutputHandler.java new file mode 100644 index 00000000000..f6b73498972 --- /dev/null +++ b/test/src/test/java/org/zstack/test/userdata/TestUserdataTagOutputHandler.java @@ -0,0 +1,44 @@ +package org.zstack.test.userdata; + +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.zstack.compute.vm.VmSystemTags; + +import java.util.Base64; +import java.util.HashMap; +import java.util.Map; + +public class TestUserdataTagOutputHandler { + private final VmSystemTags.UserdataTagOutputHandler handler = new VmSystemTags.UserdataTagOutputHandler(); + + @Before + public void setUp() throws NoSuchFieldException { + VmSystemTags.USERDATA.annotation = VmSystemTags.class.getField("USERDATA").getAnnotation(org.zstack.tag.SensitiveTag.class); + } + + @Test + public void desensitizeMalformedCloudConfigMasksUserdata() { + String userdata = "#cloud-config\nchpasswd:\n list: |\nroot:word\nexpire: False\n"; + String maskedTag = handler.desensitizeTag(VmSystemTags.USERDATA, userdataTag(userdata)); + + Assert.assertEquals("*****", VmSystemTags.USERDATA.getTokenByTag(maskedTag, VmSystemTags.USERDATA_TOKEN)); + } + + @Test + public void desensitizeValidCloudConfigKeepsStructuredMask() { + String userdata = "#cloud-config\nchpasswd:\n list: |\n root:word\n expire: False\n"; + String maskedTag = handler.desensitizeTag(VmSystemTags.USERDATA, userdataTag(userdata)); + String maskedUserdata = new String(Base64.getDecoder().decode( + VmSystemTags.USERDATA.getTokenByTag(maskedTag, VmSystemTags.USERDATA_TOKEN).getBytes())); + + Assert.assertTrue(maskedUserdata.contains("*****:*****")); + Assert.assertFalse(maskedUserdata.contains("root:word")); + } + + private String userdataTag(String userdata) { + Map tokens = new HashMap<>(); + tokens.put(VmSystemTags.USERDATA_TOKEN, new String(Base64.getEncoder().encode(userdata.getBytes()))); + return VmSystemTags.USERDATA.instantiateTag(tokens); + } +}