mirror of
https://gitee.com/kekingcn/file-online-preview.git
synced 2026-11-14 07:10:39 +00:00
fix: reject control characters in FTP command arguments
This commit is contained in:
@@ -154,9 +154,22 @@ public class FtpUtils {
|
|||||||
} catch (URISyntaxException e) {
|
} catch (URISyntaxException e) {
|
||||||
throw new IOException("无效的FTP URL: " + ftpUrl, e);
|
throw new IOException("无效的FTP URL: " + ftpUrl, e);
|
||||||
}
|
}
|
||||||
|
// URI#getPath decodes percent escapes. Validate the values actually
|
||||||
|
// sent as FTP command arguments, before opening any connection.
|
||||||
|
validateCommandArgument(info.remoteFilePath);
|
||||||
|
validateCommandArgument(info.username);
|
||||||
|
validateCommandArgument(info.password);
|
||||||
|
validateCommandArgument(new String(info.remoteFilePath.getBytes(info.controlEncoding),
|
||||||
|
StandardCharsets.ISO_8859_1));
|
||||||
return info;
|
return info;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void validateCommandArgument(String value) throws IOException {
|
||||||
|
if (value != null && value.chars().anyMatch(c -> c < 0x20 || c == 0x7f)) {
|
||||||
|
throw new IOException("FTP command arguments must not contain control characters");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* FTP连接信息对象
|
* FTP连接信息对象
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -0,0 +1,46 @@
|
|||||||
|
package cn.keking.utils;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.junit.jupiter.params.ParameterizedTest;
|
||||||
|
import org.junit.jupiter.params.provider.ValueSource;
|
||||||
|
import org.springframework.test.util.ReflectionTestUtils;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
|
|
||||||
|
class FtpCommandSecurityTests {
|
||||||
|
@ParameterizedTest
|
||||||
|
@ValueSource(strings = {
|
||||||
|
"file.txt%0D%0ANOOP", "file.txt%0d%0aNOOP/clean.txt",
|
||||||
|
"file.txt%0D%0ANOOP?fullfilename=clean.txt", "file.txt%0aNOOP", "file.txt%00"
|
||||||
|
})
|
||||||
|
void rejectsDecodedControlsBeforeDownloadOrPreviewConnects(String path) {
|
||||||
|
String url = "ftp://127.0.0.1/" + path;
|
||||||
|
IOException download = assertThrows(IOException.class,
|
||||||
|
() -> FtpUtils.download(url, "1", "unused", "user", "pass", "UTF-8"));
|
||||||
|
IOException preview = assertThrows(IOException.class,
|
||||||
|
() -> FtpUtils.preview(url, "1", "unused", "user", "pass", "UTF-8"));
|
||||||
|
assertTrue(download.getMessage().contains("control characters"));
|
||||||
|
assertTrue(preview.getMessage().contains("control characters"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void rejectsControlsInCredentialsBeforeConnecting() {
|
||||||
|
IOException error = assertThrows(IOException.class,
|
||||||
|
() -> FtpUtils.preview("ftp://127.0.0.1/file.txt", "1", "unused",
|
||||||
|
"user\r\nNOOP", "pass", "UTF-8"));
|
||||||
|
assertTrue(error.getMessage().contains("control characters"));
|
||||||
|
error = assertThrows(IOException.class,
|
||||||
|
() -> FtpUtils.download("ftp://127.0.0.1/file.txt", "1", "unused",
|
||||||
|
"user", "pass\nNOOP", "UTF-8"));
|
||||||
|
assertTrue(error.getMessage().contains("control characters"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void preservesOrdinaryEncodedNamesAndDoesNotDoubleDecode() {
|
||||||
|
Object info = ReflectionTestUtils.invokeMethod(FtpUtils.class, "parseFtpConnectionInfo",
|
||||||
|
"ftp://files.example/a%20b/%E4%B8%AD%E6%96%87%250D.txt", "21", "user", "pass", "UTF-8");
|
||||||
|
assertEquals("/a b/中文%0D.txt", ReflectionTestUtils.getField(info, "remoteFilePath"));
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user