Commit 2d683807 authored by Bryan C. Mills's avatar Bryan C. Mills

cmd/go/internal/web2: make netrc parsing more robust

- 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: default avatarJay Conrod <jayconrod@google.com>
parent 131eb8fb
...@@ -37,29 +37,61 @@ type netrcLine struct { ...@@ -37,29 +37,61 @@ type netrcLine struct {
password string password string
} }
var netrcOnce sync.Once var (
var netrc []netrcLine netrcOnce sync.Once
netrc []netrcLine
netrcErr error
)
func parseNetrc(data string) []netrcLine { 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 nrc []netrcLine
var l netrcLine var l netrcLine
inMacro := false
for _, line := range strings.Split(data, "\n") { for _, line := range strings.Split(data, "\n") {
if inMacro {
if line == "" {
inMacro = false
}
continue
}
f := strings.Fields(line) 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] { switch f[i] {
case "machine": case "machine":
l.machine = f[i+1] l = netrcLine{machine: f[i+1]}
case "default":
break
case "login": case "login":
l.login = f[i+1] l.login = f[i+1]
case "password": case "password":
l.password = f[i+1] 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 != "" { if l.machine != "" && l.login != "" && l.password != "" {
nrc = append(nrc, l) nrc = append(nrc, l)
l = netrcLine{} 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 return nrc
} }
...@@ -73,22 +105,36 @@ func havePassword(machine string) bool { ...@@ -73,22 +105,36 @@ func havePassword(machine string) bool {
return false return false
} }
func netrcPath() string { func netrcPath() (string, error) {
switch runtime.GOOS { if env := os.Getenv("NETRC"); env != "" {
case "windows": return env, nil
return filepath.Join(os.Getenv("USERPROFILE"), "_netrc") }
case "plan9": dir, err := os.UserHomeDir()
return filepath.Join(os.Getenv("home"), ".netrc") if err != nil {
default: return "", err
return filepath.Join(os.Getenv("HOME"), ".netrc")
} }
base := ".netrc"
if runtime.GOOS == "windows" {
base = "_netrc"
}
return filepath.Join(dir, base), nil
} }
func readNetrc() { 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 err != nil {
if !os.IsNotExist(err) {
netrcErr = err
}
return return
} }
netrc = parseNetrc(string(data)) netrc = parseNetrc(string(data))
} }
......
...@@ -10,6 +10,9 @@ import ( ...@@ -10,6 +10,9 @@ import (
) )
var testNetrc = ` var testNetrc = `
machine incomplete
password none
machine api.github.com machine api.github.com
login user login user
password pwd password pwd
...@@ -20,6 +23,24 @@ machine incomlete.host ...@@ -20,6 +23,24 @@ machine incomlete.host
machine test.host machine test.host
login user2 login user2
password pwd2 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) { func TestReadNetrc(t *testing.T) {
...@@ -27,6 +48,8 @@ func TestReadNetrc(t *testing.T) { ...@@ -27,6 +48,8 @@ func TestReadNetrc(t *testing.T) {
want := []netrcLine{ want := []netrcLine{
{"api.github.com", "user", "pwd"}, {"api.github.com", "user", "pwd"},
{"test.host", "user2", "pwd2"}, {"test.host", "user2", "pwd2"},
{"oneline", "user3", "pwd3"},
{"hasmacro.too", "user4", "pwd4"},
} }
if !reflect.DeepEqual(lines, want) { if !reflect.DeepEqual(lines, want) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment