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.
| Package | Affected versions | Patched versions |
|---|---|---|
github.com/charmbracelet/soft-serveGo | < 0.11.0 | 0.11.0 |
Affected products
1- Range: v0.1.0, v0.1.1, v0.1.2, …
Patches
1d9639320b8d0Merge commit from fork
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
4News mentions
0No linked articles in our index yet.