httputil: only send client authorization over HTTPS
Might just be a hypothetical concern, but better for this to fail closed
(not send credentials when needed) than to fail open (send credentials
insecurely).
Change-Id: I8542e8c0b533a3c5f53a5f03f4e3b639b6b55f71
Reviewed-on: https://go-review.googlesource.com/69290
Reviewed-by: Tuo Shan <shantuo@google.com>
diff --git a/httputil/transport.go b/httputil/transport.go
index 81af99d..b41fe59 100644
--- a/httputil/transport.go
+++ b/httputil/transport.go
@@ -71,7 +71,7 @@
reqCopy = copyRequest(req)
reqCopy.Header.Set("User-Agent", t.UserAgent)
}
- if req.URL.Host == "api.github.com" {
+ if req.URL.Host == "api.github.com" && req.URL.Scheme == "https" {
switch {
case t.ClientID != "" && t.ClientSecret != "":
if reqCopy == nil {
diff --git a/httputil/transport_test.go b/httputil/transport_test.go
new file mode 100644
index 0000000..2980d97
--- /dev/null
+++ b/httputil/transport_test.go
@@ -0,0 +1,126 @@
+// Copyright 2017 The Go Authors. All rights reserved.
+//
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file or at
+// https://developers.google.com/open-source/licenses/bsd.
+
+package httputil
+
+import (
+ "bytes"
+ "io/ioutil"
+ "net/http"
+ "net/url"
+ "testing"
+)
+
+func TestTransportGithubAuth(t *testing.T) {
+ tests := []struct {
+ name string
+
+ url string
+ token string
+ clientID string
+ clientSecret string
+
+ queryClientID string
+ queryClientSecret string
+ authorization string
+ }{
+ {
+ name: "Github token",
+ url: "https://api.github.com/",
+ token: "xyzzy",
+ authorization: "token xyzzy",
+ },
+ {
+ name: "Github client ID/secret",
+ url: "https://api.github.com/",
+ clientID: "12345",
+ clientSecret: "xyzzy",
+ queryClientID: "12345",
+ queryClientSecret: "xyzzy",
+ },
+ {
+ name: "non-Github site does not have token headers",
+ url: "http://www.example.com/",
+ token: "xyzzy",
+ },
+ {
+ name: "non-Github site does not have client ID/secret headers",
+ url: "http://www.example.com/",
+ clientID: "12345",
+ clientSecret: "xyzzy",
+ },
+ {
+ name: "Github token not sent over HTTP",
+ url: "http://api.github.com/",
+ token: "xyzzy",
+ },
+ {
+ name: "Github client ID/secret not sent over HTTP",
+ url: "http://api.github.com/",
+ clientID: "12345",
+ clientSecret: "xyzzy",
+ },
+ {
+ name: "Github token not sent over schemeless",
+ url: "//api.github.com/",
+ token: "xyzzy",
+ },
+ {
+ name: "Github client ID/secret not sent over schemeless",
+ url: "//api.github.com/",
+ clientID: "12345",
+ clientSecret: "xyzzy",
+ },
+ }
+ for _, test := range tests {
+ t.Run(test.name, func(t *testing.T) {
+ var (
+ query url.Values
+ authHeader string
+ )
+ client := &http.Client{
+ Transport: &AuthTransport{
+ Base: roundTripFunc(func(r *http.Request) {
+ query = r.URL.Query()
+ authHeader = r.Header.Get("Authorization")
+ }),
+ Token: test.token,
+ ClientID: test.clientID,
+ ClientSecret: test.clientSecret,
+ },
+ }
+ _, err := client.Get(test.url)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if got := query.Get("client_id"); got != test.queryClientID {
+ t.Errorf("url query client_id = %q; want %q", got, test.queryClientID)
+ }
+ if got := query.Get("client_secret"); got != test.queryClientSecret {
+ t.Errorf("url query client_secret = %q; want %q", got, test.queryClientSecret)
+ }
+ if authHeader != test.authorization {
+ t.Errorf("header Authorization = %q; want %q", authHeader, test.authorization)
+ }
+ })
+ }
+}
+
+type roundTripFunc func(r *http.Request)
+
+func (f roundTripFunc) RoundTrip(r *http.Request) (*http.Response, error) {
+ f(r)
+ return &http.Response{
+ Status: "200 OK",
+ StatusCode: http.StatusOK,
+ Proto: "HTTP/1.1",
+ ProtoMajor: 1,
+ ProtoMinor: 1,
+ Body: ioutil.NopCloser(bytes.NewReader(nil)),
+ ContentLength: 0,
+ Request: r,
+ }, nil
+}