Skip to content

Commit 697189d

Browse files
committed
<fix>[utils]: zmigrate imagestore
Resolves: ZSV-11449 Change-Id: I616767657867766b7170767463756e7777626574
1 parent f9e701b commit 697189d

3 files changed

Lines changed: 89 additions & 35 deletions

File tree

header/src/main/java/org/zstack/header/storage/backup/SoftwareUpgradePackageDeployMsg.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ public class SoftwareUpgradePackageDeployMsg extends NeedReplyMessage implements
1414
private String targetHostSshPassword;
1515
private String targetHostIp;
1616
private String upgradeScriptPath;
17+
private String softwareType;
1718

1819
@Override
1920
public String getBackupStorageUuid() {
@@ -87,4 +88,12 @@ public String getUpgradeScriptPath() {
8788
public void setUpgradeScriptPath(String upgradeScriptPath) {
8889
this.upgradeScriptPath = upgradeScriptPath;
8990
}
91+
92+
public String getSoftwareType() {
93+
return softwareType;
94+
}
95+
96+
public void setSoftwareType(String softwareType) {
97+
this.softwareType = softwareType;
98+
}
9099
}

plugin/ceph/src/main/java/org/zstack/storage/ceph/backup/CephBackupStorageBase.java

Lines changed: 14 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -92,13 +92,6 @@ void unlock() {
9292

9393
ReconnectMonLock reconnectMonLock = new ReconnectMonLock();
9494

95-
private static final Set<String> ALLOWED_URL_SCHEMES = Collections.unmodifiableSet(
96-
new HashSet<>(Arrays.asList("http", "https", "ftp", "sftp")));
97-
98-
// SSH username: only alphanumeric, dots, hyphens, underscores, optional trailing $
99-
private static final java.util.regex.Pattern SSH_USERNAME_PATTERN =
100-
java.util.regex.Pattern.compile("^[a-zA-Z0-9._-]+\\$?$");
101-
10295
@Autowired
10396
protected RESTFacade restf;
10497
@Autowired
@@ -753,6 +746,7 @@ public static class SoftwareUpgradePackageCmd extends AgentCommand implements Ha
753746
public String upgradePackagePath;
754747
public String upgradePackageTargetPath;
755748
public String upgradeScriptPath;
749+
public String softwareType;
756750
public int targetHostSshPort;
757751
public String targetHostSshUsername;
758752
@NoLogging
@@ -2207,27 +2201,13 @@ public void success(UploadFileResponse rsp) {
22072201
cmd.taskUuid = msg.getTaskUuid();
22082202
cmd.sendCommandUrl = restf.getSendCommandUrl();
22092203

2210-
String scheme;
2211-
try {
2212-
URI uri = new URI(msg.getUrl());
2213-
scheme = uri.getScheme();
2214-
} catch (URISyntaxException e) {
2215-
reply.setError(operr("failed to parse upload URL [%s]: %s", msg.getUrl(), e.getMessage()));
2216-
bus.reply(msg, reply);
2217-
return;
2218-
}
2219-
if (scheme == null || scheme.isEmpty()) {
2220-
reply.setError(operr("upload URL [%s] is missing a protocol prefix", msg.getUrl()));
2221-
bus.reply(msg, reply);
2222-
return;
2223-
}
2224-
if (!ALLOWED_URL_SCHEMES.contains(scheme.toLowerCase())) {
2225-
reply.setError(operr("upload URL [%s] uses unsupported protocol [%s], only %s are allowed",
2226-
msg.getUrl(), scheme, ALLOWED_URL_SCHEMES));
2204+
String[] urlResult = RemotePathValidator.validateAndExtractUrlScheme(msg.getUrl());
2205+
if (urlResult[0] != null) {
2206+
reply.setError(operr(urlResult[0]));
22272207
bus.reply(msg, reply);
22282208
return;
22292209
}
2230-
cmd.urlScheme = scheme;
2210+
cmd.urlScheme = urlResult[1];
22312211

22322212
httpCall(FILE_DOWNLOAD_PATH, cmd, DownloadFileResponse.class, new ReturnValueCompletion<DownloadFileResponse>(msg) {
22332213
@Override
@@ -2303,13 +2283,11 @@ protected void handle(final DeleteFilesOnBackupStorageHostMsg msg) {
23032283
}
23042284

23052285
// Validate each file path to prevent path traversal and injection attacks.
2306-
for (String filePath : msg.getFilePaths()) {
2307-
String pathErr = RemotePathValidator.validateRemotePath(filePath, "filePath");
2308-
if (pathErr != null) {
2309-
reply.setError(operr(pathErr));
2310-
bus.reply(msg, reply);
2311-
return;
2312-
}
2286+
String filePathErr = RemotePathValidator.validateFilePaths(msg.getFilePaths());
2287+
if (filePathErr != null) {
2288+
reply.setError(operr(filePathErr));
2289+
bus.reply(msg, reply);
2290+
return;
23132291
}
23142292

23152293
CephBackupStorageMonVO mon;
@@ -2414,9 +2392,9 @@ protected void handle(SoftwareUpgradePackageDeployMsg msg) {
24142392
return;
24152393
}
24162394

2417-
if (msg.getTargetHostSshUsername() == null || !SSH_USERNAME_PATTERN.matcher(msg.getTargetHostSshUsername()).matches()) {
2418-
reply.setError(operr("targetHostSshUsername [%s] is invalid, only alphanumeric characters, dots, hyphens, underscores and trailing dollar sign are allowed",
2419-
msg.getTargetHostSshUsername()));
2395+
String usernameErr = RemotePathValidator.validateSshUsername(msg.getTargetHostSshUsername());
2396+
if (usernameErr != null) {
2397+
reply.setError(operr(usernameErr));
24202398
bus.reply(msg, reply);
24212399
return;
24222400
}
@@ -2457,6 +2435,7 @@ protected void handle(SoftwareUpgradePackageDeployMsg msg) {
24572435
SoftwareUpgradePackageCmd cmd = new SoftwareUpgradePackageCmd();
24582436
cmd.upgradePackagePath = msg.getUpgradePackagePath();
24592437
cmd.upgradePackageTargetPath = msg.getUpgradePackageTargetPath();
2438+
cmd.softwareType = msg.getSoftwareType();
24602439
cmd.upgradeScriptPath = msg.getUpgradeScriptPath();
24612440
cmd.targetHostSshPort = msg.getTargetHostSshPort();
24622441
cmd.targetHostSshUsername = msg.getTargetHostSshUsername();

utils/src/main/java/org/zstack/utils/path/RemotePathValidator.java

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
package org.zstack.utils.path;
22

3+
import java.net.URI;
4+
import java.net.URISyntaxException;
35
import java.util.*;
6+
import java.util.Locale;
47
import java.util.regex.Matcher;
58
import java.util.regex.Pattern;
69

@@ -16,6 +19,13 @@ public class RemotePathValidator {
1619
"/", "/bin", "/boot", "/dev", "/etc", "/lib", "/lib64",
1720
"/proc", "/run", "/sbin", "/srv", "/sys", "/usr", "/var")));
1821

22+
public static final Set<String> ALLOWED_URL_SCHEMES = Collections.unmodifiableSet(
23+
new HashSet<>(Arrays.asList("http", "https", "ftp", "sftp")));
24+
25+
// SSH username: only alphanumeric, dots, hyphens, underscores, optional trailing $
26+
private static final Pattern SSH_USERNAME_PATTERN =
27+
Pattern.compile("^[a-zA-Z0-9._-]+\\$?$");
28+
1929
/**
2030
* Validate a remote path to be sent to an agent.
2131
* Returns null if valid, or an error message if invalid.
@@ -53,4 +63,60 @@ public static String validateRemotePath(String path, String paramName) {
5363
}
5464
return null;
5565
}
66+
67+
/**
68+
* Validate a URL scheme against the allowed list and return the lowercase scheme.
69+
* Returns a two-element array: [0] = error message (null if valid), [1] = scheme (null if invalid).
70+
* Parses the URI only once, combining validation and extraction.
71+
*/
72+
public static String[] validateAndExtractUrlScheme(String url) {
73+
if (url == null || url.isEmpty()) {
74+
return new String[]{"URL cannot be null or empty", null};
75+
}
76+
URI uri;
77+
try {
78+
uri = new URI(url);
79+
} catch (URISyntaxException e) {
80+
return new String[]{String.format("failed to parse URL [%s]: %s", url, e.getMessage()), null};
81+
}
82+
String scheme = uri.getScheme();
83+
if (scheme == null || scheme.isEmpty()) {
84+
return new String[]{String.format("URL [%s] is missing a protocol prefix", url), null};
85+
}
86+
String lowerScheme = scheme.toLowerCase(Locale.ROOT);
87+
if (!ALLOWED_URL_SCHEMES.contains(lowerScheme)) {
88+
return new String[]{String.format("URL [%s] uses unsupported protocol [%s], only %s are allowed",
89+
url, scheme, ALLOWED_URL_SCHEMES), null};
90+
}
91+
return new String[]{null, lowerScheme};
92+
}
93+
94+
/**
95+
* Validate an SSH username.
96+
* Returns null if valid, or an error message if invalid.
97+
*/
98+
public static String validateSshUsername(String username) {
99+
if (username == null || !SSH_USERNAME_PATTERN.matcher(username).matches()) {
100+
return String.format("SSH username [%s] is invalid, only alphanumeric characters, dots, hyphens, underscores and trailing dollar sign are allowed",
101+
username);
102+
}
103+
return null;
104+
}
105+
106+
/**
107+
* Validate a list of remote file paths.
108+
* Returns null if all valid, or the first error message encountered.
109+
*/
110+
public static String validateFilePaths(List<String> filePaths) {
111+
if (filePaths == null || filePaths.isEmpty()) {
112+
return null;
113+
}
114+
for (String filePath : filePaths) {
115+
String err = validateRemotePath(filePath, "filePath");
116+
if (err != null) {
117+
return err;
118+
}
119+
}
120+
return null;
121+
}
56122
}

0 commit comments

Comments
 (0)