Skip to content

Commit 2a99b14

Browse files
committed
cli/command/registry: refactor reading from stdin
Extract the code as a utility function, and add some GoDoc to describe the behavior. Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
1 parent 9a41c73 commit 2a99b14

1 file changed

Lines changed: 35 additions & 6 deletions

File tree

cli/command/registry/login.go

Lines changed: 35 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
package registry
22

33
import (
4+
"bytes"
45
"context"
56
"errors"
67
"fmt"
78
"io"
8-
"strings"
99

1010
"github.com/containerd/errdefs"
1111
"github.com/docker/cli/cli"
@@ -88,6 +88,38 @@ func verifyLoginFlags(flags *pflag.FlagSet, opts loginOptions) error {
8888
return nil
8989
}
9090

91+
// readSecretFromStdin reads the secret from r and returns it as a string.
92+
// It trims terminal line-endings (LF, CRLF, or CR), which may be added when
93+
// inputting interactively or piping input. The value is otherwise treated as
94+
// opaque, preserving any other whitespace, including newlines, per [NIST SP 800-63B §5.1.1.2].
95+
// Note that trimming whitespace may still happen elsewhere (see [NIST SP 800-63B (revision 4) §3.1.1.2]);
96+
//
97+
// > Verifiers **MAY** make limited allowances for mistyping (e.g., removing
98+
// > leading and trailing whitespace characters before verification, allowing
99+
// > the verification of passwords with differing cases for the leading character)
100+
//
101+
// [NIST SP 800-63B §5.1.1.2]: https://pages.nist.gov/800-63-3/sp800-63b.html#memsecretver
102+
// [NIST SP 800-63B (revision 4) §3.1.1.2]: https://pages.nist.gov/800-63-4/sp800-63b.html#passwordver
103+
func readSecretFromStdin(r io.Reader) (string, error) {
104+
b, err := io.ReadAll(r)
105+
if err != nil {
106+
return "", err
107+
}
108+
if len(b) == 0 {
109+
return "", nil
110+
}
111+
112+
for _, eol := range [][]byte{[]byte("\r\n"), []byte("\n"), []byte("\r")} {
113+
var ok bool
114+
b, ok = bytes.CutSuffix(b, eol)
115+
if ok {
116+
break
117+
}
118+
}
119+
120+
return string(b), nil
121+
}
122+
91123
func verifyLoginOptions(dockerCLI command.Streams, opts *loginOptions) error {
92124
if opts.password != "" {
93125
_, _ = fmt.Fprintln(dockerCLI.Err(), "WARNING! Using --password via the CLI is insecure. Use --password-stdin.")
@@ -97,14 +129,11 @@ func verifyLoginOptions(dockerCLI command.Streams, opts *loginOptions) error {
97129
if opts.user == "" {
98130
return errors.New("username is empty")
99131
}
100-
101-
contents, err := io.ReadAll(dockerCLI.In())
132+
p, err := readSecretFromStdin(dockerCLI.In())
102133
if err != nil {
103134
return err
104135
}
105-
106-
opts.password = strings.TrimSuffix(string(contents), "\n")
107-
opts.password = strings.TrimSuffix(opts.password, "\r")
136+
opts.password = p
108137
}
109138
return nil
110139
}

0 commit comments

Comments
 (0)