VYPR
Medium severity4.6OSV Advisory· Published Nov 8, 2025· Updated Apr 15, 2026

CVE-2025-64494

CVE-2025-64494

Description

Soft Serve is a self-hostable Git server for the command line. In versions prior to 0.10.0, there are several places where the user can insert data (e.g. names) and ANSI escape sequences are not being removed, which can then be used, for example, to show fake alerts. In the same token, git messages, when printed, are also not being sanitized. This issue is fixed in version 0.10.0.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
github.com/charmbracelet/soft-serveGo
< 0.11.00.11.0

Affected products

1

Patches

1
d9639320b8d0

Merge commit from fork

https://github.com/charmbracelet/soft-serveCarlos Alexandro BeckerNov 6, 2025via ghsa
7 files changed · +22 4
  • pkg/backend/access_token.go+2 0 modified
    @@ -7,12 +7,14 @@ import (
     
     	"github.com/charmbracelet/soft-serve/pkg/db"
     	"github.com/charmbracelet/soft-serve/pkg/proto"
    +	"github.com/charmbracelet/soft-serve/pkg/utils"
     )
     
     // CreateAccessToken creates an access token for user.
     func (b *Backend) CreateAccessToken(ctx context.Context, user proto.User, name string, expiresAt time.Time) (string, error) {
     	token := GenerateToken()
     	tokenHash := HashToken(token)
    +	name = utils.Sanitize(name)
     
     	if err := b.db.TransactionContext(ctx, func(tx *db.Tx) error {
     		_, err := b.store.CreateAccessToken(ctx, tx, name, user.ID(), tokenHash, expiresAt)
    
  • pkg/backend/repo.go+2 0 modified
    @@ -544,6 +544,7 @@ func (d *Backend) SetHidden(ctx context.Context, name string, hidden bool) error
     // It implements backend.Backend.
     func (d *Backend) SetDescription(ctx context.Context, name string, desc string) error {
     	name = utils.SanitizeRepo(name)
    +	desc = utils.Sanitize(desc)
     	rp := filepath.Join(d.repoPath(name))
     
     	// Delete cache
    @@ -617,6 +618,7 @@ func (d *Backend) SetPrivate(ctx context.Context, name string, private bool) err
     // It implements backend.Backend.
     func (d *Backend) SetProjectName(ctx context.Context, repo string, name string) error {
     	repo = utils.SanitizeRepo(repo)
    +	name = utils.Sanitize(name)
     
     	// Delete cache
     	d.cache.Delete(repo)
    
  • pkg/backend/user.go+1 0 modified
    @@ -279,6 +279,7 @@ func (d *Backend) AddPublicKey(ctx context.Context, username string, pk ssh.Publ
     //
     // It implements backend.Backend.
     func (d *Backend) CreateUser(ctx context.Context, username string, opts proto.UserOptions) (proto.User, error) {
    +	username = utils.Sanitize(username)
     	username = strings.ToLower(username)
     	if err := utils.ValidateUsername(username); err != nil {
     		return nil, err
    
  • pkg/backend/webhooks.go+2 0 modified
    @@ -9,6 +9,7 @@ import (
     	"github.com/charmbracelet/soft-serve/pkg/db/models"
     	"github.com/charmbracelet/soft-serve/pkg/proto"
     	"github.com/charmbracelet/soft-serve/pkg/store"
    +	"github.com/charmbracelet/soft-serve/pkg/utils"
     	"github.com/charmbracelet/soft-serve/pkg/webhook"
     	"github.com/google/uuid"
     )
    @@ -17,6 +18,7 @@ import (
     func (b *Backend) CreateWebhook(ctx context.Context, repo proto.Repository, url string, contentType webhook.ContentType, secret string, events []webhook.Event, active bool) error {
     	dbx := db.FromContext(ctx)
     	datastore := store.FromContext(ctx)
    +	url = utils.Sanitize(url)
     
     	return dbx.TransactionContext(ctx, func(tx *db.Tx) error {
     		lastID, err := datastore.CreateWebhook(ctx, tx, repo.ID(), url, secret, int(contentType), active)
    
  • pkg/ssh/cmd/commit.go+3 2 modified
    @@ -10,6 +10,7 @@ import (
     	"github.com/charmbracelet/soft-serve/pkg/backend"
     	"github.com/charmbracelet/soft-serve/pkg/ui/common"
     	"github.com/charmbracelet/soft-serve/pkg/ui/styles"
    +	"github.com/charmbracelet/soft-serve/pkg/utils"
     	"github.com/spf13/cobra"
     )
     
    @@ -59,9 +60,9 @@ func commitCommand() *cobra.Command {
     
     			s := strings.Builder{}
     			commitLine := "commit " + commitSHA
    -			authorLine := "Author: " + commit.Author.Name
    +			authorLine := "Author: " + utils.Sanitize(commit.Author.Name)
     			dateLine := "Date:   " + commit.Committer.When.UTC().Format(time.UnixDate)
    -			msgLine := strings.ReplaceAll(commit.Message, "\r\n", "\n")
    +			msgLine := strings.ReplaceAll(utils.Sanitize(commit.Message), "\r\n", "\n")
     			statsLine := renderStats(diff, commonStyle, color)
     			diffLine := renderDiff(patch, color)
     
    
  • pkg/ssh/cmd/webhooks.go+4 2 modified
    @@ -7,6 +7,7 @@ import (
     
     	"github.com/charmbracelet/lipgloss/v2/table"
     	"github.com/charmbracelet/soft-serve/pkg/backend"
    +	"github.com/charmbracelet/soft-serve/pkg/utils"
     	"github.com/charmbracelet/soft-serve/pkg/webhook"
     	"github.com/dustin/go-humanize"
     	"github.com/google/uuid"
    @@ -69,7 +70,7 @@ func webhookListCommand() *cobra.Command {
     
     				table = table.Row(
     					strconv.FormatInt(h.ID, 10),
    -					h.URL,
    +					utils.Sanitize(h.URL),
     					strings.Join(events, ","),
     					strconv.FormatBool(h.Active),
     					humanize.Time(h.CreatedAt),
    @@ -122,7 +123,8 @@ func webhookCreateCommand() *cobra.Command {
     				return webhook.ErrInvalidContentType
     			}
     
    -			return be.CreateWebhook(ctx, repo, strings.TrimSpace(args[1]), ct, secret, evs, active)
    +			url := utils.Sanitize(args[1])
    +			return be.CreateWebhook(ctx, repo, strings.TrimSpace(url), ct, secret, evs, active)
     		},
     	}
     
    
  • pkg/utils/utils.go+8 0 modified
    @@ -5,10 +5,13 @@ import (
     	"path"
     	"strings"
     	"unicode"
    +
    +	"github.com/charmbracelet/x/ansi"
     )
     
     // SanitizeRepo returns a sanitized version of the given repository name.
     func SanitizeRepo(repo string) string {
    +	repo = Sanitize(repo)
     	// We need to use an absolute path for the path to be cleaned correctly.
     	repo = strings.TrimPrefix(repo, "/")
     	repo = "/" + repo
    @@ -20,6 +23,11 @@ func SanitizeRepo(repo string) string {
     	return repo[1:]
     }
     
    +// Sanitize strips ANSI escape codes from the given string.
    +func Sanitize(s string) string {
    +	return ansi.Strip(s)
    +}
    +
     // ValidateUsername returns an error if any of the given usernames are invalid.
     func ValidateUsername(username string) error {
     	if username == "" {
    

Vulnerability mechanics

Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

4

News mentions

0

No linked articles in our index yet.