Home

ugit @main - refs - log -
-
https://git.jolheiser.com/ugit.git
The code powering this h*ckin' site
tree log patch
security: quick pass Nothing critical for single-users or trusted-users. Just a few niceties in the case of less trusted collaborators and some easy wins. Signed-off-by: jolheiser <git@jolheiser.com>
Signature
-----BEGIN SSH SIGNATURE----- U1NIU0lHAAAAAQAAADMAAAALc3NoLWVkMjU1MTkAAAAgBTEvCQk6VqUAdN2RuH6bj1dNkY oOpbPWj+jw4ua1B1cAAAADZ2l0AAAAAAAAAAZzaGE1MTIAAABTAAAAC3NzaC1lZDI1NTE5 AAAAQAWtxnWU7Fh2sbybVDubskCdmP/NTUsi6ytBlBFKuJwNCyBgLaPHlwl+UsdcgzM5BE XPJHsZD7qKKI9CiYsRSwM= -----END SSH SIGNATURE-----
jolheiser <git@jolheiser.com>
12 hours ago
3 changed files, 18 additions(+), 1 deletions(-)
internal/http/http.gointernal/http/repo.gointernal/ssh/wish.go
M internal/http/http.go -> internal/http/http.go
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
diff --git a/internal/http/http.go b/internal/http/http.go
index 844652d081156b4bb835e3cffdfbe0f6c8fcceea..5154474c789c5423d9da05d5d33d89831a87b28d 100644
--- a/internal/http/http.go
+++ b/internal/http/http.go
@@ -2,6 +2,7 @@ package http
 
 import (
 	"fmt"
+	stdhtml "html"
 	"net/http"
 	"net/url"
 	"strings"
@@ -52,6 +53,7 @@ }
 
 func (s Settings) goGet(repo string) string {
 	u, _ := url.Parse(s.CloneURL)
+	repo = stdhtml.EscapeString(repo)
 	return fmt.Sprintf(`<!DOCTYPE html><title>%[1]s</title><meta name="go-import" content="%[2]s/%[1]s git %[3]s/%[1]s.git"><meta name="go-source" content="%[2]s/%[1]s _ %[3]s/%[1]s/tree/main{/dir}/{file}#L{line}">`, repo, u.Hostname(), s.CloneURL)
 }
 
M internal/http/repo.go -> internal/http/repo.go
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
diff --git a/internal/http/repo.go b/internal/http/repo.go
index d59cb59bd0cb984bc1647f1c06df69f32776215d..4eb3f8da37edacee6e7de4671b9eb5d36fbacd4d 100644
--- a/internal/http/repo.go
+++ b/internal/http/repo.go
@@ -78,10 +78,16 @@ 		return httperr.Error(err)
 	}
 
 	if r.URL.Query().Has("raw") {
+		contentType := "text/plain; charset=utf-8"
 		if r.URL.Query().Has("pretty") {
 			ext := filepath.Ext(path)
-			w.Header().Set("Content-Type", mime.TypeByExtension(ext))
+			if mt := mime.TypeByExtension(ext); mt != "" {
+				contentType = mt
+			}
 		}
+		w.Header().Set("Content-Type", contentType)
+		w.Header().Set("X-Content-Type-Options", "nosniff")
+		w.Header().Set("Content-Security-Policy", "script-src 'none'; frame-ancestors 'none'")
 		w.Write([]byte(content))
 		return nil
 	}
M internal/ssh/wish.go -> internal/ssh/wish.go
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
diff --git a/internal/ssh/wish.go b/internal/ssh/wish.go
index 6bdb16748ec5499af03d1f4683800da71eb612ab..64f6be07be25325f7b183186cad75b9bfce16ca6 100644
--- a/internal/ssh/wish.go
+++ b/internal/ssh/wish.go
@@ -63,6 +63,10 @@ // checked for access on a per repo basis for a ssh.Session public key.
 // Hooks.Push and Hooks.Fetch will be called on successful completion of
 // their commands.
 func Middleware(repoDir string, cloneURL string, port int, gh Hooks) wish.Middleware {
+	repoDirAbs, err := filepath.Abs(repoDir)
+	if err != nil {
+		repoDirAbs = repoDir
+	}
 	return func(sh ssh.Handler) ssh.Handler {
 		return func(s ssh.Session) {
 			sess := Session{s: s}
@@ -75,6 +79,11 @@ 				// repo should be in the form of "repo.git" or "user/repo.git"
 				repo := strings.TrimSuffix(strings.TrimPrefix(cmd[1], "/"), "/")
 				repo = filepath.Clean(repo)
 				if n := strings.Count(repo, "/"); n > 1 {
+					Fatal(s, ErrInvalidRepo)
+					return
+				}
+				// resolved path shouldn't escape repoDir(Abs)
+				if abs := filepath.Join(repoDirAbs, repo); !strings.HasPrefix(abs, repoDirAbs+string(filepath.Separator)) {
 					Fatal(s, ErrInvalidRepo)
 					return
 				}