Repository: go-resty/resty
Pull request #633 (2023-03-20 07:14:07)

fix: client panic on missing digest value
After splitting key=value pairs, we check that there are really key AND value. A maliciously crafted response header can cause the client to panic.

codecov[bot]: ## [Codecov](https://codecov.io/gh/go-resty/resty/pull/633?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=go-resty) Report
> Merging [#633](https://codecov.io/gh/go-resty/resty/pull/633?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=go-resty) (c3ae2b7) into [master](https://codecov.io/gh/go-resty/resty/commit/38b1644dbd1c09c80acc78d5871d5c1103a9166f?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=go-resty) (38b1644) will **increase** coverage by `0.00%`.
> The diff coverage is `100.00%`.

```diff
@@           Coverage Diff           @@
##           master     #633   +/-   ##
=======================================
  Coverage   95.83%   95.83%           
=======================================
  Files          11       11           
  Lines        1559     1561    +2     
=======================================
+ Hits         1494     1496    +2     
  Misses         40       40           
  Partials       25       25           
```


| [Impacted Files](https://codecov.io/gh/go-resty/resty/pull/633?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=go-resty) | Coverage Δ | |
|---|---|---|
| [digest.go](https://codecov.io/gh/go-resty/resty/pull/633?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=go-resty#diff-ZGlnZXN0Lmdv) | `95.65% <100.00%> (+0.06%)` | :arrow_up: |

Help us with your feedback. Take ten seconds to tell us [how you rate us](https://about.codecov.io/nps?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=go-resty). Have a feature suggestion? [Share it here.](https://app.codecov.io/gh/feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=go-resty)


Patch:
diff --git a/digest.go b/digest.go
index 0a7bb10..8e6f9f7 100644
--- a/digest.go
+++ b/digest.go
@@ -121,6 +121,9 @@ func parseChallenge(input string) (*challenge, error) {
 	var r []string
 	for i := range sl {
 		r = strings.SplitN(sl[i], "=", 2)
+		if len(r) != 2 {
+			return nil, ErrDigestBadChallenge
+		}
 		switch r[0] {
 		case "realm":
 			c.realm = strings.Trim(r[1], qs)


Test patch:
diff --git a/client_test.go b/client_test.go
index 030abcc..1ebbe8e 100644
--- a/client_test.go
+++ b/client_test.go
@@ -133,6 +133,7 @@ func TestClientDigestErrors(t *testing.T) {
 		{mutateConf: func(c *digestServerConfig) { c.charset = "utf-16" }, expect: ErrDigestCharset},
 		{mutateConf: func(c *digestServerConfig) { c.uri = "/bad" }, expect: ErrDigestBadChallenge},
 		{mutateConf: func(c *digestServerConfig) { c.uri = "/unknown_param" }, expect: ErrDigestBadChallenge},
+		{mutateConf: func(c *digestServerConfig) { c.uri = "/missing_value" }, expect: ErrDigestBadChallenge},
 		{mutateConf: func(c *digestServerConfig) { c.uri = "/no_challenge" }, expect: ErrDigestBadChallenge},
 		{mutateConf: func(c *digestServerConfig) { c.uri = "/status_500" }, expect: nil},
 	}
diff --git a/resty_test.go b/resty_test.go
index 88e85ba..677ece0 100644
--- a/resty_test.go
+++ b/resty_test.go
@@ -645,22 +645,27 @@ func createDigestServer(t *testing.T, conf *digestServerConfig) *httptest.Server
 	if conf == nil {
 		conf = defaultDigestServerConf()
 	}
+
+	setWWWAuthHeader := func(w http.ResponseWriter, v string) {
+		w.Header().Set("WWW-Authenticate", v)
+		w.WriteHeader(http.StatusUnauthorized)
+	}
 	ts := createTestServer(func(w http.ResponseWriter, r *http.Request) {
 		t.Logf("Method: %v", r.Method)
 		t.Logf("Path: %v", r.URL.Path)
 
 		switch r.URL.Path {
 		case "/bad":
-			w.Header().Set("WWW-Authenticate", "Bad Challenge")
-			w.WriteHeader(http.StatusUnauthorized)
+			setWWWAuthHeader(w, "Bad Challenge")
 			return
 		case "/unknown_param":
-			w.Header().Set("WWW-Authenticate", "Digest unknown_param=true")
-			w.WriteHeader(http.StatusUnauthorized)
+			setWWWAuthHeader(w, "Digest unknown_param=true")
+			return
+		case "/missing_value":
+			setWWWAuthHeader(w, `Digest realm="hello", domain`)
 			return
 		case "/no_challenge":
-			w.Header().Set("WWW-Authenticate", "")
-			w.WriteHeader(http.StatusUnauthorized)
+			setWWWAuthHeader(w, "")
 			return
 		case "/status_500":
 			w.WriteHeader(http.StatusInternalServerError)
@@ -670,10 +675,9 @@ func createDigestServer(t *testing.T, conf *digestServerConfig) *httptest.Server
 		w.Header().Set(hdrContentTypeKey, "application/json; charset=utf-8")
 
 		if !authorizationHeaderValid(t, r, conf) {
-			w.Header().Set("WWW-Authenticate",
+			setWWWAuthHeader(w,
 				fmt.Sprintf(`Digest realm="%s", domain="%s", qop="%s", algorithm=%s, nonce="%s", opaque="%s", userhash=true, charset=%s, stale=FALSE`,
 					conf.realm, conf.uri, conf.qop, conf.algo, conf.nonce, conf.opaque, conf.charset))
-			w.WriteHeader(http.StatusUnauthorized)
 			_, _ = w.Write([]byte(`{ "id": "unauthorized", "message": "Invalid credentials" }`))
 		} else {
 			w.WriteHeader(http.StatusOK)