Skip to content

Commit 5413b07

Browse files
committed
refs: enforce name conventions for root refs
We recently taught check_refname_format() to insist that any ref outside of "refs/" match the is_root_ref_syntax() rules. This can reduce the chance of accidentally reading or writing a non-ref file in ".git" when the files backend is in use. For case-sensitive filesystems, this should mostly work. We do not generally create all-caps files under .git/ unless they are meant to be root refs. But for case-insensitive filesystems, it's less clear. Asking to write to "CONFIG" would try to access the actual ".git/config" file (though fortunately this does not work, as we refuse to write anything we can't parse as a ref). We already have functions that catalog the pattern of allowable names (i.e., ending in "_HEAD" or one of a set of historical exceptions). So even if we allowed creating such a ref, we'd skip over it while iterating over the root refs. But we don't enforce those rules; we check only the "all caps and underscore" syntax rule, not the full root-ref rules. Let's teach check_refname_format() to use those functions to further restrict what it will allow at the root level. That should make things safer and more consistent with ref iteration. Note that while is_root_ref_syntax() covers the syntax for both regular root refs and pseudo-refs (FETCH_HEAD and MERGE_HEAD), the is_root_ref() function does not include pseudo-refs. So we have to check the two classes separately. This patch doesn't touch refname_is_safe(), which generally tries to be a bit more loose (e.g., to allow deletion of bogus names). I've left it loose here, though arguably it would benefit from some of the same protection (you wouldn't want to delete ".git/config" either, though again, we'd refuse to delete something we can't parse).
1 parent c8fa5cd commit 5413b07

File tree

2 files changed

+12
-1
lines changed

2 files changed

+12
-1
lines changed

refs.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -283,7 +283,8 @@ static int check_or_sanitize_refname(const char *refname, int flags,
283283

284284
parse_worktree_ref(refname, NULL, NULL, &bare_ref);
285285
if (!starts_with(bare_ref, "refs/") &&
286-
!is_root_ref_syntax(bare_ref))
286+
!is_pseudo_ref(bare_ref) &&
287+
!is_root_ref(bare_ref))
287288
return -1;
288289
}
289290

t/t1430-bad-ref-name.sh

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -399,6 +399,11 @@ test_expect_success 'update-ref refuses non-underscore punctuation outside of re
399399
test_grep "refusing to update ref with bad name" err
400400
'
401401

402+
test_expect_success 'update-ref enforces root ref naming convention' '
403+
test_must_fail git update-ref FOO_BAR HEAD 2>err &&
404+
test_grep "refusing to update ref with bad name" err
405+
'
406+
402407
test_expect_success 'rev-parse refuses non-root-ref outside of refs/' '
403408
git rev-parse HEAD >.git/bad &&
404409
test_must_fail git rev-parse --verify bad
@@ -409,4 +414,9 @@ test_expect_success 'rev-parse recognizes non-root-ref via worktree' '
409414
test_must_fail git rev-parse --verify main-worktree/bad
410415
'
411416

417+
test_expect_success 'rev-parse enforces root ref naming convention' '
418+
git rev-parse HEAD >.git/BAD_NAME &&
419+
test_must_fail git rev-parse --verify BAD_NAME
420+
'
421+
412422
test_done

0 commit comments

Comments
 (0)