Repository: pypi/warehouse

Issue:
Set samesite=lax on session cookies
This is a strong defense-in-depth mechanism for protecting against CSRF. It's currently only respected by Chrome, but Firefox will add it as well.
Update webob to 1.8.0

This PR updates [WebOb](https://pypi.org/project/WebOb) from **1.7.4** to **1.8.0**.



<details>
  <summary>Changelog</summary>
  
  
   ### 1.8.0
   ```
   ------------------

Feature
~~~~~~~

- ``request.POST`` now supports any requests with the appropriate
  Content-Type. Allowing any HTTP method to access form encoded content,
  including DELETE, PUT, and others. See
  https://github.com/Pylons/webob/pull/352

Compatibility
~~~~~~~~~~~~~

- WebOb is no longer officially supported on Python 3.3 which was EOL&#39;ed on
  2017-09-29.

Backwards Incompatibilities
~~~~~~~~~~~~~~~~~~~~~~~~~~~

- Many changes have been made to the way WebOb does Accept handling, not just
  for the Accept header itself, but also for Accept-Charset, Accept-Encoding
  and Accept-Language. This was a `Google Summer of Code
  &lt;https://developers.google.com/open-source/gsoc/&gt;`_ project completed by
  Whiteroses (https://github.com/whiteroses). Many thanks to Google for running
  GSoC, the Python Software Foundation for organising and a huge thanks to Ira
  for completing the work. See https://github.com/Pylons/webob/pull/338 and
  https://github.com/Pylons/webob/pull/335. Documentation is available at
  https://docs.pylonsproject.org/projects/webob/en/master/api/webob.html

- When calling a ``wsgify`` decorated function, the default arguments passed
  to ``wsgify`` are now used when called with the request, and not as a
  `start_response`

  .. code::

     def hello(req, name):
         return &quot;Hello, %s!&quot; % name
     app = wsgify(hello, args=(&quot;Fred&quot;,))

     req = Request.blank(&#39;/&#39;)
     resp = req.get_response(app)   =&gt; &quot;Hello, Fred&quot;
     resp2 = app(req)  =&gt; &quot;Hello, Fred&quot;

  Previously the ``resp2`` line would have failed with a ``TypeError``. With
  this change there is no way to override the default arguments with no
  arguments. See https://github.com/Pylons/webob/pull/203

- When setting ``app_iter`` on a ``Response`` object the ``content_md5`` header
  is no longer cleared. This behaviour is odd and disallows setting the
  ``content_md5`` and then returning an iterator for chunked content encoded
  responses. See https://github.com/Pylons/webob/issues/86

Experimental Features
~~~~~~~~~~~~~~~~~~~~~

These features are experimental and may change at any point in the future.

- The cookie APIs now have the ability to set the SameSite attribute on a
  cookie in both ``webob.cookies.make_cookie`` and
  ``webob.cookies.CookieProfile``. See https://github.com/Pylons/webob/pull/255

Bugfix
~~~~~~

- Exceptions now use string.Template.safe_substitute rather than
  string.Template.substitute. The latter would raise for missing mappings, the
  former will simply not substitute the missing variable. This is safer in case
  the WSGI environ does not contain the keys necessary for the body template.
  See https://github.com/Pylons/webob/issues/345.

- Request.host_url, Request.host_port, Request.domain correctly parse IPv6 Host
  headers as provided by a browser. See
  https://github.com/Pylons/webob/pull/332

- Request.authorization would raise ValueError for unusual or malformed header
  values. See https://github.com/Pylons/webob/issues/231

- Allow unnamed fields in form data to be properly transcoded when calling
  request.decode with an alternate encoding. See
  https://github.com/Pylons/webob/pull/309

- ``Response.__init__`` would discard ``app_iter`` when a ``Response`` had no
  body, this would cause issues when ``app_iter`` was an object that was tied
  to the life-cycle of a web application and had to be properly closed.
  ``app_iter`` is more advanced API for ``Response`` and thus even if it
  contains a body and is thus against the HTTP RFC&#39;s, we should let the users
  shoot themselves by returning a body. See
  https://github.com/Pylons/webob/issues/305
   ```
   
  
</details>


 

<details>
  <summary>Links</summary>
  
  - PyPI: https://pypi.org/project/webob
  - Changelog: https://pyup.io/changelogs/webob/
  - Homepage: http://webob.org/
</details>




Discussion:
This may be blocked on https://github.com/Pylons/pyramid/issues/2733
Thanks, @alex.

Yeah, after Pyramid supports this, Warehouse should add it.
I think we're blocked on a WebOb release, not pyramid support.
I think this is unblocked now that #3554 has been merged.


Resolving patch:
diff --git a/warehouse/sessions.py b/warehouse/sessions.py
--- a/warehouse/sessions.py
+++ b/warehouse/sessions.py
@@ -263,6 +263,7 @@ def _process_response(self, request, response):
                 max_age=self.max_age,
                 httponly=True,
                 secure=request.scheme == "https",
+                samesite=b"lax"
             )
 
 


Test patch:
diff --git a/requirements/tests.txt b/requirements/tests.txt
--- a/requirements/tests.txt
+++ b/requirements/tests.txt
@@ -224,9 +224,9 @@ urllib3==1.22 \
 waitress==1.1.0 \
     --hash=sha256:40b0f297a7f3af61fbfbdc67e59090c70dc150a1601c39ecc9f5f1d283fb931b \
     --hash=sha256:d33cd3d62426c0f1b3cd84ee3d65779c7003aae3fc060dee60524d10a57f05a9
-WebOb==1.7.4 \
-    --hash=sha256:63f4220492476c5c716b615baed7bf3d27040b3105014375787160dee0943115 \
-    --hash=sha256:8d10af182fda4b92193113ee1edeb687ab9dc44336b37d6804e413f0240d40d9
+WebOb==1.8.0 \
+    --hash=sha256:ae809c05b667c3457a2937cdb4a7c7f07e90f26c651a340d37fdd1d5cf1fed27 \
+    --hash=sha256:6fca7aa39bd2f6d2ff71f15a22223ff256c91f60b1ab52dac0ab38dc6ea9142f
 WebTest==2.0.29 \
     --hash=sha256:9136514159a2e76a21751bf4ab5d3371e539c8ada8b950fcf68e307d9e584a07 \
     --hash=sha256:dbbccc15ac2465066c95dc3a7de0d30cde3791e886ccbd7e91d5d2a2580c922d
diff --git a/tests/unit/test_sessions.py b/tests/unit/test_sessions.py
--- a/tests/unit/test_sessions.py
+++ b/tests/unit/test_sessions.py
@@ -497,7 +497,7 @@ def test_invalidated_deletes_save_non_secure(self, monkeypatch,
         )
         response = pretend.stub(
             set_cookie=pretend.call_recorder(
-                lambda cookie, data, max_age, httponly, secure: None
+                lambda cookie, data, max_age, httponly, secure, samesite: None
             )
         )
         session_factory._process_response(pyramid_request, response)
@@ -532,6 +532,7 @@ def test_invalidated_deletes_save_non_secure(self, monkeypatch,
                 max_age=12 * 60 * 60,
                 httponly=True,
                 secure=False,
+                samesite=b"lax",
             ),
         ]
 
diff --git a/tests/unit/utils/test_compression.py b/tests/unit/utils/test_compression.py
--- a/tests/unit/utils/test_compression.py
+++ b/tests/unit/utils/test_compression.py
@@ -14,7 +14,7 @@
 import pytest
 
 from pyramid.response import Response
-from webob.acceptparse import Accept, NoAccept
+from webob.acceptparse import AcceptEncodingValidHeader, AcceptEncodingNoHeader
 from webob.response import gzip_app_iter
 
 from warehouse.utils.compression import _compressor as compressor
@@ -54,7 +54,7 @@ def test_bails_if_content_encoding(self):
         ],
     )
     def test_sets_vary(self, vary, expected):
-        request = pretend.stub(accept_encoding=NoAccept())
+        request = pretend.stub(accept_encoding=AcceptEncodingNoHeader())
         response = Response(body=b"foo")
         response.vary = vary
 
@@ -66,7 +66,9 @@ def test_compresses_non_streaming(self):
         decompressed_body = b"foofoofoofoofoofoofoofoofoofoofoofoofoofoo"
         compressed_body = b"".join(list(gzip_app_iter([decompressed_body])))
 
-        request = pretend.stub(accept_encoding=Accept("gzip"))
+        request = pretend.stub(
+            accept_encoding=AcceptEncodingValidHeader("gzip")
+        )
         response = Response(body=decompressed_body)
         response.md5_etag()
 
@@ -83,7 +85,9 @@ def test_compresses_streaming(self):
         decompressed_body = b"foofoofoofoofoofoofoofoofoofoofoofoofoofoo"
         compressed_body = b"".join(list(gzip_app_iter([decompressed_body])))
 
-        request = pretend.stub(accept_encoding=Accept("gzip"))
+        request = pretend.stub(
+            accept_encoding=AcceptEncodingValidHeader("gzip")
+        )
         response = Response(app_iter=iter([decompressed_body]))
 
         compressor(request, response)
@@ -96,7 +100,9 @@ def test_compresses_streaming_with_etag(self):
         decompressed_body = b"foofoofoofoofoofoofoofoofoofoofoofoofoofoo"
         compressed_body = b"".join(list(gzip_app_iter([decompressed_body])))
 
-        request = pretend.stub(accept_encoding=Accept("gzip"))
+        request = pretend.stub(
+            accept_encoding=AcceptEncodingValidHeader("gzip")
+        )
         response = Response(app_iter=iter([decompressed_body]))
         response.etag = "foo"
 
@@ -111,7 +117,9 @@ def test_buffers_small_streaming(self):
         decompressed_body = b"foofoofoofoofoofoofoofoofoofoofoofoofoofoo"
         compressed_body = b"".join(list(gzip_app_iter([decompressed_body])))
 
-        request = pretend.stub(accept_encoding=Accept("gzip"))
+        request = pretend.stub(
+            accept_encoding=AcceptEncodingValidHeader("gzip")
+        )
         response = Response(
             app_iter=iter([decompressed_body]),
             content_length=len(decompressed_body),
@@ -124,7 +132,9 @@ def test_buffers_small_streaming(self):
         assert response.body == compressed_body
 
     def test_doesnt_compress_too_small(self):
-        request = pretend.stub(accept_encoding=Accept("gzip"))
+        request = pretend.stub(
+            accept_encoding=AcceptEncodingValidHeader("gzip")
+        )
         response = Response(body=b"foo")
 
         compressor(request, response)