web frontend for git repositories, written in Go git.pocka.jp/legit.git
3

Configure Feed

Select the types of activity you want to include in your feed.

Deny git clone of ignored repo

https://tangled.org/pocka.jp/legit/issues/2
https://github.com/icyphox/legit/issues/56

There were no guard / check for "ignored" config in endpoints git client
uses, so everybody could clone ignored repositories.

Shota FUJI (Jul 14, 2026, 1:13 PM +0900) c9d54abb bbeea586

+98
+92
routes/git_test.go
··· 154 154 t.Fatalf("Unexpected commit message (%s)", commit.Message) 155 155 } 156 156 } 157 + 158 + // https://github.com/icyphox/legit/issues/56 159 + // https://tangled.org/pocka.jp/legit/issues/2 160 + func TestCloneRespectVisibilityConfig(t *testing.T) { 161 + repos := t.TempDir() 162 + 163 + for _, name := range []string{"public", "ignored", "unlisted"} { 164 + _, worktree, err := tests.CreateRepository(repos, name) 165 + if err != nil { 166 + t.Fatal(err) 167 + } 168 + 169 + readme, err := worktree.Filesystem.Create("README.md") 170 + if err != nil { 171 + t.Fatal(err) 172 + } 173 + 174 + if _, err := readme.Write([]byte(name)); err != nil { 175 + t.Fatal(err) 176 + } 177 + 178 + _ = readme.Close() 179 + 180 + if _, err := worktree.Add("README.md"); err != nil { 181 + t.Fatal(err) 182 + } 183 + 184 + _, err = worktree.Commit("Add README", &git.CommitOptions{ 185 + Author: tests.SignatureAlice(), 186 + }) 187 + if err != nil { 188 + t.Fatalf("Unable to commit: %s", err) 189 + } 190 + 191 + if err := tests.CreateBare(repos, name); err != nil { 192 + t.Fatalf("Unable to create bare repository: %s", err) 193 + } 194 + 195 + if err := os.RemoveAll(filepath.Join(repos, name)); err != nil { 196 + t.Fatal(err) 197 + } 198 + } 199 + 200 + var c config.Config 201 + c.Repo.ScanPath = repos 202 + c.Repo.Readme = []string{"README.md"} 203 + c.Repo.MainBranch = []string{"trunk"} 204 + c.Repo.Ignore = []string{"ignored.git"} 205 + c.Repo.Unlisted = []string{"unlisted.git"} 206 + 207 + server := httptest.NewServer(Handlers(&c, embed.StaticDir(), embed.TemplatesDir())) 208 + defer server.Close() 209 + 210 + publicUrl, err := url.JoinPath(server.URL, "/public.git") 211 + if err != nil { 212 + t.Fatal(err) 213 + } 214 + 215 + _, err = git.Clone(memory.NewStorage(), nil, &git.CloneOptions{ 216 + URL: publicUrl, 217 + ReferenceName: "refs/heads/trunk", 218 + }) 219 + if err != nil { 220 + t.Error(err) 221 + } 222 + 223 + ignoredUrl, err := url.JoinPath(server.URL, "/ignored.git") 224 + if err != nil { 225 + t.Fatal(err) 226 + } 227 + 228 + _, err = git.Clone(memory.NewStorage(), nil, &git.CloneOptions{ 229 + URL: ignoredUrl, 230 + ReferenceName: "refs/heads/trunk", 231 + }) 232 + if err == nil { 233 + t.Error("Cloning ignored repository succeeded (expected an error.)") 234 + } 235 + 236 + unlistedUrl, err := url.JoinPath(server.URL, "/unlisted.git") 237 + if err != nil { 238 + t.Fatal(err) 239 + } 240 + 241 + _, err = git.Clone(memory.NewStorage(), nil, &git.CloneOptions{ 242 + URL: unlistedUrl, 243 + ReferenceName: "refs/heads/trunk", 244 + }) 245 + if err != nil { 246 + t.Error(err) 247 + } 248 + }
+6
routes/handler.go
··· 11 11 // Checks for gitprotocol-http(5) specific smells; if found, passes 12 12 // the request on to the git http service, else render the web frontend. 13 13 func (d *deps) Multiplex(w http.ResponseWriter, r *http.Request) { 14 + name := r.PathValue("name") 15 + if d.isIgnored(name) { 16 + d.Write404(w) 17 + return 18 + } 19 + 14 20 path := r.PathValue("rest") 15 21 16 22 if r.URL.RawQuery == "service=git-receive-pack" {