diff --git a/bothub/authentication/authorization.py b/bothub/authentication/authorization.py index b796a156..c2e4562c 100644 --- a/bothub/authentication/authorization.py +++ b/bothub/authentication/authorization.py @@ -1,4 +1,5 @@ import logging +import re from django.utils.translation import ugettext_lazy as _ from bothub.utils import check_module_permission @@ -95,6 +96,7 @@ def create_user(self, claims): # Override existing create_user method in OIDCAuthenticationBackend email = claims.get("email") username = self.get_username(claims)[:16] + username = re.sub("[^A-Za-z0-9]+", "", username) user = self.UserModel.objects.create_user(email, username) user.name = claims.get("name", "") diff --git a/bothub/settings.py b/bothub/settings.py index b422d667..edb5e3e1 100644 --- a/bothub/settings.py +++ b/bothub/settings.py @@ -94,6 +94,15 @@ ELASTICSEARCH_LOGS_DELETE_AGE=(str, "90d"), ELASTICSEARCH_LOGS_ROLLOVER_AGE=(str, "1d"), ELASTICSEARCH_TIMESTAMP_PIPELINE_FIELD=(str, "created_at"), + CSP_DEFAULT_SRC=(tuple, "CSP_DEFAULT_SRC"), + CSP_FRAME_ANCESTORS=(tuple, "CSP_FRAME_ANCESTORS"), + CSP_FONT_SRC=(tuple, "CSP_FONT_SRC"), + CSP_STYLE_SRC=(tuple, "CSP_STYLE_SRC"), + CSP_STYLE_SRC_ELEM=(tuple, "CSP_STYLE_SRC_ELEM"), + CSP_SCRIPT_SRC=(tuple, "CSP_SCRIPT_SRC"), + CSP_SCRIPT_SRC_ELEM=(tuple, "CSP_SCRIPT_SRC_ELEM"), + CSP_FRAME_SRC=(tuple, "CSP_FRAME_SRC"), + CSP_CONNECT_SRC=(tuple, "CSP_CONNECT_SRC"), ) # Build paths inside the project like this: os.path.join(BASE_DIR, ...) @@ -146,12 +155,12 @@ "whitenoise.middleware.WhiteNoiseMiddleware", "django.contrib.sessions.middleware.SessionMiddleware", "django.middleware.locale.LocaleMiddleware", + "csp.middleware.CSPMiddleware", "corsheaders.middleware.CorsMiddleware", "django.middleware.common.CommonMiddleware", "django.middleware.csrf.CsrfViewMiddleware", "django.contrib.auth.middleware.AuthenticationMiddleware", "django.contrib.messages.middleware.MessageMiddleware", - "django.middleware.clickjacking.XFrameOptionsMiddleware", "bothub.api.v2.middleware.UserLanguageMiddleware", ] @@ -301,6 +310,23 @@ CSRF_COOKIE_SECURE = env.bool("CSRF_COOKIE_SECURE") +# CSP headers + +CSP_DEFAULT_SRC = env.tuple("CSP_DEFAULT_SRC", default=("'self'",)) +CSP_FRAME_ANCESTORS = env.tuple("CSP_FRAME_ANCESTORS", default=("'self'", "*.weni.ai")) +CSP_FONT_SRC = env.tuple("CSP_FONT_SRC", default=CSP_DEFAULT_SRC) +CSP_STYLE_SRC = env.tuple( + "CSP_STYLE_SRC", default=("'self'", "'unsafe-inline'", "'unsafe-eval'") +) +CSP_STYLE_SRC_ELEM = env.tuple("CSP_STYLE_SRC_ELEM", default=CSP_STYLE_SRC) +CSP_SCRIPT_SRC = env.tuple( + "CSP_SCRIPT_SRC", default=("'self'", "'unsafe-inline'", "'unsafe-eval'") +) +CSP_SCRIPT_SRC_ELEM = env.tuple("CSP_SCRIPT_SRC_ELEM", default=CSP_SCRIPT_SRC) +CSP_FRAME_SRC = env.tuple("CSP_FRAME_SRC", default=CSP_DEFAULT_SRC) +CSP_CONNECT_SRC = env.tuple("CSP_CONNECT_SRC", default=CSP_DEFAULT_SRC) + + # Logging LOGGING = DEFAULT_LOGGING diff --git a/poetry.lock b/poetry.lock index d67a0a89..6199b760 100644 --- a/poetry.lock +++ b/poetry.lock @@ -445,6 +445,21 @@ python-versions = ">=3.6" [package.dependencies] Django = ">=2.2" +[[package]] +name = "django-csp" +version = "3.7" +description = "Django Content Security Policy support." +category = "main" +optional = false +python-versions = "*" + +[package.dependencies] +Django = ">=1.8" + +[package.extras] +jinja2 = ["jinja2 (>=2.9.6)"] +tests = ["pytest (<4.0)", "pytest-django", "pytest-flakes (==1.0.1)", "pytest-pep8 (==1.0.6)", "pep8 (==1.4.6)", "mock (==1.0.1)", "six (==1.12.0)", "jinja2 (>=2.9.6)"] + [[package]] name = "django-elasticsearch-dsl" version = "7.2.2" @@ -1711,7 +1726,7 @@ testing = ["pytest (>=4.6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytes [metadata] lock-version = "1.1" python-versions = "~=3.6.15" -content-hash = "dd6bfc5c1373deba58e4e85a9cb3114b91f7d5d5a33db1ac48afee53bbe395f8" +content-hash = "31ccdac8c205f34af79715c1d090792b5496b461d94085bc7715f635cfbf4dfa" [metadata.files] amqp = [ @@ -1970,6 +1985,10 @@ django-cors-headers = [ {file = "django-cors-headers-3.7.0.tar.gz", hash = "sha256:96069c4aaacace786a34ee7894ff680780ec2644e4268b31181044410fecd12e"}, {file = "django_cors_headers-3.7.0-py3-none-any.whl", hash = "sha256:1ac2b1213de75a251e2ba04448da15f99bcfcbe164288ae6b5ff929dc49b372f"}, ] +django-csp = [ + {file = "django_csp-3.7-py2.py3-none-any.whl", hash = "sha256:01443a07723f9a479d498bd7bb63571aaa771e690f64bde515db6cdb76e8041a"}, + {file = "django_csp-3.7.tar.gz", hash = "sha256:01eda02ad3f10261c74131cdc0b5a6a62b7c7ad4fd017fbefb7a14776e0a9727"}, +] django-elasticsearch-dsl = [ {file = "django-elasticsearch-dsl-7.2.2.tar.gz", hash = "sha256:811d3909b3387fd55c19d9bbcf0e9a9b234f085df3f8422d59e7519a5f733e0e"}, {file = "django_elasticsearch_dsl-7.2.2-py2.py3-none-any.whl", hash = "sha256:3c58a254a6318b169eb904d41d802924b99ea8e53ddc2c596ebba90506cf47fa"}, diff --git a/pyproject.toml b/pyproject.toml index c6d48f5e..0abed417 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -52,6 +52,7 @@ psycopg2-binary = "~=2.9.1" weni-protobuffers = "~=1.2.1" black = "21.7b0" Pillow = "~=8.4.0" +django-csp = "^3.7" [tool.poetry.dev-dependencies] flake8 = "~=4.0.0"