From 76c6461ad927f02cea91c6c2c220112decb8ac64 Mon Sep 17 00:00:00 2001 From: Daniel Scherzer Date: Thu, 30 Jul 2026 09:47:26 -0700 Subject: [PATCH 01/28] [CString panic reduction] Add regression tests for `ObjectType::from_str()` Add a `#[should_panic]` regression test for `ObjectType::from_str()` with an invalid string. --- src/lib.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index 628fc94daa..8c2f0a9532 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1635,6 +1635,12 @@ mod tests { assert!(ObjectType::Blob.is_loose()); } + #[test] + #[should_panic] + fn object_type_invalid() { + ObjectType::from_str("ab\x0012"); + } + #[test] fn convert_filemode() { assert_eq!(i32::from(FileMode::Blob), 0o100644); From 5257d9670f492a50c6386013a42d434b84c3ed30 Mon Sep 17 00:00:00 2001 From: Daniel Scherzer Date: Thu, 30 Jul 2026 09:50:07 -0700 Subject: [PATCH 02/28] [CString panic reduction] Handle errors in `ObjectType::from_str()` Instead of panicking, treat failure to create a `CString` as indicating that the provided string does not correspond to valid object type. --- src/lib.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 8c2f0a9532..17212133cf 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -955,7 +955,8 @@ impl ObjectType { /// Convert a string object type representation to its object type. #[expect(clippy::should_implement_trait)] pub fn from_str(s: &str) -> Option { - let raw = unsafe { call!(raw::git_object_string2type(CString::new(s).unwrap())) }; + let cstr = CString::new(s).ok()?; + let raw = unsafe { call!(raw::git_object_string2type(cstr)) }; ObjectType::from_raw(raw) } } @@ -1636,9 +1637,8 @@ mod tests { } #[test] - #[should_panic] fn object_type_invalid() { - ObjectType::from_str("ab\x0012"); + assert_eq!(None, ObjectType::from_str("ab\x0012")); } #[test] From bf71a09a78918874088efdd60bccd76e4a24c856 Mon Sep 17 00:00:00 2001 From: Daniel Scherzer Date: Thu, 30 Jul 2026 10:07:19 -0700 Subject: [PATCH 03/28] [CString panic reduction] Add regression tests for `Refspec::dst_matches()` Add a `#[should_panic]` regression test for `Refspec::dst_matches()` with an invalid string. --- src/refspec.rs | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/refspec.rs b/src/refspec.rs index 7717ab6787..2251abf7f8 100644 --- a/src/refspec.rs +++ b/src/refspec.rs @@ -114,3 +114,23 @@ impl<'remote> Binding for Refspec<'remote> { self.raw } } + +#[cfg(test)] +mod tests { + #[test] + #[should_panic] + fn dst_matches_invalid() { + let (_td, repo) = crate::test::repo_init(); + repo.remote("origin", "https://github.com/rust-lang/git2-rs") + .expect("Remote added"); + let remote = repo.find_remote("origin").expect("Remote exists"); + let specs: Vec<_> = remote.refspecs().collect(); + assert_eq!(1, specs.len()); + assert_eq!( + "+refs/heads/*:refs/remotes/origin/*", + specs[0].str().expect("Valid string") + ); + + specs[0].dst_matches("ab\x0012"); + } +} From 9122496eb8f27ab2b760fe5c7d467c25e0084bca Mon Sep 17 00:00:00 2001 From: Daniel Scherzer Date: Thu, 30 Jul 2026 10:09:18 -0700 Subject: [PATCH 04/28] [CString panic reduction] Handle errors in `Refspec::dst_matches()` Instead of panicking, treat failure to create a `CString` as indicating that the provided string is not matched by the refspec. --- src/refspec.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/refspec.rs b/src/refspec.rs index 2251abf7f8..27aa684d8e 100644 --- a/src/refspec.rs +++ b/src/refspec.rs @@ -37,7 +37,9 @@ impl<'remote> Refspec<'remote> { /// Check if a refspec's destination descriptor matches a reference pub fn dst_matches(&self, refname: &str) -> bool { - let refname = CString::new(refname).unwrap(); + let Ok(refname) = CString::new(refname) else { + return false; + }; unsafe { raw::git_refspec_dst_matches(self.raw, refname.as_ptr()) == 1 } } @@ -118,7 +120,6 @@ impl<'remote> Binding for Refspec<'remote> { #[cfg(test)] mod tests { #[test] - #[should_panic] fn dst_matches_invalid() { let (_td, repo) = crate::test::repo_init(); repo.remote("origin", "https://github.com/rust-lang/git2-rs") @@ -131,6 +132,6 @@ mod tests { specs[0].str().expect("Valid string") ); - specs[0].dst_matches("ab\x0012"); + assert!(!specs[0].dst_matches("ab\x0012")); } } From 6d089b16a8b44cc4b57f8b88b3385119da420de8 Mon Sep 17 00:00:00 2001 From: Daniel Scherzer Date: Thu, 30 Jul 2026 10:10:32 -0700 Subject: [PATCH 05/28] [CString panic reduction] Add regression tests for `Refspec::src_matches()` Add a `#[should_panic]` regression test for `Refspec::src_matches()` with an invalid string. --- src/refspec.rs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/refspec.rs b/src/refspec.rs index 27aa684d8e..e874aecd6f 100644 --- a/src/refspec.rs +++ b/src/refspec.rs @@ -134,4 +134,21 @@ mod tests { assert!(!specs[0].dst_matches("ab\x0012")); } + + #[test] + #[should_panic] + fn src_matches_invalid() { + let (_td, repo) = crate::test::repo_init(); + repo.remote("origin", "https://github.com/rust-lang/git2-rs") + .expect("Remote added"); + let remote = repo.find_remote("origin").expect("Remote exists"); + let specs: Vec<_> = remote.refspecs().collect(); + assert_eq!(1, specs.len()); + assert_eq!( + "+refs/heads/*:refs/remotes/origin/*", + specs[0].str().expect("Valid string") + ); + + specs[0].src_matches("ab\x0012"); + } } From 15c67f3643654ce2fb694e522bdf666e3a5a92bf Mon Sep 17 00:00:00 2001 From: Daniel Scherzer Date: Thu, 30 Jul 2026 10:11:13 -0700 Subject: [PATCH 06/28] [CString panic reduction] Handle errors in `Refspec::src_matches()` Instead of panicking, treat failure to create a `CString` as indicating that the provided string is not matched by the refspec. --- src/refspec.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/refspec.rs b/src/refspec.rs index e874aecd6f..d429686b68 100644 --- a/src/refspec.rs +++ b/src/refspec.rs @@ -55,7 +55,9 @@ impl<'remote> Refspec<'remote> { /// Check if a refspec's source descriptor matches a reference pub fn src_matches(&self, refname: &str) -> bool { - let refname = CString::new(refname).unwrap(); + let Ok(refname) = CString::new(refname) else { + return false; + }; unsafe { raw::git_refspec_src_matches(self.raw, refname.as_ptr()) == 1 } } @@ -136,7 +138,6 @@ mod tests { } #[test] - #[should_panic] fn src_matches_invalid() { let (_td, repo) = crate::test::repo_init(); repo.remote("origin", "https://github.com/rust-lang/git2-rs") @@ -149,6 +150,6 @@ mod tests { specs[0].str().expect("Valid string") ); - specs[0].src_matches("ab\x0012"); + assert!(!specs[0].src_matches("ab\x0012")); } } From 6f60d80aac923abbd372d701d4750382c50ff9a7 Mon Sep 17 00:00:00 2001 From: Daniel Scherzer Date: Thu, 30 Jul 2026 10:13:47 -0700 Subject: [PATCH 07/28] [CString panic reduction] Add regression tests for `Refspec::transform()` Add a `#[should_panic]` regression test for `Refspec::transform()` with an invalid string. --- src/refspec.rs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/refspec.rs b/src/refspec.rs index d429686b68..8008152102 100644 --- a/src/refspec.rs +++ b/src/refspec.rs @@ -152,4 +152,21 @@ mod tests { assert!(!specs[0].src_matches("ab\x0012")); } + + #[test] + #[should_panic] + fn transform_invalid() { + let (_td, repo) = crate::test::repo_init(); + repo.remote("origin", "https://github.com/rust-lang/git2-rs") + .expect("Remote added"); + let remote = repo.find_remote("origin").expect("Remote exists"); + let specs: Vec<_> = remote.refspecs().collect(); + assert_eq!(1, specs.len()); + assert_eq!( + "+refs/heads/*:refs/remotes/origin/*", + specs[0].str().expect("Valid string") + ); + + let _ = specs[0].transform("ab\x0012"); + } } From fb425009f6f0d0c5e4923901e125f41d36b033b1 Mon Sep 17 00:00:00 2001 From: Daniel Scherzer Date: Thu, 30 Jul 2026 10:19:10 -0700 Subject: [PATCH 08/28] [CString panic reduction] Handle errors in `Refspec::transform()` Instead of panicking, convert the `alloc::ffi::NulError` error from the failure to create a `CString` into a git2 `Error` object and return an error variant. --- src/refspec.rs | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/refspec.rs b/src/refspec.rs index 8008152102..4663c38758 100644 --- a/src/refspec.rs +++ b/src/refspec.rs @@ -78,7 +78,7 @@ impl<'remote> Refspec<'remote> { /// Transform a reference to its target following the refspec's rules pub fn transform(&self, name: &str) -> Result { - let name = CString::new(name).unwrap(); + let name = CString::new(name)?; let buf = Buf::new(); unsafe { try_call!(raw::git_refspec_transform( @@ -154,7 +154,6 @@ mod tests { } #[test] - #[should_panic] fn transform_invalid() { let (_td, repo) = crate::test::repo_init(); repo.remote("origin", "https://github.com/rust-lang/git2-rs") @@ -167,6 +166,16 @@ mod tests { specs[0].str().expect("Valid string") ); - let _ = specs[0].transform("ab\x0012"); + // Cannot use unwrap_err() because Buf does not implement Debug + let result = match specs[0].transform("ab\x0012") { + Ok(_) => panic!("Expected an err"), + Err(e) => e, + }; + assert_eq!( + crate::Error::from_str( + "data contained a nul byte that could not be represented as a string" + ), + result, + ); } } From 6f0f05dfd5c1d4a6b2c0412f90968f44ebb95942 Mon Sep 17 00:00:00 2001 From: Daniel Scherzer Date: Thu, 30 Jul 2026 10:20:17 -0700 Subject: [PATCH 09/28] [CString panic reduction] Add regression tests for `Refspec::rtransform()` Add a `#[should_panic]` regression test for `Refspec::rtransform()` with an invalid string. --- src/refspec.rs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/refspec.rs b/src/refspec.rs index 4663c38758..827f835bd9 100644 --- a/src/refspec.rs +++ b/src/refspec.rs @@ -178,4 +178,21 @@ mod tests { result, ); } + + #[test] + #[should_panic] + fn rtransform_invalid() { + let (_td, repo) = crate::test::repo_init(); + repo.remote("origin", "https://github.com/rust-lang/git2-rs") + .expect("Remote added"); + let remote = repo.find_remote("origin").expect("Remote exists"); + let specs: Vec<_> = remote.refspecs().collect(); + assert_eq!(1, specs.len()); + assert_eq!( + "+refs/heads/*:refs/remotes/origin/*", + specs[0].str().expect("Valid string") + ); + + let _ = specs[0].rtransform("ab\x0012"); + } } From 9880a325848ff677c3bcbe6ba22dc26d5e6f6a3f Mon Sep 17 00:00:00 2001 From: Daniel Scherzer Date: Thu, 30 Jul 2026 10:20:55 -0700 Subject: [PATCH 10/28] [CString panic reduction] Handle errors in `Refspec::rtransform()` Instead of panicking, convert the `alloc::ffi::NulError` error from the failure to create a `CString` into a git2 `Error` object and return an error variant. --- src/refspec.rs | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/refspec.rs b/src/refspec.rs index 827f835bd9..afbbdfc2a9 100644 --- a/src/refspec.rs +++ b/src/refspec.rs @@ -92,7 +92,7 @@ impl<'remote> Refspec<'remote> { /// Transform a target reference to its source reference following the refspec's rules pub fn rtransform(&self, name: &str) -> Result { - let name = CString::new(name).unwrap(); + let name = CString::new(name)?; let buf = Buf::new(); unsafe { try_call!(raw::git_refspec_rtransform( @@ -180,7 +180,6 @@ mod tests { } #[test] - #[should_panic] fn rtransform_invalid() { let (_td, repo) = crate::test::repo_init(); repo.remote("origin", "https://github.com/rust-lang/git2-rs") @@ -193,6 +192,16 @@ mod tests { specs[0].str().expect("Valid string") ); - let _ = specs[0].rtransform("ab\x0012"); + // Cannot use unwrap_err() because Buf does not implement Debug + let result = match specs[0].rtransform("ab\x0012") { + Ok(_) => panic!("Expected an err"), + Err(e) => e, + }; + assert_eq!( + crate::Error::from_str( + "data contained a nul byte that could not be represented as a string" + ), + result, + ); } } From 09996affcf756c61e8311820aa11491b9994b567 Mon Sep 17 00:00:00 2001 From: Daniel Scherzer Date: Thu, 30 Jul 2026 10:24:53 -0700 Subject: [PATCH 11/28] [CString panic reduction] Add regression tests for `Transaction::lock_ref()` Add a `#[should_panic]` regression test for `Transaction::lock_ref()` with an invalid string. --- src/transaction.rs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/transaction.rs b/src/transaction.rs index d4116b050f..490fba3452 100644 --- a/src/transaction.rs +++ b/src/transaction.rs @@ -314,4 +314,13 @@ mod tests { Err(e) if is_not_locked_err(&e) )) } + + #[test] + #[should_panic] + fn invalid_lock_ref() { + let (_td, repo) = crate::test::repo_init(); + + let mut tx = t!(repo.transaction()); + let _ = tx.lock_ref("ab\x0012"); + } } From 187f5dabf50cd203956bb6a26bb6fcfd5c76d98b Mon Sep 17 00:00:00 2001 From: Daniel Scherzer Date: Thu, 30 Jul 2026 10:26:25 -0700 Subject: [PATCH 12/28] [CString panic reduction] Handle errors in `Transaction::lock_ref()` Instead of panicking, convert the `alloc::ffi::NulError` error from the failure to create a `CString` into a git2 `Error` object and return an error variant. --- src/transaction.rs | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/transaction.rs b/src/transaction.rs index 490fba3452..684711b6a0 100644 --- a/src/transaction.rs +++ b/src/transaction.rs @@ -38,7 +38,7 @@ impl<'repo> Binding for Transaction<'repo> { impl<'repo> Transaction<'repo> { /// Lock the specified reference by name. pub fn lock_ref(&mut self, refname: &str) -> Result<(), Error> { - let refname = CString::new(refname).unwrap(); + let refname = CString::new(refname)?; unsafe { try_call!(raw::git_transaction_lock_ref(self.raw, refname)); } @@ -316,11 +316,16 @@ mod tests { } #[test] - #[should_panic] fn invalid_lock_ref() { let (_td, repo) = crate::test::repo_init(); let mut tx = t!(repo.transaction()); - let _ = tx.lock_ref("ab\x0012"); + let result = tx.lock_ref("ab\x0012"); + assert_eq!( + Err(crate::Error::from_str( + "data contained a nul byte that could not be represented as a string" + )), + result, + ); } } From e2579995ab3a21ce807cb2dc0657764a28ac3cb3 Mon Sep 17 00:00:00 2001 From: Daniel Scherzer Date: Thu, 30 Jul 2026 10:29:32 -0700 Subject: [PATCH 13/28] [CString panic reduction] Add regression tests for `Transaction::set_target()` Add a `#[should_panic]` regression test for `Transaction::set_target()` with an invalid string for the `refname` parameter. --- src/transaction.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/transaction.rs b/src/transaction.rs index 684711b6a0..5c80bcc635 100644 --- a/src/transaction.rs +++ b/src/transaction.rs @@ -328,4 +328,14 @@ mod tests { result, ); } + + #[test] + #[should_panic] + fn invalid_set_target_refname() { + let (_td, repo) = crate::test::repo_init(); + + let mut tx = t!(repo.transaction()); + let oid = Oid::from_bytes(&[1u8; 20]).unwrap(); + let _ = tx.set_target("ab\x0012", oid, None, "valid message"); + } } From 4d0dc51515a442283f530aaa0fda6d4464be6acb Mon Sep 17 00:00:00 2001 From: Daniel Scherzer Date: Thu, 30 Jul 2026 10:31:49 -0700 Subject: [PATCH 14/28] [CString panic reduction] Handle errors in `Transaction::set_target()` For the `refname` parameter, instead of panicking, convert the `alloc::ffi::NulError` error from the failure to create a `CString` into a git2 `Error` object and return an error variant. --- src/transaction.rs | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/transaction.rs b/src/transaction.rs index 5c80bcc635..5e8ef523ef 100644 --- a/src/transaction.rs +++ b/src/transaction.rs @@ -59,7 +59,7 @@ impl<'repo> Transaction<'repo> { reflog_signature: Option<&Signature<'_>>, reflog_message: &str, ) -> Result<(), Error> { - let refname = CString::new(refname).unwrap(); + let refname = CString::new(refname)?; let reflog_message = CString::new(reflog_message).unwrap(); unsafe { try_call!(raw::git_transaction_set_target( @@ -330,12 +330,17 @@ mod tests { } #[test] - #[should_panic] fn invalid_set_target_refname() { let (_td, repo) = crate::test::repo_init(); let mut tx = t!(repo.transaction()); let oid = Oid::from_bytes(&[1u8; 20]).unwrap(); - let _ = tx.set_target("ab\x0012", oid, None, "valid message"); + let result = tx.set_target("ab\x0012", oid, None, "valid message"); + assert_eq!( + Err(crate::Error::from_str( + "data contained a nul byte that could not be represented as a string" + )), + result, + ); } } From a49a57ef07430bb5826bef20fc4bcd7a36dce596 Mon Sep 17 00:00:00 2001 From: Daniel Scherzer Date: Thu, 30 Jul 2026 10:33:08 -0700 Subject: [PATCH 15/28] [CString panic reduction] Add more regression tests for `Transaction::set_target()` Add a `#[should_panic]` regression test for `Transaction::set_target()` with an invalid string for the `reflog_message` parameter. --- src/transaction.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/transaction.rs b/src/transaction.rs index 5e8ef523ef..06df97b3e2 100644 --- a/src/transaction.rs +++ b/src/transaction.rs @@ -343,4 +343,14 @@ mod tests { result, ); } + + #[test] + #[should_panic] + fn invalid_set_target_message() { + let (_td, repo) = crate::test::repo_init(); + + let mut tx = t!(repo.transaction()); + let oid = Oid::from_bytes(&[1u8; 20]).unwrap(); + let _ = tx.set_target("refs/heads/main", oid, None, "ab\x0012"); + } } From db03bb6cd015c800da3c1d39ae0c6b5d719a7f77 Mon Sep 17 00:00:00 2001 From: Daniel Scherzer Date: Thu, 30 Jul 2026 10:33:49 -0700 Subject: [PATCH 16/28] [CString panic reduction] Handle more errors in `Transaction::set_target()` For the `reflog_message` parameter, instead of panicking, convert the `alloc::ffi::NulError` error from the failure to create a `CString` into a git2 `Error` object and return an error variant. --- src/transaction.rs | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/transaction.rs b/src/transaction.rs index 06df97b3e2..39432e8385 100644 --- a/src/transaction.rs +++ b/src/transaction.rs @@ -60,7 +60,7 @@ impl<'repo> Transaction<'repo> { reflog_message: &str, ) -> Result<(), Error> { let refname = CString::new(refname)?; - let reflog_message = CString::new(reflog_message).unwrap(); + let reflog_message = CString::new(reflog_message)?; unsafe { try_call!(raw::git_transaction_set_target( self.raw, @@ -345,12 +345,17 @@ mod tests { } #[test] - #[should_panic] fn invalid_set_target_message() { let (_td, repo) = crate::test::repo_init(); let mut tx = t!(repo.transaction()); let oid = Oid::from_bytes(&[1u8; 20]).unwrap(); - let _ = tx.set_target("refs/heads/main", oid, None, "ab\x0012"); + let result = tx.set_target("refs/heads/main", oid, None, "ab\x0012"); + assert_eq!( + Err(crate::Error::from_str( + "data contained a nul byte that could not be represented as a string" + )), + result, + ); } } From c88924f10fbb9d46bd6bf4b1a3c7e7e93486b6f9 Mon Sep 17 00:00:00 2001 From: Daniel Scherzer Date: Thu, 30 Jul 2026 10:37:21 -0700 Subject: [PATCH 17/28] [CString panic reduction] Add regression tests for `Transaction::set_symbolic_target()` Add a `#[should_panic]` regression test for `Transaction::set_symbolic_target()` with an invalid string for the `refname` parameter. --- src/transaction.rs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/transaction.rs b/src/transaction.rs index 39432e8385..022d5bfd2d 100644 --- a/src/transaction.rs +++ b/src/transaction.rs @@ -358,4 +358,13 @@ mod tests { result, ); } + + #[test] + #[should_panic] + fn invalid_set_symbolic_target_refname() { + let (_td, repo) = crate::test::repo_init(); + + let mut tx = t!(repo.transaction()); + let _ = tx.set_symbolic_target("ab\x0012", "refs/heads/main", None, "valid message"); + } } From 048883c97a2edd401dc872982a905508fd3fb8d2 Mon Sep 17 00:00:00 2001 From: Daniel Scherzer Date: Thu, 30 Jul 2026 10:38:14 -0700 Subject: [PATCH 18/28] [CString panic reduction] Handle errors in `Transaction::set_symbolic_target()` For the `refname` parameter, instead of panicking, convert the `alloc::ffi::NulError` error from the failure to create a `CString` into a git2 `Error` object and return an error variant. --- src/transaction.rs | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/transaction.rs b/src/transaction.rs index 022d5bfd2d..2534b4542e 100644 --- a/src/transaction.rs +++ b/src/transaction.rs @@ -87,7 +87,7 @@ impl<'repo> Transaction<'repo> { reflog_signature: Option<&Signature<'_>>, reflog_message: &str, ) -> Result<(), Error> { - let refname = CString::new(refname).unwrap(); + let refname = CString::new(refname)?; let target = CString::new(target).unwrap(); let reflog_message = CString::new(reflog_message).unwrap(); unsafe { @@ -360,11 +360,16 @@ mod tests { } #[test] - #[should_panic] fn invalid_set_symbolic_target_refname() { let (_td, repo) = crate::test::repo_init(); let mut tx = t!(repo.transaction()); - let _ = tx.set_symbolic_target("ab\x0012", "refs/heads/main", None, "valid message"); + let result = tx.set_symbolic_target("ab\x0012", "refs/heads/main", None, "valid message"); + assert_eq!( + Err(crate::Error::from_str( + "data contained a nul byte that could not be represented as a string" + )), + result, + ); } } From 4299fb542a5734a85c9b8b83a558867efdcf17db Mon Sep 17 00:00:00 2001 From: Daniel Scherzer Date: Thu, 30 Jul 2026 10:39:41 -0700 Subject: [PATCH 19/28] [CString panic reduction] Add more regression tests for `Transaction::set_symbolic_target()` Add a `#[should_panic]` regression test for `Transaction::set_symbolic_target()` with an invalid string for the `target` parameter. --- src/transaction.rs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/transaction.rs b/src/transaction.rs index 2534b4542e..148f8f6afe 100644 --- a/src/transaction.rs +++ b/src/transaction.rs @@ -372,4 +372,13 @@ mod tests { result, ); } + + #[test] + #[should_panic] + fn invalid_set_symbolic_target_target() { + let (_td, repo) = crate::test::repo_init(); + + let mut tx = t!(repo.transaction()); + let _ = tx.set_symbolic_target("refs/heads/next", "ab\x0012", None, "valid message"); + } } From 59ecc4f54ac1f0e025bfd7f21affd029bcdc7b1c Mon Sep 17 00:00:00 2001 From: Daniel Scherzer Date: Thu, 30 Jul 2026 10:40:37 -0700 Subject: [PATCH 20/28] [CString panic reduction] Handle more errors in `Transaction::set_symbolic_target()` For the `target` parameter, instead of panicking, convert the `alloc::ffi::NulError` error from the failure to create a `CString` into a git2 `Error` object and return an error variant. --- src/transaction.rs | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/transaction.rs b/src/transaction.rs index 148f8f6afe..2f6b605cb4 100644 --- a/src/transaction.rs +++ b/src/transaction.rs @@ -88,7 +88,7 @@ impl<'repo> Transaction<'repo> { reflog_message: &str, ) -> Result<(), Error> { let refname = CString::new(refname)?; - let target = CString::new(target).unwrap(); + let target = CString::new(target)?; let reflog_message = CString::new(reflog_message).unwrap(); unsafe { try_call!(raw::git_transaction_set_symbolic_target( @@ -374,11 +374,16 @@ mod tests { } #[test] - #[should_panic] fn invalid_set_symbolic_target_target() { let (_td, repo) = crate::test::repo_init(); let mut tx = t!(repo.transaction()); - let _ = tx.set_symbolic_target("refs/heads/next", "ab\x0012", None, "valid message"); + let result = tx.set_symbolic_target("refs/heads/next", "ab\x0012", None, "valid message"); + assert_eq!( + Err(crate::Error::from_str( + "data contained a nul byte that could not be represented as a string" + )), + result, + ); } } From 973de3796a14512f22c17b86b8819e4608e4c2da Mon Sep 17 00:00:00 2001 From: Daniel Scherzer Date: Thu, 30 Jul 2026 10:41:45 -0700 Subject: [PATCH 21/28] [CString panic reduction] Add more regression tests for `Transaction::set_symbolic_target()` Add a `#[should_panic]` regression test for `Transaction::set_symbolic_target()` with an invalid string for the `reflog_message` parameter. --- src/transaction.rs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/transaction.rs b/src/transaction.rs index 2f6b605cb4..dd4c89870a 100644 --- a/src/transaction.rs +++ b/src/transaction.rs @@ -386,4 +386,13 @@ mod tests { result, ); } + + #[test] + #[should_panic] + fn invalid_set_symbolic_target_message() { + let (_td, repo) = crate::test::repo_init(); + + let mut tx = t!(repo.transaction()); + let _ = tx.set_symbolic_target("refs/heads/next", "refs/heads/main", None, "ab\x0012"); + } } From 4366472b5878d684b85c7e0c157eb1d01dce709c Mon Sep 17 00:00:00 2001 From: Daniel Scherzer Date: Thu, 30 Jul 2026 10:42:32 -0700 Subject: [PATCH 22/28] [CString panic reduction] Handle more errors in `Transaction::set_symbolic_target()` For the `reflog_message` parameter, instead of panicking, convert the `alloc::ffi::NulError` error from the failure to create a `CString` into a git2 `Error` object and return an error variant. --- src/transaction.rs | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/transaction.rs b/src/transaction.rs index dd4c89870a..c6a5116097 100644 --- a/src/transaction.rs +++ b/src/transaction.rs @@ -89,7 +89,7 @@ impl<'repo> Transaction<'repo> { ) -> Result<(), Error> { let refname = CString::new(refname)?; let target = CString::new(target)?; - let reflog_message = CString::new(reflog_message).unwrap(); + let reflog_message = CString::new(reflog_message)?; unsafe { try_call!(raw::git_transaction_set_symbolic_target( self.raw, @@ -388,11 +388,16 @@ mod tests { } #[test] - #[should_panic] fn invalid_set_symbolic_target_message() { let (_td, repo) = crate::test::repo_init(); let mut tx = t!(repo.transaction()); - let _ = tx.set_symbolic_target("refs/heads/next", "refs/heads/main", None, "ab\x0012"); + let result = tx.set_symbolic_target("refs/heads/next", "refs/heads/main", None, "ab\x0012"); + assert_eq!( + Err(crate::Error::from_str( + "data contained a nul byte that could not be represented as a string" + )), + result, + ); } } From 263af9ca44fa266d35a26d7a463b6df65ca45ba0 Mon Sep 17 00:00:00 2001 From: Daniel Scherzer Date: Thu, 30 Jul 2026 10:44:55 -0700 Subject: [PATCH 23/28] [CString panic reduction] Add regression tests for `Transaction::set_reflog()` Add a `#[should_panic]` regression test for `Transaction::set_reflog()` with an invalid string. --- src/transaction.rs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/transaction.rs b/src/transaction.rs index c6a5116097..bdd01d57d4 100644 --- a/src/transaction.rs +++ b/src/transaction.rs @@ -400,4 +400,15 @@ mod tests { result, ); } + + #[test] + #[should_panic] + fn invalid_set_reflog() { + let (_td, repo) = crate::test::repo_init(); + + let reflog = repo.reflog("dummy").expect("Valid name"); + + let mut tx = t!(repo.transaction()); + let _ = tx.set_reflog("ab\x0012", reflog); + } } From f2db0b848b29db7c99358a0df889ee7e203c78cc Mon Sep 17 00:00:00 2001 From: Daniel Scherzer Date: Thu, 30 Jul 2026 10:46:32 -0700 Subject: [PATCH 24/28] [CString panic reduction] Handle errors in `Transaction::set_reflog()` Instead of panicking, convert the `alloc::ffi::NulError` error from the failure to create a `CString` into a git2 `Error` object and return an error variant. --- src/transaction.rs | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/transaction.rs b/src/transaction.rs index bdd01d57d4..6dcbc9df08 100644 --- a/src/transaction.rs +++ b/src/transaction.rs @@ -113,7 +113,7 @@ impl<'repo> Transaction<'repo> { /// written to the log (i.e. the `reflog_signature` and `reflog_message` /// parameters will be ignored). pub fn set_reflog(&mut self, refname: &str, reflog: Reflog) -> Result<(), Error> { - let refname = CString::new(refname).unwrap(); + let refname = CString::new(refname)?; unsafe { try_call!(raw::git_transaction_set_reflog( self.raw, @@ -402,13 +402,18 @@ mod tests { } #[test] - #[should_panic] fn invalid_set_reflog() { let (_td, repo) = crate::test::repo_init(); let reflog = repo.reflog("dummy").expect("Valid name"); let mut tx = t!(repo.transaction()); - let _ = tx.set_reflog("ab\x0012", reflog); + let result = tx.set_reflog("ab\x0012", reflog); + assert_eq!( + Err(crate::Error::from_str( + "data contained a nul byte that could not be represented as a string" + )), + result, + ); } } From f4fd4b8ea7cd354e82d528740955986ed62ab6f4 Mon Sep 17 00:00:00 2001 From: Daniel Scherzer Date: Thu, 30 Jul 2026 10:47:46 -0700 Subject: [PATCH 25/28] [CString panic reduction] Add regression tests for `Transaction::remove()` Add a `#[should_panic]` regression test for `Transaction::remove()` with an invalid string. --- src/transaction.rs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/transaction.rs b/src/transaction.rs index 6dcbc9df08..02413332ff 100644 --- a/src/transaction.rs +++ b/src/transaction.rs @@ -416,4 +416,13 @@ mod tests { result, ); } + + #[test] + #[should_panic] + fn invalid_remove() { + let (_td, repo) = crate::test::repo_init(); + + let mut tx = t!(repo.transaction()); + let _ = tx.remove("ab\x0012"); + } } From d3892d7102dbf7750d4ca8a23a81cc1deb1dee35 Mon Sep 17 00:00:00 2001 From: Daniel Scherzer Date: Thu, 30 Jul 2026 10:48:27 -0700 Subject: [PATCH 26/28] [CString panic reduction] Handle errors in `Transaction::remove()` Instead of panicking, convert the `alloc::ffi::NulError` error from the failure to create a `CString` into a git2 `Error` object and return an error variant. --- src/transaction.rs | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/transaction.rs b/src/transaction.rs index 02413332ff..ffdb9e9ed2 100644 --- a/src/transaction.rs +++ b/src/transaction.rs @@ -129,7 +129,7 @@ impl<'repo> Transaction<'repo> { /// /// The reference must have been locked via `lock_ref`. pub fn remove(&mut self, refname: &str) -> Result<(), Error> { - let refname = CString::new(refname).unwrap(); + let refname = CString::new(refname)?; unsafe { try_call!(raw::git_transaction_remove(self.raw, refname)); } @@ -418,11 +418,16 @@ mod tests { } #[test] - #[should_panic] fn invalid_remove() { let (_td, repo) = crate::test::repo_init(); let mut tx = t!(repo.transaction()); - let _ = tx.remove("ab\x0012"); + let result = tx.remove("ab\x0012"); + assert_eq!( + Err(crate::Error::from_str( + "data contained a nul byte that could not be represented as a string" + )), + result, + ); } } From e5cd76ad923ba3819ac4291515d80e22c7698ecd Mon Sep 17 00:00:00 2001 From: Daniel Scherzer Date: Thu, 30 Jul 2026 10:51:40 -0700 Subject: [PATCH 27/28] [CString panic reduction] Add regression tests for `Tree::get_name_bytes()` Add a `#[should_panic]` regression test for `Tree::get_name_bytes()` with an invalid string. --- src/tree.rs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/tree.rs b/src/tree.rs index 2fde618c83..bd1d525f32 100644 --- a/src/tree.rs +++ b/src/tree.rs @@ -597,4 +597,22 @@ mod tests { let e = tree.walk(TreeWalkMode::PreOrder, |_, _| -1).unwrap_err(); assert_eq!(e.class(), crate::ErrorClass::Callback); } + + #[test] + #[should_panic] + fn invalid_name_bytes() { + let (td, repo) = crate::test::repo_init(); + + setup_repo(&td, &repo); + + let head = repo.head().unwrap(); + let target = head.target().unwrap(); + let commit = repo.find_commit(target).unwrap(); + + let tree = repo.find_tree(commit.tree_id()).unwrap(); + assert_eq!(tree.id(), commit.tree_id()); + assert_eq!(tree.len(), 8); + + tree.get_name_bytes(b"ab\x0012"); + } } From 75bf90b2dd9ca86e02a2ec6da184fdf7ca3f5f45 Mon Sep 17 00:00:00 2001 From: Daniel Scherzer Date: Thu, 30 Jul 2026 10:52:55 -0700 Subject: [PATCH 28/28] [CString panic reduction] Handle errors in `Tree::get_name_bytes()` Instead of panicking, convert the `alloc::ffi::NulError` error from the failure to create a `CString` into a git2 `Error` object and return an error variant. --- src/tree.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/tree.rs b/src/tree.rs index bd1d525f32..4f50bf5358 100644 --- a/src/tree.rs +++ b/src/tree.rs @@ -164,7 +164,7 @@ impl<'repo> Tree<'repo> { /// /// This allows for non-UTF-8 filenames. pub fn get_name_bytes(&self, filename: &[u8]) -> Option> { - let filename = CString::new(filename).unwrap(); + let filename = CString::new(filename).ok()?; let ptr = unsafe { call!(raw::git_tree_entry_byname(&*self.raw(), filename)) }; if ptr.is_null() { None @@ -599,7 +599,6 @@ mod tests { } #[test] - #[should_panic] fn invalid_name_bytes() { let (td, repo) = crate::test::repo_init(); @@ -613,6 +612,7 @@ mod tests { assert_eq!(tree.id(), commit.tree_id()); assert_eq!(tree.len(), 8); - tree.get_name_bytes(b"ab\x0012"); + let result = tree.get_name_bytes(b"ab\x0012"); + assert!(result.is_none()); } }