Repository: mitmproxy/mitmproxy

Issue:
Incorrect displaying of set-cookie attributes, if no name-value pairs were set in set-cookie header editor
##### Steps to reproduce the problem:

1. Run mitmproxy.
2. Press `n` -> `Enter` -> `Enter` -> `e` -> `set-cookies`
3. Press `a` to add a new row.
4. Choose `Attributes` column and enter subeditor. 
5. Add any name-value pair, for example `a    1`.
6. Go to flowview tab by pressing `q` twice. You will see this in Response tab:
![image](https://user-images.githubusercontent.com/20267977/35624324-5704d498-0697-11e8-8352-6b691bcf0507.png)
I am not sure it is correct to display just `=` without anything 

7. Press `e` -> `set-cookies`. You will see, that name-value from `Attributes` column moved to high-level name-value:
![image](https://user-images.githubusercontent.com/20267977/35624767-a2dc5e80-0698-11e8-8206-397f98d933b3.png)

`set-cookies` was changed in Response tab as well. 
Strange behaviour.
 
##### Any other comments? What have you tried so far?

This small bug is relevant for 2.0.x version as well.


##### System information

Mitmproxy: 3.0.0.dev75 (commit 02d2ef7) 
Python:    3.5.2
OpenSSL:   OpenSSL 1.1.0g  2 Nov 2017
Platform:  Linux-4.4.0-112-generic-x86_64-with-Ubuntu-16.04-xenial



Discussion:
Hi. I am new to open source and would like to contribute to mitmproxy. Can I work on this issue?
@tran-tien-dat Hi! Please, take it, if you want :) 
I have found the cause the problem. It is around this part of the code:

https://github.com/mitmproxy/mitmproxy/blob/326bb546da214eb2c3ca82f9c506f3c051b1e660/mitmproxy/net/http/cookies.py#L143-L165

`lhs` and `rhs` are respectively variables to store the name and value of the cookie or the attribute. They are added to `pairs` in line 165. The code will interpret the first pair as the name and value of the cookie, and the remaining pairs are interpreted as attributes. As can be seen from the code, line 165 is only reached if `lhs` is not an empty string. Thus if we specify an empty string as the name of the cookie, weird behaviours will occur. In particular, if we leave both name and value of the cookie to be empty, then the 1st pair to be added is the name/value of the attribute, so they are "pushed higher up" to become the name/value of the cookie. Besides, if we just leave the name empty, while fill in something for the value of the cookie, mitmproxy will crash.

So the problem here is because of not validating the `Set-Cookie` header. I think when mitmproxy parse the `Set-Cookie` header (i.e. in `mitmproxy.net.http.cookies._read_set_cookie_pairs` quoted above), it should only parse valid header. If the `Set-Cookie` header is invalid and the user wants to edit it, mitmproxy should tell the user that the header is invalid and allows editing of the raw header instead of using the current grid editor.

I will work on an implementation of this idea.

Resolving patch:
diff --git a/mitmproxy/net/http/cookies.py b/mitmproxy/net/http/cookies.py
--- a/mitmproxy/net/http/cookies.py
+++ b/mitmproxy/net/http/cookies.py
@@ -159,13 +159,17 @@ def _read_set_cookie_pairs(s: str, off=0) -> Tuple[List[TPairs], int]:
                 if len(rhs) <= 3:
                     trail, off = _read_value(s, off + 1, ";,")
                     rhs = rhs + "," + trail
-        if rhs or lhs:
+
+            # as long as there's a "=", we consider it a pair
+            pairs.append([lhs, rhs])
+
+        elif lhs:
             pairs.append([lhs, rhs])
 
-            # comma marks the beginning of a new cookie
-            if off < len(s) and s[off] == ",":
-                cookies.append(pairs)
-                pairs = []
+        # comma marks the beginning of a new cookie
+        if off < len(s) and s[off] == ",":
+            cookies.append(pairs)
+            pairs = []
 
         off += 1
 


Test patch:
diff --git a/test/mitmproxy/net/http/test_cookies.py b/test/mitmproxy/net/http/test_cookies.py
--- a/test/mitmproxy/net/http/test_cookies.py
+++ b/test/mitmproxy/net/http/test_cookies.py
@@ -142,6 +142,27 @@ def test_cookie_roundtrips():
 
 def test_parse_set_cookie_pairs():
     pairs = [
+        [
+            "=",
+            [[
+                ["", ""]
+            ]]
+        ],
+        [
+            "=;foo=bar",
+            [[
+                ["", ""],
+                ["foo", "bar"]
+            ]]
+        ],
+        [
+            "=;=;foo=bar",
+            [[
+                ["", ""],
+                ["", ""],
+                ["foo", "bar"]
+            ]]
+        ],
         [
             "=uno",
             [[