Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 2 additions & 6 deletions cmd/harbor/root/login.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ package root
import (
"context"
"fmt"
"os"
"strings"

"github.com/goharbor/go-client/pkg/harbor"
Expand All @@ -28,7 +27,6 @@ import (
"github.com/goharbor/harbor-cli/pkg/views/login"
log "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
"golang.org/x/term"
)

var (
Expand All @@ -53,13 +51,11 @@ func LoginCommand() *cobra.Command {
}

if passwordStdin {
fmt.Print("Password: ")
passwordBytes, err := term.ReadPassword(int(os.Stdin.Fd())) // #nosec G115 - fd fits in int on all supported platforms
password, err := utils.GetSecretStdin("Password: ")
if err != nil {
return fmt.Errorf("failed to read password from stdin: %v", err)
}
fmt.Println()
Password = string(passwordBytes)
Password = password
}

loginView := login.LoginView{
Expand Down
22 changes: 17 additions & 5 deletions pkg/utils/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,11 @@ import (
"encoding/json"
"errors"
"fmt"
"io"
"os"
"regexp"
"strconv"
"strings"
"syscall"

"github.com/charmbracelet/bubbles/table"
"github.com/gocarina/gocsv"
Expand Down Expand Up @@ -187,13 +187,25 @@ func SavePayloadJSON(filename string, payload any) {

// Get Password as Stdin
func GetSecretStdin(prompt string) (string, error) {
fmt.Print(prompt)
bytePassword, err := term.ReadPassword(int(syscall.Stdin))
if term.IsTerminal(int(os.Stdin.Fd())) { // #nosec G115 - fd fits in int on all supported platforms
fmt.Print(prompt)
bytePassword, err := term.ReadPassword(int(os.Stdin.Fd())) // #nosec G115 - fd fits in int on all supported platforms
if err != nil {
return "", err
}
fmt.Println() // move to the next line after input
return trimSecretLineEnding(bytePassword), nil
}

bytePassword, err := io.ReadAll(os.Stdin)
if err != nil {
return "", err
}
fmt.Println() // move to the next line after input
return strings.TrimSpace(string(bytePassword)), nil
return trimSecretLineEnding(bytePassword), nil
}

func trimSecretLineEnding(secret []byte) string {
return strings.TrimRight(string(secret), "\r\n")
}

func ToKebabCase(s string) string {
Expand Down
37 changes: 37 additions & 0 deletions pkg/utils/utils_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,13 @@ package utils_test

import (
"fmt"
"os"
"testing"

"github.com/goharbor/harbor-cli/pkg/utils"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

func Test_Sanitize_ServerAddress(t *testing.T) {
Expand Down Expand Up @@ -145,3 +147,38 @@ func TestStorageStringToBytes(t *testing.T) {
_, err := utils.StorageStringToBytes("1025TiB")
assert.Error(t, err, "Expected error for input exceeding 1024TiB but got none")
}

func TestGetSecretStdinReadsPipedInput(t *testing.T) {
secret := getSecretFromPipe(t, "Abcd1234\n")

assert.Equal(t, "Abcd1234", secret)
}

func TestGetSecretStdinPreservesSecretWhitespace(t *testing.T) {
secret := getSecretFromPipe(t, " Abcd1234 \r\n")

assert.Equal(t, " Abcd1234 ", secret)
}

func getSecretFromPipe(t *testing.T, input string) string {
t.Helper()

oldStdin := os.Stdin
reader, writer, err := os.Pipe()
require.NoError(t, err)

_, err = writer.WriteString(input)
require.NoError(t, err)
require.NoError(t, writer.Close())

os.Stdin = reader
t.Cleanup(func() {
os.Stdin = oldStdin
_ = reader.Close()
})

secret, err := utils.GetSecretStdin("Password: ")
require.NoError(t, err)

return secret
}