Commit 9f03ebd14b6beb00a9bed52c0568a13f8d5ebb08

Edward Thomson 2021-11-29T13:44:42

object: introduce a raw content validation function Users may want to validate raw object content; provide them a function to do so.

diff --git a/include/git2/object.h b/include/git2/object.h
index 984dbb7..dbf480e 100644
--- a/include/git2/object.h
+++ b/include/git2/object.h
@@ -224,6 +224,28 @@ GIT_EXTERN(int) git_object_peel(
  */
 GIT_EXTERN(int) git_object_dup(git_object **dest, git_object *source);
 
+/**
+ * Analyzes a buffer of raw object content and determines its validity.
+ * Tree, commit, and tag objects will be parsed and ensured that they
+ * are valid, parseable content.  (Blobs are always valid by definition.)
+ * An error message will be set with an informative message if the object
+ * is not valid.
+ *
+ * @warning This function is experimental and its signature may change in
+ * the future.
+ *
+ * @param valid Output pointer to set with validity of the object content
+ * @param buf The contents to validate
+ * @param len The length of the buffer
+ * @param type The type of the object in the buffer
+ * @return 0 on success or an error code
+ */
+GIT_EXTERN(int) git_object_rawcontent_is_valid(
+	int *valid,
+	const char *buf,
+	size_t len,
+	git_object_t type);
+
 /** @} */
 GIT_END_DECL
 
diff --git a/src/object.c b/src/object.c
index 7e6ad3a..7bc256f 100644
--- a/src/object.c
+++ b/src/object.c
@@ -567,3 +567,35 @@ bool git_object__is_valid(
 
 	return true;
 }
+
+int git_object_rawcontent_is_valid(
+	int *valid,
+	const char *buf,
+	size_t len,
+	git_object_t type)
+{
+	git_object *obj = NULL;
+	int error;
+
+	GIT_ASSERT_ARG(valid);
+	GIT_ASSERT_ARG(buf);
+
+	/* Blobs are always valid; don't bother parsing. */
+	if (type == GIT_OBJECT_BLOB) {
+		*valid = 1;
+		return 0;
+	}
+
+	error = git_object__from_raw(&obj, buf, len, type);
+	git_object_free(obj);
+
+	if (error == 0) {
+		*valid = 1;
+		return 0;
+	} else if (error == GIT_EINVALID) {
+		*valid = 0;
+		return 0;
+	}
+
+	return error;
+}
diff --git a/tests/object/validate.c b/tests/object/validate.c
new file mode 100644
index 0000000..87193de
--- /dev/null
+++ b/tests/object/validate.c
@@ -0,0 +1,50 @@
+#include "clar_libgit2.h"
+
+#define VALID_COMMIT "tree bdd24e358576f1baa275df98cdcaf3ac9a3f4233\n" \
+               "parent d6d956f1d66210bfcd0484166befab33b5987a39\n" \
+               "author Edward Thomson <ethomson@edwardthomson.com> 1638286404 -0500\n" \
+               "committer Edward Thomson <ethomson@edwardthomson.com> 1638324642 -0500\n" \
+               "\n" \
+               "commit go here.\n"
+#define VALID_TREE "100644 HEADER\0\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42"
+
+#define INVALID_COMMIT "tree bdd24e358576f1baa275df98cdcaf3ac9a3f4233\n" \
+               "parent d6d956f1d66210bfcd0484166befab33b5987a39\n" \
+               "committer Edward Thomson <ethomson@edwardthomson.com> 1638324642 -0500\n" \
+               "\n" \
+               "commit go here.\n"
+#define INVALID_TREE "100644 HEADER \x42\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42"
+
+void test_object_validate__valid(void)
+{
+	int valid;
+
+	cl_git_pass(git_object_rawcontent_is_valid(&valid, "", 0, GIT_OBJECT_BLOB));
+	cl_assert_equal_i(1, valid);
+
+	cl_git_pass(git_object_rawcontent_is_valid(&valid, "foobar", 0, GIT_OBJECT_BLOB));
+	cl_assert_equal_i(1, valid);
+
+	cl_git_pass(git_object_rawcontent_is_valid(&valid, VALID_COMMIT, CONST_STRLEN(VALID_COMMIT), GIT_OBJECT_COMMIT));
+	cl_assert_equal_i(1, valid);
+
+	cl_git_pass(git_object_rawcontent_is_valid(&valid, VALID_TREE, CONST_STRLEN(VALID_TREE), GIT_OBJECT_TREE));
+	cl_assert_equal_i(1, valid);
+}
+
+void test_object_validate__invalid(void)
+{
+	int valid;
+
+	cl_git_pass(git_object_rawcontent_is_valid(&valid, "", 0, GIT_OBJECT_COMMIT));
+	cl_assert_equal_i(0, valid);
+
+	cl_git_pass(git_object_rawcontent_is_valid(&valid, "foobar", 0, GIT_OBJECT_COMMIT));
+	cl_assert_equal_i(0, valid);
+
+	cl_git_pass(git_object_rawcontent_is_valid(&valid, INVALID_COMMIT, CONST_STRLEN(INVALID_COMMIT), GIT_OBJECT_COMMIT));
+	cl_assert_equal_i(0, valid);
+
+	cl_git_pass(git_object_rawcontent_is_valid(&valid, INVALID_TREE, CONST_STRLEN(INVALID_TREE), GIT_OBJECT_TREE));
+	cl_assert_equal_i(0, valid);
+}