Changes
4 changed files (+473/-0)
-
-
@@ -13,3 +13,6 @@ profile# Temporary file, for example snapshots and download files. tmp.* # Go produces testdata/fuzz directory when "go test -fuzz" failed. fuzz
-
-
pages/escaping_test.go (new)
-
@@ -0,0 +1,149 @@// Copyright 2026 Shota FUJI <pockawoooh@gmail.com> // SPDX-License-Identifier: MIT package pages import ( "io" "net/http" "net/http/httptest" "net/url" "strings" "testing" "github.com/go-git/go-git/v5" "github.com/pocka/legit/config" "github.com/pocka/legit/core" "github.com/pocka/legit/tests" ) func TestRepoTreeHandleSlashes(t *testing.T) { repos := t.TempDir() _, worktree, err := tests.CreateRepository(repos, "foo") if err != nil { t.Fatal(err) } readme, err := worktree.Filesystem.Create("README.md") if err != nil { t.Fatal(err) } if _, err := readme.Write([]byte("* iawsoiwjfngbhfg812uhjikwe6789asfd")); err != nil { t.Fatal(err) } _ = readme.Close() if _, err := worktree.Add("README.md"); err != nil { t.Fatal(err) } _, err = worktree.Commit("Add README", &git.CommitOptions{ Author: tests.SignatureAlice(), }) if err != nil { t.Fatalf("Unable to commit: %s", err) } var c config.Config c.Repo.ScanPath = repos c.Repo.Readme = []string{"README.md"} c.Repo.MainBranch = []string{"trunk"} core, err := core.New(&c) if err != nil { t.Fatal(err) } server := httptest.NewServer(New(core)) defer server.Close() for _, p := range []string{"/", "/./", "//", "/////////////////"} { target, err := url.JoinPath(server.URL, "/foo/tree/trunk") if err != nil { t.Fatal(err) } res, err := http.Get(target + p) if err != nil { t.Fatal(err) } if res.StatusCode != http.StatusOK { t.Fatalf("(%s) Expected HTTP %d, Got %d", p, http.StatusOK, res.StatusCode) } body, err := io.ReadAll(res.Body) res.Body.Close() if err != nil { t.Fatal(err) } if !strings.Contains(string(body), "href=\"/foo/blob/trunk/README.md\"") { t.Errorf("(%s) No link for README.md", p) t.Log(string(body)) } } } func TestRepoTreePreventsPathTraversal(t *testing.T) { repos := t.TempDir() _, worktree, err := tests.CreateRepository(repos, "foo") if err != nil { t.Fatal(err) } readme, err := worktree.Filesystem.Create("README.md") if err != nil { t.Fatal(err) } if _, err := readme.Write([]byte("* iawsoiwjfngbhfg812uhjikwe6789asfd")); err != nil { t.Fatal(err) } _ = readme.Close() if _, err := worktree.Add("README.md"); err != nil { t.Fatal(err) } _, err = worktree.Commit("Add README", &git.CommitOptions{ Author: tests.SignatureAlice(), }) if err != nil { t.Fatalf("Unable to commit: %s", err) } var c config.Config c.Repo.ScanPath = repos c.Repo.Readme = []string{"README.md"} c.Repo.MainBranch = []string{"trunk"} core, err := core.New(&c) if err != nil { t.Fatal(err) } server := httptest.NewServer(New(core)) defer server.Close() for _, p := range []string{"/../", "/.././../"} { target, err := url.JoinPath(server.URL, "/foo/tree/trunk") if err != nil { t.Fatal(err) } res, err := http.Get(target + p) if err != nil { t.Fatal(err) } if res.StatusCode != http.StatusNotFound { t.Fatalf("(%s) Expected HTTP %d, Got %d", p, http.StatusNotFound, res.StatusCode) } } }
-
-
pages/reporefs_test.go (new)
-
@@ -0,0 +1,193 @@// Copyright 2026 Shota FUJI <pockawoooh@gmail.com> // SPDX-License-Identifier: MIT package pages import ( "io" "net/http" "net/http/httptest" "net/url" "strings" "testing" "github.com/go-git/go-git/v5" "github.com/go-git/go-git/v5/plumbing" "github.com/pocka/legit/config" "github.com/pocka/legit/core" "github.com/pocka/legit/tests" ) // TestRepoHandleSlashRefsOK tests repository router handles slashed refs // such as "feature/new-banana" correctly. func TestRepoHandleSlashRefsOK(t *testing.T) { repos := t.TempDir() repo, worktree, err := tests.CreateRepository(repos, "foo") if err != nil { t.Fatal(err) } readme, err := worktree.Filesystem.Create("README.md") if err != nil { t.Fatal(err) } if _, err := readme.Write([]byte("* iawsoiwjfngbhfg812uhjikwe6789asfd")); err != nil { t.Fatal(err) } _ = readme.Close() if _, err := worktree.Add("README.md"); err != nil { t.Fatal(err) } commit, err := worktree.Commit("Add README", &git.CommitOptions{ Author: tests.SignatureAlice(), }) if err != nil { t.Fatalf("Unable to commit: %s", err) } if err := worktree.Checkout(&git.CheckoutOptions{ Branch: plumbing.NewBranchReferenceName("feature/john/new-renderer"), Create: true, }); err != nil { t.Fatalf("Unable to create a branch: %s", err) } if _, err := repo.CreateTag("foo/bar/baz", commit, nil); err != nil { t.Fatalf("Unable to create a tag: %s", err) } var c config.Config c.Repo.ScanPath = repos c.Repo.Readme = []string{"README.md"} c.Repo.MainBranch = []string{"trunk"} core, err := core.New(&c) if err != nil { t.Fatal(err) } server := httptest.NewServer(New(core)) defer server.Close() // List refs { target, err := url.JoinPath(server.URL, "/foo/refs") if err != nil { t.Fatal(err) } res, err := http.Get(target) if err != nil { t.Fatal(err) } if res.StatusCode != http.StatusOK { t.Fatalf("Expected HTTP %d, Got %d", http.StatusOK, res.StatusCode) } body, err := io.ReadAll(res.Body) res.Body.Close() if err != nil { t.Fatal(err) } if !strings.Contains(string(body), "href=\"/foo/log/feature/john/new-renderer\"") { t.Error("No link for feature/john/new-renderer branch") } if !strings.Contains(string(body), "href=\"/foo/log/trunk\"") { t.Error("No link for trunk branch") } if !strings.Contains(string(body), "href=\"/foo/log/foo/bar/baz\"") { t.Error("No link for foo/bar/baz tag") } } // Browse files of "foo/bar/baz" tag { target, err := url.JoinPath(server.URL, "/foo/tree/foo/bar/baz") if err != nil { t.Fatal(err) } res, err := http.Get(target) if err != nil { t.Fatal(err) } if res.StatusCode != http.StatusOK { t.Fatalf("Expected HTTP %d, Got %d", http.StatusOK, res.StatusCode) } body, err := io.ReadAll(res.Body) res.Body.Close() if err != nil { t.Fatal(err) } if !strings.Contains(string(body), "href=\"/foo/blob/foo/bar/baz/README.md\"") { t.Error("No link for foo/bar/baz/README.md") } } // View "README.md" at "foo/bar/baz" tag { target, err := url.JoinPath(server.URL, "/foo/blob/foo/bar/baz/README.md") if err != nil { t.Fatal(err) } res, err := http.Get(target) if err != nil { t.Fatal(err) } if res.StatusCode != http.StatusOK { t.Fatalf("Expected HTTP %d, Got %d", http.StatusOK, res.StatusCode) } body, err := io.ReadAll(res.Body) res.Body.Close() if err != nil { t.Fatal(err) } if !strings.Contains(string(body), "* iawsoiwjfngbhfg812uhjikwe6789asfd") { t.Error("README.md does not containg magic string") } } // Preview "README.md" in HTML at "foo/bar/baz" tag { target, err := url.JoinPath(server.URL, "/foo/blob/foo/bar/baz/README.md") if err != nil { t.Fatal(err) } res, err := http.Get(target + "?preview=html") if err != nil { t.Fatal(err) } if res.StatusCode != http.StatusOK { t.Fatalf("Expected HTTP %d, Got %d", http.StatusOK, res.StatusCode) } body, err := io.ReadAll(res.Body) res.Body.Close() if err != nil { t.Fatal(err) } if !strings.Contains(string(body), "<li>iawsoiwjfngbhfg812uhjikwe6789asfd</li>") { t.Error("HTML preview of README.md does not containg magic string") } } }
-
-
pages/routing_test.go (new)
-
@@ -0,0 +1,128 @@// Copyright 2026 Shota FUJI <pockawoooh@gmail.com> // SPDX-License-Identifier: MIT package pages import ( "io" "net/http" "net/http/httptest" "net/url" "os" "path/filepath" "strings" "testing" "github.com/go-git/go-git/v5" "github.com/pocka/legit/config" "github.com/pocka/legit/core" "github.com/pocka/legit/tests" ) func FuzzPathParsing(f *testing.F) { repos := f.TempDir() secret, err := os.Create(filepath.Join(repos, "secret.txt")) if err != nil { f.Fatal(err) } defer secret.Close() if _, err := secret.WriteString("pepperoni"); err != nil { f.Fatal(err) } _, worktree, err := tests.CreateRepository(repos, "foo") if err != nil { f.Fatal(err) } readme, err := worktree.Filesystem.Create("README.md") if err != nil { f.Fatal(err) } if _, err := readme.Write([]byte("* iawsoiwjfngbhfg812uhjikwe6789asfd")); err != nil { f.Fatal(err) } _ = readme.Close() if _, err := worktree.Add("README.md"); err != nil { f.Fatal(err) } _, err = worktree.Commit("Add README", &git.CommitOptions{ Author: tests.SignatureAlice(), }) if err != nil { f.Fatalf("Unable to commit: %s", err) } var c config.Config c.Repo.ScanPath = repos c.Repo.Readme = []string{"README.md"} c.Repo.MainBranch = []string{"trunk"} core, err := core.New(&c) if err != nil { f.Fatal(err) } handler := New(core) f.Add("/") f.Fuzz(func(t *testing.T, path string) { target := "/" + path url, err := url.Parse(target) if err != nil { // Skip non-GET-table URLs t.SkipNow() } // httptest.NewRequest panics even on slight erroneous path r, err := http.NewRequest(http.MethodGet, url.String(), nil) if err != nil { t.SkipNow() } w := httptest.NewRecorder() // httptest.Server actually uses socket and if we run that, OS stops this // test due to socket usage limit. handler.ServeHTTP(w, r) res := w.Result() defer res.Body.Close() // No InternalServerError switch res.StatusCode { case http.StatusOK: case http.StatusNotFound: default: t.Errorf( "Expected HTTP %d or %d, got %d", http.StatusOK, http.StatusNotFound, res.StatusCode, ) } b, err := io.ReadAll(res.Body) if err != nil { t.Fatal(err) } body := string(b) if strings.Contains(body, "pepperoni") { t.Error("Returned body contains content of secret file under repos directory") } if strings.Index(target, "/static/") != 0 { if !strings.Contains(body, "href=\"/static/noscript.css?r=") { t.Errorf("Returned HTML does not use an embedded template") } } }) }
-