Commit 5f774dbf7b738aa467b53acc8643651e5c745f58

Edward Thomson 2018-02-11T10:14:13

git_index_add_frombuffer: only accept files/links Ensure that the buffer given to `git_index_add_frombuffer` represents a regular blob, an executable blob, or a link. Explicitly reject commit entries (submodules) - it makes little sense to allow users to add a submodule from a string; there's no possible path to success.

diff --git a/src/index.c b/src/index.c
index b7602fb..5222d8b 100644
--- a/src/index.c
+++ b/src/index.c
@@ -1396,12 +1396,16 @@ static int index_conflict_to_reuc(git_index *index, const char *path)
 	return ret;
 }
 
-static bool valid_filemode(const int filemode)
+GIT_INLINE(bool) is_file_or_link(const int filemode)
 {
 	return (filemode == GIT_FILEMODE_BLOB ||
 		filemode == GIT_FILEMODE_BLOB_EXECUTABLE ||
-		filemode == GIT_FILEMODE_LINK ||
-		filemode == GIT_FILEMODE_COMMIT);
+		filemode == GIT_FILEMODE_LINK);
+}
+
+GIT_INLINE(bool) valid_filemode(const int filemode)
+{
+	return (is_file_or_link(filemode) || filemode == GIT_FILEMODE_COMMIT);
 }
 
 int git_index_add_frombuffer(
@@ -1419,7 +1423,7 @@ int git_index_add_frombuffer(
 			"could not initialize index entry. "
 			"Index is not backed up by an existing repository.");
 
-	if (!valid_filemode(source_entry->mode)) {
+	if (!is_file_or_link(source_entry->mode)) {
 		giterr_set(GITERR_INDEX, "invalid filemode");
 		return -1;
 	}
@@ -1605,7 +1609,7 @@ int git_index_add(git_index *index, const git_index_entry *source_entry)
 	assert(index && source_entry && source_entry->path);
 
 	if (!valid_filemode(source_entry->mode)) {
-		giterr_set(GITERR_INDEX, "invalid filemode");
+		giterr_set(GITERR_INDEX, "invalid entry mode");
 		return -1;
 	}
 
diff --git a/tests/index/filemodes.c b/tests/index/filemodes.c
index 2efad5b..f53414d 100644
--- a/tests/index/filemodes.c
+++ b/tests/index/filemodes.c
@@ -256,3 +256,64 @@ void test_index_filemodes__invalid(void)
 
 	git_index_free(index);
 }
+
+void test_index_filemodes__frombuffer_requires_files(void)
+{
+	git_index *index;
+	git_index_entry new_entry;
+	const git_index_entry *ret_entry;
+	const char *content = "hey there\n";
+
+	cl_git_pass(git_repository_index(&index, g_repo));
+
+	/* regular blob */
+	new_entry.path = "dummy-file.txt";
+	new_entry.mode = GIT_FILEMODE_BLOB;
+
+	cl_git_pass(git_index_add_frombuffer(index,
+		&new_entry, content, strlen(content)));
+
+	cl_assert((ret_entry = git_index_get_bypath(index, "dummy-file.txt", 0)));
+	cl_assert_equal_s("dummy-file.txt", ret_entry->path);
+	cl_assert_equal_i(GIT_FILEMODE_BLOB, ret_entry->mode);
+
+	/* executable blob */
+	new_entry.path = "dummy-file.txt";
+	new_entry.mode = GIT_FILEMODE_BLOB_EXECUTABLE;
+
+	cl_git_pass(git_index_add_frombuffer(index,
+		&new_entry, content, strlen(content)));
+
+	cl_assert((ret_entry = git_index_get_bypath(index, "dummy-file.txt", 0)));
+	cl_assert_equal_s("dummy-file.txt", ret_entry->path);
+	cl_assert_equal_i(GIT_FILEMODE_BLOB_EXECUTABLE, ret_entry->mode);
+
+	/* links are also acceptable */
+	new_entry.path = "dummy-link.txt";
+	new_entry.mode = GIT_FILEMODE_LINK;
+
+	cl_git_pass(git_index_add_frombuffer(index,
+		&new_entry, content, strlen(content)));
+
+	cl_assert((ret_entry = git_index_get_bypath(index, "dummy-link.txt", 0)));
+	cl_assert_equal_s("dummy-link.txt", ret_entry->path);
+	cl_assert_equal_i(GIT_FILEMODE_LINK, ret_entry->mode);
+
+	/* trees are rejected */
+	new_entry.path = "invalid_mode.txt";
+	new_entry.mode = GIT_FILEMODE_TREE;
+
+	cl_git_fail(git_index_add_frombuffer(index,
+		&new_entry, content, strlen(content)));
+	cl_assert_equal_p(NULL, git_index_get_bypath(index, "invalid_mode.txt", 0));
+
+	/* submodules are rejected */
+	new_entry.path = "invalid_mode.txt";
+	new_entry.mode = GIT_FILEMODE_COMMIT;
+
+	cl_git_fail(git_index_add_frombuffer(index,
+		&new_entry, content, strlen(content)));
+	cl_assert_equal_p(NULL, git_index_get_bypath(index, "invalid_mode.txt", 0));
+
+	git_index_free(index);
+}