Переглянути джерело

Fixed #206

- Added web safe character detection to File Explorer
- Remove file from rendering if contains unsafe web characters
Toby Chui 1 день тому
батько
коміт
c8a0667292

+ 26 - 13
src/file_system.go

@@ -1274,10 +1274,17 @@ func system_fs_handleNewObjects(w http.ResponseWriter, r *http.Request) {
 			return
 		}
 
+		//Check if the filename contains web-unsafe characters
+		if !utils.FilenameIsWebSafe(filename) {
+			utils.SendErrorResponse(w, "Filename contains illegal characters")
+			return
+		}
+
 		//Check if the file already exists. If yes, fix its filename.
 		newfilePath := filepath.ToSlash(filepath.Join(rpath, filename))
 
-		if fileType == "file" {
+		switch fileType {
+		case "file":
 			for fshAbs.FileExists(newfilePath) {
 				utils.SendErrorResponse(w, "Given filename already exists")
 				return
@@ -1300,7 +1307,7 @@ func system_fs_handleNewObjects(w http.ResponseWriter, r *http.Request) {
 				return
 			}
 
-		} else if fileType == "folder" {
+		case "folder":
 			if fshAbs.FileExists(newfilePath) {
 				utils.SendErrorResponse(w, "Given folder already exists")
 				return
@@ -1914,15 +1921,22 @@ func system_fs_handleOpr(w http.ResponseWriter, r *http.Request) {
 
 				//Check if the target dir is not readonly
 				accmode := userinfo.GetPathAccessPermission(string(vsrcFile))
-				if accmode == arozfs.FsReadOnly {
+				switch accmode {
+				case arozfs.FsReadOnly:
 					utils.SendErrorResponse(w, "This directory is Read Only")
 					return
-				} else if accmode == arozfs.FsDenied {
+				case arozfs.FsDenied:
 					utils.SendErrorResponse(w, "Access Denied")
 					return
 				}
 
 				thisFilename := filepath.Base(newFilenames[i])
+
+				//Check if the new filename contains web-unsafe characters
+				if !utils.FilenameIsWebSafe(thisFilename) {
+					utils.SendErrorResponse(w, "Filename contains illegal characters")
+					return
+				}
 				//Check if the name already exists. If yes, return false
 				if srcFshAbs.FileExists(filepath.Join(filepath.Dir(rsrcFile), thisFilename)) {
 					utils.SendErrorResponse(w, "File already exists")
@@ -2516,8 +2530,6 @@ func system_fs_getFileProperties(w http.ResponseWriter, r *http.Request) {
 	Usage: Pass in dir like the following examples:
 	AOR:/Desktop	<= Open /user/{username}/Desktop
 	S1:/			<= Open {uuid=S1}/
-
-
 */
 
 func system_fs_handleList(w http.ResponseWriter, r *http.Request) {
@@ -2526,8 +2538,6 @@ func system_fs_handleList(w http.ResponseWriter, r *http.Request) {
 		utils.SendErrorResponse(w, err.Error())
 		return
 	}
-	//Commented this line to handle dirname that contains "+" sign
-	//currentDir, _ = url.QueryUnescape(currentDir)
 	sortMode, _ := utils.PostPara(r, "sort")
 	showHidden, _ := utils.PostPara(r, "showHidden")
 
@@ -2543,8 +2553,8 @@ func system_fs_handleList(w http.ResponseWriter, r *http.Request) {
 		return
 	}
 
-	//Pad a slash at the end of currentDir if not exists
-	if currentDir[len(currentDir)-1:] != "/" {
+	// Pad a slash at the end of currentDir if not exists
+	if !strings.HasSuffix(currentDir, "/") {
 		currentDir = currentDir + "/"
 	}
 
@@ -2556,12 +2566,12 @@ func system_fs_handleList(w http.ResponseWriter, r *http.Request) {
 
 	fshAbs := fsh.FileSystemAbstraction
 
-	//Normal file systems
 	realpath, err := fshAbs.VirtualPathToRealPath(subpath, userinfo.Username)
 	if err != nil {
 		utils.SendErrorResponse(w, err.Error())
 		return
 	}
+
 	if !fshAbs.FileExists(realpath) {
 		//Path not exists
 		userRoot, _ := fshAbs.VirtualPathToRealPath("/", userinfo.Username)
@@ -2599,7 +2609,6 @@ func system_fs_handleList(w http.ResponseWriter, r *http.Request) {
 	//Sorting use list
 	realpathList := []string{}
 	fileInfoList := []fs.FileInfo{}
-
 	for _, f := range files {
 		//Check if it is hidden file
 		isHidden, _ := hidden.IsHidden(f.Name(), false)
@@ -2608,6 +2617,11 @@ func system_fs_handleList(w http.ResponseWriter, r *http.Request) {
 			continue
 		}
 
+		//Check if this file contains invalid characters
+		if !utils.FilenameIsWebSafe(f.Name()) {
+			continue
+		}
+
 		//Check if this is an aodb file
 		if f.Name() == "aofs.db" || f.Name() == "aofs.db.lock" {
 			//Database file (reserved)
@@ -2666,7 +2680,6 @@ func system_fs_handleList(w http.ResponseWriter, r *http.Request) {
 
 	jsonString, _ := json.Marshal(results)
 	utils.SendJSONResponse(w, string(jsonString))
-
 }
 
 // Handle getting a hash from a given contents in the given path

+ 10 - 0
src/mod/utils/utils.go

@@ -226,3 +226,13 @@ func TemplateApply(templateString string, data map[string]string) string {
 
 	return string(content)
 }
+
+func FilenameIsWebSafe(filename string) bool {
+	unsafeChars := []string{"/", "\\", "?", "%", "*", ":", "|", "\"", "<", ">"}
+	for _, char := range unsafeChars {
+		if strings.Contains(filename, char) {
+			return false
+		}
+	}
+	return true
+}

+ 124 - 91
src/web/SystemAO/file_system/file_explorer.html

@@ -596,17 +596,98 @@
             //Intiiation functions
             $(document).ready(function(){
                 $("#contextmenu").css("display", "hidden");
+                
+                //Function to complete initialization after applocale is ready
+                function completeInitialization(){
+                    initRootDirs();
+                    initSystemInfo();
+                    initUploadMode();
+                    $(".dropdown").dropdown();
+                    $("#sortingMethodSelector").dropdown("set selected", sortMode);
+                    updateSelectedObjectsCount();
+                    initWindowSizes(false);
+                    
+                    //Initialize view mode buttons
+                    updateViewmodeButtons();
+                    
+                    //Initialize system theme
+                    loadPreference("file_explorer/theme",function(data){
+                        if (data.error === undefined){
+                            if (data == "darkTheme"){
+                                toggleDarkTheme();
+                            }else{
+                                //White theme
+                            
+                            }
+                        }
+                    });
+
+                    //Initialize properties view
+                    if (localStorage.getItem("file_explorer/viewProperties") == "true"){
+                        $("#togglePropertiesViewBtn").click();
+                    }
+
+                    //Initialize directory views based on hash
+                    if (window.location.hash != ""){
+                        //Check if the hash is standard open protocol. If yes, translate it
+                        if (ao_module_loadInputFiles() === null){
+                            //Window location hash set. List the desire directory
+                            currentPath = window.location.hash.substring(1,window.location.hash.length);
+                            if (currentPath.substring(currentPath.length -1) != "/"){
+                                currentPath = currentPath + "/";
+                            }
+                            currentPath = decodeURIComponent(currentPath);
+                            loadListModeFromDB(function(){
+                                listDirectory(currentPath);
+                            });
+                            
+                        }else{
+                            //This is ao_module load file input. Handle the file opening
+                            var filelist = ao_module_loadInputFiles();
+                            if (filelist.length > 0){
+                                filelist = filelist[0];
+                                //Check if this is folder or file. Only opendir when it is folder
+                                //Updates 27-12-2020: Open folder and highlight the file if it is file
+                                if (filelist.filename.includes(".") == false){
+                                    //Try to open it and overwrite the hash to filesystem hash
+                                    loadListModeFromDB(function(){
+                                        listDirectory(filelist.filepath);
+                                    });
+                                }else{
+                                    //File. Open its parent folder and highlight the target file if exists
+                                    var parentdir = filelist.filepath.split("/");
+                                    let focusFilename = JSON.parse(JSON.stringify(filelist.filename));
+                                    parentdir.pop();
+                                    parentdir = parentdir.join("/");
+                                    loadListModeFromDB(function(){
+                                        listDirectory(parentdir, function(){
+                                            if (focusFilename != ""){
+                                                //Timeout to give the DOM time to render
+                                                //DO NOT REPLACE THIS WITH listDirectoryAndHighlight
+                                                //Additional delay are required on page load
+                                                setTimeout(function(){
+                                                    focusFileObject(focusFilename);
+                                                }, 300);
+                                                
+                                            }
+                                        })
+                                    });
+                                }
+                            }
+                        }
+                    }else{
+                        //Initialized directory views
+                        loadListModeFromDB(function(){
+                            listDirectory(currentPath);
+                        });
+                    }
+                }
+                
                 if (applocale){
                     //Applocale found. Do localization
                     applocale.init("../locale/file_explorer.json", function(){
                         applocale.translate();
-                        initRootDirs();
-                        initSystemInfo();
-                        initUploadMode();
-                        $(".dropdown").dropdown();
-                        $("#sortingMethodSelector").dropdown("set selected", sortMode);
-                        updateSelectedObjectsCount();
-                        initWindowSizes(false);
+                        completeInitialization();
                     });
                 }else{
                     //Applocale not found. Is this a trim down version of ArozOS?
@@ -615,10 +696,7 @@
                             return original;
                         }
                     }
-                    initRootDirs();
-                    initSystemInfo();
-                    initUploadMode();
-                    $(".dropdown").dropdown();
+                    completeInitialization();
                 }
            
                 if (isMobile){
@@ -648,82 +726,6 @@
                     $(".mobileOnly").hide();
                     $(".desktopOnly").show();
                 }
-                //Initialize view mode buttons
-                updateViewmodeButtons();
-                loadListModeFromDB();
-
-                //Initialize system theme
-                loadPreference("file_explorer/theme",function(data){
-                    if (data.error === undefined){
-                        if (data == "darkTheme"){
-                            toggleDarkTheme();
-                        }else{
-                            //White theme
-                        
-                        }
-                    }
-                });
-
-                //Initialize properties view
-                if (localStorage.getItem("file_explorer/viewProperties") == "true"){
-                    $("#togglePropertiesViewBtn").click();
-                }
-
-
-                if (window.location.hash != ""){
-                    //Check if the hash is standard open protocol. If yes, translate it
-                    if (ao_module_loadInputFiles() === null){
-                        //Window location hash set. List the desire directory
-                        currentPath = window.location.hash.substring(1,window.location.hash.length);
-                        if (currentPath.substring(currentPath.length -1) != "/"){
-                            currentPath = currentPath + "/";
-                        }
-                        currentPath = decodeURIComponent(currentPath);
-                        loadListModeFromDB(function(){
-                            listDirectory(currentPath);
-                        });
-                        
-                    }else{
-                        //This is ao_module load file input. Handle the file opening
-                        var filelist = ao_module_loadInputFiles();
-                        if (filelist.length > 0){
-                            filelist = filelist[0];
-                            //Check if this is folder or file. Only opendir when it is folder
-                            //Updates 27-12-2020: Open folder and highlight the file if it is file
-                            if (filelist.filename.includes(".") == false){
-                                //Try to open it and overwrite the hash to filesystem hash
-                                loadListModeFromDB(function(){
-                                    listDirectory(filelist.filepath);
-                                });
-                            }else{
-                                //File. Open its parent folder and highlight the target file if exists
-                                var parentdir = filelist.filepath.split("/");
-                                let focusFilename = JSON.parse(JSON.stringify(filelist.filename));
-                                parentdir.pop();
-                                parentdir = parentdir.join("/");
-                                loadListModeFromDB(function(){
-                                    listDirectory(parentdir, function(){
-                                        if (focusFilename != ""){
-                                            //Timeout to give the DOM time to render
-                                            //DO NOT REPLACE THIS WITH listDirectoryAndHighlight
-                                            //Additional delay are required on page load
-                                            setTimeout(function(){
-                                                focusFileObject(focusFilename);
-                                            }, 300);
-                                            
-                                        }
-                                    })
-                                });
-                            }
-                        }
-                    }
-                }else{
-
-                    //Initialized directory views
-                    loadListModeFromDB(function(){
-                        listDirectory(currentPath);
-                    });
-                }
                 
 
                 
@@ -909,8 +911,12 @@
                 if (recordPreviousPage){
                     viewHistory.push(currentPath);
                 }
-               
-                window.location.hash = path;
+                
+                if (!ao_module_virtualDesktop){
+                    //Update the window hash
+                    window.location.hash = encodeURIComponent(path);
+                }
+                
                 updatePathDisplay(path);
                 currentPath = path;
 
@@ -2363,6 +2369,17 @@
             }
             
 
+            //Check if filename contains web-unsafe characters
+            function filenameContainsIllegalCharacters(filename){
+                var illegalChars = ['/', '\\', '?', '%', '*', ':', '|', '"', '<', '>'];
+                for (var i = 0; i < illegalChars.length; i++){
+                    if (filename.includes(illegalChars[i])){
+                        return true;
+                    }
+                }
+                return false;
+            }
+
             function confirmRename(oldName=undefined, newName=undefined){
                 renameMode = false;
                 if (oldName == undefined){
@@ -2373,6 +2390,12 @@
                     newName = $("#renameBox").find(".newfn").val().trim();
                 }
 
+                //Check for illegal characters
+                if (filenameContainsIllegalCharacters(newName)){
+                    msgbox("red remove", applocale.getString("message/illegalCharacters", "Filename contains illegal characters"));
+                    return;
+                }
+
                 if (newName == oldName){
                     msgbox("red remove",applocale.getString("message/newFilenameIdentical", "New filename is identical to the original filename."));
                     hideAllPopupWindows();
@@ -3208,8 +3231,9 @@
                     }
 
                     //Check for invalid characters
-                    if (newFoldername.includes("%")){
+                    if (filenameContainsIllegalCharacters(newFoldername)){
                         $("#createNewFolder").parent().addClass("error");
+                        msgbox("red remove", applocale.getString("message/illegalCharacters", "Folder name contains illegal characters"));
                         return;
                     }
 
@@ -3253,7 +3277,16 @@
                     //Filename not set
                     $("#createNewFileName").parent().addClass("error");
                     return;
-                }else if(currentFilelist.includes(filename)){
+                }
+                
+                //Check for illegal characters
+                if (filenameContainsIllegalCharacters(filename)){
+                    $("#createNewFileName").parent().addClass("error");
+                    msgbox("red remove", applocale.getString("message/illegalCharacters", "Filename contains illegal characters"));
+                    return;
+                }
+                
+                if(currentFilelist.includes(filename)){
                     //File with this name already exists
                     $("#createNewFileName").parent().addClass("error");
                     $("#newFile").find(".duplicateWarning").show();

+ 10 - 4
src/web/SystemAO/locale/file_explorer.json

@@ -244,7 +244,8 @@
                 "message/destIdentical": "檔案來源與目的地相同",
                 "message/decodeFilelistFail": "載案置入失敗:無法讀取檔案列表",
                 "message/uploadFailed": "載案上載失敗:檔案太大或目標儲存裝置已滿",
-                "message/newFilenameIdentical": "重新命名失敗:新舊檔案名稱相同"
+                "message/newFilenameIdentical": "重新命名失敗:新舊檔案名稱相同",
+                "message/illegalCharacters": "名稱包含非法字元(不可使用:/ \\ ? % * : | \" < >)"
             },
             "titles": {
                 "Back": "上一頁",
@@ -528,7 +529,8 @@
                 "message/destIdentical": "檔案來源與目的地相同",
                 "message/decodeFilelistFail": "載案置入失敗:無法讀取檔案列表",
                 "message/uploadFailed": "載案上載失敗:檔案太大或目標儲存裝置已滿",
-                "message/newFilenameIdentical": "重新命名失敗:新舊檔案名稱相同"
+                "message/newFilenameIdentical": "重新命名失敗:新舊檔案名稱相同",
+                "message/illegalCharacters": "名稱包含非法字元(不可使用:/ \\ ? % * : | \" < >)"
             },
             "titles": {
                 "Back": "上一頁",
@@ -811,7 +813,8 @@
                 "message/destIdentical": "文件来源及目标相同",
                 "message/decodeFilelistFail": "文件加载失败:无法读取文件列表",
                 "message/uploadFailed": "文件上传失败:文件太大或目标存储设备已满",
-                "message/newFilenameIdentical": "重命名失败:新旧文件名称相同"
+                "message/newFilenameIdentical": "重命名失败:新旧文件名称相同",
+                "message/illegalCharacters": "名称包含非法字符(不可使用:/ \\ ? % * : | \" < >)"
             },
             "titles": {
                 "Back": "上一页",
@@ -1097,6 +1100,7 @@
                 "message/decodeFilelistFail": "Failed to load folder: could not read the file list.",
                 "message/uploadFailed": "File upload failed: file is too large or the storage device is full.",
                 "message/newFilenameIdentical": "Failed to rename file: new name is identical to the old name.",
+                "message/illegalCharacters": "Name contains illegal characters (cannot use: / \\ ? % * : | \" < >)",
                 "":""
             },
             "titles": {
@@ -1378,6 +1382,7 @@
                 "message/decodeFilelistFail": "フォルダを読み込めませんでした:ファイルリストを読み込めませんでした",
                 "message/uploadFailed": "ファイルのアップロードに失敗しました:ファイルが大きすぎるか、ストレージデバイスがいっぱいです。",
                 "message/newFilenameIdentical": "ファイル名の変更に失敗しました:新しい名前が古い名前と同じです。",
+                "message/illegalCharacters": "名前に使用できない文字が含まれています(使用不可:/ \\ ? % * : | \" < >)",
                 "":""
             },
             "titles": {
@@ -1664,7 +1669,8 @@
                 "message/destIdentical": "소스 및 대상 경로가 동일합니다",
                 "message/decodeFilelistFail": "파일 목록을 읽을 수 없어 파일 로드 실패",
                 "message/uploadFailed": "파일 업로드 실패: 파일이 너무 크거나 대상 저장 장치가 가득 찼습니다",
-                "message/newFilenameIdentical": "이름 변경 실패: 새로운 파일 이름이 기존과 동일합니다"
+                "message/newFilenameIdentical": "이름 변경 실패: 새로운 파일 이름이 기존과 동일합니다",
+                "message/illegalCharacters": "이름에 사용할 수 없는 문자가 포함되어 있습니다 (사용 불가: / \\ ? % * : | \" < >)"
             },
             "titles": {
                 "Back": "뒤로",