diff --git a/appview/pages/templates/knots.html b/appview/pages/templates/knots.html
index 8e57e53..4a2df4c 100644
--- a/appview/pages/templates/knots.html
+++ b/appview/pages/templates/knots.html
@@ -24,6 +24,7 @@
my knots
+ This is a list of knots
{{ range .Registrations }}
{{ if .Registered }}
diff --git a/appview/state/repo.go b/appview/state/repo.go
index 2b1234b..b21802b 100644
--- a/appview/state/repo.go
+++ b/appview/state/repo.go
@@ -6,9 +6,9 @@ import (
"io"
"log"
"net/http"
- "path/filepath"
"github.com/bluesky-social/indigo/atproto/identity"
+ securejoin "github.com/cyphar/filepath-securejoin"
"github.com/go-chi/chi/v5"
"github.com/sotangled/tangled/appview/auth"
"github.com/sotangled/tangled/appview/pages"
@@ -419,7 +419,8 @@ func (f *FullyResolvedRepo) OwnerHandle() string {
}
func (f *FullyResolvedRepo) OwnerSlashRepo() string {
- return filepath.Join(f.OwnerDid(), f.RepoName)
+ p, _ := securejoin.SecureJoin(f.OwnerDid(), f.RepoName)
+ return p
}
func fullyResolvedRepo(r *http.Request) (*FullyResolvedRepo, error) {
diff --git a/appview/state/state.go b/appview/state/state.go
index 6143e4b..22fdfd4 100644
--- a/appview/state/state.go
+++ b/appview/state/state.go
@@ -9,7 +9,6 @@ import (
"fmt"
"log"
"net/http"
- "path/filepath"
"strings"
"time"
@@ -17,6 +16,7 @@ import (
"github.com/bluesky-social/indigo/atproto/syntax"
lexutil "github.com/bluesky-social/indigo/lex/util"
"github.com/bluesky-social/jetstream/pkg/models"
+ securejoin "github.com/cyphar/filepath-securejoin"
"github.com/go-chi/chi/v5"
tangled "github.com/sotangled/tangled/api/tangled"
"github.com/sotangled/tangled/appview"
@@ -529,7 +529,8 @@ func (s *State) AddRepo(w http.ResponseWriter, r *http.Request) {
}
// acls
- err = s.enforcer.AddRepo(user.Did, domain, filepath.Join(user.Did, repoName))
+ p, _ := securejoin.SecureJoin(domain, repoName)
+ err = s.enforcer.AddRepo(user.Did, domain, p)
if err != nil {
s.pages.Notice(w, "repo", "Failed to set up repository permissions.")
return
diff --git a/cmd/repoguard/main.go b/cmd/repoguard/main.go
index 0ebe8ff..7dd0c6c 100644
--- a/cmd/repoguard/main.go
+++ b/cmd/repoguard/main.go
@@ -9,10 +9,10 @@ import (
"net/url"
"os"
"os/exec"
- "path/filepath"
"strings"
"time"
+ securejoin "github.com/cyphar/filepath-securejoin"
"github.com/sotangled/tangled/appview"
)
@@ -79,7 +79,7 @@ func main() {
didOrHandle := components[0]
did := resolveToDid(didOrHandle)
repoName := components[1]
- qualifiedRepoName := filepath.Join(did, repoName)
+ qualifiedRepoName, _ := securejoin.SecureJoin(did, repoName)
validCommands := map[string]bool{
"git-receive-pack": true,
@@ -100,8 +100,7 @@ func main() {
}
}
- fullPath := filepath.Join(*baseDirFlag, qualifiedRepoName)
- fullPath = filepath.Clean(fullPath)
+ fullPath, _ := securejoin.SecureJoin(*baseDirFlag, qualifiedRepoName)
logEvent("Processing command", map[string]interface{}{
"user": *incomingUser,
diff --git a/go.mod b/go.mod
index 68a1878..589bd06 100644
--- a/go.mod
+++ b/go.mod
@@ -10,6 +10,7 @@ require (
github.com/bluesky-social/indigo v0.0.0-20250123072624-9e3b84fdbb20
github.com/bluesky-social/jetstream v0.0.0-20241210005130-ea96859b93d1
github.com/casbin/casbin/v2 v2.103.0
+ github.com/cyphar/filepath-securejoin v0.3.3
github.com/dustin/go-humanize v1.0.1
github.com/gliderlabs/ssh v0.3.5
github.com/go-chi/chi/v5 v5.2.0
@@ -36,7 +37,6 @@ require (
github.com/casbin/govaluate v1.3.0 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/cloudflare/circl v1.4.0 // indirect
- github.com/cyphar/filepath-securejoin v0.3.3 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/emirpasic/gods v1.18.1 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
diff --git a/knotserver/git.go b/knotserver/git.go
index d8ebfd0..a790f6d 100644
--- a/knotserver/git.go
+++ b/knotserver/git.go
@@ -6,6 +6,7 @@ import (
"net/http"
"path/filepath"
+ securejoin "github.com/cyphar/filepath-securejoin"
"github.com/go-chi/chi/v5"
"github.com/sotangled/tangled/knotserver/git/service"
)
@@ -13,7 +14,7 @@ import (
func (d *Handle) InfoRefs(w http.ResponseWriter, r *http.Request) {
did := chi.URLParam(r, "did")
name := chi.URLParam(r, "name")
- repo := filepath.Join(d.c.Repo.ScanPath, did, name)
+ repo, _ := securejoin.SecureJoin(d.c.Repo.ScanPath, filepath.Join(did, name))
w.Header().Set("content-type", "application/x-git-upload-pack-advertisement")
w.WriteHeader(http.StatusOK)
@@ -33,7 +34,7 @@ func (d *Handle) InfoRefs(w http.ResponseWriter, r *http.Request) {
func (d *Handle) UploadPack(w http.ResponseWriter, r *http.Request) {
did := chi.URLParam(r, "did")
name := chi.URLParam(r, "name")
- repo := filepath.Join(d.c.Repo.ScanPath, did, name)
+ repo, _ := securejoin.SecureJoin(d.c.Repo.ScanPath, filepath.Join(did, name))
w.Header().Set("content-type", "application/x-git-upload-pack-result")
w.Header().Set("Connection", "Keep-Alive")
diff --git a/knotserver/routes.go b/knotserver/routes.go
index 155ec80..c86fe46 100644
--- a/knotserver/routes.go
+++ b/knotserver/routes.go
@@ -14,6 +14,7 @@ import (
"strconv"
"strings"
+ securejoin "github.com/cyphar/filepath-securejoin"
"github.com/gliderlabs/ssh"
"github.com/go-chi/chi/v5"
"github.com/go-git/go-git/v5/plumbing"
@@ -29,7 +30,7 @@ func (h *Handle) Index(w http.ResponseWriter, r *http.Request) {
}
func (h *Handle) RepoIndex(w http.ResponseWriter, r *http.Request) {
- path := filepath.Join(h.c.Repo.ScanPath, didPath(r))
+ path, _ := securejoin.SecureJoin(h.c.Repo.ScanPath, didPath(r))
l := h.l.With("path", path, "handler", "RepoIndex")
gr, err := git.Open(path, "")
@@ -116,7 +117,7 @@ func (h *Handle) RepoTree(w http.ResponseWriter, r *http.Request) {
l := h.l.With("handler", "RepoTree", "ref", ref, "treePath", treePath)
- path := filepath.Join(h.c.Repo.ScanPath, didPath(r))
+ path, _ := securejoin.SecureJoin(h.c.Repo.ScanPath, didPath(r))
gr, err := git.Open(path, ref)
if err != nil {
notFound(w)
@@ -148,7 +149,7 @@ func (h *Handle) Blob(w http.ResponseWriter, r *http.Request) {
l := h.l.With("handler", "FileContent", "ref", ref, "treePath", treePath)
- path := filepath.Join(h.c.Repo.ScanPath, didPath(r))
+ path, _ := securejoin.SecureJoin(h.c.Repo.ScanPath, didPath(r))
gr, err := git.Open(path, ref)
if err != nil {
notFound(w)
@@ -192,7 +193,7 @@ func (h *Handle) Archive(w http.ResponseWriter, r *http.Request) {
setContentDisposition(w, filename)
setGZipMIME(w)
- path := filepath.Join(h.c.Repo.ScanPath, didPath(r))
+ path, _ := securejoin.SecureJoin(h.c.Repo.ScanPath, didPath(r))
gr, err := git.Open(path, ref)
if err != nil {
notFound(w)
@@ -222,7 +223,7 @@ func (h *Handle) Archive(w http.ResponseWriter, r *http.Request) {
func (h *Handle) Log(w http.ResponseWriter, r *http.Request) {
ref := chi.URLParam(r, "ref")
- path := filepath.Join(h.c.Repo.ScanPath, didPath(r))
+ path, _ := securejoin.SecureJoin(h.c.Repo.ScanPath, didPath(r))
l := h.l.With("handler", "Log", "ref", ref, "path", path)
@@ -288,7 +289,7 @@ func (h *Handle) Diff(w http.ResponseWriter, r *http.Request) {
l := h.l.With("handler", "Diff", "ref", ref)
- path := filepath.Join(h.c.Repo.ScanPath, didPath(r))
+ path, _ := securejoin.SecureJoin(h.c.Repo.ScanPath, didPath(r))
gr, err := git.Open(path, ref)
if err != nil {
notFound(w)
@@ -312,7 +313,7 @@ func (h *Handle) Diff(w http.ResponseWriter, r *http.Request) {
}
func (h *Handle) Tags(w http.ResponseWriter, r *http.Request) {
- path := filepath.Join(h.c.Repo.ScanPath, didPath(r))
+ path, _ := securejoin.SecureJoin(h.c.Repo.ScanPath, didPath(r))
l := h.l.With("handler", "Refs")
gr, err := git.Open(path, "")
@@ -353,7 +354,7 @@ func (h *Handle) Tags(w http.ResponseWriter, r *http.Request) {
}
func (h *Handle) Branches(w http.ResponseWriter, r *http.Request) {
- path := filepath.Join(h.c.Repo.ScanPath, didPath(r))
+ path, _ := securejoin.SecureJoin(h.c.Repo.ScanPath, didPath(r))
l := h.l.With("handler", "Branches")
gr, err := git.Open(path, "")
@@ -445,7 +446,7 @@ func (h *Handle) NewRepo(w http.ResponseWriter, r *http.Request) {
name := data.Name
relativeRepoPath := filepath.Join(did, name)
- repoPath := filepath.Join(h.c.Repo.ScanPath, relativeRepoPath)
+ repoPath, _ := securejoin.SecureJoin(h.c.Repo.ScanPath, relativeRepoPath)
err := git.InitBare(repoPath)
if err != nil {
l.Error("initializing bare repo", "error", err.Error())
@@ -522,7 +523,7 @@ func (h *Handle) AddRepoCollaborator(w http.ResponseWriter, r *http.Request) {
}
h.jc.AddDid(data.Did)
- repoName := filepath.Join(ownerDid, repo)
+ repoName, _ := securejoin.SecureJoin(ownerDid, repo)
if err := h.e.AddCollaborator(data.Did, ThisServer, repoName); err != nil {
l.Error("adding repo collaborator", "error", err.Error())
writeError(w, err.Error(), http.StatusInternalServerError)
diff --git a/knotserver/util.go b/knotserver/util.go
index 5ab30e7..947a7d6 100644
--- a/knotserver/util.go
+++ b/knotserver/util.go
@@ -5,6 +5,7 @@ import (
"os"
"path/filepath"
+ securejoin "github.com/cyphar/filepath-securejoin"
"github.com/go-chi/chi/v5"
"github.com/microcosm-cc/bluemonday"
)
@@ -16,7 +17,7 @@ func sanitize(content []byte) []byte {
func didPath(r *http.Request) string {
did := chi.URLParam(r, "did")
name := chi.URLParam(r, "name")
- path := filepath.Join(did, name)
+ path, _ := securejoin.SecureJoin(did, name)
filepath.Clean(path)
return path
}