Message ID | 4dedc5704c3af6ab4ec8cc7503dc826480775b8e.1707324277.git.gitgitgadget@gmail.com (mailing list archive) |
---|---|
State | Accepted |
Commit | 1dbe40156307763255d96653cc853ef4ff841920 |
Headers | show |
Series | show-ref --verify: accept psuedorefs | expand |
"Phillip Wood via GitGitGadget" <gitgitgadget@gmail.com> writes: > ... when CHERRY_PICK_HEAD exists. By calling refname_is_safe() instead > of comparing the refname to "HEAD" we can accept all one-level refs that > contain only uppercase ascii letters and underscores. Geez. We have at least three implementations to determine if a ref is a valid name? > diff --git a/builtin/show-ref.c b/builtin/show-ref.c > index 79955c2856e..1c15421e600 100644 > --- a/builtin/show-ref.c > +++ b/builtin/show-ref.c > @@ -172,7 +172,7 @@ static int cmd_show_ref__verify(const struct show_one_options *show_one_opts, > while (*refs) { > struct object_id oid; > > - if ((starts_with(*refs, "refs/") || !strcmp(*refs, "HEAD")) && > + if ((starts_with(*refs, "refs/") || refname_is_safe(*refs)) && I think the helper you picked is the most sensible one, modulo a few nits. - We would want to teach refname_is_safe() to honor is_pseudoref() from Karthik's series to make rules more consistent. - The refname_is_safe() helper is not just about the stuff at the root level. While starts_with("refs/") is overly lenient, it rejects nonsense like "refs/../trash". We would want to lose "starts_with() ||" part of the condition from here. These are minor non-blocking nits that we should keep in mind only for longer term maintenance, #leftoverbits after the dust settles. Will queue. Thanks.
On Wed, Feb 07, 2024 at 09:12:29AM -0800, Junio C Hamano wrote: > "Phillip Wood via GitGitGadget" <gitgitgadget@gmail.com> writes: > > > ... when CHERRY_PICK_HEAD exists. By calling refname_is_safe() instead > > of comparing the refname to "HEAD" we can accept all one-level refs that > > contain only uppercase ascii letters and underscores. > > Geez. We have at least three implementations to determine if a ref > is a valid name? `check_refname_format()` and `refname_is_safe()` are often used in tandem. `check_refname_format()` performs the first set of checks to verify whether the pathname components are valid, whereas `refname_is_safe()` checks for refs which are unsafe e.g. because they try to escape out of "refs/". I think that we really ought to merge `refname_is_safe()` into `check_refname_format()`. It would require us to introduce a new flag `REFNAME_ALLOW_BAD_NAME` to the latter function so that it would accept refs with a bad name that are otherwise safe. But I think we shouldn't ever allow unsafe names, so merging these two functions would overall reduce the potential for security-relevant issues. Patrick
Hi Junio On 07/02/2024 17:12, Junio C Hamano wrote: > "Phillip Wood via GitGitGadget" <gitgitgadget@gmail.com> writes: > I think the helper you picked is the most sensible one, modulo a few > nits. > > - We would want to teach refname_is_safe() to honor is_pseudoref() > from Karthik's series to make rules more consistent. Yes, I held off sending this series waiting for a while but then got impatient. We may want to split out a helper from is_pseudoref() that just checks the name without trying to read the ref for callers like this which are going to read the ref anyway. > - The refname_is_safe() helper is not just about the stuff at the > root level. While starts_with("refs/") is overly lenient, it > rejects nonsense like "refs/../trash". We would want to lose > "starts_with() ||" part of the condition from here. I left the "starts_with()" in as we check the refname when we look it up with read_ref() so it seemed like wasted effort to do it here as well. > These are minor non-blocking nits that we should keep in mind only > for longer term maintenance, #leftoverbits after the dust settles. > > Will queue. Thanks Phillip
diff --git a/builtin/show-ref.c b/builtin/show-ref.c index 79955c2856e..1c15421e600 100644 --- a/builtin/show-ref.c +++ b/builtin/show-ref.c @@ -172,7 +172,7 @@ static int cmd_show_ref__verify(const struct show_one_options *show_one_opts, while (*refs) { struct object_id oid; - if ((starts_with(*refs, "refs/") || !strcmp(*refs, "HEAD")) && + if ((starts_with(*refs, "refs/") || refname_is_safe(*refs)) && !read_ref(*refs, &oid)) { show_one(show_one_opts, *refs, &oid); } diff --git a/t/t1403-show-ref.sh b/t/t1403-show-ref.sh index d0a8f7b121c..33fb7a38fff 100755 --- a/t/t1403-show-ref.sh +++ b/t/t1403-show-ref.sh @@ -174,6 +174,14 @@ test_expect_success 'show-ref --verify HEAD' ' test_must_be_empty actual ' +test_expect_success 'show-ref --verify pseudorefs' ' + git update-ref CHERRY_PICK_HEAD HEAD $ZERO_OID && + test_when_finished "git update-ref -d CHERRY_PICK_HEAD" && + git show-ref -s --verify HEAD >actual && + git show-ref -s --verify CHERRY_PICK_HEAD >expect && + test_cmp actual expect +' + test_expect_success 'show-ref --verify with dangling ref' ' sha1_file() { echo "$*" | sed "s#..#.git/objects/&/#"