Skip to content

Commit 4f3014b

Browse files
committed
fix(security): sanitize redirect URLs after altcha validation
Signed-off-by: Keshav Priyadarshi <git@keshav.space>
1 parent ededf43 commit 4f3014b

3 files changed

Lines changed: 30 additions & 16 deletions

File tree

vulnerabilities/middleware/altcha_protection.py

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -15,19 +15,20 @@
1515

1616
SESSION_TIMEOUT = 900 # 15 minutes
1717

18+
ALTCHA_PROTECTED_PREFIXES = (
19+
"/packages/",
20+
"/vulnerabilities/",
21+
"/advisories/",
22+
"/affected-by-advisories/v2/",
23+
"/fixing-advisories/v2/",
24+
"/pipelines/",
25+
)
26+
1827

1928
class AltchaProtectionMiddleware(MiddlewareMixin):
20-
PROTECTED_PREFIXES = (
21-
"/packages/",
22-
"/vulnerabilities/",
23-
"/advisories/",
24-
"/affected-by-advisories/v2/",
25-
"/fixing-advisories/v2/",
26-
"/pipelines/",
27-
)
2829

2930
def __call__(self, request):
30-
protected = any(request.path.startswith(prefix) for prefix in self.PROTECTED_PREFIXES)
31+
protected = any(request.path.startswith(prefix) for prefix in ALTCHA_PROTECTED_PREFIXES)
3132

3233
if not protected:
3334
return self.get_response(request)

vulnerabilities/utils.py

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,11 @@
2020
from functools import total_ordering
2121
from http import HTTPStatus
2222
from typing import List
23-
from typing import NamedTuple
2423
from typing import Optional
25-
from typing import Set
2624
from typing import Tuple
2725
from typing import Union
2826
from unittest.mock import MagicMock
27+
from urllib.parse import unquote
2928
from urllib.parse import urljoin
3029

3130
import dateparser
@@ -36,6 +35,8 @@
3635
from cwe2.database import Database
3736
from cwe2.database import InvalidCWEError
3837
from django.db.models import Prefetch
38+
from django.shortcuts import redirect
39+
from django.utils.http import url_has_allowed_host_and_scheme
3940
from packageurl import PackageURL
4041
from packageurl.contrib.django.utils import without_empty_values
4142
from univers.version_range import RANGE_CLASS_BY_SCHEMES
@@ -44,6 +45,7 @@
4445
from univers.version_range import VersionRange
4546

4647
from aboutcode.hashid import build_vcid
48+
from vulnerabilities.middleware.altcha_protection import ALTCHA_PROTECTED_PREFIXES
4749

4850
logger = logging.getLogger(__name__)
4951

@@ -1114,3 +1116,16 @@ def build_alias_to_advisory_map(aliases_strs):
11141116
):
11151117
alias_to_advisories[advisory.advisory_id].add(advisory)
11161118
return alias_to_advisories
1119+
1120+
1121+
def safe_altcha_redirect(next_url: str) -> redirect:
1122+
"""Safely redirect to ALTCHA protected URL, block external URLs."""
1123+
is_safe = url_has_allowed_host_and_scheme(url=next_url, allowed_hosts=None, require_https=False)
1124+
1125+
decoded_url = unquote(next_url) if next_url else ""
1126+
is_protected = any(decoded_url.startswith(prefix) for prefix in ALTCHA_PROTECTED_PREFIXES)
1127+
1128+
if is_safe and is_protected:
1129+
return redirect(next_url)
1130+
1131+
return redirect("/")

vulnerabilities/views.py

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616
from cvss.exceptions import CVSS2MalformedError
1717
from cvss.exceptions import CVSS3MalformedError
1818
from cvss.exceptions import CVSS4MalformedError
19-
from django import forms
2019
from django.contrib import messages
2120
from django.contrib.auth.views import LoginView
2221
from django.core.cache import cache
@@ -29,7 +28,6 @@
2928
from django.http import HttpResponse
3029
from django.http.response import Http404
3130
from django.shortcuts import get_object_or_404
32-
from django.shortcuts import redirect
3331
from django.shortcuts import render
3432
from django.urls import reverse_lazy
3533
from django.views import View
@@ -38,7 +36,6 @@
3836
from django.views.generic.edit import FormMixin
3937
from django.views.generic.edit import FormView
4038
from django.views.generic.list import ListView
41-
from django_altcha import AltchaField
4239

4340
from vulnerabilities import models
4441
from vulnerabilities.forms import AdminLoginForm
@@ -67,6 +64,7 @@
6764
from vulnerabilities.throttling import AnonUserUIThrottle
6865
from vulnerabilities.utils import TYPES_WITH_MULTIPLE_IMPORTERS
6966
from vulnerabilities.utils import get_advisories_from_groups
67+
from vulnerabilities.utils import safe_altcha_redirect
7068
from vulnerablecode import __version__ as VULNERABLECODE_VERSION
7169
from vulnerablecode.settings import env
7270

@@ -1173,12 +1171,12 @@ def dispatch(self, request, *args, **kwargs):
11731171
if verified_at:
11741172
if time.time() - verified_at < ALTCHA_SESSION_TIMEOUT:
11751173
next_url = request.GET.get("next", "/")
1176-
return redirect(next_url)
1174+
return safe_altcha_redirect(next_url)
11771175

11781176
return super().dispatch(request, *args, **kwargs)
11791177

11801178
def form_valid(self, form):
11811179
self.request.session["altcha_verified_at"] = time.time()
11821180

11831181
next_url = self.request.GET.get("next", "/")
1184-
return redirect(next_url)
1182+
return safe_altcha_redirect(next_url)

0 commit comments

Comments
 (0)