From aee8be7942c8a0061305e29821686cbc8b6b80be Mon Sep 17 00:00:00 2001
From: feiniks <36756310+feiniks@users.noreply.github.com>
Date: Thu, 31 Oct 2024 17:13:23 +0800
Subject: [PATCH] Check if file or dir is .. (#711)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* Check if file or dir is ..

* Diff and check name is ..

* Go diff and check name is ..

* Check if ignore new name or name

---------

Co-authored-by: 杨赫然 <heran.yang@seafile.com>
---
 fileserver/fileop.go   | 14 +++++++++++
 fileserver/sync_api.go | 27 ++++++++++++++++++++
 server/http-server.c   | 56 ++++++++++++++++++++++++++++++++++++++++++
 server/repo-mgr.c      | 14 +++++++++++
 4 files changed, 111 insertions(+)

diff --git a/fileserver/fileop.go b/fileserver/fileop.go
index 38b684a..56ea7ff 100644
--- a/fileserver/fileop.go
+++ b/fileserver/fileop.go
@@ -2126,7 +2126,21 @@ func nameExists(entries []*fsmgr.SeafDirent, fileName string) bool {
 	return false
 }
 
+func shouldIgnore(fileName string) bool {
+	parts := strings.Split(fileName, "/")
+	for _, name := range parts {
+		if name == ".." {
+			return true
+		}
+	}
+	return false
+}
+
 func shouldIgnoreFile(fileName string) bool {
+	if shouldIgnore(fileName) {
+		return true
+	}
+
 	if !utf8.ValidString(fileName) {
 		log.Printf("file name %s contains non-UTF8 characters, skip", fileName)
 		return true
diff --git a/fileserver/sync_api.go b/fileserver/sync_api.go
index dd6c788..3490b26 100644
--- a/fileserver/sync_api.go
+++ b/fileserver/sync_api.go
@@ -1041,6 +1041,11 @@ func putUpdateBranchCB(rsp http.ResponseWriter, r *http.Request) *appError {
 		return &appError{err, "", http.StatusInternalServerError}
 	}
 
+	if includeInvalidPath(base, newCommit) {
+		msg := fmt.Sprintf("Dir or file name is ..")
+		return &appError{nil, msg, http.StatusBadRequest}
+	}
+
 	ret, err := checkQuota(repoID, 0)
 	if err != nil {
 		err := fmt.Errorf("Failed to check quota: %v", err)
@@ -1064,6 +1069,28 @@ func putUpdateBranchCB(rsp http.ResponseWriter, r *http.Request) *appError {
 	return nil
 }
 
+func includeInvalidPath(baseCommit, newCommit *commitmgr.Commit) bool {
+	var results []*diff.DiffEntry
+	if err := diff.DiffCommits(baseCommit, newCommit, &results, true); err != nil {
+		log.Infof("Failed to diff commits: %v", err)
+		return false
+	}
+
+	for _, entry := range results {
+		if entry.NewName != "" {
+			if shouldIgnore(entry.NewName) {
+				return true
+			}
+		} else {
+			if shouldIgnore(entry.Name) {
+				return true
+			}
+		}
+	}
+
+	return false
+}
+
 func getHeadCommit(rsp http.ResponseWriter, r *http.Request) *appError {
 	vars := mux.Vars(r)
 	repoID := vars["repoid"]
diff --git a/server/http-server.c b/server/http-server.c
index 77c137b..cddee72 100644
--- a/server/http-server.c
+++ b/server/http-server.c
@@ -1028,6 +1028,57 @@ out:
     return ret;
 }
 
+gboolean
+should_ignore (const char *filename)
+{
+    char **components = g_strsplit (filename, "/", -1);
+    int n_comps = g_strv_length (components);
+    int j = 0;
+    char *file_name;
+
+    for (; j < n_comps; ++j) {
+        file_name = components[j];
+        if (g_strcmp0(file_name, "..") == 0) {
+            g_strfreev (components);
+            return TRUE;
+        }
+    }
+    g_strfreev (components);
+
+    return FALSE;
+}
+
+static gboolean
+include_invalid_path (SeafCommit *base_commit, SeafCommit *new_commit) {
+    GList *diff_entries = NULL;
+    gboolean ret = FALSE;
+
+    int rc = diff_commits (base_commit, new_commit, &diff_entries, TRUE);
+    if (rc < 0) {
+        seaf_warning ("Failed to check invalid path.\n");
+        return FALSE;
+    }
+
+    GList *ptr;
+    DiffEntry *diff_entry;
+    for (ptr = diff_entries; ptr; ptr = ptr->next) {
+        diff_entry = ptr->data;
+        if (diff_entry->new_name) {
+            if (should_ignore(diff_entry->new_name)) {
+                ret = TRUE;
+                break;
+            }
+        } else {
+            if (should_ignore(diff_entry->name)) {
+                ret = TRUE;
+                break;
+            }
+        }
+    }
+
+    return ret;
+}
+
 static void
 put_update_branch_cb (evhtp_request_t *req, void *arg)
 {
@@ -1084,6 +1135,11 @@ put_update_branch_cb (evhtp_request_t *req, void *arg)
         goto out;
     }
 
+    if (include_invalid_path (base, new_commit)) {
+        evhtp_send_reply (req, EVHTP_RES_BADREQ);
+        goto out;
+    }
+
     if (seaf_quota_manager_check_quota (seaf->quota_mgr, repo_id) < 0) {
         evhtp_send_reply (req, SEAF_HTTP_RES_NOQUOTA);
         goto out;
diff --git a/server/repo-mgr.c b/server/repo-mgr.c
index 03acd42..566030c 100644
--- a/server/repo-mgr.c
+++ b/server/repo-mgr.c
@@ -242,6 +242,20 @@ should_ignore_file(const char *filename, void *data)
 {
     /* GPatternSpec **spec = ignore_patterns; */
 
+    char **components = g_strsplit (filename, "/", -1);
+    int n_comps = g_strv_length (components);
+    int j = 0;
+    char *file_name;
+
+    for (; j < n_comps; ++j) {
+        file_name = components[j];
+        if (g_strcmp0(file_name, "..") == 0) {
+            g_strfreev (components);
+            return TRUE;
+        }
+    }
+    g_strfreev (components);
+
     if (!g_utf8_validate (filename, -1, NULL)) {
         seaf_warning ("File name %s contains non-UTF8 characters, skip.\n", filename);
         return TRUE;