Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,9 @@ Unreleased
and in some tests. MD5 is not available in some environments, such
as FIPS 140. This may invalidate some caches since the ETag will be
different. :issue:`1897`

- Add ``Cross-Origin-Opener-Policy`` and
``Cross-Origin-Embedder-Policy`` response header properties.
:pr:`2008`

Version 1.0.2
-------------
Expand Down
16 changes: 16 additions & 0 deletions src/werkzeug/http.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from datetime import datetime
from datetime import timedelta
from email.utils import parsedate_tz
from enum import Enum
from hashlib import sha1
from time import gmtime
from time import struct_time
Expand Down Expand Up @@ -172,6 +173,21 @@
}


class COEP(Enum):
"""Cross Origin Embedder Policies"""

UNSAFE_NONE = "unsafe-none"
REQUIRE_CORP = "require-corp"


class COOP(Enum):
"""Cross Origin Opener Policies"""

UNSAFE_NONE = "unsafe-none"
SAME_ORIGIN_ALLOW_POPUPS = "same-origin-allow-popups"
SAME_ORIGIN = "same-origin"


def quote_header_value(
value: t.Union[str, int], extra_chars: str = "", allow_token: bool = True
) -> str:
Expand Down
21 changes: 21 additions & 0 deletions src/werkzeug/wrappers/response.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@
from werkzeug.datastructures import ContentRange
from werkzeug.datastructures import ResponseCacheControl
from werkzeug.datastructures import WWWAuthenticate
from werkzeug.http import COEP
from werkzeug.http import COOP
from werkzeug.http import dump_age
from werkzeug.http import dump_csp_header
from werkzeug.http import dump_header
Expand Down Expand Up @@ -1376,6 +1378,25 @@ def access_control_allow_credentials(self, value: t.Optional[bool]) -> None:
doc="The maximum age in seconds the access control settings can be cached for.",
)

cross_origin_opener_policy = header_property[COOP](
"Cross-Origin-Opener-Policy",
load_func=lambda value: COOP(value),
dump_func=lambda value: value.value,
default=COOP.UNSAFE_NONE,
doc="""Allows control over sharing of browsing context group with cross-origin
documents. Values must be a member of the :class:`werkzeug.http.COOP` enum.""",
)

cross_origin_embedder_policy = header_property[COEP](
"Cross-Origin-Embedder-Policy",
load_func=lambda value: COEP(value),
dump_func=lambda value: value.value,
default=COEP.UNSAFE_NONE,
doc="""Prevents a document from loading any cross-origin resources that do not
explicitly grant the document permission. Values must be a member of the
:class:`werkzeug.http.COEP` enum.""",
)


class ResponseStream:
"""A file descriptor like object used by the :class:`ResponseStreamMixin` to
Expand Down
16 changes: 16 additions & 0 deletions tests/test_wrappers.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@
from werkzeug.exceptions import BadRequest
from werkzeug.exceptions import RequestedRangeNotSatisfiable
from werkzeug.exceptions import SecurityError
from werkzeug.http import COEP
from werkzeug.http import COOP
from werkzeug.http import generate_etag
from werkzeug.test import Client
from werkzeug.test import create_environ
Expand Down Expand Up @@ -1576,3 +1578,17 @@ def test_check_base_deprecated():
def test_response_freeze_no_etag_deprecated():
with pytest.raises(DeprecationWarning, match="no_etag"):
Response("Hello, World!").freeze(no_etag=True)


def test_response_coop():
response = wrappers.Response("Hello World")
assert response.cross_origin_opener_policy is COOP.UNSAFE_NONE
response.cross_origin_opener_policy = COOP.SAME_ORIGIN
assert response.headers["Cross-Origin-Opener-Policy"] == "same-origin"


def test_response_coep():
response = wrappers.Response("Hello World")
assert response.cross_origin_embedder_policy is COEP.UNSAFE_NONE
response.cross_origin_embedder_policy = COEP.REQUIRE_CORP
assert response.headers["Cross-Origin-Embedder-Policy"] == "require-corp"