{"thread":{"id":"61385","subject":"[PATCH 7/8] refs: check refnames as fully qualified when writing","startedAt":"2024-04-29T08:34:02Z","lastAt":"2024-04-29T08:34:02Z","messageCount":1,"participants":["Jeff King"],"isPatch":true,"patchVersion":1,"patchTotal":8},"messages":[{"id":"493618","messageId":"20240429083401.GF233423@coredump.intra.peff.net","threadId":"61385","inReplyTo":null,"subject":"[PATCH 7/8] refs: check refnames as fully qualified when writing","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2024-04-29T08:34:01Z","receivedAt":"2024-04-29T08:34:02Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"When a ref update is queued via ref_transaction_update(), we call\ncheck_refname_format() to make sure the name is acceptable. We pass\nREFNAME_ALLOW_ONELEVEL, which allows pseudorefs like MERGE_HEAD. But\nthat's not enough to forbid names outside of refs/ like \"foo/bar\" or\neven scary stuff like \"config\" (though fortunately I think that should\nnever work because we cannot resolve \"config\" to read the old value).\n\nLet's instead pass REFNAME_FULLY_QUALIFIED, which tells the checking\nfunction that we really do have a full refname, and it can enforce\nit as such.\n\nThis means that \"git update-ref foo/bar HEAD\" will now be rejected. Note\nthat _deleting_ such a ref is already forbidden (and there is a test in\nt1430 for that already), due to some confusing differences between\ncheck_refname_format() and refname_is_safe(). See the previous commit\nfor more details.\n\nSigned-off-by: Jeff King <peff@peff.net>\n---\n refs.c                  |  2 +-\n t/t1430-bad-ref-name.sh | 10 ++++++++++\n 2 files changed, 11 insertions(+), 1 deletion(-)\n\ndiff --git a/refs.c b/refs.c\nindex 44b4419050..57663cfe9e 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -1267,7 +1267,7 @@ int ref_transaction_update(struct ref_transaction *transaction,\n \n \tif (!(flags & REF_SKIP_REFNAME_VERIFICATION) &&\n \t    ((new_oid && !is_null_oid(new_oid)) ?\n-\t\t     check_refname_format(refname, REFNAME_ALLOW_ONELEVEL) :\n+\t\t     check_refname_format(refname, REFNAME_FULLY_QUALIFIED) :\n \t\t\t   !refname_is_safe(refname))) {\n \t\tstrbuf_addf(err, _(\"refusing to update ref with bad name '%s'\"),\n \t\t\t    refname);\ndiff --git a/t/t1430-bad-ref-name.sh b/t/t1430-bad-ref-name.sh\nindex 0c00118c2b..120e1557d7 100755\n--- a/t/t1430-bad-ref-name.sh\n+++ b/t/t1430-bad-ref-name.sh\n@@ -390,4 +390,14 @@ test_expect_success 'branch -m can rename refs/heads/-dash' '\n \tgit show-ref refs/heads/dash\n '\n \n+test_expect_success 'update-ref refuses lowercase outside of refs/' '\n+\ttest_must_fail git update-ref lowercase HEAD 2>err &&\n+\ttest_grep \"refusing to update ref with bad name\" err\n+'\n+\n+test_expect_success 'update-ref refuses non-underscore outside of refs/' '\n+\ttest_must_fail git update-ref FOO/HEAD HEAD 2>err &&\n+\ttest_grep \"refusing to update ref with bad name\" err\n+'\n+\n test_done\n-- \n2.45.0.rc1.416.gbe2a76c799\n\n"}]}