add an util method to convert []byte to string

Signed-off-by: Nicola Murino <nicola.murino@gmail.com>
This commit is contained in:
Nicola Murino 2024-05-08 19:01:58 +02:00
parent 65753fe23e
commit 5d24d665bd
No known key found for this signature in database
GPG key ID: 935D2952DEC4EECF
23 changed files with 70 additions and 40 deletions

2
go.mod
View file

@ -79,7 +79,7 @@ require (
require (
cloud.google.com/go v0.112.2 // indirect
cloud.google.com/go/auth v0.3.0 // indirect
cloud.google.com/go/auth v0.4.0 // indirect
cloud.google.com/go/auth/oauth2adapt v0.2.2 // indirect
cloud.google.com/go/compute/metadata v0.3.0 // indirect
cloud.google.com/go/iam v1.1.8 // indirect

4
go.sum
View file

@ -3,8 +3,11 @@ cloud.google.com/go v0.112.2 h1:ZaGT6LiG7dBzi6zNOvVZwacaXlmf3lRqnC4DQzqyRQw=
cloud.google.com/go v0.112.2/go.mod h1:iEqjp//KquGIJV/m+Pk3xecgKNhV+ry+vVTsy4TbDms=
cloud.google.com/go/auth v0.3.0 h1:PRyzEpGfx/Z9e8+lHsbkoUVXD0gnu4MNmm7Gp8TQNIs=
cloud.google.com/go/auth v0.3.0/go.mod h1:lBv6NKTWp8E3LPzmO1TbiiRKc4drLOfHsgmlH9ogv5w=
cloud.google.com/go/auth v0.4.0 h1:vcJWEguhY8KuiHoSs/udg1JtIRYm3YAWPBE1moF1m3U=
cloud.google.com/go/auth v0.4.0/go.mod h1:tO/chJN3obc5AbRYFQDsuFbL4wW5y8LfbPtDCfgwOVE=
cloud.google.com/go/auth/oauth2adapt v0.2.2 h1:+TTV8aXpjeChS9M+aTtN/TjdQnzJvmzKFt//oWu7HX4=
cloud.google.com/go/auth/oauth2adapt v0.2.2/go.mod h1:wcYjgpZI9+Yu7LyYBg4pqSiaRkfEK3GQcpb7C/uyF1Q=
cloud.google.com/go/compute v1.26.0 h1:uHf0NN2nvxl1Gh4QO83yRCOdMK4zivtMS5gv0dEX0hg=
cloud.google.com/go/compute/metadata v0.3.0 h1:Tz+eQXMEqDIKRsmY3cHTL6FVaynIjX2QxYC4trgAKZc=
cloud.google.com/go/compute/metadata v0.3.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k=
cloud.google.com/go/iam v1.1.8 h1:r7umDwhj+BQyz0ScZMp4QrGXjSTI3ZINnpgU2nlB/K0=
@ -236,6 +239,7 @@ github.com/jackc/pgservicefile v0.0.0-20231201235250-de7065d80cb9 h1:L0QtFUgDarD
github.com/jackc/pgservicefile v0.0.0-20231201235250-de7065d80cb9/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
github.com/jackc/pgx/v5 v5.5.5 h1:amBjrZVmksIdNjxGW/IiIMzxMKZFelXbUoPNb+8sjQw=
github.com/jackc/pgx/v5 v5.5.5/go.mod h1:ez9gk+OAat140fv9ErkZDYFWmXLfV+++K0uAOiwgm1A=
github.com/jackc/puddle v1.3.0 h1:eHK/5clGOatcjX3oWGBO/MpxpbHzSwud5EWTSCI+MX0=
github.com/jackc/puddle/v2 v2.2.1 h1:RhxXJtFG022u4ibrCSMSiu5aOq1i77R3OHKNJj77OAk=
github.com/jackc/puddle/v2 v2.2.1/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4=
github.com/jhump/protoreflect v1.15.1 h1:HUMERORf3I3ZdX05WaQ6MIpd/NJ434hTp5YiKgfCL6c=

View file

@ -329,7 +329,7 @@ func (c *Configuration) getLockTime() (time.Time, error) {
acmeLog(logger.LevelError, "unable to read lock file %q: %v", c.lockPath, err)
return time.Time{}, err
}
msec, err := strconv.ParseInt(strings.TrimSpace(string(content)), 10, 64)
msec, err := strconv.ParseInt(strings.TrimSpace(util.BytesToString(content)), 10, 64)
if err != nil {
acmeLog(logger.LevelError, "unable to parse lock time: %v", err)
return time.Time{}, fmt.Errorf("unable to parse lock time: %w", err)

View file

@ -196,7 +196,7 @@ Please take a look at the usage below to customize the serving parameters`,
fmt.Printf("Unable to read password file %q: %v", portablePasswordFile, err)
os.Exit(1)
}
pwd = strings.TrimSpace(string(content))
pwd = strings.TrimSpace(util.BytesToString(content))
}
service.SetGraceTime(graceTime)
service := service.Service{
@ -523,7 +523,7 @@ func getFileContents(name string) (string, error) {
if err != nil {
return "", err
}
return string(contents), nil
return util.BytesToString(contents), nil
}
func convertFsProvider() string {

View file

@ -345,7 +345,7 @@ func notificationAsEnvVars(event *notifier.FsEvent) []string {
if len(event.Metadata) > 0 {
data, err := json.Marshal(event.Metadata)
if err == nil {
result = append(result, fmt.Sprintf("SFTPGO_ACTION_METADATA=%s", string(data)))
result = append(result, fmt.Sprintf("SFTPGO_ACTION_METADATA=%s", util.BytesToString(data)))
}
}
return result

View file

@ -479,7 +479,7 @@ func (c *RetentionCheck) sendHookNotification(elapsed time.Duration, errCheck er
cmd := exec.CommandContext(ctx, Config.DataRetentionHook, args...)
cmd.Env = append(env,
fmt.Sprintf("SFTPGO_DATA_RETENTION_RESULT=%s", string(jsonData)))
fmt.Sprintf("SFTPGO_DATA_RETENTION_RESULT=%s", util.BytesToString(jsonData)))
err := cmd.Run()
c.conn.Log(logger.LevelDebug, "notified result using command: %q, elapsed: %s err: %v",

View file

@ -811,7 +811,7 @@ func (p *EventParams) getStringReplacements(addObjectData, jsonEscaped bool) []s
if addObjectData {
data, err := p.Object.RenderAsJSON(p.Event != operationDelete)
if err == nil {
dataString := string(data)
dataString := util.BytesToString(data)
replacements[len(replacements)-3] = p.getStringReplacement(dataString, false)
replacements[len(replacements)-1] = p.getStringReplacement(dataString, true)
}
@ -826,7 +826,7 @@ func (p *EventParams) getStringReplacements(addObjectData, jsonEscaped bool) []s
if len(p.Metadata) > 0 {
data, err := json.Marshal(p.Metadata)
if err == nil {
dataString := string(data)
dataString := util.BytesToString(data)
replacements[len(replacements)-3] = p.getStringReplacement(dataString, false)
replacements[len(replacements)-1] = p.getStringReplacement(dataString, true)
}
@ -1466,7 +1466,8 @@ func executeHTTPRuleAction(c dataprovider.EventActionHTTPConfig, params *EventPa
endpoint, time.Since(startTime), resp.StatusCode)
if resp.StatusCode < http.StatusOK || resp.StatusCode > http.StatusNoContent {
if rb, err := io.ReadAll(io.LimitReader(resp.Body, 2048)); err == nil {
eventManagerLog(logger.LevelDebug, "error notification response from endpoint %q: %s", endpoint, string(rb))
eventManagerLog(logger.LevelDebug, "error notification response from endpoint %q: %s",
endpoint, util.BytesToString(rb))
}
return fmt.Errorf("unexpected status code: %d", resp.StatusCode)
}

View file

@ -148,7 +148,7 @@ func executeNotificationCommand(operation, executor, ip, objectType, objectName,
fmt.Sprintf("SFTPGO_PROVIDER_IP=%s", ip),
fmt.Sprintf("SFTPGO_PROVIDER_ROLE=%s", role),
fmt.Sprintf("SFTPGO_PROVIDER_TIMESTAMP=%d", util.GetTimeAsMsSinceEpoch(time.Now())),
fmt.Sprintf("SFTPGO_PROVIDER_OBJECT=%s", string(objectAsJSON)))
fmt.Sprintf("SFTPGO_PROVIDER_OBJECT=%s", util.BytesToString(objectAsJSON)))
startTime := time.Now()
err := cmd.Run()

View file

@ -297,7 +297,7 @@ func (a *Admin) hashPassword() error {
if err != nil {
return err
}
a.Password = string(pwd)
a.Password = util.BytesToString(pwd)
} else {
pwd, err := argon2id.CreateHash(a.Password, argon2Params)
if err != nil {

View file

@ -118,7 +118,7 @@ func (k *APIKey) hashKey() error {
if err != nil {
return err
}
k.Key = string(hashed)
k.Key = util.BytesToString(hashed)
} else {
hashed, err := argon2id.CreateHash(k.Key, argon2Params)
if err != nil {

View file

@ -3255,7 +3255,7 @@ func hashPlainPassword(plainPwd string) (string, error) {
if err != nil {
return "", fmt.Errorf("bcrypt hashing error: %w", err)
}
return string(pwd), nil
return util.BytesToString(pwd), nil
}
pwd, err := argon2id.CreateHash(plainPwd, argon2Params)
if err != nil {
@ -4115,7 +4115,7 @@ func getPreLoginHookResponse(loginMethod, ip, protocol string, userAsJSON []byte
cmd := exec.CommandContext(ctx, config.PreLoginHook, args...)
cmd.Env = append(env,
fmt.Sprintf("SFTPGO_LOGIND_USER=%s", string(userAsJSON)),
fmt.Sprintf("SFTPGO_LOGIND_USER=%s", util.BytesToString(userAsJSON)),
fmt.Sprintf("SFTPGO_LOGIND_METHOD=%s", loginMethod),
fmt.Sprintf("SFTPGO_LOGIND_IP=%s", ip),
fmt.Sprintf("SFTPGO_LOGIND_PROTOCOL=%s", protocol),
@ -4162,7 +4162,7 @@ func executePreLoginHook(username, loginMethod, ip, protocol string, oidcTokenFi
recoveryCodes := u.Filters.RecoveryCodes
err = json.Unmarshal(out, &u)
if err != nil {
return u, fmt.Errorf("invalid pre-login hook response %q, error: %v", string(out), err)
return u, fmt.Errorf("invalid pre-login hook response %q, error: %v", util.BytesToString(out), err)
}
u.ID = userID
u.UsedQuotaSize = userUsedQuotaSize
@ -4257,7 +4257,7 @@ func ExecutePostLoginHook(user *User, loginMethod, ip, protocol string, err erro
cmd := exec.CommandContext(ctx, config.PostLoginHook, args...)
cmd.Env = append(env,
fmt.Sprintf("SFTPGO_LOGIND_USER=%s", string(userAsJSON)),
fmt.Sprintf("SFTPGO_LOGIND_USER=%s", util.BytesToString(userAsJSON)),
fmt.Sprintf("SFTPGO_LOGIND_IP=%s", ip),
fmt.Sprintf("SFTPGO_LOGIND_METHOD=%s", loginMethod),
fmt.Sprintf("SFTPGO_LOGIND_STATUS=%s", status),
@ -4326,7 +4326,7 @@ func getExternalAuthResponse(username, password, pkey, keyboardInteractive, ip,
cmd := exec.CommandContext(ctx, config.ExternalAuthHook, args...)
cmd.Env = append(env,
fmt.Sprintf("SFTPGO_AUTHD_USERNAME=%s", username),
fmt.Sprintf("SFTPGO_AUTHD_USER=%s", string(userAsJSON)),
fmt.Sprintf("SFTPGO_AUTHD_USER=%s", util.BytesToString(userAsJSON)),
fmt.Sprintf("SFTPGO_AUTHD_IP=%s", ip),
fmt.Sprintf("SFTPGO_AUTHD_PASSWORD=%s", password),
fmt.Sprintf("SFTPGO_AUTHD_PUBLIC_KEY=%s", pkey),

View file

@ -99,7 +99,7 @@ func (n *NodeData) validate() error {
if n.Proto != NodeProtoHTTP && n.Proto != NodeProtoHTTPS {
return util.NewValidationError(fmt.Sprintf("invalid node proto: %s", n.Proto))
}
n.Key = kms.NewPlainSecret(string(util.GenerateRandomBytes(32)))
n.Key = kms.NewPlainSecret(util.BytesToString(util.GenerateRandomBytes(32)))
n.Key.SetAdditionalData(n.Host)
if err := n.Key.Encrypt(); err != nil {
return fmt.Errorf("unable to encrypt node key: %w", err)
@ -191,7 +191,7 @@ func (n *Node) generateAuthToken(username, role string) (string, error) {
if err != nil {
return "", fmt.Errorf("unable to sign authentication token: %w", err)
}
return string(payload), nil
return util.BytesToString(payload), nil
}
func (n *Node) prepareRequest(ctx context.Context, username, role, relativeURL, method string,

View file

@ -155,7 +155,7 @@ func (s *Share) hashPassword() error {
if err != nil {
return err
}
s.Password = string(hashed)
s.Password = util.BytesToString(hashed)
} else {
hashed, err := argon2id.CreateHash(s.Password, argon2Params)
if err != nil {

View file

@ -63,7 +63,7 @@ func NewServer(config *Configuration, configDir string, binding Binding, id int)
}
bannerContent, err := os.ReadFile(bannerFilePath)
if err == nil {
server.initialMsg = string(bannerContent)
server.initialMsg = util.BytesToString(bannerContent)
} else {
logger.WarnToConsole("unable to read FTPD banner file: %v", err)
logger.Warn(logSender, "", "unable to read banner file: %v", err)

View file

@ -25,6 +25,7 @@ import (
"github.com/drakkan/sftpgo/v2/internal/common"
"github.com/drakkan/sftpgo/v2/internal/dataprovider"
"github.com/drakkan/sftpgo/v2/internal/util"
)
func getDefenderHosts(w http.ResponseWriter, r *http.Request) {
@ -76,7 +77,7 @@ func getIPFromID(r *http.Request) (string, error) {
if err != nil {
return "", errors.New("invalid host id")
}
ip := string(decoded)
ip := util.BytesToString(decoded)
err = validateIPAddress(ip)
if err != nil {
return "", err

View file

@ -1594,7 +1594,7 @@ func getGCSConfig(r *http.Request) (vfs.GCSFsConfig, error) {
}
return config, err
}
config.Credentials = kms.NewPlainSecret(string(fileBytes))
config.Credentials = kms.NewPlainSecret(util.BytesToString(fileBytes))
config.AutomaticCredentials = 0
return config, err
}

View file

@ -23,6 +23,7 @@ import (
"errors"
"io"
"github.com/drakkan/sftpgo/v2/internal/util"
sdkkms "github.com/sftpgo/sdk/kms"
)
@ -132,7 +133,7 @@ func (s *builtinSecret) Decrypt() error {
return err
}
s.Status = sdkkms.SecretStatusPlain
s.Payload = string(plaintext)
s.Payload = util.BytesToString(plaintext)
s.Key = ""
s.AdditionalData = ""
return nil

View file

@ -21,6 +21,7 @@ import (
"encoding/hex"
"io"
"github.com/drakkan/sftpgo/v2/internal/util"
sdkkms "github.com/sftpgo/sdk/kms"
"gocloud.dev/secrets/localsecrets"
"golang.org/x/crypto/hkdf"
@ -104,7 +105,7 @@ func (s *localSecret) Decrypt() error {
return err
}
s.Status = sdkkms.SecretStatusPlain
s.Payload = string(plaintext)
s.Payload = util.BytesToString(plaintext)
s.Key = ""
s.AdditionalData = ""
s.Mode = 0

View file

@ -29,6 +29,7 @@ import (
"os"
"path/filepath"
"time"
"unsafe"
ftpserverlog "github.com/fclairamb/go-log"
"github.com/rs/zerolog"
@ -283,7 +284,7 @@ func (l *StdLoggerWrapper) Write(p []byte) (n int, err error) {
p = p[0 : n-1]
}
Log(LevelError, l.Sender, "", string(p))
Log(LevelError, l.Sender, "", bytesToString(p))
return
}
@ -363,3 +364,12 @@ func (l *LeveledLogger) With(keysAndValues ...any) ftpserverlog.Logger {
additionalKeyVals: append(l.additionalKeyVals, keysAndValues...),
}
}
func bytesToString(b []byte) string {
// unsafe.SliceData relies on cap whereas we want to rely on len
if len(b) == 0 {
return ""
}
// https://github.com/golang/go/blob/4ed358b57efdad9ed710be7f4fc51495a7620ce2/src/strings/builder.go#L41
return unsafe.String(unsafe.SliceData(b), len(b))
}

View file

@ -513,7 +513,7 @@ func (c *Configuration) configureLoginBanner(serverConfig *ssh.ServerConfig, con
}
bannerContent, err := os.ReadFile(bannerFilePath)
if err == nil {
banner := string(bannerContent)
banner := util.BytesToString(bannerContent)
serverConfig.BannerCallback = func(_ ssh.ConnMetadata) string {
return banner
}
@ -603,7 +603,8 @@ func (c *Configuration) AcceptInboundConnection(conn net.Conn, config *ssh.Serve
logger.Log(logger.LevelInfo, common.ProtocolSSH, connectionID,
"User %q logged in with %q, from ip %q, client version %q, negotiated algorithms: %+v",
user.Username, loginType, ipAddr, string(sconn.ClientVersion()), sconn.Conn.(ssh.AlgorithmsConnMetadata).Algorithms())
user.Username, loginType, ipAddr, util.BytesToString(sconn.ClientVersion()),
sconn.Conn.(ssh.AlgorithmsConnMetadata).Algorithms())
dataprovider.UpdateLastLogin(&user)
sshConnection := common.NewSSHConnection(connectionID, conn)
@ -639,12 +640,12 @@ func (c *Configuration) AcceptInboundConnection(conn net.Conn, config *ssh.Serve
switch req.Type {
case "subsystem":
if string(req.Payload[4:]) == "sftp" {
if util.BytesToString(req.Payload[4:]) == "sftp" {
ok = true
connection := &Connection{
BaseConnection: common.NewBaseConnection(connID, common.ProtocolSFTP, conn.LocalAddr().String(),
conn.RemoteAddr().String(), user),
ClientVersion: string(sconn.ClientVersion()),
ClientVersion: util.BytesToString(sconn.ClientVersion()),
RemoteAddr: conn.RemoteAddr(),
LocalAddr: conn.LocalAddr(),
channel: channel,
@ -656,7 +657,7 @@ func (c *Configuration) AcceptInboundConnection(conn net.Conn, config *ssh.Serve
connection := Connection{
BaseConnection: common.NewBaseConnection(connID, "sshd_exec", conn.LocalAddr().String(),
conn.RemoteAddr().String(), user),
ClientVersion: string(sconn.ClientVersion()),
ClientVersion: util.BytesToString(sconn.ClientVersion()),
RemoteAddr: conn.RemoteAddr(),
LocalAddr: conn.LocalAddr(),
channel: channel,
@ -816,7 +817,7 @@ func loginUser(user *dataprovider.User, loginMethod, publicKey string, conn ssh.
}
p := &ssh.Permissions{}
p.Extensions = make(map[string]string)
p.Extensions["sftpgo_user"] = string(json)
p.Extensions["sftpgo_user"] = util.BytesToString(json)
p.Extensions["sftpgo_login_method"] = loginMethod
return p, nil
}
@ -1180,7 +1181,7 @@ func (c *Configuration) validatePasswordCredentials(conn ssh.ConnMetadata, pass
var sshPerm *ssh.Permissions
ipAddr := util.GetIPFromRemoteAddress(conn.RemoteAddr().String())
if user, err = dataprovider.CheckUserAndPass(conn.User(), string(pass), ipAddr, common.ProtocolSSH); err == nil {
if user, err = dataprovider.CheckUserAndPass(conn.User(), util.BytesToString(pass), ipAddr, common.ProtocolSSH); err == nil {
sshPerm, err = loginUser(&user, method, "", conn)
}
user.Username = conn.User()

View file

@ -367,7 +367,7 @@ func (e *I18nError) Args() string {
if len(e.args) > 0 {
data, err := json.Marshal(e.args)
if err == nil {
return string(data)
return BytesToString(data)
}
}
return "{}"

View file

@ -44,6 +44,7 @@ import (
"strings"
"time"
"unicode"
"unsafe"
"github.com/google/uuid"
"github.com/lithammer/shortuuid/v3"
@ -288,6 +289,16 @@ func ParseBytes(s string) (int64, error) {
return 0, fmt.Errorf("unhandled size name: %v", extra)
}
// BytesToString converts []byte to string without allocations.
func BytesToString(b []byte) string {
// unsafe.SliceData relies on cap whereas we want to rely on len
if len(b) == 0 {
return ""
}
// https://github.com/golang/go/blob/4ed358b57efdad9ed710be7f4fc51495a7620ce2/src/strings/builder.go#L41
return unsafe.String(unsafe.SliceData(b), len(b))
}
// GetIPFromRemoteAddress returns the IP from the remote address.
// If the given remote address cannot be parsed it will be returned unchanged
func GetIPFromRemoteAddress(remoteAddress string) string {
@ -660,7 +671,7 @@ func EncodeTLSCertToPem(tlsCert *x509.Certificate) (string, error) {
Type: "CERTIFICATE",
Bytes: tlsCert.Raw,
}
return string(pem.EncodeToMemory(&publicKeyBlock)), nil
return BytesToString(pem.EncodeToMemory(&publicKeyBlock)), nil
}
// CheckTCP4Port quits the app if bind on the given IPv4 port fails.
@ -704,7 +715,7 @@ func GetSSHPublicKeyAsString(pubKey []byte) (string, error) {
if err != nil {
return "", err
}
return string(ssh.MarshalAuthorizedKey(k)), nil
return BytesToString(ssh.MarshalAuthorizedKey(k)), nil
}
// GetRealIP returns the ip address as result of parsing the specified
@ -880,7 +891,7 @@ func JSONEscape(val string) string {
if err != nil {
return ""
}
return string(b[1 : len(b)-1])
return BytesToString(b[1 : len(b)-1])
}
// ReadConfigFromFile reads a configuration parameter from the specified file
@ -901,5 +912,5 @@ func ReadConfigFromFile(name, configDir string) (string, error) {
if err != nil {
return "", err
}
return strings.TrimSpace(string(val)), nil
return strings.TrimSpace(BytesToString(val)), nil
}

View file

@ -448,10 +448,10 @@ func (f *webDavFile) Patch(patches []webdav.Proppatch) ([]webdav.Propstat, error
for _, p := range patch.Props {
if status == http.StatusForbidden && !hasError {
if !patch.Remove && util.Contains(lastModifiedProps, p.XMLName.Local) {
parsed, err := parseTime(string(p.InnerXML))
parsed, err := parseTime(util.BytesToString(p.InnerXML))
if err != nil {
f.Connection.Log(logger.LevelWarn, "unsupported last modification time: %q, err: %v",
string(p.InnerXML), err)
util.BytesToString(p.InnerXML), err)
hasError = true
continue
}