Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add additional parameters for SecurityPolicy: trusted types and worker src #1064

Merged
merged 3 commits into from
Oct 27, 2024
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
5 changes: 4 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,5 +24,8 @@
"bazel-mesop/**": true
},
"python.analysis.extraPaths": ["./bazel-bin"],
"typescript.tsdk": "node_modules/typescript/lib"
"typescript.tsdk": "node_modules/typescript/lib",
"python.testing.pytestArgs": ["."],
"python.testing.unittestEnabled": false,
"python.testing.pytestEnabled": true
}
3 changes: 3 additions & 0 deletions mesop/examples/testing/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@
from mesop.examples.testing import (
csp_escaping as csp_escaping,
)
from mesop.examples.testing import (
csp_trusted_types as csp_trusted_types,
)
from mesop.examples.testing import (
error_register_page_too_late as error_register_page_too_late,
)
Expand Down
4 changes: 4 additions & 0 deletions mesop/examples/testing/csp_escaping.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@
"http://google.com/allowed_script_srcs/1,1;2?q=a",
"http://google.com/allowed_script_srcs/2,1;2?q=a",
],
allowed_worker_srcs=[
"http://google.com/allowed_worker_srcs/1,1;2?q=a",
"http://google.com/allowed_worker_srcs/2,1;2?q=a",
],
),
)
def page():
Expand Down
12 changes: 12 additions & 0 deletions mesop/examples/testing/csp_trusted_types.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import mesop as me


@me.page(
path="/testing/csp_trusted_types",
title="CSP trusted types",
security_policy=me.SecurityPolicy(
allowed_trusted_types=["ttpolicy1", "ttpolicy2"],
),
)
def page():
me.text("CSP trusted types")
1 change: 0 additions & 1 deletion mesop/features/page.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@ def wrapper() -> None:

# Note: this will be replaced downstream, so do not inline/rename.
default_stylesheets = []

runtime().register_page(
path=path,
page_config=PageConfig(
Expand Down
18 changes: 16 additions & 2 deletions mesop/security/BUILD
Original file line number Diff line number Diff line change
@@ -1,10 +1,24 @@
load("//build_defs:defaults.bzl", "py_library")
load("//build_defs:defaults.bzl", "THIRD_PARTY_PY_PYTEST", "py_library", "py_test")

package(
default_visibility = ["//build_defs:mesop_internal"],
)

py_library(
name = "security",
srcs = glob(["*.py"]),
srcs = glob(
["*.py"],
exclude = [
"*_test.py",
],
),
deps = [
"//mesop/exceptions",
],
)

py_test(
name = "security_policy_test",
srcs = ["security_policy_test.py"],
deps = [":security"] + THIRD_PARTY_PY_PYTEST,
)
14 changes: 13 additions & 1 deletion mesop/security/security_policy.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
from dataclasses import dataclass, field

from mesop.exceptions import MesopDeveloperException


@dataclass(kw_only=True)
class SecurityPolicy:
Expand All @@ -9,7 +11,9 @@ class SecurityPolicy:
Attributes:
allowed_iframe_parents: A list of allowed iframe parents.
allowed_connect_srcs: A list of sites you can connect to, see [MDN](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/connect-src).
allowed_script_srcs: A list of sites you load scripts from, see [MDN](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/script-src).
allowed_script_srcs: A list of sites you can load scripts from, see [MDN](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/script-src).
allowed_worker_srcs. A list of sites you can load workers from, see [MDN](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/worker-src).
allowed_trusted_types: A list of trusted type policy names, see [MDN](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/trusted-types).
dangerously_disable_trusted_types: A flag to disable trusted types.
Highly recommended to not disable trusted types because
it's an important web security feature!
Expand All @@ -18,4 +22,12 @@ class SecurityPolicy:
allowed_iframe_parents: list[str] = field(default_factory=list)
allowed_connect_srcs: list[str] = field(default_factory=list)
allowed_script_srcs: list[str] = field(default_factory=list)
allowed_worker_srcs: list[str] = field(default_factory=list)
allowed_trusted_types: list[str] = field(default_factory=list)
dangerously_disable_trusted_types: bool = False

def __post_init__(self):
if self.dangerously_disable_trusted_types and self.allowed_trusted_types:
raise MesopDeveloperException(
"Cannot disable trusted types and configure allow trusted types on SecurityPolicy at the same time. Set either allowed_trusted_types or dangerously_disable_trusted_types SecurityPolicy parameter."
)
45 changes: 45 additions & 0 deletions mesop/security/security_policy_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import pytest

from mesop.exceptions import MesopDeveloperException
from mesop.security.security_policy import SecurityPolicy


def test_default_security_policy_is_valid():
policy = SecurityPolicy()
assert policy.dangerously_disable_trusted_types is False


def test_disable_trusted_types_and_configuring_trusted_types_raises_exception():
with pytest.raises(MesopDeveloperException) as e:
SecurityPolicy(
allowed_trusted_types=["ttpolicy1", "ttpolicy2"],
dangerously_disable_trusted_types=True,
)
assert (
"Cannot disable trusted types and configure allow trusted types on SecurityPolicy at the same time"
in str(e.value)
)


def test_configuring_trusted_types_without_disabling_trusted_types():
try:
SecurityPolicy(
allowed_trusted_types=["ttpolicy1", "ttpolicy2"],
dangerously_disable_trusted_types=False,
)
except MesopDeveloperException:
pytest.fail("MesopDeveloperException was raised unexpectedly!")


def test_disabling_trusted_types_without_configuring_trusted_types():
try:
SecurityPolicy(
allowed_trusted_types=[],
dangerously_disable_trusted_types=True,
)
except MesopDeveloperException:
pytest.fail("MesopDeveloperException was raised unexpectedly!")


if __name__ == "__main__":
raise SystemExit(pytest.main([__file__]))
11 changes: 11 additions & 0 deletions mesop/server/static_file_serving.py
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,17 @@ def add_security_headers(response: Response):
for url in security_policy.allowed_script_srcs
]
)
if security_policy and security_policy.allowed_worker_srcs:
csp["worker-src"] = "'self' " + " ".join(
[
sanitize_url_for_csp(url)
for url in security_policy.allowed_worker_srcs
]
)
if security_policy and security_policy.allowed_trusted_types:
csp["trusted-types"] += " " + " ".join(
security_policy.allowed_trusted_types
)
if security_policy and security_policy.dangerously_disable_trusted_types:
del csp["trusted-types"]
del csp["require-trusted-types-for"]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,5 @@ trusted-types angular angular#unsafe-bypass lit-html highlight.js
require-trusted-types-for 'script'
report-uri /__csp__
connect-src 'self' http://google.com/allowed_connect_srcs/1%2C1%3B2 http://google.com/allowed_connect_srcs/2%2C1%3B2
worker-src 'self' http://google.com/allowed_worker_srcs/1%2C1%3B2 http://google.com/allowed_worker_srcs/2%2C1%3B2
frame-ancestors 'self' http://google.com/allowed_iframe_parents/1%2C1%3B2 http://google.com/allowed_iframe_parents/2%2C1%3B2
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
default-src 'self'
font-src fonts.gstatic.com data:
frame-src *
img-src 'self' data: https: http:
media-src 'self' data: https:
style-src 'self' 'unsafe-inline' fonts.googleapis.com
script-src 'self' 'nonce-{{NONCE}}'
trusted-types angular angular#unsafe-bypass lit-html highlight.js ttpolicy1 ttpolicy2
require-trusted-types-for 'script'
report-uri /__csp__
frame-ancestors 'self'
6 changes: 6 additions & 0 deletions mesop/tests/e2e/web_security_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,12 @@ testInProdOnly('csp escaping', async ({page}) => {
expect(cleanCsp(csp)).toMatchSnapshot('csp_escaping.txt');
});

testInProdOnly('csp trusted types', async ({page}) => {
const response = await page.goto('/testing/csp_trusted_types');
const csp = response?.headers()['content-security-policy']!;
expect(cleanCsp(csp)).toMatchSnapshot('csp_trusted_types.txt');
});

function cleanCsp(csp: string): string {
return (
csp
Expand Down
Loading