Commit 2bbc7d3e564ed262e9555ea4fd081ece5ceb3bff

Edward Thomson 2016-02-23T15:00:27

treebuilder: validate tree entries (optionally) When `GIT_OPT_ENABLE_STRICT_OBJECT_CREATION` is turned on, validate the tree and parent ids given to treebuilder insertion.

diff --git a/src/tree.c b/src/tree.c
index cfceb3f..2c31515 100644
--- a/src/tree.c
+++ b/src/tree.c
@@ -726,6 +726,18 @@ on_error:
 	return -1;
 }
 
+static git_otype otype_from_mode(git_filemode_t filemode)
+{
+	switch (filemode) {
+	case GIT_FILEMODE_TREE:
+		return GIT_OBJ_TREE;
+	case GIT_FILEMODE_COMMIT:
+		return GIT_OBJ_COMMIT;
+	default:
+		return GIT_OBJ_BLOB;
+	}
+}
+
 int git_treebuilder_insert(
 	const git_tree_entry **entry_out,
 	git_treebuilder *bld,
@@ -745,6 +757,9 @@ int git_treebuilder_insert(
 	if (!valid_entry_name(bld->repo, filename))
 		return tree_error("Failed to insert entry. Invalid name for a tree entry", filename);
 
+	if (!git_object__is_valid(bld->repo, id, otype_from_mode(filemode)))
+		return tree_error("Failed to insert entry; invalid object specified", filename);
+
 	pos = git_strmap_lookup_index(bld->map, filename);
 	if (git_strmap_valid_index(bld->map, pos)) {
 		entry = git_strmap_value_at(bld->map, pos);
diff --git a/tests/object/tree/write.c b/tests/object/tree/write.c
index 5433e5f..4cd1607 100644
--- a/tests/object/tree/write.c
+++ b/tests/object/tree/write.c
@@ -18,6 +18,8 @@ void test_object_tree_write__initialize(void)
 void test_object_tree_write__cleanup(void)
 {
    cl_git_sandbox_cleanup();
+
+	cl_git_pass(git_libgit2_opts(GIT_OPT_ENABLE_STRICT_OBJECT_CREATION, 0));
 }
 
 void test_object_tree_write__from_memory(void)
@@ -440,3 +442,56 @@ void test_object_tree_write__protect_filesystems(void)
 
 	git_treebuilder_free(builder);
 }
+
+static void test_invalid_objects(bool should_allow_invalid)
+{
+	git_treebuilder *builder;
+	git_oid valid_blob_id, invalid_blob_id, valid_tree_id, invalid_tree_id;
+
+#define assert_allowed(expr) \
+	clar__assert(!(expr) == should_allow_invalid, __FILE__, __LINE__, \
+		(should_allow_invalid ? \
+		 "Expected function call to succeed: " #expr : \
+		 "Expected function call to fail: " #expr), \
+		NULL, 1)
+
+	cl_git_pass(git_oid_fromstr(&valid_blob_id, blob_oid));
+	cl_git_pass(git_oid_fromstr(&invalid_blob_id,
+		"1234567890123456789012345678901234567890"));
+	cl_git_pass(git_oid_fromstr(&valid_tree_id, first_tree));
+	cl_git_pass(git_oid_fromstr(&invalid_tree_id,
+		"0000000000111111111122222222223333333333"));
+
+	cl_git_pass(git_treebuilder_new(&builder, g_repo, NULL));
+
+	/* test valid blobs and trees (these should always pass) */
+	cl_git_pass(git_treebuilder_insert(NULL, builder, "file.txt", &valid_blob_id, GIT_FILEMODE_BLOB));
+	cl_git_pass(git_treebuilder_insert(NULL, builder, "folder", &valid_tree_id, GIT_FILEMODE_TREE));
+
+	/* replace valid files and folders with invalid ones */
+	assert_allowed(git_treebuilder_insert(NULL, builder, "file.txt", &invalid_blob_id, GIT_FILEMODE_BLOB));
+	assert_allowed(git_treebuilder_insert(NULL, builder, "folder", &invalid_blob_id, GIT_FILEMODE_BLOB));
+
+	/* insert new invalid files and folders */
+	assert_allowed(git_treebuilder_insert(NULL, builder, "invalid_file.txt", &invalid_blob_id, GIT_FILEMODE_BLOB));
+	assert_allowed(git_treebuilder_insert(NULL, builder, "invalid_folder", &invalid_blob_id, GIT_FILEMODE_BLOB));
+
+	/* insert valid blobs as trees and trees as blobs */
+	assert_allowed(git_treebuilder_insert(NULL, builder, "file_as_folder", &valid_blob_id, GIT_FILEMODE_TREE));
+	assert_allowed(git_treebuilder_insert(NULL, builder, "folder_as_file.txt", &valid_tree_id, GIT_FILEMODE_BLOB));
+
+#undef assert_allowed
+
+	git_treebuilder_free(builder);
+}
+
+void test_object_tree_write__object_validity(void)
+{
+	/* Ensure that we can add invalid objects by default */
+	test_invalid_objects(true);
+
+	/* Ensure that we can turn on validation */
+	cl_git_pass(git_libgit2_opts(GIT_OPT_ENABLE_STRICT_OBJECT_CREATION, 1));
+	test_invalid_objects(false);
+}
+