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.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103
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);
+}
+