Bug 1891234, additional filename filter checks, r=Gijs,extension-reviewers

Differential Revision: https://phabricator.services.mozilla.com/D208659
This commit is contained in:
Neil Deakin 2024-05-14 17:35:05 +00:00
parent 75385837ce
commit e17b53a054
6 changed files with 70 additions and 61 deletions

View file

@ -34,29 +34,29 @@ add_task(async function test_sanitize() {
// Platform-dependent conversion of special characters to spaces.
const kSpecialChars = 'A:*?|""<<>>;,+=[]B][=+,;>><<""|?*:C';
if (AppConstants.platform == "android") {
testSanitize(kSpecialChars, "A B C");
testSanitize(" :: Website :: ", "Website");
testSanitize("* Website!", "Website!");
testSanitize("Website | Page!", "Website Page!");
testSanitize("Directory Listing: /a/b/", "Directory Listing _a_b_");
testSanitize(kSpecialChars, "A________________B________________C");
testSanitize(" :: Website :: ", "__ Website __");
testSanitize("* Website!", "_ Website!");
testSanitize("Website | Page!", "Website _ Page!");
testSanitize("Directory Listing: /a/b/", "Directory Listing_ _a_b_");
} else if (AppConstants.platform == "win") {
testSanitize(kSpecialChars, "A ;,+=[]B][=+,; C");
testSanitize(" :: Website :: ", "Website");
testSanitize("* Website!", "Website!");
testSanitize("Website | Page!", "Website Page!");
testSanitize("Directory Listing: /a/b/", "Directory Listing _a_b_");
testSanitize(kSpecialChars, "A__________;,+=[]B][=+,;__________C");
testSanitize(" :: Website :: ", "__ Website __");
testSanitize("* Website!", "_ Website!");
testSanitize("Website | Page!", "Website _ Page!");
testSanitize("Directory Listing: /a/b/", "Directory Listing_ _a_b_");
} else if (AppConstants.platform == "macosx") {
testSanitize(kSpecialChars, "A ;,+=[]B][=+,; C");
testSanitize(" :: Website :: ", "Website");
testSanitize("* Website!", "Website!");
testSanitize("Website | Page!", "Website Page!");
testSanitize("Directory Listing: /a/b/", "Directory Listing _a_b_");
testSanitize(kSpecialChars, "A__________;,+=[]B][=+,;__________C");
testSanitize(" :: Website :: ", "__ Website __");
testSanitize("* Website!", "_ Website!");
testSanitize("Website | Page!", "Website _ Page!");
testSanitize("Directory Listing: /a/b/", "Directory Listing_ _a_b_");
} else {
testSanitize(kSpecialChars, "A ;,+=[]B][=+,; C");
testSanitize(" :: Website :: ", "Website");
testSanitize("* Website!", "Website!");
testSanitize("Website | Page!", "Website Page!");
testSanitize("Directory Listing: /a/b/", "Directory Listing _a_b_");
testSanitize(kSpecialChars, "A__________;,+=[]B][=+,;__________C");
testSanitize(" :: Website :: ", "__ Website __");
testSanitize("* Website!", "_ Website!");
testSanitize("Website | Page!", "Website _ Page!");
testSanitize("Directory Listing: /a/b/", "Directory Listing_ _a_b_");
}
// Conversion of consecutive runs of slashes and backslashes to underscores.
@ -64,9 +64,9 @@ add_task(async function test_sanitize() {
// Removal of leading and trailing whitespace and dots after conversion.
testSanitize(" Website ", "Website");
testSanitize(". . Website . Page . .", "Website . Page");
testSanitize(" File . txt ", "File . txt");
testSanitize("\f\n\r\t\v\x00\x1f\x7f\x80\x9f\xa0 . txt", "txt");
testSanitize(". . Website . Page . .", "Website .Page");
testSanitize(" File . txt ", "File .txt");
testSanitize("\f\n\r\t\v\x00\x1f\x7f\x80\x9f\xa0 . txt", "_________ .txt");
testSanitize("\u1680\u180e\u2000\u2008\u200a . txt", "txt");
testSanitize("\u2028\u2029\u202f\u205f\u3000\ufeff . txt", "txt");
@ -77,25 +77,25 @@ add_task(async function test_sanitize() {
testSanitize(" . ", "");
// Stripping of BIDI formatting characters.
testSanitize("\u200e \u202b\u202c\u202d\u202etest\x7f\u200f", "_ ____test _");
testSanitize("AB\x7f\u202a\x7f\u202a\x7fCD", "AB _ _ CD");
testSanitize("\u200e \u202b\u202c\u202d\u202etest\x7f\u200f", "_ ____test__");
testSanitize("AB\x7f\u202a\x7f\u202a\x7fCD", "AB_____CD");
// Stripping of colons:
testSanitize("foo:bar", "foo bar");
testSanitize("foo:bar", "foo_bar");
// not compressing whitespaces.
testSanitize("foo : bar", "foo bar", { compressWhitespaces: false });
testSanitize("foo : bar", "foo _ bar", { compressWhitespaces: false });
testSanitize("thing.lnk", "thing.lnk.download");
testSanitize("thing.lnk\n", "thing.lnk.download");
testSanitize("thing.lnk\n", "thing.lnk_");
testSanitize("thing.lnk", "thing.lnk", {
allowInvalidFilenames: true,
});
testSanitize("thing.lnk\n", "thing.lnk", {
testSanitize("thing.lnk\n", "thing.lnk_", {
allowInvalidFilenames: true,
});
testSanitize("thing.URl", "thing.URl.download");
testSanitize("thing.URl \n", "thing.URl", {
testSanitize("thing.URl \n", "thing.URl_", {
allowInvalidFilenames: true,
});
@ -107,12 +107,12 @@ add_task(async function test_sanitize() {
allowInvalidFilenames: true,
});
testSanitize("thing.local|", "thing.local.download");
testSanitize("thing.lo|cal", "thing.lo cal");
testSanitize('thing.local/*"', "thing.local_");
testSanitize("thing.local|", "thing.local_");
testSanitize("thing.lo|cal", "thing.lo_cal");
testSanitize('thing.local/*"', "thing.local___");
testSanitize("thing.desktoP", "thing.desktoP.download");
testSanitize("thing.desktoP \n", "thing.desktoP", {
testSanitize("thing.desktoP \n", "thing.desktoP_", {
allowInvalidFilenames: true,
});
});

View file

@ -83,7 +83,7 @@ add_task(async function test_decoded_filename_download() {
const FILE_NAME_DECODED_2 = "file\u{0001F6B2}encoded.txt";
const FILE_NAME_ENCODED_URL_2 = BASE + "/" + FILE_NAME_ENCODED_2;
const FILE_NAME_ENCODED_3 = "file%X%20encode.txt";
const FILE_NAME_DECODED_3 = "file%X encode.txt";
const FILE_NAME_DECODED_3 = "file_X encode.txt";
const FILE_NAME_ENCODED_URL_3 = BASE + "/" + FILE_NAME_ENCODED_3;
const FILE_NAME_ENCODED_4 = "file%E3%80%82encode.txt";
const FILE_NAME_DECODED_4 = "file\u3002encode.txt";

View file

@ -3485,8 +3485,8 @@ void nsExternalHelperAppService::SanitizeFileName(nsAString& aFileName,
nsAutoString fileName(aFileName);
// Replace known invalid characters.
fileName.ReplaceChar(u"" KNOWN_PATH_SEPARATORS, u'_');
fileName.ReplaceChar(u"" FILE_ILLEGAL_CHARACTERS, u' ');
fileName.ReplaceChar(u"" KNOWN_PATH_SEPARATORS FILE_ILLEGAL_CHARACTERS "%",
u'_');
fileName.StripChar(char16_t(0));
const char16_t *startStr, *endStr;
@ -3668,6 +3668,14 @@ void nsExternalHelperAppService::SanitizeFileName(nsAString& aFileName,
outFileName.Truncate(lastNonTrimmable);
}
nsAutoString extension;
int32_t dotidx = outFileName.RFind(u".");
if (dotidx != -1) {
extension = Substring(outFileName, dotidx + 1);
extension.StripWhitespace();
outFileName = Substring(outFileName, 0, dotidx + 1) + extension;
}
#ifdef XP_WIN
if (nsLocalFile::CheckForReservedFileName(outFileName)) {
outFileName.Truncate();

View file

@ -18,7 +18,7 @@
<img id="i1" src="http://localhost:8000/save_filename.sjs?type=png&filename=simple.png" data-filename="simple.png">
<!-- invalid characters in the filename -->
<img id="i2" src="http://localhost:8000/save_filename.sjs?type=png&filename=invalidfilename/a:b*c%63d.png" data-filename="invalidfilename_a b ccd.png">
<img id="i2" src="http://localhost:8000/save_filename.sjs?type=png&filename=invalidfilename/a:b*c%63d.png" data-filename="invalidfilename_a_b_ccd.png">
<!-- invalid extension for a png image -->
<img id="i3" src="http://localhost:8000/save_filename.sjs?type=png&filename=invalidextension.pang" data-filename="invalidextension.png">
@ -329,11 +329,11 @@
<!-- filename which is changed to an invalid filename within the file picker -->
<a id="mod1" href="http://localhost:8000/save_filename.sjs?type=png&filename=orange.png"
data-pickedfilename='"peach".png' data-filename='peach .png'>
data-pickedfilename='"peach".png' data-filename='_peach_.png'>
<!-- an invalid filename which is changed to another invalid filename within the file picker -->
<a id="mod2" href="http://localhost:8000/save_filename.sjs?type=png&filename=%maroon%34.png"
data-pickedfilename='"violet".png' data-filename="violet .png">
data-pickedfilename='"violet".png' data-filename="_violet_.png">
</span>

View file

@ -11,7 +11,7 @@
var tests = [
["test.png:large", "test.png"],
["test.png/large", "test.png"],
[":test.png::large:", "test.png"],
[":test.png::large:", "_test.png"],
];
add_task(async function() {

View file

@ -31,8 +31,9 @@ add_task(async function validate_filename_method() {
Assert.equal(checkFilename("\\path.png", 0), "_path.png");
Assert.equal(
checkFilename("\\path*and/$?~file.png", 0),
"_path and_$ ~file.png"
"_path_and_$_~file.png"
);
Assert.equal(
checkFilename(" \u180e whit\u180ee.png \u180e", 0),
"whit\u180ee.png"
@ -103,12 +104,12 @@ add_task(async function validate_filename_method() {
// For whatever reason, the Android mime handler accepts the .jpeg
// extension for image/png, so skip this test there.
if (AppConstants.platform != "android") {
Assert.equal(checkFilename("thi/*rd.jpeg", 0), "thi_ rd.png");
Assert.equal(checkFilename("thi/*rd.jpeg", 0), "thi__rd.png");
}
Assert.equal(
checkFilename("f*\\ourth file.jpg", mimeService.VALIDATE_SANITIZE_ONLY),
"f _ourth file.jpg"
"f__ourth file.jpg"
);
Assert.equal(
checkFilename(
@ -116,7 +117,7 @@ add_task(async function validate_filename_method() {
mimeService.VALIDATE_SANITIZE_ONLY |
mimeService.VALIDATE_DONT_COLLAPSE_WHITESPACE
),
"f _ift h.jpe _g"
"f__ift h.jpe__g"
);
Assert.equal(checkFilename("sixth.j pe/*g", 0), "sixth.png");
@ -157,25 +158,25 @@ add_task(async function validate_filename_method() {
repeatStr.substring(0, 254 - ext.length) + ext
);
ext = "lo%?n/ginvalid? ch\\ars";
ext = "lo#?n/ginvalid? ch\\ars";
Assert.equal(
checkFilename(repeatStr + ext, mimeService.VALIDATE_SANITIZE_ONLY),
repeatStr + "lo% n_"
repeatStr + "lo#_n_"
);
ext = ".long/invalid%? ch\\ars";
ext = ".long/invalid#? ch\\ars";
Assert.equal(
checkFilename(repeatStr + ext, mimeService.VALIDATE_SANITIZE_ONLY),
repeatStr.substring(0, 233) + ".long_invalid% ch_ars"
repeatStr.substring(0, 232) + ".long_invalid#_ch_ars"
);
Assert.equal(
checkFilename("test_テスト_T\x83E\\S\x83T.png", 0),
"test_テスト_T E_S T.png"
"test_テスト_T_E_S_T.png"
);
Assert.equal(
checkFilename("test_テスト_T\x83E\\S\x83T.pテ\x83ng", 0),
"test_テスト_T E_S T.png"
"test_テスト_T_E_S_T.png"
);
// Check we don't invalidate surrogate pairs when trimming.
@ -248,11 +249,11 @@ add_task(async function validate_filename_method() {
// cropped to fit into 255 bytes.
Assert.equal(
mimeService.validateFileNameForSaving(
"라이브9.9만 시청컬처렐 다이제스티브 3박스 - 3박스 더 (뚱랑이 굿즈 증정) - 선물용 쇼핑백 2장컬처렐 다이제스티브 3박스 - 3박스 더 (뚱랑이 굿즈 증정) - 선물용 쇼핑백 2장24%102 000원 브랜드데이 앵콜 🎁 1.등 유산균 컬처렐 특가!",
"라이브9.9만 시청컬처렐 다이제스티브 3박스 - 3박스 더 (뚱랑이 굿즈 증정) - 선물용 쇼핑백 2장컬처렐 다이제스티브 3박스 - 3박스 더 (뚱랑이 굿즈 증정) - 선물용 쇼핑백 2장24%102 000원 브랜드데이 앵콜 🎁 1.등-유산균-컬처렐-특가!",
"text/unknown",
mimeService.VALIDATE_SANITIZE_ONLY
),
"라이브9.9만 시청컬처렐 다이제스티브 3박스 - 3박스 더 (뚱랑이 굿즈 증정) - 선물용 쇼핑백 2장컬처렐 다이제스티브 3박스 - 3박스 더 (뚱랑이 굿즈 .등 유산균 컬처렐 특가!",
"라이브9.9만 시청컬처렐 다이제스티브 3박스 - 3박스 더 (뚱랑이 굿즈 증정) - 선물용 쇼핑백 2장컬처렐 다이제스티브 3박스 - 3박스 더 (뚱랑이 굿즈 .등-유산균-컬처렐-특가!",
"very long filename with extension"
);
@ -270,11 +271,11 @@ add_task(async function validate_filename_method() {
// This filename is cropped at 254 bytes.
Assert.equal(
mimeService.validateFileNameForSaving(
".라이브99만 시청컬처렐 다이제스티브 3박스 - 3박스 더 (뚱랑이 굿즈 증정) - 선물용 쇼핑백 2장컬처렐 다이제스티브 3박스 - 3박스 더 (뚱랑이 굿즈 증정) - 선물용 쇼핑백 2장24%102 000원 브랜드데이 앵콜 🎁 1등 유산균 컬처렐 특가!",
".라이브99만 시청컬처렐 다이제스티브 3박스 - 3박스 더 (뚱랑이 굿즈 증정) - 선물용 쇼핑백 2장컬처렐 다이제스티브 3박스 - 3박스 더 (뚱랑이 굿즈 증정) - 선물용 쇼핑백 2장24_102 000원 브랜드데이 앵콜 🎁 1등 유산균 컬처렐 특가!",
"text/unknown",
mimeService.VALIDATE_SANITIZE_ONLY
),
"라이브99만 시청컬처렐 다이제스티브 3박스 - 3박스 더 (뚱랑이 굿즈 증정) - 선물용 쇼핑백 2장컬처렐 다이제스티브 3박스 - 3박스 더 (뚱랑이 굿즈 증정) - 선물용 쇼핑백 2장24%102 000원 브랜드데",
"라이브99만 시청컬처렐 다이제스티브 3박스 - 3박스 더 (뚱랑이 굿즈 증정) - 선물용 쇼핑백 2장컬처렐 다이제스티브 3박스 - 3박스 더 (뚱랑이 굿즈 증정) - 선물용 쇼핑백 2장24_102 000원 브랜드데",
"very filename with extension only"
);
@ -311,7 +312,7 @@ add_task(async function validate_filename_method() {
Assert.equal(
mimeService.validateFileNameForSaving("filename.lnk\n", "text/unknown", 0),
"filename.lnk.download",
"filename.lnk_",
"filename.lnk with newline"
);
@ -321,7 +322,7 @@ add_task(async function validate_filename_method() {
"text/unknown",
0
),
"filename.lnk.download",
"filename.lnk_",
"filename.lnk with newline"
);
@ -331,7 +332,7 @@ add_task(async function validate_filename_method() {
"text/unknown",
0
),
"filename. lnk",
"filename.__lnk",
"filename.lnk with space and newline"
);
@ -361,7 +362,7 @@ add_task(async function validate_filename_method() {
"text/unknown",
mimeService.VALIDATE_ALLOW_INVALID_FILENAMES
),
"filename.LNK",
"filename.LNK_",
"filename.LNK allow invalid"
);
@ -372,7 +373,7 @@ add_task(async function validate_filename_method() {
mimeService.VALIDATE_SANITIZE_ONLY |
mimeService.VALIDATE_ALLOW_INVALID_FILENAMES
),
"filename.URL",
"filename.URL_",
"filename.URL allow invalid, sanitize only"
);
@ -392,7 +393,7 @@ add_task(async function validate_filename_method() {
mimeService.VALIDATE_SANITIZE_ONLY |
mimeService.VALIDATE_ALLOW_INVALID_FILENAMES
),
"filename.DESKTOP",
"filename.DESKTOP_",
"filename.DESKTOP allow invalid, sanitize only"
);
});