]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/go/internal/web2: make netrc parsing more robust
authorBryan C. Mills <bcmills@google.com>
Fri, 8 Feb 2019 16:24:29 +0000 (11:24 -0500)
committerBryan C. Mills <bcmills@google.com>
Tue, 2 Apr 2019 19:51:13 +0000 (19:51 +0000)
- Respect the NETRC environment variable if set.

- Ignore lines that contain macro definitions.

- Associate the 'machine' token with only the tokens that follow (not
  precede) it.

Updates #29888
Updates #26232

Change-Id: I3128b7d6da2d6492df7c864e165eea1a27384f0f
Reviewed-on: https://go-review.googlesource.com/c/go/+/161698
Run-TryBot: Bryan C. Mills <bcmills@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Jay Conrod <jayconrod@google.com>
src/cmd/go/internal/web2/web.go
src/cmd/go/internal/web2/web_test.go

index 64934f1d506dbfa4142550a57656a8816dec75c4..02b828ffa6754524a6b9bce44b6beea179dc144c 100644 (file)
@@ -37,29 +37,61 @@ type netrcLine struct {
        password string
 }
 
-var netrcOnce sync.Once
-var netrc []netrcLine
+var (
+       netrcOnce sync.Once
+       netrc     []netrcLine
+       netrcErr  error
+)
 
 func parseNetrc(data string) []netrcLine {
+       // See https://www.gnu.org/software/inetutils/manual/html_node/The-_002enetrc-file.html
+       // for documentation on the .netrc format.
        var nrc []netrcLine
        var l netrcLine
+       inMacro := false
        for _, line := range strings.Split(data, "\n") {
+               if inMacro {
+                       if line == "" {
+                               inMacro = false
+                       }
+                       continue
+               }
+
                f := strings.Fields(line)
-               for i := 0; i < len(f)-1; i += 2 {
+               i := 0
+               for ; i < len(f)-1; i += 2 {
+                       // Reset at each "machine" token.
+                       // “The auto-login process searches the .netrc file for a machine token
+                       // that matches […]. Once a match is made, the subsequent .netrc tokens
+                       // are processed, stopping when the end of file is reached or another
+                       // machine or a default token is encountered.”
                        switch f[i] {
                        case "machine":
-                               l.machine = f[i+1]
+                               l = netrcLine{machine: f[i+1]}
+                       case "default":
+                               break
                        case "login":
                                l.login = f[i+1]
                        case "password":
                                l.password = f[i+1]
+                       case "macdef":
+                               // “A macro is defined with the specified name; its contents begin with
+                               // the next .netrc line and continue until a null line (consecutive
+                               // new-line characters) is encountered.”
+                               inMacro = true
+                       }
+                       if l.machine != "" && l.login != "" && l.password != "" {
+                               nrc = append(nrc, l)
+                               l = netrcLine{}
                        }
                }
-               if l.machine != "" && l.login != "" && l.password != "" {
-                       nrc = append(nrc, l)
-                       l = netrcLine{}
+
+               if i < len(f) && f[i] == "default" {
+                       // “There can be only one default token, and it must be after all machine tokens.”
+                       break
                }
        }
+
        return nrc
 }
 
@@ -73,22 +105,36 @@ func havePassword(machine string) bool {
        return false
 }
 
-func netrcPath() string {
-       switch runtime.GOOS {
-       case "windows":
-               return filepath.Join(os.Getenv("USERPROFILE"), "_netrc")
-       case "plan9":
-               return filepath.Join(os.Getenv("home"), ".netrc")
-       default:
-               return filepath.Join(os.Getenv("HOME"), ".netrc")
+func netrcPath() (string, error) {
+       if env := os.Getenv("NETRC"); env != "" {
+               return env, nil
        }
+       dir, err := os.UserHomeDir()
+       if err != nil {
+               return "", err
+       }
+       base := ".netrc"
+       if runtime.GOOS == "windows" {
+               base = "_netrc"
+       }
+       return filepath.Join(dir, base), nil
 }
 
 func readNetrc() {
-       data, err := ioutil.ReadFile(netrcPath())
+       path, err := netrcPath()
+       if err != nil {
+               netrcErr = err
+               return
+       }
+
+       data, err := ioutil.ReadFile(path)
        if err != nil {
+               if !os.IsNotExist(err) {
+                       netrcErr = err
+               }
                return
        }
+
        netrc = parseNetrc(string(data))
 }
 
index c6f6b1eff4dd76b1079e4edcbc689bd8a450b363..e6787a5b54e42e7acc54c441f82b0e898e79140a 100644 (file)
@@ -10,16 +10,37 @@ import (
 )
 
 var testNetrc = `
+machine incomplete
+password none
+
 machine api.github.com
   login user
   password pwd
 
 machine incomlete.host
   login justlogin
-  
+
 machine test.host
 login user2
 password pwd2
+
+machine oneline login user3 password pwd3
+
+machine ignore.host macdef ignore
+  login nobody
+  password nothing
+
+machine hasmacro.too macdef ignore-next-lines login user4 password pwd4
+  login nobody
+  password nothing
+
+default
+login anonymous
+password gopher@golang.org
+
+machine after.default
+login oops
+password too-late-in-file
 `
 
 func TestReadNetrc(t *testing.T) {
@@ -27,6 +48,8 @@ func TestReadNetrc(t *testing.T) {
        want := []netrcLine{
                {"api.github.com", "user", "pwd"},
                {"test.host", "user2", "pwd2"},
+               {"oneline", "user3", "pwd3"},
+               {"hasmacro.too", "user4", "pwd4"},
        }
 
        if !reflect.DeepEqual(lines, want) {