From 76ca263e2aa2a3e478ed2a8ef0dbcdcd1b986046 Mon Sep 17 00:00:00 2001 From: Bryce Kwon Date: Thu, 16 Jul 2026 09:33:12 -1000 Subject: Date repositories from HEAD and guard its branch The index derived a repository's age from `refs/heads/master` alone, so a repository on any other default branch showed no age. HEAD is repo-controlled and the age stat walks under `refs/heads`, so a branch name carrying a parent-directory component is rejected. --- source/ui-repolist.c | 29 +++++++++++++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) (limited to 'source/ui-repolist.c') diff --git a/source/ui-repolist.c b/source/ui-repolist.c index 5174c5a..fc198e5 100644 --- a/source/ui-repolist.c +++ b/source/ui-repolist.c @@ -35,8 +35,10 @@ static time_t read_agefile(const char *path) static int get_repo_modtime(const struct cgit_repo *repo, time_t *mtime) { struct strbuf path = STRBUF_INIT; + struct strbuf head = STRBUF_INIT; struct stat s; struct cgit_repo *r = (struct cgit_repo *)repo; + const char *branch; if (repo->mtime != -1) { *mtime = repo->mtime; @@ -51,9 +53,31 @@ static int get_repo_modtime(const struct cgit_repo *repo, time_t *mtime) } } + /* Stat the tip of the default branch. Prefer a configured defbranch, + * otherwise read HEAD so a repo on "main" (or any branch name) is + * handled, not only "master". + */ + branch = repo->defbranch; + if (!branch) { + strbuf_reset(&path); + strbuf_addf(&path, "%s/HEAD", repo->path); + if (strbuf_read_file(&head, path.buf, 0) > 0) { + strbuf_rtrim(&head); + if (!skip_prefix(head.buf, "ref: refs/heads/", &branch)) + branch = NULL; + /* HEAD is repo-controlled, so a crafted target such as + * "ref: refs/heads/../../.." must not let the stat() + * below walk outside the repository. Git forbids ".." + * in ref names anyway. */ + if (branch && strstr(branch, "..")) + branch = NULL; + } + if (!branch) + branch = "master"; + } + strbuf_reset(&path); - strbuf_addf(&path, "%s/refs/heads/%s", repo->path, - repo->defbranch ? repo->defbranch : "master"); + strbuf_addf(&path, "%s/refs/heads/%s", repo->path, branch); if (stat(path.buf, &s) == 0) { *mtime = s.st_mtime; r->mtime = *mtime; @@ -72,6 +96,7 @@ static int get_repo_modtime(const struct cgit_repo *repo, time_t *mtime) r->mtime = *mtime; end: strbuf_release(&path); + strbuf_release(&head); return (r->mtime != 0); } -- cgit v2.8.0