diff --git a/internal/repoutil/repoutil.go b/internal/repoutil/repoutil.go
index e35312c32..9e3fb33e8 100644
--- a/internal/repoutil/repoutil.go
+++ b/internal/repoutil/repoutil.go
@@ -7,6 +7,7 @@ import (
 	"strings"
 
 	"gogs.io/gogs/internal/conf"
+	"gogs.io/gogs/internal/pathutil"
 )
 
 // CloneLink represents different types of clone URLs of repository.
@@ -49,13 +50,15 @@ func CompareCommitsPath(owner, repo, oldCommitID, newCommitID string) string {
 
 // UserPath returns the absolute path for storing user repositories.
 func UserPath(user string) string {
-	return filepath.Join(conf.Repository.Root, strings.ToLower(user))
+	// 🚨 SECURITY: Prevent path traversal in user name.
+	return filepath.Join(conf.Repository.Root, pathutil.Clean(strings.ToLower(user)))
 }
 
 // RepositoryPath returns the absolute path using given user and repository
 // name.
 func RepositoryPath(owner, repo string) string {
-	return filepath.Join(UserPath(owner), strings.ToLower(repo)+".git")
+	// 🚨 SECURITY: Prevent path traversal in repository name.
+	return filepath.Join(UserPath(owner), pathutil.Clean(strings.ToLower(repo))+".git")
 }
 
 // RepositoryLocalPath returns the absolute path of the repository local copy
diff --git a/internal/route/api/v1/org/org.go b/internal/route/api/v1/org/org.go
index aa7182ff3..ba141a24a 100644
--- a/internal/route/api/v1/org/org.go
+++ b/internal/route/api/v1/org/org.go
@@ -3,6 +3,8 @@ package org
 import (
 	"net/http"
 
+	"github.com/cockroachdb/errors"
+	"github.com/go-macaron/binding"
 	api "github.com/gogs/go-gogs-client"
 
 	"gogs.io/gogs/internal/context"
@@ -16,6 +18,18 @@ func CreateOrgForUser(c *context.APIContext, apiForm api.CreateOrgOption, user *
 		return
 	}
 
+	// 🚨 SECURITY: Reject org names containing characters that could let a
+	// traversal sequence reach the filesystem layer in repoutil.UserPath /
+	// repoutil.RepositoryPath. The web form already validates the same way, but
+	// the JSON struct lives in an external module and cannot carry binding tags
+	// on this branch, so enforce here.
+	if apiForm.UserName == "" ||
+		len(apiForm.UserName) > 35 ||
+		binding.AlphaDashDotPattern.MatchString(apiForm.UserName) {
+		c.ErrorStatus(http.StatusUnprocessableEntity, errors.Newf("invalid org name: %q", apiForm.UserName))
+		return
+	}
+
 	org := &database.User{
 		Name:        apiForm.UserName,
 		FullName:    apiForm.FullName,
